From c5a592bd9d308257f83c73b467f814a10441d10d Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 18 Jun 2013 19:17:56 +0200 Subject: [PATCH] DATAREST-93 - Further fixes in repository mappings. Simplified new RepositoryMappings infrastructure. Integrated EvoInflectionRelProvider to build collection resource rels. Bumped version number to 2.0 as we're going to break backwards compatibility with the next release to straighten out the rel construction and mapping. --- build.gradle | 4 + gradle.properties | 2 +- .../springframework/data/rest/core/Path.java | 124 +++++++++++++++ .../data/rest/core/PathUnitTests.java | 49 ++++++ .../CollectionResourceMappingBuilder.java | 114 -------------- .../mapping/InternalMappingBuilder.java | 26 ---- .../repository/mapping/MappingBuilder.java | 33 ---- .../RepositoryAwareResourceInformation.java | 19 ++- .../RepositoryCollectionResourceMapping.java | 97 ++++++++++++ .../RepositoryMethodResourceMapping.java | 14 +- .../repository/mapping/ResourceMapping.java | 22 +-- .../mapping/ResourceMappingFactory.java | 147 ------------------ .../repository/mapping/ResourceMappings.java | 33 ++-- .../repository/mapping/ResourceMetadata.java | 10 +- .../mapping/ResourceMetadataProvider.java | 28 ---- .../SimpleCollectionResourceMapping.java | 68 -------- .../TypeBasedCollectionResourceMapping.java | 106 +++++++++++++ .../rest/config/ResourceMappingUnitTests.java | 19 ++- ...yAwareResourceMappingFactoryUnitTests.java | 59 ------- ...ryCollectionResourceMappingUnitTests.java} | 59 +++---- ...ositoryMethodResourceMappingUnitTests.java | 66 ++++++++ ...asedCollectionResourceMappingUnitTest.java | 62 ++++++++ .../webmvc/support/RepositoryLinkBuilder.java | 2 +- .../webmvc/AbstractWebIntegrationTests.java | 3 +- .../PersistentEntitySerializationTests.java | 4 +- .../rest/webmvc/mongodb/MongoWebTests.java | 4 +- 26 files changed, 598 insertions(+), 576 deletions(-) create mode 100644 spring-data-rest-core/src/main/java/org/springframework/data/rest/core/Path.java create mode 100644 spring-data-rest-core/src/test/java/org/springframework/data/rest/core/PathUnitTests.java delete mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/CollectionResourceMappingBuilder.java delete mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/InternalMappingBuilder.java delete mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/MappingBuilder.java create mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/RepositoryCollectionResourceMapping.java delete mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMappingFactory.java delete mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/ResourceMetadataProvider.java delete mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/SimpleCollectionResourceMapping.java create mode 100644 spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMapping.java delete mode 100644 spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryAwareResourceMappingFactoryUnitTests.java rename spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/{ResourceMappingFactoryUnitTests.java => RepositoryCollectionResourceMappingUnitTests.java} (50%) create mode 100644 spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/RepositoryMethodResourceMappingUnitTests.java create mode 100644 spring-data-rest-repository/src/test/java/org/springframework/data/rest/repository/mapping/TypeBasedCollectionResourceMappingUnitTest.java 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))); } }