DATAREST-753 - Added support for Groovy-based domain objects.
Groovy-based objects inherit from GroovyObject. This brings along attributes that Spring Data REST may try to parse when generating metadata like JSON Schema. Also verify ALPS is supported. We now exclude those artificial properties from being exposed in the schema as well as causing issues. Original pull request: #206.
This commit is contained in:
committed by
Oliver Gierke
parent
3c411d1261
commit
18cc22738c
@@ -20,6 +20,7 @@
|
||||
<cassandra.version>2.0.9</cassandra.version>
|
||||
<cassandraunit.version>2.0.2.1</cassandraunit.version>
|
||||
<spring-security.version>4.0.1.RELEASE</spring-security.version>
|
||||
<groovy.version>2.3.7</groovy.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@@ -122,6 +123,13 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>${groovy.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
@@ -481,6 +489,9 @@
|
||||
<outputDirectory>target/generated-sources/annotations</outputDirectory>
|
||||
<processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor>
|
||||
<logOnlyOnError>true</logOnlyOnError>
|
||||
<options>
|
||||
<querydsl.excludedPackages>org.springframework.data.rest.webmvc.mongodb.groovy,groovy.lang</querydsl.excludedPackages>
|
||||
</options>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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")
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<SimulatedGroovyDomainClass, Long> {
|
||||
|
||||
}
|
||||
@@ -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<Constraint> constraints = new ArrayList<Constraint>();
|
||||
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<Constraint> constraints) {
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<SimulatedGroovyDomainClass, String> {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user