DATAREST-654 - Added suport for internationalization of enum values.

RepositoryRestConfiguration now exposes an option to enable enum value serialization and a nested configuration object to tweak the details.

If enabled, a Jackson serializer and deserializer is registered trying to resolve the enum values from the Spring Data REST resource bundle using the fully-qualified enum value name as key. If no explicitly configured value is configured a default translation is triggered that capitalizes the lowercased value name replacing the underscores with spaces (e.g. PAYMENT_EXPECTED -> Payment expected). This can be opted out of, of course.

On the parsing side the deserializer will also consult the resourcebundle and default translation but also accepting the enum name as is (also opt-outable).

Deprecated non-bean-style accessors for projection and metadata configuration on RepositoryRestConfiguration to make these options tweakable via Spring Boot application properties by default.
This commit is contained in:
Oliver Gierke
2015-08-20 16:16:07 +02:00
parent 39eb338025
commit 06b6b266db
21 changed files with 799 additions and 71 deletions

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.rest.core.config;
/**
* Configuration options for enum value translation.
*
* @author Oliver Gierke
* @since 2.4
* @soundtrack Wallis Bird - Measuring Cities (Yeah! Wallis Bird live 2007-2014)
*/
public interface EnumTranslationConfiguration {
/**
* Configures whether the default translation of enum names shall be applied. Defaults to {@literal true}. This means
* the configuration will turn enum names into human friendly {@link String}s and also parse them if - only if - no
* explicit translation is available.
*
* @param enableDefaultTranslation whether to enable the default translation of enum names.
*/
void setEnableDefaultTranslation(boolean enableDefaultTranslation);
/**
* Configures whether to always accept the raw enum name when parsing. This is useful if clients were used to send the
* Java enum names shall not be broken even if on the serialization side enum translation is activated.
*
* @param parseEnumNameAsFallback whether to parse the raw enum value as fallback, even if an explicit translation is
* available.
*/
void setParseEnumNameAsFallback(boolean parseEnumNameAsFallback);
}

View File

@@ -55,12 +55,8 @@ public class RepositoryRestConfiguration {
private final ProjectionDefinitionConfiguration projectionConfiguration;
private final MetadataConfiguration metadataConfiguration;
/**
* Creates a new default {@link RepositoryRestConfiguration}.
*/
public RepositoryRestConfiguration() {
this(new ProjectionDefinitionConfiguration(), new MetadataConfiguration());
}
private final EnumTranslationConfiguration enumSerializationConfiguration;
private boolean enableEnumTranslation = false;
/**
* Creates a new {@link RepositoryRestConfiguration} with the given {@link ProjectionDefinitionConfiguration}.
@@ -69,13 +65,15 @@ public class RepositoryRestConfiguration {
* @param metadataConfiguration must not be {@literal null}.
*/
public RepositoryRestConfiguration(ProjectionDefinitionConfiguration projectionConfiguration,
MetadataConfiguration metadataConfiguration) {
MetadataConfiguration metadataConfiguration, EnumTranslationConfiguration enumTranslationConfiguration) {
Assert.notNull(projectionConfiguration, "ProjectionDefinitionConfiguration must not be null!");
Assert.notNull(metadataConfiguration, "MetadataConfiguration must not be null!");
Assert.notNull(enumTranslationConfiguration, " must not be null!");
this.projectionConfiguration = projectionConfiguration;
this.metadataConfiguration = metadataConfiguration;
this.enumSerializationConfiguration = enumTranslationConfiguration;
}
/**
@@ -449,8 +447,19 @@ public class RepositoryRestConfiguration {
* Returns the {@link ProjectionDefinitionConfiguration} to register addition projections.
*
* @return
* @deprecated since 2.4, use {@link #getProjectionConfiguration()} instead.
*/
@Deprecated
public ProjectionDefinitionConfiguration projectionConfiguration() {
return getProjectionConfiguration();
}
/**
* Returns the {@link ProjectionDefinitionConfiguration} to register addition projections.
*
* @return
*/
public ProjectionDefinitionConfiguration getProjectionConfiguration() {
return projectionConfiguration;
}
@@ -458,8 +467,49 @@ public class RepositoryRestConfiguration {
* Returns the {@link MetadataConfiguration} to customize metadata exposure.
*
* @return
* @deprecated since 2.4, use {@link #getMetadataConfiguration()} instead.
*/
@Deprecated
public MetadataConfiguration metadataConfiguration() {
return metadataConfiguration;
}
/**
* Returns the {@link MetadataConfiguration} to customize metadata exposure.
*
* @return
*/
public MetadataConfiguration getMetadataConfiguration() {
return metadataConfiguration;
}
/**
* Configures whether to enable enum value translation via the Spring Data REST default resource bundle. Defaults to
* {@literal false} for backwards compatibility reasons. Will use the fully qualified enum name as key. For further
* details see {@link EnumTranslator}.
*
* @param enableEnumTranslation
* @see #getEnumSerializationConfiguration()
*/
public void setEnableEnumTranslation(boolean enableEnumTranslation) {
this.enableEnumTranslation = enableEnumTranslation;
}
/**
* Returns whether enum value translation is enabled.
*
* @return
*/
public boolean isEnableEnumTranslation() {
return this.enableEnumTranslation;
}
/**
* Returns the {@link EnumTranslator} for
*
* @return
*/
public EnumTranslationConfiguration getEnumSerializationConfiguration() {
return this.enumSerializationConfiguration;
}
}

View File

@@ -17,8 +17,13 @@ package org.springframework.data.rest.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.rest.core.config.EnumTranslationConfiguration;
import org.springframework.data.rest.core.config.MetadataConfiguration;
import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.http.MediaType;
@@ -30,14 +35,21 @@ import org.springframework.http.MediaType;
*/
public class RepositoryRestConfigurationUnitTests {
RepositoryRestConfiguration configuration;
@Before
public void setUp() {
this.configuration = new RepositoryRestConfiguration(new ProjectionDefinitionConfiguration(),
new MetadataConfiguration(), mock(EnumTranslationConfiguration.class));
}
/**
* @see DATAREST-34
*/
@Test
public void returnsBodiesIfAcceptHeaderPresentByDefault() {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration();
assertThat(configuration.returnBodyOnCreate(MediaType.APPLICATION_JSON_VALUE), is(true));
assertThat(configuration.returnBodyOnUpdate(MediaType.APPLICATION_JSON_VALUE), is(true));
}
@@ -48,8 +60,6 @@ public class RepositoryRestConfigurationUnitTests {
@Test
public void doesNotReturnBodiesIfNoAcceptHeaderPresentByDefault() {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration();
assertThat(configuration.returnBodyOnCreate(null), is(false));
assertThat(configuration.returnBodyOnUpdate(null), is(false));
}
@@ -60,8 +70,6 @@ public class RepositoryRestConfigurationUnitTests {
@Test
public void doesNotReturnBodiesIfEmptyAcceptHeaderPresentByDefault() {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration();
assertThat(configuration.returnBodyOnCreate(""), is(false));
assertThat(configuration.returnBodyOnUpdate(""), is(false));
}
@@ -72,7 +80,6 @@ public class RepositoryRestConfigurationUnitTests {
@Test
public void doesNotReturnBodyForUpdateIfExplicitlyDeactivated() {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration();
configuration.setReturnBodyOnUpdate(false);
assertThat(configuration.returnBodyOnUpdate(null), is(false));
@@ -86,7 +93,6 @@ public class RepositoryRestConfigurationUnitTests {
@Test
public void doesNotReturnBodyForCreateIfExplicitlyDeactivated() {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration();
configuration.setReturnBodyOnCreate(false);
assertThat(configuration.returnBodyOnCreate(null), is(false));
@@ -100,7 +106,6 @@ public class RepositoryRestConfigurationUnitTests {
@Test
public void returnsBodyForUpdateIfExplicitlyActivated() {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration();
configuration.setReturnBodyOnUpdate(true);
assertThat(configuration.returnBodyOnUpdate(null), is(true));
@@ -114,7 +119,6 @@ public class RepositoryRestConfigurationUnitTests {
@Test
public void returnsBodyForCreateIfExplicitlyActivated() {
RepositoryRestConfiguration configuration = new RepositoryRestConfiguration();
configuration.setReturnBodyOnCreate(true);
assertThat(configuration.returnBodyOnCreate(null), is(true));

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.rest.core;
import static org.mockito.Mockito.*;
import java.util.Collections;
import java.util.List;
@@ -26,6 +28,9 @@ import org.springframework.context.annotation.Import;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.config.EnumTranslationConfiguration;
import org.springframework.data.rest.core.config.MetadataConfiguration;
import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.domain.jpa.ConfiguredPersonRepository;
import org.springframework.data.rest.core.domain.jpa.JpaRepositoryConfig;
@@ -52,7 +57,8 @@ public class RepositoryTestsConfig {
@SuppressWarnings("deprecation")
@Bean
public RepositoryRestConfiguration config() {
RepositoryRestConfiguration config = new RepositoryRestConfiguration();
RepositoryRestConfiguration config = new RepositoryRestConfiguration(new ProjectionDefinitionConfiguration(),
new MetadataConfiguration(), mock(EnumTranslationConfiguration.class));
config.setResourceMappingForDomainType(Person.class).setRel("person");