From f05a6baf11acfab08a2fc8a04cf172e6809641c4 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 7 May 2015 14:23:11 +0200 Subject: [PATCH] DATAREST-531 - JSON Schema now exposes read-only fields. Properties considered read-only are now explicitly marked as such in the JSON Schema output we render. Related tickets: DATAREST-530. --- .../data/rest/webmvc/json/JsonSchema.java | 31 ++++++++++++++----- ...PersistentEntityToJsonSchemaConverter.java | 15 ++++++--- ...tEntityToJsonSchemaConverterUnitTests.java | 3 ++ .../data/rest/webmvc/mongodb/User.java | 7 +++-- 4 files changed, 40 insertions(+), 16 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 86dc7854c..a47df0fc7 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 @@ -65,7 +65,8 @@ public class JsonSchema { * @param properties must not be {@literal null}. * @param descriptors must not be {@literal null}. */ - public JsonSchema(String title, String description, Collection properties, Descriptors descriptors) { + public JsonSchema(String title, String description, Collection> properties, + Descriptors descriptors) { Assert.hasText(title, "Title must not be null or empty!"); Assert.notNull(properties, "JsonSchemaProperties must not be null!"); @@ -174,7 +175,7 @@ public class JsonSchema { * @param type must not be {@literal null}. * @param properties must not be {@literal null}. */ - public Item(TypeInformation type, Collection properties) { + public Item(TypeInformation type, Collection> properties) { this.type = toJsonSchemaType(type); this.properties = new PropertiesContainer(properties); @@ -199,7 +200,7 @@ public class JsonSchema { @JsonInclude(Include.NON_EMPTY) static class PropertiesContainer { - public final Map properties; + public final Map> properties; public final Collection requiredProperties; /** @@ -207,14 +208,14 @@ public class JsonSchema { * * @param properties must not be {@literal null}. */ - public PropertiesContainer(Collection properties) { + public PropertiesContainer(Collection> properties) { Assert.notNull(properties, "JsonSchemaPropertys must not be null!"); - this.properties = new HashMap(); + this.properties = new HashMap>(); this.requiredProperties = new ArrayList(); - for (JsonSchemaProperty property : properties) { + for (JsonSchemaProperty property : properties) { this.properties.put(property.getName(), property); if (property.isRequired()) { @@ -272,14 +273,18 @@ public class JsonSchema { * @since 2.3 */ @JsonInclude(Include.NON_EMPTY) - abstract static class JsonSchemaProperty { + abstract static class JsonSchemaProperty> { private final String name; private final boolean required; + private boolean readOnly; + protected JsonSchemaProperty(String name, boolean required) { + this.name = name; this.required = required; + this.readOnly = false; } @JsonIgnore @@ -290,6 +295,16 @@ public class JsonSchema { private boolean isRequired() { return required; } + + public boolean isReadOnly() { + return readOnly; + } + + @SuppressWarnings("unchecked") + protected T withReadOnly() { + this.readOnly = true; + return (T) this; + } } /** @@ -298,7 +313,7 @@ public class JsonSchema { * @author Oliver Gierke * @since 2.3 */ - static class Property extends JsonSchemaProperty { + static class Property extends JsonSchemaProperty { private static final TypeInformation STRING_TYPE_INFORMATION = ClassTypeInformation.from(String.class); 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 bf7c49a6c..f53303bab 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 @@ -140,13 +140,13 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric final ResourceMetadata metadata = mappings.getMappingFor(persistentEntity.getType()); Descriptors descriptors = new Descriptors(); - List propertiesFor = getPropertiesFor(persistentEntity.getType(), metadata, descriptors); + List> propertiesFor = getPropertiesFor(persistentEntity.getType(), metadata, descriptors); return new JsonSchema(persistentEntity.getName(), resolveMessage(metadata.getItemResourceDescription()), propertiesFor, descriptors); } - private List getPropertiesFor(Class type, final ResourceMetadata metadata, + private List> getPropertiesFor(Class type, final ResourceMetadata metadata, final Descriptors descriptors) { final PersistentEntity entity = entities.getPersistentEntity(type); @@ -154,10 +154,10 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric final AssociationLinks associationLinks = new AssociationLinks(mappings); if (entity == null) { - return Collections. emptyList(); + return Collections.> emptyList(); } - final List properties = new ArrayList(); + final List> properties = new ArrayList>(); for (BeanPropertyDefinition definition : jackson) { @@ -171,6 +171,10 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric : getDescriptionFor(persistentProperty, metadata); Property property = getSchemaProperty(definition, propertyType, description); + if (persistentProperty != null && !persistentProperty.isWritable()) { + property = property.withReadOnly(); + } + if (format != null) { // Types with explicitly registered format -> value object with format @@ -222,7 +226,8 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric return properties; } - private Collection getNestedPropertiesFor(PersistentProperty property, Descriptors descriptors) { + private Collection> getNestedPropertiesFor(PersistentProperty property, + Descriptors descriptors) { if (!property.isEntity()) { return Collections.emptyList(); 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 7f0758bab..4bcb3e557 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 @@ -124,6 +124,9 @@ public class PersistentEntityToJsonSchemaConverterUnitTests { constraints.add(new Constraint("$.properties.shippingAddresses.items['$ref']", is("#/descriptors/address"), "References descriptor of complex element type.")); + // DATAREST-531 + constraints.add(new Constraint("$.properties.email.readOnly", is(true), "Email is read-only property")); + assertConstraints(User.class, constraints); } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/User.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/User.java index a893b9a99..59512c3a2 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/User.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/User.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-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. @@ -20,6 +20,7 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Set; +import org.springframework.data.annotation.ReadOnlyProperty; import org.springframework.data.mongodb.core.mapping.DBRef; import org.springframework.data.mongodb.core.mapping.Document; @@ -41,7 +42,7 @@ public class User { public Set
shippingAddresses; public List nicknames; public Gender gender; - public EmailAddress email; + public @ReadOnlyProperty EmailAddress email; public LocalDateTime java8DateTime; public org.joda.time.LocalDateTime jodaDateTime; public TypeWithPattern pattern; @@ -57,8 +58,8 @@ public class User { public EmailAddress(String value) { this.value = value; } - @Override + @Override @JsonValue public String toString() { return value;