diff --git a/build.gradle b/build.gradle index 282ea426e..8213c9d21 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,7 @@ ext { hibernateVersion = "4.2.0.Final" hibernateValidatorVersion = "4.3.1.Final" hsqldbVersion = "2.2.9" + evoVersion="1.0.1" // Supporting libraries cglibVersion = "2.2.2" @@ -182,6 +183,9 @@ project("spring-data-rest-repository") { // JSR 303 Validation compile("javax.validation:validation-api:1.0.0.GA", optional) + // Evo Inflector + runtime "org.atteo:evo-inflector:${evoVersion}" + // Testing testCompile "org.hsqldb:hsqldb:$hsqldbVersion" testCompile "org.hibernate:hibernate-entitymanager:$hibernateVersion" diff --git a/gradle.properties b/gradle.properties index 2a49e68a9..79777e115 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version = 1.1.0.BUILD-SNAPSHOT +version = 2.0.0.BUILD-SNAPSHOT diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Path.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Path.java new file mode 100644 index 000000000..7acbe0fb7 --- /dev/null +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Path.java @@ -0,0 +1,124 @@ +/* + * 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; + +import org.springframework.util.StringUtils; + +/** + * Simple value object to build up (URI) paths. Allows easy concatenation of {@link String}s and will take care of + * removal of whitespace and reducing slashes to single ones. + * + * @author Oliver Gierke + */ +public class Path { + + private static final String SLASH = "/"; + + private final String path; + + /** + * Creates a new {@link Path} from the given {@link String}. + * + * @param path + */ + public Path(String path) { + this(path, true); + } + + /** + * Creates a new {@link Path} from the given string and potentially bypasses the cleanup. + * + * @param path + * @param cleanUp + */ + private Path(String path, boolean cleanUp) { + this.path = cleanUp ? cleanUp(path) : path; + } + + /** + * Appends the given {@link String} to the current {@link Path}. + * + * @param path + * @return + */ + public Path slash(String path) { + return new Path(this.path + cleanUp(path), false); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return path.hashCode(); + } + + private static String cleanUp(String path) { + + if (!StringUtils.hasText(path)) { + return ""; + } + + String trimmed = path.trim().replaceAll(" ", ""); + trimmed = SLASH + trimmed.substring(getFirstNoneSlashIndex(trimmed)); + + while (trimmed.endsWith("/")) { + trimmed = trimmed.substring(0, trimmed.length() - 1); + } + + return trimmed; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return path; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (!(obj instanceof Path)) { + return false; + } + + Path that = (Path) obj; + return this.path.equals(that.path); + } + + private static int getFirstNoneSlashIndex(String input) { + + for (int i = 0; i < input.length(); i++) { + if (input.charAt(i) != '/') { + return i; + } + } + + return input.length(); + } +} diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/PathUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/PathUnitTests.java new file mode 100644 index 000000000..572a80143 --- /dev/null +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/PathUnitTests.java @@ -0,0 +1,49 @@ +/* + * 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; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.data.rest.core.Path; + +/** + * @author Oliver Gierke + */ +public class PathUnitTests { + + @Test + public void combinesSimplePaths() { + + Path builder = new Path("foo").slash("bar"); + assertThat(builder.toString(), is("/foo/bar")); + } + + @Test + public void removesLeadingAndTrailingSlashes() { + + Path builder = new Path("foo/").slash("/bar").slash("//foobar///"); + assertThat(builder.toString(), is("/foo/bar/foobar")); + } + + @Test + public void removesWhitespace() { + + Path builder = new Path("foo/ ").slash("/ b a r").slash(" //foobar/// "); + assertThat(builder.toString(), is("/foo/bar/foobar")); + } +} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/CollectionResourceMappingBuilder.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/CollectionResourceMappingBuilder.java deleted file mode 100644 index 39df2ff71..000000000 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/CollectionResourceMappingBuilder.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.repository.mapping; - -import org.springframework.util.Assert; - -class CollectionResourceMappingBuilder implements InternalMappingBuilder { - - private final CollectionResourceMapping mapping; - - public CollectionResourceMappingBuilder(CollectionResourceMapping mapping) { - this.mapping = mapping; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withCollectionRel(java.lang.String) - */ - public CollectionResourceMappingBuilder withCollectionRel(String rel) { - - SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(rel != null ? rel - : mapping.getRel(), mapping.getSingleResourceRel(), mapping.getPath(), mapping.isExported()); - - return new CollectionResourceMappingBuilder(newMapping); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withSingleRel(java.lang.String) - */ - @Override - public CollectionResourceMappingBuilder withSingleRel(String rel) { - - SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(mapping.getRel(), - rel != null ? rel : mapping.getSingleResourceRel(), mapping.getPath(), mapping.isExported()); - - return new CollectionResourceMappingBuilder(newMapping); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withPath(java.lang.String) - */ - @Override - public CollectionResourceMappingBuilder withPath(String path) { - - SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(mapping.getRel(), - mapping.getSingleResourceRel(), path != null ? path : mapping.getPath(), mapping.isExported()); - - return new CollectionResourceMappingBuilder(newMapping); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#withExposed(java.lang.Boolean) - */ - @Override - public CollectionResourceMappingBuilder withExposed(Boolean exported) { - - SimpleCollectionResourceMapping newMapping = new SimpleCollectionResourceMapping(mapping.getRel(), - mapping.getSingleResourceRel(), mapping.getPath(), exported != null ? exported : mapping.isExported()); - - return new CollectionResourceMappingBuilder(newMapping); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#merge(org.springframework.data.rest.repository.mapping.CollectionResourceMapping) - */ - @Override - public InternalMappingBuilder merge(CollectionResourceMapping mapping) { - - if (mapping == null) { - return this; - } - - return withCollectionRel(mapping.getRel()). // - withSingleRel(mapping.getSingleResourceRel()). // - withPath(mapping.getPath()). // - withExposed(mapping.isExported()); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.InternalMappingBuilder#getMapping() - */ - @Override - public CollectionResourceMapping getMapping() { - - Assert.hasText(mapping.getRel(), "Rel must not be null or empty!"); - - return mapping; - } - - /** - * @return the exported - */ - public Boolean isExported() { - return mapping.isExported(); - } -} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/InternalMappingBuilder.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/InternalMappingBuilder.java deleted file mode 100644 index c313997be..000000000 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/InternalMappingBuilder.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.repository.mapping; - -/** - * @author Oliver Gierke - */ -interface InternalMappingBuilder extends MappingBuilder { - - InternalMappingBuilder merge(CollectionResourceMapping mapping); - - CollectionResourceMapping getMapping(); -} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/MappingBuilder.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/MappingBuilder.java deleted file mode 100644 index 0122d9443..000000000 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/MappingBuilder.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.repository.mapping; - -/** - * SPI to allow users to register - * - * @author Oliver Gierke - */ -public interface MappingBuilder { - - MappingBuilder withCollectionRel(String rel); - - MappingBuilder withSingleRel(String rel); - - MappingBuilder withPath(String path); - - MappingBuilder withExposed(Boolean exposed); - -} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceInformation.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceInformation.java index e6d8ed983..8f24845dd 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceInformation.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceInformation.java @@ -15,11 +15,14 @@ */ package org.springframework.data.rest.repository.mapping; +import java.util.Map; + import org.springframework.context.annotation.Primary; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.support.Repositories; +import org.springframework.data.rest.core.Path; import org.springframework.util.Assert; /** @@ -29,7 +32,7 @@ public class RepositoryAwareResourceInformation implements ResourceMetadata { private final Repositories repositories; private final CollectionResourceMapping mapping; - private final ResourceMetadataProvider provider; + private final ResourceMappings provider; private final RepositoryInformation repositoryInterface; /** @@ -38,7 +41,7 @@ public class RepositoryAwareResourceInformation implements ResourceMetadata { * @param provider must not be {@literal null}. */ public RepositoryAwareResourceInformation(Repositories repositories, CollectionResourceMapping mapping, - ResourceMetadataProvider provider, RepositoryInformation repositoryInterface) { + ResourceMappings provider, RepositoryInformation repositoryInterface) { Assert.notNull(repositories, "Repositories must not be null!"); Assert.notNull(mapping, "ResourceMapping must not be null!"); @@ -113,7 +116,17 @@ public class RepositoryAwareResourceInformation implements ResourceMetadata { * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getPath() */ @Override - public String getPath() { + public Path getPath() { return mapping.getPath(); } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.ResourceMetadata#getSearchResourceMappings() + */ + @Override + public Map getSearchResourceMappings() { + return provider.getSearchResourceMappings(repositoryInterface.getRepositoryInterface()); + } + } diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryCollectionResourceMapping.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryCollectionResourceMapping.java new file mode 100644 index 000000000..deab592e0 --- /dev/null +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryCollectionResourceMapping.java @@ -0,0 +1,97 @@ +/* + * 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.repository.mapping; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.rest.core.Path; +import org.springframework.data.rest.repository.annotation.RestResource; +import org.springframework.data.rest.repository.support.RepositoriesUtils; +import org.springframework.hateoas.RelProvider; +import org.springframework.hateoas.core.EvoInflectorRelProvider; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * {@link CollectionResourceMapping} to be built from repository interfaces. Will inspect {@link RestResource} + * annotations on the repository interface but fall back to the mapping information of the managed domain type for + * defaults. + * + * @author Oliver Gierke + */ +public class RepositoryCollectionResourceMapping implements CollectionResourceMapping { + + private final RestResource annotation; + private final CollectionResourceMapping domainTypeMapping; + + public RepositoryCollectionResourceMapping(Class repositoryType) { + this(repositoryType, new EvoInflectorRelProvider()); + } + + /** + * Creates a new {@link RepositoryCollectionResourceMapping} for the given repository using the given + * {@link RelProvider}. + * + * @param repositoryType must not be {@literal null}. + * @param relProvider must not be {@literal null}. + */ + public RepositoryCollectionResourceMapping(Class repositoryType, RelProvider relProvider) { + + Assert.isTrue(RepositoriesUtils.isRepositoryInterface(repositoryType), "Given type is not a repository!"); + Assert.notNull(relProvider, "RelProvider must not be null!"); + + this.annotation = AnnotationUtils.findAnnotation(repositoryType, RestResource.class); + this.domainTypeMapping = new TypeBasedCollectionResourceMapping(RepositoriesUtils.getDomainType(repositoryType), + relProvider); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.ResourceMapping#getPath() + */ + @Override + public Path getPath() { + + return annotation == null || !StringUtils.hasText(annotation.path()) ? domainTypeMapping.getPath() + : new Path(annotation.path()); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.ResourceMapping#getRel() + */ + @Override + public String getRel() { + return annotation == null || !StringUtils.hasText(annotation.rel()) ? domainTypeMapping.getRel() : annotation.rel(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getSingleResourceRel() + */ + @Override + public String getSingleResourceRel() { + return domainTypeMapping.getSingleResourceRel(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.ResourceMapping#isExported() + */ + @Override + public Boolean isExported() { + return annotation == null ? domainTypeMapping.isExported() : annotation.exported(); + } +} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMapping.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMapping.java index 1638d933b..4efb1a063 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMapping.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMapping.java @@ -18,7 +18,9 @@ package org.springframework.data.rest.repository.mapping; import java.lang.reflect.Method; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.rest.core.Path; import org.springframework.data.rest.repository.annotation.RestResource; +import org.springframework.util.StringUtils; /** * A {@link RepositoryMethodResourceMapping} created from a {@link Method}. @@ -29,20 +31,24 @@ public class RepositoryMethodResourceMapping implements ResourceMapping { private final boolean isExported; private final String rel; - private final String path; + private final Path path; /** * Creates a new {@link RepositoryMethodResourceMapping} for the given {@link Method}. * * @param method must not be {@literal null}. */ - public RepositoryMethodResourceMapping(Method method) { + public RepositoryMethodResourceMapping(Method method, ResourceMapping resourceMapping) { RestResource annotation = AnnotationUtils.findAnnotation(method, RestResource.class); this.isExported = annotation != null ? annotation.exported() : true; this.rel = annotation != null ? annotation.rel() : method.getName(); - this.path = annotation != null ? annotation.path() : method.getName(); + + Path resourcePath = resourceMapping.getPath(); + String toAppend = annotation == null || !StringUtils.hasText(annotation.path()) ? method.getName() : annotation + .path(); + this.path = resourcePath.slash(toAppend); } /* @@ -68,7 +74,7 @@ public class RepositoryMethodResourceMapping implements ResourceMapping { * @see org.springframework.data.rest.repository.mapping.ResourceMapping#getPath() */ @Override - public String getPath() { + public Path getPath() { return path; } } diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMapping.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMapping.java index d9363c708..24af6b058 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMapping.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMapping.java @@ -15,6 +15,8 @@ */ package org.springframework.data.rest.repository.mapping; +import org.springframework.data.rest.core.Path; + /** * Mapping information for components to be exported as REST resources. * @@ -22,24 +24,6 @@ package org.springframework.data.rest.repository.mapping; */ public interface ResourceMapping { - public static ResourceMapping NO_MAPPING = new ResourceMapping() { - - @Override - public Boolean isExported() { - return false; - } - - @Override - public String getRel() { - return null; - } - - @Override - public String getPath() { - return null; - } - }; - /** * Returns whether the component shall be exported at all. * @@ -59,5 +43,5 @@ public interface ResourceMapping { * * @return will never be {@literal null}. */ - String getPath(); + Path getPath(); } diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappingFactory.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappingFactory.java deleted file mode 100644 index 262c158c5..000000000 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappingFactory.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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.repository.mapping; - -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.data.rest.repository.annotation.RestResource; -import org.springframework.data.rest.repository.support.RepositoriesUtils; -import org.springframework.hateoas.RelProvider; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * @author Oliver Gierke - */ -public class ResourceMappingFactory { - - private final RelProvider relProvider; - - public ResourceMappingFactory(RelProvider relProvider) { - this.relProvider = relProvider; - } - - public CollectionResourceMapping getMappingForType(Class type) { - return getMappingForType(type, new CollectionResourceMapping[0]); - } - - public CollectionResourceMapping getMappingForType(Class type, CollectionResourceMapping... manualMapping) { - - Class typeToInspect = getTypeToInspect(type); - - InternalMappingBuilder mapping = getBaseMetadata(typeToInspect). // - merge(discoverConfig(typeToInspect)); - - if (type != typeToInspect) { - mapping = mapping.merge(discoverConfig(type)); - } - - for (CollectionResourceMapping externalMapping : manualMapping) { - mapping = mapping.merge(externalMapping); - } - - return mapping.getMapping(); - } - - private static Class getTypeToInspect(Class type) { - - if (!RepositoriesUtils.isRepositoryInterface(type)) { - return type; - } - - return RepositoriesUtils.getDomainType(type); - } - - private InternalMappingBuilder getBaseMetadata(Class domainType) { - - String path = StringUtils.uncapitalize(domainType.getSimpleName()); - String defaultCollectionRel = relProvider.getCollectionResourceRelFor(domainType); - String defaultSingleRel = relProvider.getSingleResourceRelFor(domainType); - - CollectionResourceMapping mapping = new SimpleCollectionResourceMapping(defaultCollectionRel, defaultSingleRel, - path, true); - - return new CollectionResourceMappingBuilder(mapping); - } - - private static final CollectionResourceMapping discoverConfig(Class type) { - - RestResource resource = AnnotationUtils.findAnnotation(type, RestResource.class); - - if (resource == null) { - return null; - } - - return new AnnotationResourceMapping(resource); - } - - /** - * {@link CollectionResourceMapping} based on an {@link RestResource} annotation. - * - * @author Oliver Gierke - */ - private static class AnnotationResourceMapping implements CollectionResourceMapping { - - private final RestResource annotation; - - /** - * Creates a new {@link AnnotationResourceMapping} for the given {@link RestResource}. - * - * @param annotation must not be {@literal null}. - */ - public AnnotationResourceMapping(RestResource annotation) { - Assert.notNull(annotation, "Annotation must not be null!"); - this.annotation = annotation; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#isExported() - */ - @Override - public Boolean isExported() { - return annotation.exported(); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getCollectionRel() - */ - @Override - public String getRel() { - return StringUtils.hasText(annotation.rel()) ? annotation.rel() : null; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getSingleResourceRel() - */ - @Override - public String getSingleResourceRel() { - - String rel = getRel(); - return rel == null ? null : String.format("%s.%s", rel, rel); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getPath() - */ - @Override - public String getPath() { - return StringUtils.hasText(annotation.path()) ? annotation.path() : null; - } - } -} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappings.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappings.java index 24d530a25..e7ceccafc 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappings.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappings.java @@ -35,12 +35,11 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -@SuppressWarnings("deprecation") -public class ResourceMappings implements ResourceMetadataProvider, Iterable { +public class ResourceMappings implements Iterable { + - private final RepositoryRestConfiguration config; - private final ResourceMappingFactory factory; private final Repositories repositories; + private final RelProvider relProvider; private final Map, ResourceMetadata> cache = new HashMap, ResourceMetadata>(); private final Map, Map> searchCache = new HashMap, Map>(); @@ -66,13 +65,11 @@ public class ResourceMappings implements ResourceMetadataProvider, Iterable repositoryInterface = repositoryInformation.getRepositoryInterface(); - CollectionResourceMapping mapping = factory.getMappingForType(repositoryInterface, fromConfig(type), - fromConfig(repositoryInterface)); + CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(repositoryInterface, relProvider); RepositoryAwareResourceInformation information = new RepositoryAwareResourceInformation(repositories, mapping, this, repositoryInformation); @@ -125,9 +121,11 @@ public class ResourceMappings implements ResourceMetadataProvider, Iterable domainType = RepositoriesUtils.getDomainType(type); RepositoryInformation repositoryInformation = repositories.getRepositoryInformationFor(domainType); Map mappings = new HashMap(); + ResourceMetadata repositoryMapping = getMappingFor(repositoryInformation.getRepositoryInterface()); for (Method queryMethod : repositoryInformation.getQueryMethods()) { - mappings.put(queryMethod.getName(), new RepositoryMethodResourceMapping(queryMethod)); + mappings.put(queryMethod.getName(), new RepositoryMethodResourceMapping(queryMethod, + repositoryMapping)); } searchCache.put(type, mappings); @@ -177,8 +175,7 @@ public class ResourceMappings implements ResourceMetadataProvider, Iterable property) { + ResourceMapping getMappingFor(PersistentProperty property) { return getMappingFor(property.getActualType()); } @@ -186,7 +183,6 @@ public class ResourceMappings implements ResourceMetadataProvider, Iterable property) { ResourceMapping metadata = getMappingFor(property); @@ -201,15 +197,4 @@ public class ResourceMappings implements ResourceMetadataProvider, Iterable iterator() { return cache.values().iterator(); } - - private CollectionResourceMapping fromConfig(Class domainType) { - - org.springframework.data.rest.config.ResourceMapping mapping = config.getResourceMappingForDomainType(domainType); - - if (mapping == null) { - return null; - } - - return new SimpleCollectionResourceMapping(mapping.getRel(), null, mapping.getPath(), mapping.isExported()); - } } diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadata.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadata.java index 3823b6066..4db9a95a6 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadata.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadata.java @@ -15,12 +15,20 @@ */ package org.springframework.data.rest.repository.mapping; +import java.util.Map; + import org.springframework.data.mapping.PersistentProperty; /** * @author Oliver Gierke */ -public interface ResourceMetadata extends CollectionResourceMapping, ResourceMetadataProvider { +public interface ResourceMetadata extends CollectionResourceMapping { boolean isManaged(PersistentProperty property); + + boolean isMapped(PersistentProperty property); + + ResourceMapping getMappingFor(PersistentProperty property); + + Map getSearchResourceMappings(); } diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadataProvider.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadataProvider.java deleted file mode 100644 index 148c4f584..000000000 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadataProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.repository.mapping; - -import org.springframework.data.mapping.PersistentProperty; - -/** - * @author Oliver Gierke - */ -public interface ResourceMetadataProvider { - - boolean isMapped(PersistentProperty property); - - ResourceMapping getMappingFor(PersistentProperty property); -} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/SimpleCollectionResourceMapping.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/SimpleCollectionResourceMapping.java deleted file mode 100644 index b92911a88..000000000 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/SimpleCollectionResourceMapping.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.repository.mapping; - -public class SimpleCollectionResourceMapping implements CollectionResourceMapping { - - private final String collectionRel; - private final String singleRel; - private final String path; - private final Boolean exported; - - public SimpleCollectionResourceMapping(String relsAndPath) { - this(relsAndPath, relsAndPath, relsAndPath, true); - } - - public SimpleCollectionResourceMapping(String collectionRel, String singleRel, String path, Boolean exported) { - - this.collectionRel = collectionRel; - this.singleRel = singleRel; - this.path = path; - this.exported = exported; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getCollectionRel() - */ - public String getRel() { - return collectionRel; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getSingleResourceRel() - */ - public String getSingleResourceRel() { - return singleRel; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getPath() - */ - @Override - public String getPath() { - return path; - } - - /** - * @return the exported - */ - public Boolean isExported() { - return exported; - } -} diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMapping.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMapping.java new file mode 100644 index 000000000..9f682b8db --- /dev/null +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMapping.java @@ -0,0 +1,106 @@ +/* + * 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.repository.mapping; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.rest.core.Path; +import org.springframework.data.rest.repository.annotation.RestResource; +import org.springframework.hateoas.RelProvider; +import org.springframework.hateoas.core.EvoInflectorRelProvider; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * {@link CollectionResourceMapping} based on a type. Will derive default relation types and pathes from the type but + * inspect it for {@link RestResource} annotations for customization. + * + * @author Oliver Gierke + */ +public class TypeBasedCollectionResourceMapping implements CollectionResourceMapping { + + private final Class type; + private final RestResource annotation; + private final RelProvider relProvider; + + /** + * Creates a new {@link TypeBasedCollectionResourceMapping} using the given type. + * + * @param type must not be {@literal null}. + */ + public TypeBasedCollectionResourceMapping(Class type) { + this(type, new EvoInflectorRelProvider()); + } + + /** + * Creates a new {@link TypeBasedCollectionResourceMapping} using the given type and {@link RelProvider}. + * + * @param type must not be {@literal null}. + * @param relProvider must not be {@literal null}. + */ + public TypeBasedCollectionResourceMapping(Class type, RelProvider relProvider) { + + Assert.notNull(type, "Type must not be null!"); + Assert.notNull(relProvider, "RelProvider must not be null!"); + + this.type = type; + this.relProvider = relProvider; + this.annotation = AnnotationUtils.findAnnotation(type, RestResource.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.ResourceMapping#getPath() + */ + @Override + public Path getPath() { + + String path = annotation == null ? null : annotation.path().trim(); + path = StringUtils.hasText(path) ? path : StringUtils.uncapitalize(type.getSimpleName()); + return new Path(path); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.ResourceMapping#isExported() + */ + @Override + public Boolean isExported() { + return annotation == null ? true : annotation.exported(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.ResourceMapping#getRel() + */ + @Override + public String getRel() { + + if (annotation == null || !StringUtils.hasText(annotation.rel())) { + return relProvider.getCollectionResourceRelFor(type); + } + + return annotation.rel(); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.rest.repository.mapping.CollectionResourceMapping#getSingleResourceRel() + */ + @Override + public String getSingleResourceRel() { + return relProvider.getSingleResourceRelFor(type); + } +} diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java index 339c68b93..965bbb445 100644 --- a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java +++ b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/config/ResourceMappingUnitTests.java @@ -25,13 +25,15 @@ import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.repository.query.Param; +import org.springframework.data.rest.core.Path; import org.springframework.data.rest.repository.annotation.RestResource; import org.springframework.data.rest.repository.domain.jpa.AnnotatedPersonRepository; import org.springframework.data.rest.repository.domain.jpa.Person; import org.springframework.data.rest.repository.domain.jpa.PlainPersonRepository; +import org.springframework.data.rest.repository.mapping.RepositoryCollectionResourceMapping; import org.springframework.data.rest.repository.mapping.ResourceMapping; -import org.springframework.data.rest.repository.mapping.ResourceMappingFactory; -import org.springframework.data.rest.repository.support.SimpleRelProvider; +import org.springframework.hateoas.RelProvider; +import org.springframework.hateoas.core.EvoInflectorRelProvider; /** * Ensure the {@link ResourceMapping} components convey the correct information. @@ -41,25 +43,26 @@ import org.springframework.data.rest.repository.support.SimpleRelProvider; @SuppressWarnings("deprecation") public class ResourceMappingUnitTests { - ResourceMappingFactory factory = new ResourceMappingFactory(new SimpleRelProvider()); + RelProvider relProvider = new EvoInflectorRelProvider(); + @Test public void shouldDetectDefaultRelAndPath() throws Exception { - ResourceMapping newMapping = factory.getMappingForType(PlainPersonRepository.class); + ResourceMapping newMapping = new RepositoryCollectionResourceMapping(PlainPersonRepository.class, relProvider); - assertThat(newMapping.getRel(), is("person")); - assertThat(newMapping.getPath(), is("person")); + assertThat(newMapping.getRel(), is("persons")); + assertThat(newMapping.getPath(), is(new Path("person"))); assertThat(newMapping.isExported(), is(true)); } @Test public void shouldDetectAnnotatedRelAndPath() throws Exception { - ResourceMapping newMapping = factory.getMappingForType(AnnotatedPersonRepository.class); + ResourceMapping newMapping = new RepositoryCollectionResourceMapping(AnnotatedPersonRepository.class, relProvider); assertThat(newMapping.getRel(), is("people")); - assertThat(newMapping.getPath(), is("person")); + assertThat(newMapping.getPath(), is(new Path("person"))); assertThat(newMapping.isExported(), is(false)); } diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceMappingFactoryUnitTests.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceMappingFactoryUnitTests.java deleted file mode 100644 index 5c2c7a2eb..000000000 --- a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceMappingFactoryUnitTests.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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.repository.mapping; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; - -import org.junit.Test; -import org.springframework.data.rest.repository.annotation.RestResource; -import org.springframework.data.rest.repository.support.SimpleRelProvider; - -/** - * @author Oliver Gierke - */ -public class RepositoryAwareResourceMappingFactoryUnitTests { - - ResourceMappingFactory factory = new ResourceMappingFactory(new SimpleRelProvider()); - - @Test - public void foo() { - - CollectionResourceMapping mapping = factory.getMappingForType(Person.class); - assertThat(mapping.getPath(), is("person")); - assertThat(mapping.getRel(), is("person")); - assertThat(mapping.getSingleResourceRel(), is("person.person")); - } - - @Test - public void honorsAnnotatedMapping() { - - CollectionResourceMapping mapping = factory.getMappingForType(AnnotatedPerson.class); - assertThat(mapping.getPath(), is("bar")); - assertThat(mapping.getRel(), is("foo")); - assertThat(mapping.getSingleResourceRel(), is("foo.foo")); - assertThat(mapping.isExported(), is(false)); - } - - static class Person { - - } - - @RestResource(path = "bar", rel = "foo", exported = false) - static class AnnotatedPerson { - - } -} diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/ResourceMappingFactoryUnitTests.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryCollectionResourceMappingUnitTests.java similarity index 50% rename from spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/ResourceMappingFactoryUnitTests.java rename to spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryCollectionResourceMappingUnitTests.java index 8552cafe9..b5206ecd9 100644 --- a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/ResourceMappingFactoryUnitTests.java +++ b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryCollectionResourceMappingUnitTests.java @@ -20,70 +20,59 @@ import static org.junit.Assert.*; import org.junit.Test; import org.springframework.data.repository.Repository; +import org.springframework.data.rest.core.Path; import org.springframework.data.rest.repository.annotation.RestResource; -import org.springframework.data.rest.repository.support.SimpleRelProvider; /** + * Unit tests for {@link RepositoryCollectionResourceMapping}. + * * @author Oliver Gierke */ -public class ResourceMappingFactoryUnitTests { - - ResourceMappingFactory factory = new ResourceMappingFactory(new SimpleRelProvider()); +public class RepositoryCollectionResourceMappingUnitTests { @Test - public void foo() { + public void buildsDefaultMappingForRepository() { - CollectionResourceMapping mapping = factory.getMappingForType(Person.class); - assertThat(mapping.getPath(), is("person")); - assertThat(mapping.getRel(), is("person")); - assertThat(mapping.getSingleResourceRel(), is("person.person")); - } + CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(PersonRepository.class); - @Test - public void honorsAnnotatedMapping() { - - CollectionResourceMapping mapping = factory.getMappingForType(AnnotatedPerson.class); - assertThat(mapping.getPath(), is("bar")); - assertThat(mapping.getRel(), is("foo")); - assertThat(mapping.getSingleResourceRel(), is("foo.foo")); - assertThat(mapping.isExported(), is(false)); + assertThat(mapping.getPath(), is(new Path("person"))); + assertThat(mapping.getRel(), is("persons")); + assertThat(mapping.getSingleResourceRel(), is("person")); + assertThat(mapping.isExported(), is(true)); } @Test public void honorsAnnotatedsMapping() { - CollectionResourceMapping mapping = factory.getMappingForType(PersonRepository.class); - assertThat(mapping.getPath(), is("bar")); + CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(AnnotatedPersonRepository.class); + + assertThat(mapping.getPath(), is(new Path("bar"))); assertThat(mapping.getRel(), is("foo")); - assertThat(mapping.getSingleResourceRel(), is("foo.foo")); + assertThat(mapping.getSingleResourceRel(), is("annotatedPerson")); assertThat(mapping.isExported(), is(false)); } @Test public void repositoryAnnotationTrumpsDomainTypeMapping() { - CollectionResourceMapping mapping = factory.getMappingForType(AnnotatedAnnotatedPersonRepository.class); - assertThat(mapping.getPath(), is("trumpsAll")); + CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping( + AnnotatedAnnotatedPersonRepository.class); + + assertThat(mapping.getPath(), is(new Path("/trumpsAll"))); assertThat(mapping.getRel(), is("foo")); - assertThat(mapping.getSingleResourceRel(), is("foo.foo")); + assertThat(mapping.getSingleResourceRel(), is("annotatedPerson")); assertThat(mapping.isExported(), is(true)); } - static class Person { - - } + static class Person {} @RestResource(path = "bar", rel = "foo", exported = false) - static class AnnotatedPerson { + static class AnnotatedPerson {} - } + interface PersonRepository extends Repository {} - interface PersonRepository extends Repository { - - } + interface AnnotatedPersonRepository extends Repository {} @RestResource(path = "trumpsAll") - interface AnnotatedAnnotatedPersonRepository extends Repository { - - } + interface AnnotatedAnnotatedPersonRepository extends Repository {} } diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMappingUnitTests.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMappingUnitTests.java new file mode 100644 index 000000000..17e655a5f --- /dev/null +++ b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMappingUnitTests.java @@ -0,0 +1,66 @@ +/* + * 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.repository.mapping; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.lang.reflect.Method; + +import org.junit.Test; +import org.springframework.data.repository.Repository; +import org.springframework.data.rest.core.Path; +import org.springframework.data.rest.repository.annotation.RestResource; + +/** + * @author Oliver Gierke + */ +public class RepositoryMethodResourceMappingUnitTests { + + RepositoryCollectionResourceMapping resourceMapping = new RepositoryCollectionResourceMapping( + PersonRepository.class); + + @Test + public void foo() throws Exception { + + Method method = PersonRepository.class.getMethod("findByLastname", String.class); + ResourceMapping mapping = new RepositoryMethodResourceMapping(method, resourceMapping); + + assertThat(mapping.getPath(), is(new Path("person/findByLastname"))); + } + + @Test + public void usesConfiguredNameWithLeadingSlash() throws Exception { + + Method method = PersonRepository.class.getMethod("findByFirstname", String.class); + ResourceMapping mapping = new RepositoryMethodResourceMapping(method, resourceMapping); + + assertThat(mapping.getPath(), is(new Path("person/bar"))); + } + + static class Person {} + + interface PersonRepository extends Repository { + + Iterable findByLastname(String lastname); + + @RestResource(path = "/bar") + Iterable findByFirstname(String firstname); + + @RestResource(path = "foo") + Iterable findByEmailAddress(String email); + } +} diff --git a/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMappingUnitTest.java b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMappingUnitTest.java new file mode 100644 index 000000000..e91cc9e0c --- /dev/null +++ b/spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMappingUnitTest.java @@ -0,0 +1,62 @@ +/* + * 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.repository.mapping; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.data.rest.core.Path; +import org.springframework.data.rest.repository.annotation.RestResource; + +/** + * Unit tests for {@link TypeBasedCollectionResourceMapping}. + * + * @author Oliver Gierke + */ +public class TypeBasedCollectionResourceMappingUnitTest { + + @Test + public void defaultsMappingsByType() { + + CollectionResourceMapping mapping = new TypeBasedCollectionResourceMapping(Sample.class); + + assertThat(mapping.getPath(), is(new Path("sample"))); + assertThat(mapping.getRel(), is("samples")); + assertThat(mapping.getSingleResourceRel(), is("sample")); + assertThat(mapping.isExported(), is(true)); + } + + @Test + public void usesCustomizedRel() { + + CollectionResourceMapping mapping = new TypeBasedCollectionResourceMapping(CustomizedSample.class); + + assertThat(mapping.getPath(), is(new Path("customizedSample"))); + assertThat(mapping.getRel(), is("myRel")); + assertThat(mapping.getSingleResourceRel(), is("customizedSample")); + assertThat(mapping.isExported(), is(true)); + } + + class Sample { + + } + + @RestResource(rel = "myRel") + class CustomizedSample { + + } +} diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java index a67e35feb..7c9627c94 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/support/RepositoryLinkBuilder.java @@ -43,7 +43,7 @@ public class RepositoryLinkBuilder extends LinkBuilderSupport expectedRootLinkRels() { - return Arrays.asList("profile"); + return Arrays.asList("profiles"); } @Test public void foo() throws Exception { - Link profileLink = discoverUnique("profile"); + Link profileLink = discoverUnique("profiles"); follow(profileLink).andExpect(jsonPath("$.content").value(hasSize(2))); } }