DATAREST-473 - Introduced configuration option to control default exposure of repositories.
RepositoryRestConfiguration now exposes a setRepositoryDetectionStrategy(…) to define which repositories should be detected for exposure by default. The default value for that will consider the repository interfaces visibility but also take the exported flag of @(Repository)RestResource into account. See all other options in RepositoryDetectionStrategies. Tweaked the auto-registration of excerpt projections to avoid a circular dependency between configuration and resource mappings and moved it onto a BeanPostProcessor implementation.
This commit is contained in:
@@ -15,13 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.rest.core.config;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.data.rest.core.projection.ProjectionDefinitions;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -44,28 +42,7 @@ public class ProjectionDefinitionConfiguration implements ProjectionDefinitions
|
||||
* Creates a new {@link ProjectionDefinitionConfiguration}.
|
||||
*/
|
||||
public ProjectionDefinitionConfiguration() {
|
||||
this(Collections.<ResourceMetadata> emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionDefinitionConfiguration} from the given {@link ResourceMetadata} instances.
|
||||
*
|
||||
* @param resourceMetadata must not be {@literal null}.
|
||||
*/
|
||||
public ProjectionDefinitionConfiguration(Iterable<ResourceMetadata> resourceMetadata) {
|
||||
|
||||
Assert.notNull(resourceMetadata, "ResourceMetadata must not be null!");
|
||||
|
||||
this.projectionDefinitions = new HashMap<ProjectionDefinitionKey, Class<?>>();
|
||||
|
||||
for (ResourceMetadata metadata : resourceMetadata) {
|
||||
|
||||
Class<?> projection = metadata.getExcerptProjection();
|
||||
|
||||
if (projection != null) {
|
||||
addProjection(projection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -106,8 +83,8 @@ public class ProjectionDefinitionConfiguration implements ProjectionDefinitions
|
||||
String name = annotation.name();
|
||||
Class<?>[] sourceTypes = annotation.types();
|
||||
|
||||
return StringUtils.hasText(name) ? addProjection(projectionType, name, sourceTypes) : addProjection(projectionType,
|
||||
sourceTypes);
|
||||
return StringUtils.hasText(name) ? addProjection(projectionType, name, sourceTypes)
|
||||
: addProjection(projectionType, sourceTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +109,8 @@ public class ProjectionDefinitionConfiguration implements ProjectionDefinitions
|
||||
* @param sourceTypes must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public ProjectionDefinitionConfiguration addProjection(Class<?> projectionType, String name, Class<?>... sourceTypes) {
|
||||
public ProjectionDefinitionConfiguration addProjection(Class<?> projectionType, String name,
|
||||
Class<?>... sourceTypes) {
|
||||
|
||||
Assert.notNull(projectionType, "Projection type must not be null!");
|
||||
Assert.hasText(name, "Name must not be null or empty!");
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy;
|
||||
import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy.RepositoryDetectionStrategies;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -52,10 +54,12 @@ public class RepositoryRestConfiguration {
|
||||
private List<Class<?>> exposeIdsFor = new ArrayList<Class<?>>();
|
||||
private ResourceMappingConfiguration domainMappings = new ResourceMappingConfiguration();
|
||||
private ResourceMappingConfiguration repoMappings = new ResourceMappingConfiguration();
|
||||
private RepositoryDetectionStrategy repositoryDetectionStrategy = RepositoryDetectionStrategies.DEFAULT;
|
||||
|
||||
private final ProjectionDefinitionConfiguration projectionConfiguration;
|
||||
private final MetadataConfiguration metadataConfiguration;
|
||||
|
||||
private final EnumTranslationConfiguration enumSerializationConfiguration;
|
||||
private final EnumTranslationConfiguration enumTranslationConfiguration;
|
||||
private boolean enableEnumTranslation = false;
|
||||
|
||||
/**
|
||||
@@ -63,17 +67,18 @@ public class RepositoryRestConfiguration {
|
||||
*
|
||||
* @param projectionConfiguration must not be {@literal null}.
|
||||
* @param metadataConfiguration must not be {@literal null}.
|
||||
* @param enumTranslationConfiguration must not be {@literal null}.
|
||||
*/
|
||||
public RepositoryRestConfiguration(ProjectionDefinitionConfiguration projectionConfiguration,
|
||||
MetadataConfiguration metadataConfiguration, EnumTranslationConfiguration enumTranslationConfiguration) {
|
||||
|
||||
Assert.notNull(projectionConfiguration, "ProjectionDefinitionConfiguration must not be null!");
|
||||
Assert.notNull(metadataConfiguration, "MetadataConfiguration must not be null!");
|
||||
Assert.notNull(enumTranslationConfiguration, " must not be null!");
|
||||
Assert.notNull(enumTranslationConfiguration, "EnumTranslationConfiguration must not be null!");
|
||||
|
||||
this.projectionConfiguration = projectionConfiguration;
|
||||
this.metadataConfiguration = metadataConfiguration;
|
||||
this.enumSerializationConfiguration = enumTranslationConfiguration;
|
||||
this.enumTranslationConfiguration = enumTranslationConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,7 +494,7 @@ public class RepositoryRestConfiguration {
|
||||
* details see {@link EnumTranslator}.
|
||||
*
|
||||
* @param enableEnumTranslation
|
||||
* @see #getEnumSerializationConfiguration()
|
||||
* @see #getEnumTranslationConfiguration()
|
||||
*/
|
||||
public void setEnableEnumTranslation(boolean enableEnumTranslation) {
|
||||
this.enableEnumTranslation = enableEnumTranslation;
|
||||
@@ -499,17 +504,43 @@ public class RepositoryRestConfiguration {
|
||||
* Returns whether enum value translation is enabled.
|
||||
*
|
||||
* @return
|
||||
* @since 2.4
|
||||
*/
|
||||
public boolean isEnableEnumTranslation() {
|
||||
return this.enableEnumTranslation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EnumTranslator} for
|
||||
* Returns the {@link EnumTranslationConfiguration} to be used.
|
||||
*
|
||||
* @return
|
||||
* @return must not be {@literal null}.
|
||||
* @since 2.4
|
||||
*/
|
||||
public EnumTranslationConfiguration getEnumSerializationConfiguration() {
|
||||
return this.enumSerializationConfiguration;
|
||||
public EnumTranslationConfiguration getEnumTranslationConfiguration() {
|
||||
return this.enumTranslationConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RepositoryDetectionStrategy} to be used to decide which repositories get exposed. Will be
|
||||
* {@link RepositoryDetectionStrategies#DEFAULT} by default.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
* @see RepositoryDetectionStrategies
|
||||
* @since 2.5
|
||||
*/
|
||||
public RepositoryDetectionStrategy getRepositoryDetectionStrategy() {
|
||||
return repositoryDetectionStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link RepositoryDetectionStrategy} to be used to determine which repositories get exposed. Defaults
|
||||
* to {@link RepositoryDetectionStrategies#DEFAULT}.
|
||||
*
|
||||
* @param repositoryDetectionStrategy can be {@literal null}.
|
||||
* @since 2.5
|
||||
*/
|
||||
public void setRepositoryDetectionStrategy(RepositoryDetectionStrategy repositoryDetectionStrategy) {
|
||||
this.repositoryDetectionStrategy = repositoryDetectionStrategy == null ? RepositoryDetectionStrategies.DEFAULT
|
||||
: repositoryDetectionStrategy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.rest.core.mapping;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
@@ -45,11 +43,11 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
|
||||
private final RestResource annotation;
|
||||
private final RepositoryRestResource repositoryAnnotation;
|
||||
private final CollectionResourceMapping domainTypeMapping;
|
||||
private final boolean repositoryIsExportCandidate;
|
||||
private final boolean repositoryExported;
|
||||
private final RepositoryMetadata metadata;
|
||||
|
||||
public RepositoryCollectionResourceMapping(RepositoryMetadata metadata) {
|
||||
this(metadata, new EvoInflectorRelProvider());
|
||||
public RepositoryCollectionResourceMapping(RepositoryMetadata metadata, RepositoryDetectionStrategy strategy) {
|
||||
this(metadata, new EvoInflectorRelProvider(), strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,22 +56,26 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
|
||||
*
|
||||
* @param repositoryType must not be {@literal null}.
|
||||
* @param relProvider must not be {@literal null}.
|
||||
* @param strategy must not be {@literal null}.
|
||||
*/
|
||||
public RepositoryCollectionResourceMapping(RepositoryMetadata metadata, RelProvider relProvider) {
|
||||
RepositoryCollectionResourceMapping(RepositoryMetadata metadata, RelProvider relProvider,
|
||||
RepositoryDetectionStrategy strategy) {
|
||||
|
||||
Assert.notNull(metadata, "Repository metadata must not be null!");
|
||||
Assert.notNull(relProvider, "RelProvider must not be null!");
|
||||
Assert.notNull(strategy, "RepositoryDetectionStrategy must not be null!");
|
||||
|
||||
Class<?> repositoryType = metadata.getRepositoryInterface();
|
||||
|
||||
this.metadata = metadata;
|
||||
this.annotation = AnnotationUtils.findAnnotation(repositoryType, RestResource.class);
|
||||
this.repositoryAnnotation = AnnotationUtils.findAnnotation(repositoryType, RepositoryRestResource.class);
|
||||
this.repositoryIsExportCandidate = Modifier.isPublic(repositoryType.getModifiers());
|
||||
this.repositoryExported = strategy.isExported(metadata);
|
||||
|
||||
Class<?> domainType = metadata.getDomainType();
|
||||
this.domainTypeMapping = EVO_INFLECTOR_IS_PRESENT ? new EvoInflectorTypeBasedCollectionResourceMapping(domainType,
|
||||
relProvider) : new TypeBasedCollectionResourceMapping(domainType, relProvider);
|
||||
this.domainTypeMapping = EVO_INFLECTOR_IS_PRESENT
|
||||
? new EvoInflectorTypeBasedCollectionResourceMapping(domainType, relProvider)
|
||||
: new TypeBasedCollectionResourceMapping(domainType, relProvider);
|
||||
|
||||
if (annotation != null) {
|
||||
LOGGER.warn(
|
||||
@@ -149,16 +151,7 @@ class RepositoryCollectionResourceMapping implements CollectionResourceMapping {
|
||||
*/
|
||||
@Override
|
||||
public boolean isExported() {
|
||||
|
||||
if (repositoryAnnotation != null) {
|
||||
return repositoryAnnotation.exported();
|
||||
}
|
||||
|
||||
if (annotation != null) {
|
||||
return annotation.exported();
|
||||
}
|
||||
|
||||
return repositoryIsExportCandidate && domainTypeMapping.isExported();
|
||||
return repositoryExported;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.core.mapping;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
|
||||
/**
|
||||
* The strategy to determine whether a given repository is to be exported by Spring Data REST.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 2.5
|
||||
* @soundtrack Katinka - Ausverkauf
|
||||
*/
|
||||
public interface RepositoryDetectionStrategy {
|
||||
|
||||
/**
|
||||
* Returns whether the repository described by the given {@link RepositoryMetadata} is exported or not.
|
||||
*
|
||||
* @param metadata must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
boolean isExported(RepositoryMetadata metadata);
|
||||
|
||||
/**
|
||||
* A variety of strategies to determine repository exposure.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 2.5
|
||||
* @soundtrack Katinka - Ausverkauf
|
||||
*/
|
||||
public static enum RepositoryDetectionStrategies implements RepositoryDetectionStrategy {
|
||||
|
||||
/**
|
||||
* Considers all repositories.
|
||||
*/
|
||||
ALL {
|
||||
|
||||
@Override
|
||||
public boolean isExported(RepositoryMetadata metadata) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposes public interfaces or ones explicitly annotated with {@link RepositoryRestResource}.
|
||||
*
|
||||
* @see #VISIBILITY
|
||||
* @see #ANNOTATED
|
||||
*/
|
||||
DEFAULT {
|
||||
|
||||
@Override
|
||||
public boolean isExported(RepositoryMetadata metadata) {
|
||||
|
||||
return isExplicitlyExported(metadata.getRepositoryInterface(),
|
||||
isExplicitlyExported(metadata.getDomainType(), VISIBILITY.isExported(metadata)));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Considers the repository interface's visibility, which means only public interfaces will be exposed.
|
||||
*/
|
||||
VISIBILITY {
|
||||
|
||||
@Override
|
||||
public boolean isExported(RepositoryMetadata metadata) {
|
||||
return Modifier.isPublic(metadata.getRepositoryInterface().getModifiers());
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Considers repositories that are annotated with {@link RepositoryRestResource} or {@link RestResource} and don't
|
||||
* have the {@code exported} flag not set to {@literal false}.
|
||||
*/
|
||||
ANNOTATED {
|
||||
|
||||
@Override
|
||||
public boolean isExported(RepositoryMetadata metadata) {
|
||||
return isExplicitlyExported(metadata.getRepositoryInterface(), false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the given type was explicitly exported using {@link RepositoryRestResource} or
|
||||
* {@link RestResource}. In case no decision can be made based on the annotations, the fallback will be used.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param fallback
|
||||
* @return
|
||||
*/
|
||||
private static boolean isExplicitlyExported(Class<?> type, boolean fallback) {
|
||||
|
||||
RepositoryRestResource restResource = AnnotationUtils.findAnnotation(type, RepositoryRestResource.class);
|
||||
|
||||
if (restResource != null) {
|
||||
return restResource.exported();
|
||||
}
|
||||
|
||||
RestResource resource = AnnotationUtils.findAnnotation(type, RestResource.class);
|
||||
|
||||
if (resource != null) {
|
||||
return resource.exported();
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,9 +48,11 @@ public class RepositoryResourceMappings extends PersistentEntitiesResourceMappin
|
||||
*
|
||||
* @param repositories must not be {@literal null}.
|
||||
* @param entities must not be {@literal null}.
|
||||
* @param strategy must not be {@literal null}.
|
||||
*/
|
||||
public RepositoryResourceMappings(Repositories repositories, PersistentEntities entities) {
|
||||
this(repositories, entities, new EvoInflectorRelProvider());
|
||||
public RepositoryResourceMappings(Repositories repositories, PersistentEntities entities,
|
||||
RepositoryDetectionStrategy strategy) {
|
||||
this(repositories, entities, new EvoInflectorRelProvider(), strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,18 +62,22 @@ public class RepositoryResourceMappings extends PersistentEntitiesResourceMappin
|
||||
* @param repositories must not be {@literal null}.
|
||||
* @param entities must not be {@literal null}.
|
||||
* @param relProvider must not be {@literal null}.
|
||||
* @param strategy must not be {@literal null}.
|
||||
*/
|
||||
public RepositoryResourceMappings(Repositories repositories, PersistentEntities entities, RelProvider relProvider) {
|
||||
RepositoryResourceMappings(Repositories repositories, PersistentEntities entities, RelProvider relProvider,
|
||||
RepositoryDetectionStrategy strategy) {
|
||||
|
||||
super(entities);
|
||||
|
||||
Assert.notNull(repositories, "Repositories must not be null!");
|
||||
Assert.notNull(strategy, "RepositoryDetectionStrategy must not be null!");
|
||||
|
||||
this.repositories = repositories;
|
||||
this.populateCache(repositories, relProvider);
|
||||
this.populateCache(repositories, relProvider, strategy);
|
||||
}
|
||||
|
||||
private final void populateCache(Repositories repositories, RelProvider provider) {
|
||||
private final void populateCache(Repositories repositories, RelProvider provider,
|
||||
RepositoryDetectionStrategy strategy) {
|
||||
|
||||
for (Class<?> type : repositories) {
|
||||
|
||||
@@ -79,7 +85,8 @@ public class RepositoryResourceMappings extends PersistentEntitiesResourceMappin
|
||||
Class<?> repositoryInterface = repositoryInformation.getRepositoryInterface();
|
||||
PersistentEntity<?, ?> entity = repositories.getPersistentEntity(type);
|
||||
|
||||
CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(repositoryInformation, provider);
|
||||
CollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(repositoryInformation, provider,
|
||||
strategy);
|
||||
RepositoryAwareResourceMetadata information = new RepositoryAwareResourceMetadata(entity, mapping, this,
|
||||
repositoryInformation);
|
||||
|
||||
|
||||
@@ -17,14 +17,9 @@ package org.springframework.data.rest.core.config;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.rest.core.config.ProjectionDefinitionConfiguration.ProjectionDefinitionKey;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ProjectionDefinitionConfiguration}.
|
||||
@@ -155,19 +150,6 @@ public class ProjectionDefinitionConfigurationUnitTests {
|
||||
is(typeCompatibleWith(ParentProjection.class)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-577
|
||||
*/
|
||||
@Test
|
||||
public void registersExcerptProjectionsByDefault() {
|
||||
|
||||
ResourceMetadata metadata = mock(ResourceMetadata.class);
|
||||
doReturn(SampleProjection.class).when(metadata).getExcerptProjection();
|
||||
|
||||
assertThat(new ProjectionDefinitionConfiguration(Arrays.asList(metadata)).getProjectionsFor(Integer.class),
|
||||
Matchers.<String, Class<?>> hasEntry("name", SampleProjection.class));
|
||||
}
|
||||
|
||||
@Projection(name = "name", types = Integer.class)
|
||||
interface SampleProjection {}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
|
||||
import org.springframework.data.rest.core.Path;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy.RepositoryDetectionStrategies;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RepositoryCollectionResourceMapping}.
|
||||
@@ -105,7 +106,8 @@ public class RepositoryCollectionResourceMappingUnitTests {
|
||||
}
|
||||
};
|
||||
|
||||
RepositoryCollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(metadata);
|
||||
RepositoryCollectionResourceMapping mapping = new RepositoryCollectionResourceMapping(metadata,
|
||||
RepositoryDetectionStrategies.DEFAULT);
|
||||
|
||||
assertThat(mapping.getPath(), is(new Path("/objects")));
|
||||
}
|
||||
@@ -113,7 +115,7 @@ public class RepositoryCollectionResourceMappingUnitTests {
|
||||
private static CollectionResourceMapping getResourceMappingFor(Class<?> repositoryInterface) {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(repositoryInterface);
|
||||
return new RepositoryCollectionResourceMapping(metadata);
|
||||
return new RepositoryCollectionResourceMapping(metadata, RepositoryDetectionStrategies.DEFAULT);
|
||||
}
|
||||
|
||||
public static class Person {}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.rest.core.mapping;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy.RepositoryDetectionStrategies.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy.RepositoryDetectionStrategies;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RepositoryDetectionStrategies}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @soundtrack Katinka - Ausverkauf
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RepositoryDetectionStrategiesUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAREST-473
|
||||
*/
|
||||
@Test
|
||||
public void allExposesAllRepositories() {
|
||||
|
||||
assertExposures(ALL, new HashMap<Class<?>, Boolean>() {
|
||||
{
|
||||
put(AnnotatedRepository.class, true);
|
||||
put(HiddenRepository.class, true);
|
||||
put(PublicRepository.class, true);
|
||||
put(PackageProtectedRepository.class, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-473
|
||||
*/
|
||||
@Test
|
||||
public void defaultHonorsVisibilityAndAnnotations() {
|
||||
|
||||
assertExposures(DEFAULT, new HashMap<Class<?>, Boolean>() {
|
||||
{
|
||||
put(AnnotatedRepository.class, true);
|
||||
put(HiddenRepository.class, false);
|
||||
put(PublicRepository.class, true);
|
||||
put(PackageProtectedRepository.class, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-473
|
||||
*/
|
||||
@Test
|
||||
public void visibilityHonorsTypeVisibilityOnly() {
|
||||
|
||||
assertExposures(VISIBILITY, new HashMap<Class<?>, Boolean>() {
|
||||
{
|
||||
put(AnnotatedRepository.class, false);
|
||||
put(HiddenRepository.class, true);
|
||||
put(PublicRepository.class, true);
|
||||
put(PackageProtectedRepository.class, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-473
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void annotatedHonorsAnnotationsOnly() {
|
||||
|
||||
assertExposures(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()) {
|
||||
assertThat(strategy.isExported(new DefaultRepositoryMetadata(entry.getKey())), is(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
interface PackageProtectedRepository extends Repository<Object, Long> {}
|
||||
|
||||
public interface PublicRepository extends Repository<Object, Long> {}
|
||||
|
||||
@RepositoryRestResource
|
||||
interface AnnotatedRepository extends Repository<Object, Long> {}
|
||||
|
||||
@RepositoryRestResource(exported = false)
|
||||
public interface HiddenRepository extends Repository<Object, Long> {}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadat
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.core.Path;
|
||||
import org.springframework.data.rest.core.annotation.RestResource;
|
||||
import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy.RepositoryDetectionStrategies;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link RepositoryMethodResourceMapping}.
|
||||
@@ -39,7 +40,8 @@ import org.springframework.data.rest.core.annotation.RestResource;
|
||||
public class RepositoryMethodResourceMappingUnitTests {
|
||||
|
||||
RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class);
|
||||
RepositoryCollectionResourceMapping resourceMapping = new RepositoryCollectionResourceMapping(metadata);
|
||||
RepositoryCollectionResourceMapping resourceMapping = new RepositoryCollectionResourceMapping(metadata,
|
||||
RepositoryDetectionStrategies.DEFAULT);
|
||||
|
||||
@Test
|
||||
public void defaultsMappingToMethodName() throws Exception {
|
||||
|
||||
@@ -38,6 +38,8 @@ import org.springframework.data.rest.core.domain.jpa.Author;
|
||||
import org.springframework.data.rest.core.domain.jpa.CreditCard;
|
||||
import org.springframework.data.rest.core.domain.jpa.JpaRepositoryConfig;
|
||||
import org.springframework.data.rest.core.domain.jpa.Person;
|
||||
import org.springframework.data.rest.core.mapping.RepositoryDetectionStrategy.RepositoryDetectionStrategies;
|
||||
import org.springframework.hateoas.core.EvoInflectorRelProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -62,7 +64,8 @@ public class RepositoryResourceMappingsIntegrationTests {
|
||||
public void setUp() {
|
||||
|
||||
Repositories repositories = new Repositories(factory);
|
||||
this.mappings = new RepositoryResourceMappings(repositories, new PersistentEntities(Arrays.asList(mappingContext)));
|
||||
this.mappings = new RepositoryResourceMappings(repositories, new PersistentEntities(Arrays.asList(mappingContext)),
|
||||
new EvoInflectorRelProvider(), RepositoryDetectionStrategies.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2015 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.webmvc.config;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
|
||||
import org.springframework.data.rest.core.mapping.RepositoryResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMappings;
|
||||
import org.springframework.data.rest.core.mapping.ResourceMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to make sure all excerpt projections defined in {@link RepositoryResourceMappings} are
|
||||
* registered with the {@link RepositoryRestConfiguration}. This rather external configuration has been used to make
|
||||
* sure we don't introduce a cyclic dependency between {@link RepositoryRestConfiguration} an
|
||||
* {@link RepositoryResourceMappings} as the latter need access to the former to discover mappings in the first place.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @since 2.5
|
||||
* @soundtrack Katinka - Ausverkauf
|
||||
*/
|
||||
public class ProjectionDefinitionRegistar extends InstantiationAwareBeanPostProcessorAdapter {
|
||||
|
||||
private final ObjectFactory<RepositoryRestConfiguration> config;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionDefinitionRegistar} for the given {@link RepositoryRestConfiguration}.
|
||||
*
|
||||
* @param config must not be {@literal null}.
|
||||
*/
|
||||
public ProjectionDefinitionRegistar(ObjectFactory<RepositoryRestConfiguration> config) {
|
||||
|
||||
Assert.notNull(config, "RepositoryRestConfiguration must not be null!");
|
||||
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter#postProcessAfterInitialization(java.lang.Object, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (!(bean instanceof ResourceMappings)) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
ResourceMappings mappings = (ResourceMappings) bean;
|
||||
|
||||
for (ResourceMetadata resourceMetadata : mappings) {
|
||||
|
||||
Class<?> projection = resourceMetadata.getExcerptProjection();
|
||||
|
||||
if (projection != null) {
|
||||
config.getObject().getProjectionConfiguration().addProjection(projection);
|
||||
}
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
@@ -244,7 +244,7 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
@Bean
|
||||
public RepositoryRestConfiguration config() {
|
||||
|
||||
ProjectionDefinitionConfiguration configuration = new ProjectionDefinitionConfiguration(resourceMappings());
|
||||
ProjectionDefinitionConfiguration configuration = new ProjectionDefinitionConfiguration();
|
||||
|
||||
// Register projections found in packages
|
||||
for (Class<?> projection : getProjections(repositories())) {
|
||||
@@ -259,6 +259,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
return config;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ProjectionDefinitionRegistar projectionDefinitionRegistrar(ObjectFactory<RepositoryRestConfiguration> config) {
|
||||
return new ProjectionDefinitionRegistar(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MetadataConfiguration metadataConfiguration() {
|
||||
return new MetadataConfiguration();
|
||||
@@ -568,8 +573,9 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceMappings resourceMappings() {
|
||||
return new RepositoryResourceMappings(repositories(), persistentEntities());
|
||||
public RepositoryResourceMappings resourceMappings() {
|
||||
return new RepositoryResourceMappings(repositories(), persistentEntities(),
|
||||
config().getRepositoryDetectionStrategy());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,7 +108,8 @@ public class RepositoryTestsConfig {
|
||||
@Bean
|
||||
public Module persistentEntityModule() {
|
||||
|
||||
RepositoryResourceMappings mappings = new RepositoryResourceMappings(repositories(), persistentEntities());
|
||||
RepositoryResourceMappings mappings = new RepositoryResourceMappings(repositories(), persistentEntities(),
|
||||
config().getRepositoryDetectionStrategy());
|
||||
EntityLinks entityLinks = new RepositoryEntityLinks(repositories(), mappings, config(),
|
||||
mock(PagingAndSortingTemplateVariables.class),
|
||||
OrderAwarePluginRegistry.<Class<?>, BackendIdConverter> create(Arrays.asList(DefaultIdConverter.INSTANCE)));
|
||||
|
||||
Reference in New Issue
Block a user