From b4bbc1d79c3e3cc94ec792d2b43b3dbd625db88e Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 2 Sep 2015 10:10:50 +0200 Subject: [PATCH] DATAREST-665 - Switched to a more consistent scheme for I18N of JsonSchema. We now use the following keys in the resource bundle to determine values for the title attribute of JSON schema property (in descending order of preference): $fullyQualifiedClassName.$property._title $simpleClassName.$property._title $property._title Also tweaked JsonProperty.format to make sure it always serializes as its toString() value. --- .../data/rest/webmvc/json/JsonSchema.java | 4 +- ...PersistentEntityToJsonSchemaConverter.java | 69 +++++++++++++++++-- ...tEntityToJsonSchemaConverterUnitTests.java | 6 ++ .../test/resources/rest-messages.properties | 15 +++- 4 files changed, 86 insertions(+), 8 deletions(-) diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java index 606a97070..6bfd0d423 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/JsonSchema.java @@ -38,6 +38,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; /** * Model class to render JSON schema documents. @@ -326,7 +328,7 @@ public class JsonSchema { public String description; public String type; - public JsonSchemaFormat format; + public @JsonSerialize(using = ToStringSerializer.class) JsonSchemaFormat format; public String pattern; public Boolean uniqueItems; public @JsonProperty("$ref") String reference; diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java index b3ab57c5e..e8083e57a 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverter.java @@ -25,6 +25,7 @@ import java.util.Locale; import java.util.Set; import java.util.regex.Pattern; +import org.springframework.context.MessageSource; import org.springframework.context.MessageSourceResolvable; import org.springframework.context.NoSuchMessageException; import org.springframework.context.support.DefaultMessageSourceResolvable; @@ -40,7 +41,6 @@ import org.springframework.data.rest.core.mapping.ResourceDescription; import org.springframework.data.rest.core.mapping.ResourceMapping; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.core.mapping.ResourceMetadata; -import org.springframework.data.rest.core.mapping.SimpleResourceDescription; import org.springframework.data.rest.webmvc.json.JsonSchema.AbstractJsonSchemaProperty; import org.springframework.data.rest.webmvc.json.JsonSchema.Definitions; import org.springframework.data.rest.webmvc.json.JsonSchema.EnumProperty; @@ -148,8 +148,7 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric List> propertiesFor = getPropertiesFor(persistentEntity.getType(), metadata, descriptors); - String title = resolveMessageWithDefault(new DefaultMessageSourceResolvable( - SimpleResourceDescription.DEFAULT_KEY_PREFIX.concat(".").concat(persistentEntity.getType().getSimpleName()))); + String title = resolveMessageWithDefault(new ResolvableType(persistentEntity.getType())); return new JsonSchema(title, resolveMessage(metadata.getItemResourceDescription()), propertiesFor, descriptors); } @@ -261,13 +260,11 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric return getPropertiesFor(property.getActualType(), mappings.getMetadataFor(property.getActualType()), descriptors); } - @SuppressWarnings("unchecked") private JsonSchemaProperty getSchemaProperty(BeanPropertyDefinition definition, TypeInformation type, ResourceDescription description) { String name = definition.getName(); - String title = resolveMessageWithDefault( - new DefaultMessageSourceResolvable(description.getMessage().concat("._title"))); + String title = resolveMessageWithDefault(new ResolvableProperty(definition)); String resolvedDescription = resolveMessage(description); boolean required = definition.isRequired(); Class rawType = type.getType(); @@ -417,4 +414,64 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric .collectionToDelimitedString(Arrays.asList(SPLIT_CAMEL_CASE.split(tail)), " ").toLowerCase(Locale.US)); } } + + /** + * A {@link BeanPropertyDefinition} that can be resolved via a {@link MessageSource}. + * + * @author Oliver Gierke + * @since 2.4.1 + */ + private static class ResolvableProperty extends DefaultMessageSourceResolvable { + + private static final long serialVersionUID = -5603381674553244480L; + + /** + * Creates a new {@link ResolvableProperty} for the given {@link BeanPropertyDefinition}. + * + * @param property must not be {@literal null}. + */ + public ResolvableProperty(BeanPropertyDefinition property) { + super(getCodes(property)); + } + + private static String[] getCodes(BeanPropertyDefinition property) { + + Assert.notNull(property, "BeanPropertyDefinition must not be null!"); + + Class owner = property.getPrimaryMember().getDeclaringClass(); + + String propertyTitle = property.getInternalName().concat("._title"); + String localName = owner.getSimpleName().concat(".").concat(propertyTitle); + String fullName = owner.getName().concat(".").concat(propertyTitle); + + return new String[] { fullName, localName, propertyTitle }; + } + } + + /** + * A type whose title can be resolved through a {@link MessageSource}. + * + * @author Oliver Gierke + * @since 2.4.1 + */ + private static class ResolvableType extends DefaultMessageSourceResolvable { + + private static final long serialVersionUID = -7199875272753949857L; + + /** + * Creates a new {@link ResolvableType} for the given type. + * + * @param type must not be {@literal null}. + */ + public ResolvableType(Class type) { + super(getTitleCodes(type)); + } + + private static String[] getTitleCodes(Class type) { + + Assert.notNull(type, "Type must not be null!"); + + return new String[] { type.getName().concat("._title"), type.getSimpleName().concat("._title") }; + } + } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverterUnitTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverterUnitTests.java index 77e27f51f..57177e23f 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverterUnitTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/PersistentEntityToJsonSchemaConverterUnitTests.java @@ -146,6 +146,12 @@ public class PersistentEntityToJsonSchemaConverterUnitTests { constraints.add(new Constraint("$.properties.shippingAddresses.title", is("Shipping addresses"), "Defaults titles correctly (split at camel case)")); + // DATAREST-665 + constraints.add(new Constraint("$.properties.address.title", is("Adresse"), "I18n from simple property")); + constraints.add(new Constraint("$.properties.gender.title", is("Geschlecht"), "I18n from property on local type")); + constraints.add( + new Constraint("$.properties.firstname.title", is("Vorname"), "I18n from property on fully-qualified type")); + assertConstraints(User.class, constraints); } diff --git a/spring-data-rest-webmvc/src/test/resources/rest-messages.properties b/spring-data-rest-webmvc/src/test/resources/rest-messages.properties index c388374b3..8b5d0d9c9 100644 --- a/spring-data-rest-webmvc/src/test/resources/rest-messages.properties +++ b/spring-data-rest-webmvc/src/test/resources/rest-messages.properties @@ -1 +1,14 @@ -rest.description.profile=Profile description \ No newline at end of file +rest.description.profile=Profile description + +# User +# Local property +address._title=Adresse + +# Property on simple type +User.gender._title=Geschlecht + +# Property on fully-qualified property +org.springframework.data.rest.webmvc.mongodb.User.firstname._title=Vorname + +# Local property that should be trumped by the more precise definition above +firstname._title=emanroV \ No newline at end of file