diff --git a/spring-data-rest-webmvc/pom.xml b/spring-data-rest-webmvc/pom.xml index 09eba5e7f..ed5652daa 100644 --- a/spring-data-rest-webmvc/pom.xml +++ b/spring-data-rest-webmvc/pom.xml @@ -20,6 +20,7 @@ 2.0.9 2.0.2.1 4.0.1.RELEASE + 2.3.7 @@ -122,6 +123,13 @@ test + + org.codehaus.groovy + groovy-all + ${groovy.version} + test + + @@ -481,6 +489,9 @@ target/generated-sources/annotations org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor true + + org.springframework.data.rest.webmvc.mongodb.groovy,groovy.lang + 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 4d02b0d8e..613bffb2b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -54,6 +54,7 @@ import org.springframework.util.StringUtils; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedMember; import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; /** @@ -61,6 +62,7 @@ import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; * * @author Jon Brisbin * @author Oliver Gierke + * @author Greg Turnquist */ public class PersistentEntityToJsonSchemaConverter implements ConditionalGenericConverter { @@ -186,18 +188,25 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric } } - TypeInformation propertyType = persistentProperty == null - ? ClassTypeInformation.from(definition.getPrimaryMember().getRawType()) + boolean isPersistentPropertyNull = persistentProperty == null; + + AnnotatedMember primaryMember = definition.getPrimaryMember(); + if (primaryMember == null) { // Ignore bean definitions with neither a getter nor a setter + continue; + } + + TypeInformation propertyType = isPersistentPropertyNull + ? ClassTypeInformation.from(primaryMember.getRawType()) : persistentProperty.getTypeInformation(); TypeInformation actualPropertyType = propertyType.getActualType(); Class rawPropertyType = propertyType.getType(); JsonSchemaFormat format = configuration.getMetadataConfiguration().getSchemaFormatFor(rawPropertyType); - ResourceDescription description = persistentProperty == null + ResourceDescription description = isPersistentPropertyNull ? jackson.getFallbackDescription(metadata, definition) : getDescriptionFor(persistentProperty, metadata); JsonSchemaProperty property = getSchemaProperty(definition, propertyType, description); - boolean isSyntheticProperty = persistentProperty == null; + boolean isSyntheticProperty = isPersistentPropertyNull; boolean isNotWritable = !isSyntheticProperty && !persistentProperty.isWritable(); boolean isJacksonReadOnly = !isSyntheticProperty && jackson.isReadOnly(persistentProperty); @@ -224,7 +233,7 @@ public class PersistentEntityToJsonSchemaConverter implements ConditionalGeneric continue; } - if (persistentProperty == null) { + if (isPersistentPropertyNull) { registrar.register(property, actualPropertyType); continue; } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java index c0805c562..3081e49a2 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/RepositoryControllerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 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. @@ -66,7 +66,7 @@ public class RepositoryControllerIntegrationTests extends AbstractControllerInte RepositoryLinksResource resource = controller.listRepositories().getBody(); - assertThat(resource.getLinks(), hasSize(7)); + assertThat(resource.getLinks(), hasSize(8)); assertThat(resource.hasLink("people"), is(true)); assertThat(resource.hasLink("orders"), is(true)); diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/alps/AlpsControllerIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/alps/AlpsControllerIntegrationTests.java index fa66a3543..c870f6a35 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/alps/AlpsControllerIntegrationTests.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/alps/AlpsControllerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 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. @@ -17,6 +17,7 @@ package org.springframework.data.rest.webmvc.alps; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.junit.After; @@ -199,4 +200,20 @@ public class AlpsControllerIntegrationTests extends AbstractControllerIntegratio "$.alps.descriptors[?(@.id == 'person-representation')].descriptors[?(@.name == 'gender')][0].doc.value", is("Male, Female, Undefined"))); } + + /** + * @see DATAREST-753 + */ + @Test + public void alpsCanHandleGroovyDomainObjects() throws Exception { + + Link profileLink = client.discoverUnique("profile"); + Link groovyDomainObjectLink = client.discoverUnique(profileLink, "simulatedGroovyDomainClasses"); + client.follow(groovyDomainObjectLink)// + .andExpect(jsonPath( + "$.alps.descriptors[?(@.id == 'simulatedGroovyDomainClass-representation')][0].descriptors[0].name", + is("name") + )); + + } } diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/groovy/SimulatedGroovyDomainClass.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/groovy/SimulatedGroovyDomainClass.java new file mode 100644 index 000000000..2c739f195 --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/groovy/SimulatedGroovyDomainClass.java @@ -0,0 +1,88 @@ +/* + * Copyright 2016 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.webmvc.jpa.groovy; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import org.springframework.data.mongodb.core.mapping.Document; + +import groovy.lang.GroovyObject; +import groovy.lang.MetaClass; + +/** + * Simulates a Groovy domain object by extending {@link GroovyObject}. + * + * @author Greg Turnquist + * @see DATAREST-754 + */ +@Entity +public class SimulatedGroovyDomainClass implements GroovyObject { + + @Id @GeneratedValue private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + protected SimulatedGroovyDomainClass() {} + + // + // The following fields don't actually have to be implemented since the test cases don't + // make any Groovy calls. This just simulates the structure of a Groovy object to + // verify proper handling. + // + + @Override + public Object invokeMethod(String s, Object o) { + return null; + } + + @Override + public Object getProperty(String s) { + return null; + } + + @Override + public void setProperty(String s, Object o) { + + } + + @Override + public MetaClass getMetaClass() { + return null; + } + + @Override + public void setMetaClass(MetaClass metaClass) { + + } +} diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/groovy/SimulatedGroovyDomainClassRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/groovy/SimulatedGroovyDomainClassRepository.java new file mode 100644 index 000000000..90f7bd8f8 --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/jpa/groovy/SimulatedGroovyDomainClassRepository.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 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.webmvc.jpa.groovy; + +import org.springframework.data.repository.CrudRepository; + +/** + * Simulates a repository built on a Groovy domain object. + * + * @author Greg Turnquist + * @see DATAREST-754 + */ +public interface SimulatedGroovyDomainClassRepository extends CrudRepository { + +} 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 8579a3917..6b95964a4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2016 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. @@ -43,6 +43,7 @@ import org.springframework.data.rest.webmvc.mongodb.Profile; import org.springframework.data.rest.webmvc.mongodb.User; import org.springframework.data.rest.webmvc.mongodb.User.EmailAddress; import org.springframework.data.rest.webmvc.mongodb.User.TypeWithPattern; +import org.springframework.data.rest.webmvc.mongodb.groovy.SimulatedGroovyDomainClass; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -73,6 +74,7 @@ public class PersistentEntityToJsonSchemaConverterUnitTests { config.getMetadataConfiguration().registerJsonSchemaFormat(JsonSchemaFormat.EMAIL, EmailAddress.class); config.getMetadataConfiguration().registerFormattingPatternFor("[A-Z]+", TypeWithPattern.class); + config.getMetadataConfiguration().registerJsonSchemaFormat(JsonSchemaFormat.EMAIL); config.exposeIdsFor(Profile.class); } @@ -159,6 +161,18 @@ public class PersistentEntityToJsonSchemaConverterUnitTests { assertConstraints(User.class, constraints); } + /** + * @see DATAREST-754 + */ + @Test + public void handlesGroovyDomainObjects() { + + List constraints = new ArrayList(); + constraints.add(new Constraint("$.properties.name", is(notNullValue()), "Has descriptor for name property")); + + assertConstraints(SimulatedGroovyDomainClass.class, constraints); + } + @SuppressWarnings("unchecked") private void assertConstraints(Class type, Iterable constraints) { diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/groovy/SimulatedGroovyDomainClass.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/groovy/SimulatedGroovyDomainClass.java new file mode 100644 index 000000000..2701bf6ee --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/groovy/SimulatedGroovyDomainClass.java @@ -0,0 +1,81 @@ +/* + * Copyright 2016 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.webmvc.mongodb.groovy; + +import org.springframework.data.mongodb.core.mapping.Document; + +import groovy.lang.GroovyObject; +import groovy.lang.MetaClass; + +/** + * Simulates a Groovy domain object by extending {@link GroovyObject}. + * + * @author Greg Turnquist + * @see DATAREST-754 + */ +@Document +public class SimulatedGroovyDomainClass implements GroovyObject { + + private String id; + private String name; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + // + // The following fields don't actually have to be implemented since the test cases don't + // make any Groovy calls. This just simulates the structure of a Groovy object to + // verify proper handling. + // + + @Override + public Object invokeMethod(String s, Object o) { + return null; + } + + @Override + public Object getProperty(String s) { + return null; + } + + @Override + public void setProperty(String s, Object o) { + + } + + @Override + public MetaClass getMetaClass() { + return null; + } + + @Override + public void setMetaClass(MetaClass metaClass) { + + } +} diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/groovy/SimulatedGroovyDomainClassRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/groovy/SimulatedGroovyDomainClassRepository.java new file mode 100644 index 000000000..b11e50699 --- /dev/null +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/mongodb/groovy/SimulatedGroovyDomainClassRepository.java @@ -0,0 +1,28 @@ +/* + * Copyright 2016 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.webmvc.mongodb.groovy; + +import org.springframework.data.repository.CrudRepository; + +/** + * Simulates a repository built on a Groovy domain object. + * + * @author Greg Turnquist + * @see DATAREST-754 + */ +public interface SimulatedGroovyDomainClassRepository extends CrudRepository { + +}