diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java index 5587b3319..2564eedda 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/mapping/ResourceMappings.java @@ -23,14 +23,17 @@ import java.util.List; import java.util.Map; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.support.Repositories; import org.springframework.data.rest.core.Path; +import org.springframework.data.rest.core.annotation.RestResource; import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.support.RepositoriesUtils; import org.springframework.hateoas.RelProvider; import org.springframework.hateoas.core.EvoInflectorRelProvider; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Central abstraction obtain {@link ResourceMetadata} and {@link ResourceMapping} instances for domain types and @@ -216,8 +219,8 @@ public class ResourceMappings implements Iterable { } ResourceMetadata propertyTypeMapping = getMappingFor(property.getActualType()); - propertyMapping = new PersistentPropertyResourceMapping(property, propertyTypeMapping == null ? false - : propertyTypeMapping.isExported()); + propertyMapping = new PersistentPropertyResourceMapping(property, propertyTypeMapping); + propertyCache.put(property, propertyMapping); return propertyMapping; @@ -247,10 +250,11 @@ public class ResourceMappings implements Iterable { * * @author Oliver Gierke */ - private static class PersistentPropertyResourceMapping implements ResourceMapping { + static class PersistentPropertyResourceMapping implements ResourceMapping { private final PersistentProperty property; - private final boolean exported; + private final ResourceMapping typeMapping; + private final RestResource annotation; /** * Creates a new {@link PersistentPropertyResourceMapping}. @@ -258,11 +262,13 @@ public class ResourceMappings implements Iterable { * @param property must not be {@literal null}. * @param exported whether the property is exported or not. */ - public PersistentPropertyResourceMapping(PersistentProperty property, boolean exported) { + public PersistentPropertyResourceMapping(PersistentProperty property, ResourceMapping typeMapping) { Assert.notNull(property, "PersistentProperty must not be null!"); this.property = property; - this.exported = exported; + this.typeMapping = typeMapping; + this.annotation = property instanceof AnnotationBasedPersistentProperty ? ((AnnotationBasedPersistentProperty) property) + .findAnnotation(RestResource.class) : null; } /* @@ -271,7 +277,8 @@ public class ResourceMappings implements Iterable { */ @Override public Path getPath() { - return new Path(property.getName()); + return annotation != null && StringUtils.hasText(annotation.path()) ? new Path(annotation.path()) : new Path( + property.getName()); } /* @@ -280,7 +287,7 @@ public class ResourceMappings implements Iterable { */ @Override public String getRel() { - return property.getName(); + return annotation != null && StringUtils.hasText(annotation.rel()) ? annotation.rel() : property.getName(); } /* @@ -289,7 +296,12 @@ public class ResourceMappings implements Iterable { */ @Override public Boolean isExported() { - return exported; + + if (typeMapping == null) { + return false; + } + + return !typeMapping.isExported() ? false : annotation == null ? true : annotation.exported(); } } } diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMappingUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMappingUnitTests.java new file mode 100644 index 000000000..aa86580dd --- /dev/null +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/mapping/PersistentPropertyResourceMappingUnitTests.java @@ -0,0 +1,115 @@ +/* + * Copyright 2013 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.core.mapping; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.mongodb.core.mapping.MongoMappingContext; +import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.rest.core.Path; +import org.springframework.data.rest.core.annotation.RestResource; +import org.springframework.data.rest.core.mapping.ResourceMappings.PersistentPropertyResourceMapping; + +/** + * Unit tests for {@link PersistentPropertyResourceMapping}. + * + * @author Oliver Gierke + */ +@RunWith(MockitoJUnitRunner.class) +public class PersistentPropertyResourceMappingUnitTests { + + MongoMappingContext mappingContext = new MongoMappingContext(); + MongoPersistentEntity persistentEntity = mappingContext.getPersistentEntity(Entity.class); + + @Mock ResourceMapping typeMapping; + + @Before + public void setUp() { + when(typeMapping.isExported()).thenReturn(true); + } + + /** + * @see DATAREST-175 + */ + @Test + public void usesPropertyNameAsDefaultResourceMappingRelAndPath() { + + MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty("first"); + ResourceMapping propertyMapping = new PersistentPropertyResourceMapping(persistentProperty, typeMapping); + + assertThat(propertyMapping, is(notNullValue())); + assertThat(propertyMapping.getPath(), is(new Path("first"))); + assertThat(propertyMapping.getRel(), is("first")); + assertThat(propertyMapping.isExported(), is(true)); + } + + /** + * @see DATAREST-175 + */ + @Test + public void considersMappingAnnotationOnDomainClassProperty() { + + MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty("second"); + ResourceMapping propertyMapping = new PersistentPropertyResourceMapping(persistentProperty, typeMapping); + + assertThat(propertyMapping, is(notNullValue())); + assertThat(propertyMapping.getPath(), is(new Path("secPath"))); + assertThat(propertyMapping.getRel(), is("secRel")); + assertThat(propertyMapping.isExported(), is(false)); + } + + /** + * @see DATAREST-175 + */ + @Test + public void considersMappingAnnotationOnDomainClassPropertyMethod() { + + MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty("third"); + ResourceMapping propertyMapping = new PersistentPropertyResourceMapping(persistentProperty, typeMapping); + + assertThat(propertyMapping, is(notNullValue())); + assertThat(propertyMapping.getPath(), is(new Path("thirdPath"))); + assertThat(propertyMapping.getRel(), is("thirdRel")); + assertThat(propertyMapping.isExported(), is(false)); + } + + static class Entity { + + Related first, third; + + @RestResource(path = "secPath", rel = "secRel", exported = false)// + List second; + + @RestResource(path = "thirdPath", rel = "thirdRel", exported = false) + public Related getThird() { + return third; + } + } + + static class Related { + + } +}