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.
This commit is contained in:
Oliver Gierke
2015-09-02 10:10:50 +02:00
parent 191f4b69e6
commit b4bbc1d79c
4 changed files with 86 additions and 8 deletions

View File

@@ -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;

View File

@@ -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<AbstractJsonSchemaProperty<?>> 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") };
}
}
}

View File

@@ -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);
}

View File

@@ -1 +1,14 @@
rest.description.profile=Profile description
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