DATAREST-1176 - Add explicitly method annotated detection strategy

This commit is contained in:
Tobias Weiß
2018-01-12 16:35:02 +01:00
committed by Oliver Gierke
parent b01ea05164
commit d688e70ade
8 changed files with 72 additions and 13 deletions

View File

@@ -19,7 +19,6 @@ import static org.springframework.data.rest.core.mapping.ResourceType.*;
import static org.springframework.http.HttpMethod.*;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.lang.reflect.Method;
import java.util.Collections;
@@ -49,11 +48,14 @@ public class CrudMethodsSupportedHttpMethods implements SupportedHttpMethods {
*
* @param crudMethods must not be {@literal null}.
*/
public CrudMethodsSupportedHttpMethods(CrudMethods crudMethods) {
public CrudMethodsSupportedHttpMethods(CrudMethods crudMethods, RepositoryResourceMappings provider) {
Assert.notNull(crudMethods, "CrudMethods must not be null!");
this.exposedMethods = new DefaultExposureAwareCrudMethods(crudMethods);
boolean exportedDefault = provider.getRepositoryDetectionStrategy()
!= RepositoryDetectionStrategy.RepositoryDetectionStrategies.EXPLICIT_METHOD_ANNOTATED;
this.exposedMethods = new DefaultExposureAwareCrudMethods(crudMethods, exportedDefault);
}
/*
@@ -139,10 +141,15 @@ public class CrudMethodsSupportedHttpMethods implements SupportedHttpMethods {
/**
* @author Oliver Gierke
*/
@RequiredArgsConstructor
private static class DefaultExposureAwareCrudMethods implements ExposureAwareCrudMethods {
private final @NonNull CrudMethods crudMethods;
private final boolean exportedDefault;
DefaultExposureAwareCrudMethods(CrudMethods crudMethods, boolean exportedDefault) {
this.crudMethods = crudMethods;
this.exportedDefault = exportedDefault;
}
/*
* (non-Javadoc)
@@ -180,12 +187,12 @@ public class CrudMethodsSupportedHttpMethods implements SupportedHttpMethods {
return exposes(crudMethods.getFindAllMethod());
}
private static boolean exposes(Optional<Method> method) {
private boolean exposes(Optional<Method> method) {
return method.map(it -> {
RestResource annotation = AnnotationUtils.findAnnotation(it, RestResource.class);
return annotation == null ? true : annotation.exported();
return annotation == null ? exportedDefault : annotation.exported();
}).orElse(false);
}

View File

@@ -57,7 +57,7 @@ class RepositoryAwareResourceMetadata implements ResourceMetadata {
this.mapping = mapping;
this.provider = provider;
this.repositoryMetadata = repositoryMetadata;
this.crudMethodsSupportedHttpMethods = new CrudMethodsSupportedHttpMethods(repositoryMetadata.getCrudMethods());
this.crudMethodsSupportedHttpMethods = new CrudMethodsSupportedHttpMethods(repositoryMetadata.getCrudMethods(), provider);
}
/**

View File

@@ -92,6 +92,18 @@ public interface RepositoryDetectionStrategy {
*/
ANNOTATED {
@Override
public boolean isExported(RepositoryMetadata metadata) {
return isExplicitlyExported(metadata.getRepositoryInterface(), false);
}
},
/**
* Behaves like the annotated strategy on repository level. But it does not export all methods
* of an exported Repository. The methods have to be annotated explicitly too.
*/
EXPLICIT_METHOD_ANNOTATED {
@Override
public boolean isExported(RepositoryMetadata metadata) {
return isExplicitlyExported(metadata.getRepositoryInterface(), false);

View File

@@ -62,7 +62,8 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
* @param method must not be {@literal null}.
* @param resourceMapping must not be {@literal null}.
*/
public RepositoryMethodResourceMapping(Method method, ResourceMapping resourceMapping, RepositoryMetadata metadata) {
public RepositoryMethodResourceMapping(Method method, ResourceMapping resourceMapping, RepositoryMetadata metadata,
RepositoryDetectionStrategy strategy) {
Assert.notNull(method, "Method must not be null!");
Assert.notNull(resourceMapping, "ResourceMapping must not be null!");
@@ -70,7 +71,7 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
RestResource annotation = AnnotationUtils.findAnnotation(method, RestResource.class);
String resourceRel = resourceMapping.getRel();
this.isExported = annotation != null ? annotation.exported() : true;
this.isExported = determineIsExported(strategy, annotation);
this.rel = annotation == null || !StringUtils.hasText(annotation.rel()) ? method.getName() : annotation.rel();
this.path = annotation == null || !StringUtils.hasText(annotation.path()) ? new Path(method.getName())
: new Path(annotation.path());
@@ -84,6 +85,14 @@ class RepositoryMethodResourceMapping implements MethodResourceMapping {
this.metadata = metadata;
}
private boolean determineIsExported(RepositoryDetectionStrategy strategy, RestResource annotation) {
boolean exportedDefault = strategy
!= RepositoryDetectionStrategy.RepositoryDetectionStrategies.EXPLICIT_METHOD_ANNOTATED;
return annotation != null ? annotation.exported() : exportedDefault;
}
private static final List<ParameterMetadata> discoverParameterMetadata(Method method, String baseRel) {
List<ParameterMetadata> result = new ArrayList<ParameterMetadata>();

View File

@@ -40,6 +40,7 @@ import org.springframework.util.Assert;
public class RepositoryResourceMappings extends PersistentEntitiesResourceMappings {
private final Repositories repositories;
private final RepositoryDetectionStrategy strategy;
private final Map<Class<?>, SearchResourceMappings> searchCache = new HashMap<Class<?>, SearchResourceMappings>();
/**
@@ -73,6 +74,7 @@ public class RepositoryResourceMappings extends PersistentEntitiesResourceMappin
Assert.notNull(strategy, "RepositoryDetectionStrategy must not be null!");
this.repositories = repositories;
this.strategy = strategy;
this.populateCache(repositories, relProvider, strategy);
}
@@ -118,7 +120,7 @@ public class RepositoryResourceMappings extends PersistentEntitiesResourceMappin
if (resourceMapping.isExported()) {
for (Method queryMethod : repositoryInformation.getQueryMethods()) {
RepositoryMethodResourceMapping methodMapping = new RepositoryMethodResourceMapping(queryMethod,
resourceMapping, repositoryInformation);
resourceMapping, repositoryInformation, strategy);
if (methodMapping.isExported()) {
mappings.add(methodMapping);
}
@@ -156,4 +158,8 @@ public class RepositoryResourceMappings extends PersistentEntitiesResourceMappin
public boolean isMapped(PersistentProperty<?> property) {
return repositories.hasRepositoryFor(property.getActualType()) && super.isMapped(property);
}
public RepositoryDetectionStrategy getRepositoryDetectionStrategy() {
return strategy;
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.rest.core.mapping;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.rest.core.mapping.ResourceType.*;
import static org.springframework.http.HttpMethod.*;
@@ -23,8 +24,10 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Reference;
@@ -47,6 +50,14 @@ import org.springframework.http.HttpMethod;
@RunWith(MockitoJUnitRunner.class)
public class CrudMethodsSupportedHttpMethodsUnitTests {
@Mock
private RepositoryResourceMappings mappings;
@Before
public void setUp() {
when(mappings.exposeMethodsByDefault()).thenReturn(true);
}
@Test // DATACMNS-589, DATAREST-409
public void doesNotSupportAnyHttpMethodForEmptyRepository() {
@@ -118,12 +129,12 @@ public class CrudMethodsSupportedHttpMethodsUnitTests {
assertMethodsSupported(getSupportedHttpMethodsFor(NoFindOne.class), ITEM, false, DELETE);
}
private static SupportedHttpMethods getSupportedHttpMethodsFor(Class<?> repositoryInterface) {
private SupportedHttpMethods getSupportedHttpMethodsFor(Class<?> repositoryInterface) {
RepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
CrudMethods crudMethods = new DefaultCrudMethods(metadata);
return new CrudMethodsSupportedHttpMethods(crudMethods);
return new CrudMethodsSupportedHttpMethods(crudMethods, mappings);
}
private static void assertMethodsSupported(SupportedHttpMethods methods, ResourceType type, boolean supported,

View File

@@ -89,6 +89,19 @@ public class RepositoryDetectionStrategiesUnitTests {
});
}
@Test // DATAREST-1176
public void onlyExplicitAnnotatedMethodsAreExposed() {
assertExposures(EXPLICIT_METHOD_ANNOTATED, new HashMap<Class<?>, Boolean>() {
{
put(AnnotatedRepository.class, true);
put(HiddenRepository.class, false);
put(PublicRepository.class, false);
put(PackageProtectedRepository.class, false);
}
});
}
private static void assertExposures(RepositoryDetectionStrategy strategy, Map<Class<?>, Boolean> expected) {
for (Entry<Class<?>, Boolean> entry : expected.entrySet()) {

View File

@@ -131,7 +131,8 @@ public class RepositoryMethodResourceMappingUnitTests {
}
private RepositoryMethodResourceMapping getMappingFor(Method method) {
return new RepositoryMethodResourceMapping(method, resourceMapping, metadata);
RepositoryDetectionStrategy strategy = RepositoryDetectionStrategies.DEFAULT;
return new RepositoryMethodResourceMapping(method, resourceMapping, metadata, strategy);
}
static class Person {}