DATAREST-78 - Fixed customization of query methods through @RestResource(path="…").
This commit is contained in:
committed by
Oliver Gierke
parent
db2bbfca0e
commit
7a1151432d
@@ -1,5 +1,21 @@
|
||||
/*
|
||||
* Copyright 2012-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.support;
|
||||
|
||||
import static org.springframework.data.rest.repository.support.ResourceStringUtils.*;
|
||||
import static org.springframework.core.annotation.AnnotationUtils.*;
|
||||
import static org.springframework.util.StringUtils.*;
|
||||
|
||||
@@ -14,136 +30,130 @@ import org.springframework.data.rest.repository.annotation.RestResource;
|
||||
|
||||
/**
|
||||
* Helper methods to get the default rel and path values or to use values supplied by annotations.
|
||||
*
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
* @author Florent Biville
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class ResourceMappingUtils {
|
||||
|
||||
protected ResourceMappingUtils() {
|
||||
}
|
||||
protected ResourceMappingUtils() {}
|
||||
|
||||
public static String findRel(Class<?> type) {
|
||||
RestResource anno;
|
||||
if(null != (anno = findAnnotation(type, RestResource.class))) {
|
||||
if(hasText(anno.rel())) {
|
||||
return anno.rel();
|
||||
}
|
||||
}
|
||||
public static String findRel(Class<?> type) {
|
||||
|
||||
return uncapitalize(type.getSimpleName().replaceAll("Repository", ""));
|
||||
}
|
||||
RestResource anno = findAnnotation(type, RestResource.class);
|
||||
if (anno != null) {
|
||||
if (hasText(anno.rel())) {
|
||||
return anno.rel();
|
||||
}
|
||||
}
|
||||
|
||||
public static String findRel(Method method) {
|
||||
RestResource anno;
|
||||
if(null != (anno = findAnnotation(method, RestResource.class))) {
|
||||
if(hasText(anno.rel())) {
|
||||
return anno.rel();
|
||||
}
|
||||
}
|
||||
return uncapitalize(type.getSimpleName().replaceAll("Repository", ""));
|
||||
}
|
||||
|
||||
return method.getName();
|
||||
}
|
||||
public static String findRel(Method method) {
|
||||
|
||||
public static String formatRel(RepositoryRestConfiguration config,
|
||||
RepositoryInformation repoInfo,
|
||||
PersistentProperty<?> persistentProperty) {
|
||||
if(null == persistentProperty) {
|
||||
return null;
|
||||
}
|
||||
RestResource anno = findAnnotation(method, RestResource.class);
|
||||
|
||||
ResourceMapping repoMapping = getResourceMapping(config, repoInfo);
|
||||
ResourceMapping entityMapping = getResourceMapping(config, persistentProperty.getOwner());
|
||||
ResourceMapping propertyMapping = entityMapping.getResourceMappingFor(persistentProperty.getName());
|
||||
if (anno != null) {
|
||||
if (hasText(anno.rel())) {
|
||||
return anno.rel();
|
||||
}
|
||||
}
|
||||
|
||||
return String.format("%s.%s.%s",
|
||||
repoMapping.getRel(),
|
||||
entityMapping.getRel(),
|
||||
(null != propertyMapping ? propertyMapping.getRel() : persistentProperty.getName()));
|
||||
}
|
||||
return method.getName();
|
||||
}
|
||||
|
||||
public static String findPath(Class<?> type) {
|
||||
RestResource anno;
|
||||
if(null != (anno = findAnnotation(type, RestResource.class))) {
|
||||
if(hasText(anno.path())) {
|
||||
return anno.path();
|
||||
}
|
||||
}
|
||||
public static String formatRel(RepositoryRestConfiguration config, RepositoryInformation repoInfo,
|
||||
PersistentProperty<?> persistentProperty) {
|
||||
|
||||
if (persistentProperty == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return uncapitalize(type.getSimpleName().replaceAll("Repository", ""));
|
||||
}
|
||||
ResourceMapping repoMapping = getResourceMapping(config, repoInfo);
|
||||
ResourceMapping entityMapping = getResourceMapping(config, persistentProperty.getOwner());
|
||||
ResourceMapping propertyMapping = entityMapping.getResourceMappingFor(persistentProperty.getName());
|
||||
|
||||
public static String findPath(Method method) {
|
||||
RestResource anno;
|
||||
if(null != (anno = findAnnotation(method, RestResource.class))) {
|
||||
if(hasText(anno.path())) {
|
||||
return anno.path();
|
||||
}
|
||||
}
|
||||
return String.format("%s.%s.%s", repoMapping.getRel(), entityMapping.getRel(),
|
||||
(null != propertyMapping ? propertyMapping.getRel() : persistentProperty.getName()));
|
||||
}
|
||||
|
||||
return method.getName();
|
||||
}
|
||||
public static String findPath(Class<?> type) {
|
||||
|
||||
RestResource anno = findAnnotation(type, RestResource.class);
|
||||
|
||||
if (anno != null) {
|
||||
if (hasTextExceptSlash(anno.path())) {
|
||||
return removeLeadingSlash(anno.path());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean findExported(Class<?> type) {
|
||||
RestResource anno;
|
||||
return null == (anno = findAnnotation(type, RestResource.class)) || anno.exported();
|
||||
}
|
||||
return uncapitalize(type.getSimpleName().replaceAll("Repository", ""));
|
||||
}
|
||||
|
||||
public static boolean findExported(Method method) {
|
||||
RestResource anno;
|
||||
return null == (anno = findAnnotation(method, RestResource.class)) || anno.exported();
|
||||
}
|
||||
public static String findPath(Method method) {
|
||||
|
||||
RestResource anno = findAnnotation(method, RestResource.class);
|
||||
|
||||
if (anno != null) {
|
||||
if (hasTextExceptSlash(anno.path())) {
|
||||
return removeLeadingSlash(anno.path());
|
||||
}
|
||||
}
|
||||
|
||||
public static ResourceMapping getResourceMapping(RepositoryRestConfiguration config,
|
||||
RepositoryInformation repoInfo) {
|
||||
if(null == repoInfo) {
|
||||
return null;
|
||||
}
|
||||
Class<?> repoType = repoInfo.getRepositoryInterface();
|
||||
ResourceMapping mapping = (null != config ? config.getResourceMappingForRepository(repoType) : null);
|
||||
return merge(repoType, mapping);
|
||||
}
|
||||
return method.getName();
|
||||
}
|
||||
|
||||
public static ResourceMapping getResourceMapping(RepositoryRestConfiguration config,
|
||||
PersistentEntity<?, ?> persistentEntity) {
|
||||
if(null == persistentEntity) {
|
||||
return null;
|
||||
}
|
||||
Class<?> domainType = persistentEntity.getType();
|
||||
ResourceMapping mapping = (null != config ? config.getResourceMappingForDomainType(domainType) : null);
|
||||
return merge(domainType, mapping);
|
||||
}
|
||||
public static boolean findExported(Class<?> type) {
|
||||
RestResource anno = findAnnotation(type, RestResource.class);
|
||||
return anno == null || anno.exported();
|
||||
}
|
||||
|
||||
public static ResourceMapping merge(Method method, ResourceMapping mapping) {
|
||||
ResourceMapping defaultMapping = new ResourceMapping(
|
||||
findRel(method),
|
||||
findPath(method),
|
||||
findExported(method)
|
||||
);
|
||||
if(null != mapping) {
|
||||
return new ResourceMapping(
|
||||
(null != mapping.getRel() ? mapping.getRel() : defaultMapping.getRel()),
|
||||
(null != mapping.getPath() ? mapping.getPath() : defaultMapping.getPath()),
|
||||
(mapping.isExported() != defaultMapping.isExported() ? mapping.isExported() : defaultMapping.isExported())
|
||||
);
|
||||
}
|
||||
return defaultMapping;
|
||||
}
|
||||
public static boolean findExported(Method method) {
|
||||
RestResource anno = findAnnotation(method, RestResource.class);
|
||||
return anno == null || anno.exported();
|
||||
}
|
||||
|
||||
public static ResourceMapping merge(Class<?> type, ResourceMapping mapping) {
|
||||
ResourceMapping defaultMapping = new ResourceMapping(
|
||||
findRel(type),
|
||||
findPath(type),
|
||||
findExported(type)
|
||||
);
|
||||
if(null != mapping) {
|
||||
return new ResourceMapping(
|
||||
(null != mapping.getRel() ? mapping.getRel() : defaultMapping.getRel()),
|
||||
(null != mapping.getPath() ? mapping.getPath() : defaultMapping.getPath()),
|
||||
(mapping.isExported() != defaultMapping.isExported() ? mapping.isExported() : defaultMapping.isExported()))
|
||||
.addResourceMappings(mapping.getResourceMappings());
|
||||
}
|
||||
return defaultMapping;
|
||||
}
|
||||
public static ResourceMapping getResourceMapping(RepositoryRestConfiguration config, RepositoryInformation repoInfo) {
|
||||
if (null == repoInfo) {
|
||||
return null;
|
||||
}
|
||||
Class<?> repoType = repoInfo.getRepositoryInterface();
|
||||
ResourceMapping mapping = (null != config ? config.getResourceMappingForRepository(repoType) : null);
|
||||
return merge(repoType, mapping);
|
||||
}
|
||||
|
||||
public static ResourceMapping getResourceMapping(RepositoryRestConfiguration config,
|
||||
PersistentEntity<?, ?> persistentEntity) {
|
||||
if (null == persistentEntity) {
|
||||
return null;
|
||||
}
|
||||
Class<?> domainType = persistentEntity.getType();
|
||||
ResourceMapping mapping = (null != config ? config.getResourceMappingForDomainType(domainType) : null);
|
||||
return merge(domainType, mapping);
|
||||
}
|
||||
|
||||
public static ResourceMapping merge(Method method, ResourceMapping mapping) {
|
||||
ResourceMapping defaultMapping = new ResourceMapping(findRel(method), findPath(method), findExported(method));
|
||||
if (null != mapping) {
|
||||
return new ResourceMapping((null != mapping.getRel() ? mapping.getRel() : defaultMapping.getRel()),
|
||||
(null != mapping.getPath() ? mapping.getPath() : defaultMapping.getPath()),
|
||||
(mapping.isExported() != defaultMapping.isExported() ? mapping.isExported() : defaultMapping.isExported()));
|
||||
}
|
||||
return defaultMapping;
|
||||
}
|
||||
|
||||
public static ResourceMapping merge(Class<?> type, ResourceMapping mapping) {
|
||||
ResourceMapping defaultMapping = new ResourceMapping(findRel(type), findPath(type), findExported(type));
|
||||
if (null != mapping) {
|
||||
return new ResourceMapping((null != mapping.getRel() ? mapping.getRel() : defaultMapping.getRel()),
|
||||
(null != mapping.getPath() ? mapping.getPath() : defaultMapping.getPath()),
|
||||
(mapping.isExported() != defaultMapping.isExported() ? mapping.isExported() : defaultMapping.isExported()))
|
||||
.addResourceMappings(mapping.getResourceMappings());
|
||||
}
|
||||
return defaultMapping;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
/**
|
||||
* Helper methods aiming at handling String representations of resources.
|
||||
*
|
||||
* @author Florent Biville
|
||||
*/
|
||||
public class ResourceStringUtils {
|
||||
|
||||
/**
|
||||
* Checks whether the given input contains actual text (slash excluded). This is a specializing variant of
|
||||
* {@link org.springframework.util.StringUtils )}#hasText.
|
||||
*
|
||||
* @param input
|
||||
*/
|
||||
public static boolean hasTextExceptSlash(CharSequence input) {
|
||||
|
||||
int strLen = input.length();
|
||||
|
||||
for (int i = 0; i < strLen; i++) {
|
||||
if (!Character.isWhitespace(input.charAt(i)) && !startsWithSlash(input.charAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string without the leading slash, if any.
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
public static String removeLeadingSlash(String path) {
|
||||
|
||||
if (path.length() == 0) {
|
||||
return path;
|
||||
}
|
||||
|
||||
boolean hasLeadingSlash = startsWithSlash(path);
|
||||
|
||||
if (path.length() == 1) {
|
||||
return hasLeadingSlash ? "" : path;
|
||||
}
|
||||
|
||||
return hasLeadingSlash ? path.substring(1) : path;
|
||||
}
|
||||
|
||||
private static boolean startsWithSlash(String path) {
|
||||
return path.charAt(0) == '/';
|
||||
}
|
||||
|
||||
private static boolean startsWithSlash(char c) {
|
||||
return c == '/';
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012-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.config;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.springframework.data.rest.repository.support.ResourceMappingUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
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.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.ResourceMapping;
|
||||
import org.springframework.data.rest.repository.mapping.ResourceMappingFactory;
|
||||
@@ -15,6 +38,7 @@ import org.springframework.data.rest.repository.support.SimpleRelProvider;
|
||||
*
|
||||
* @author Jon Brisbin
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ResourceMappingUnitTests {
|
||||
|
||||
ResourceMappingFactory factory = new ResourceMappingFactory(new SimpleRelProvider());
|
||||
@@ -38,4 +62,62 @@ public class ResourceMappingUnitTests {
|
||||
assertThat(newMapping.getPath(), is("person"));
|
||||
assertThat(newMapping.isExported(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetectPathAndRemoveLeadingSlashIfAny() {
|
||||
org.springframework.data.rest.config.ResourceMapping mapping = new org.springframework.data.rest.config.ResourceMapping(
|
||||
findRel(AnnotatedWithLeadingSlashPersonRepository.class),
|
||||
findPath(AnnotatedWithLeadingSlashPersonRepository.class),
|
||||
findExported(AnnotatedWithLeadingSlashPersonRepository.class)
|
||||
);
|
||||
|
||||
// The rel attribute defaults to class name
|
||||
assertThat(mapping.getRel(), is("annotatedWithLeadingSlashPerson"));
|
||||
assertThat(mapping.getPath(), is("people"));
|
||||
// The exported defaults to true
|
||||
assertThat(mapping.isExported(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetectPathAndRemoveLeadingSlashIfAnyOnMethod() throws Exception {
|
||||
Method method = AnnotatedWithLeadingSlashPersonRepository.class.getMethod("findByFirstName", String.class, Pageable.class);
|
||||
org.springframework.data.rest.config.ResourceMapping mapping = new org.springframework.data.rest.config.ResourceMapping(
|
||||
findRel(method),
|
||||
findPath(method),
|
||||
findExported(method)
|
||||
);
|
||||
|
||||
// The rel attribute defaults to class name
|
||||
assertThat(mapping.getRel(), is("findByFirstName"));
|
||||
assertThat(mapping.getPath(), is("firstname"));
|
||||
// The exported defaults to true
|
||||
assertThat(mapping.isExported(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnDefaultIfPathContainsOnlySlashTextOnMethod() throws Exception {
|
||||
Method method = AnnotatedWithLeadingSlashPersonRepository.class.getMethod("findByLastName", String.class, Pageable.class);
|
||||
org.springframework.data.rest.config.ResourceMapping mapping = new org.springframework.data.rest.config.ResourceMapping(
|
||||
findRel(method),
|
||||
findPath(method),
|
||||
findExported(method)
|
||||
);
|
||||
|
||||
// The rel defaults to method name
|
||||
assertThat(mapping.getRel(), is("findByLastName"));
|
||||
// The path contains only a leading slash therefore defaults to method name
|
||||
assertThat(mapping.getPath(), is("findByLastName"));
|
||||
// The exported defaults to true
|
||||
assertThat(mapping.isExported(), is(true));
|
||||
}
|
||||
|
||||
@RestResource(path = "/people")
|
||||
interface AnnotatedWithLeadingSlashPersonRepository {
|
||||
|
||||
@RestResource(path = "/firstname")
|
||||
Page<Person> findByFirstName(@Param("firstName") String firstName, Pageable pageable);
|
||||
|
||||
@RestResource(path = " / ")
|
||||
Page<Person> findByLastName(@Param("lastName") String firstName, Pageable pageable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
/**
|
||||
* Ensures proper detection and removal of leading slash in strings.
|
||||
*
|
||||
* @author Florent Biville
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class ResourceStringUtilsTest {
|
||||
|
||||
final String actual;
|
||||
final String expected;
|
||||
final boolean hasText;
|
||||
|
||||
public ResourceStringUtilsTest(String testDescription, String actual, String expected, boolean hasText) {
|
||||
|
||||
this.actual = actual;
|
||||
this.expected = expected;
|
||||
this.hasText = hasText;
|
||||
}
|
||||
|
||||
@Parameters(name = "{0}")
|
||||
public static Collection<?> parameters() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ "empty string has no text and should remain empty", "", "", false },
|
||||
{ "blank string has no text and should remain as is", " ", " ", false },
|
||||
{ "string made of only a leading slash has no text and should be returned empty", "/", "", false },
|
||||
{ "blank string with only slashes has no text and should be returned as is", " / ", " / ", false },
|
||||
{ "normal string has text and should be returned as such", "hello", "hello", true },
|
||||
{ "normal string with leading slash has text and should be returned without leading slash", "/hello", "hello",
|
||||
true }, });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetectTextPresence() {
|
||||
assertThat(ResourceStringUtils.hasTextExceptSlash(actual), is(hasText));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRemoveLeadingSlashIfAny() {
|
||||
assertThat(ResourceStringUtils.removeLeadingSlash(actual), is(expected));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user