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