DATAREST-741 - UriToEntityDeserializer now uses RepositoryInvoker directly.

UriToEntityDeserializer now uses the RepositoryInvokerFactory to resolve entity instances directly to make sure potentially registered EntityLookup instances are considered for the lookup.
This commit is contained in:
Oliver Gierke
2016-01-06 14:17:42 +01:00
parent 000f645543
commit 3ce10774d8
4 changed files with 72 additions and 37 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@@ -21,18 +21,18 @@ import java.util.HashSet;
import java.util.Set;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.repository.support.RepositoryInvokerFactory;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* A {@link GenericConverter} that can convert a {@link URI} domain entity.
* A {@link GenericConverter} that can convert a {@link URI} into an entity.
*
* @author Jon Brisbin
* @author Oliver Gierke
@@ -40,23 +40,26 @@ import org.springframework.util.Assert;
public class UriToEntityConverter implements ConditionalGenericConverter {
private static final TypeDescriptor URI_TYPE = TypeDescriptor.valueOf(URI.class);
private static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
private final PersistentEntities entities;
private final RepositoryInvokerFactory invokerFactory;
private final Repositories repositories;
private final Set<ConvertiblePair> convertiblePairs;
private final ConversionService conversionService;
/**
* Creates a new {@link UriToEntityConverter} using the given {@link PersistentEntities} and
* {@link DomainClassConverter}.
* Creates a new {@link UriToEntityConverter} using the given {@link PersistentEntities},
* {@link RepositoryInvokerFactory} and {@link Repositories}.
*
* @param entities must not be {@literal null}.
* @param conversionService must not be {@literal null}.
* @param invokerFactory must not be {@literal null}.
* @param repositories must not be {@literal null}.
*/
public UriToEntityConverter(PersistentEntities entities, ConversionService conversionService) {
public UriToEntityConverter(PersistentEntities entities, RepositoryInvokerFactory invokerFactory,
Repositories repositories) {
Assert.notNull(entities, "PersistentEntities must not be null!");
Assert.notNull(conversionService, "ConversionService must not be null!");
Assert.notNull(invokerFactory, "RepositoryInvokerFactory must not be null!");
Assert.notNull(repositories, "Repositories must not be null!");
Set<ConvertiblePair> convertiblePairs = new HashSet<ConvertiblePair>();
@@ -72,7 +75,8 @@ public class UriToEntityConverter implements ConditionalGenericConverter {
this.convertiblePairs = Collections.unmodifiableSet(convertiblePairs);
this.entities = entities;
this.conversionService = conversionService;
this.invokerFactory = invokerFactory;
this.repositories = repositories;
}
/*
@@ -81,7 +85,8 @@ public class UriToEntityConverter implements ConditionalGenericConverter {
*/
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return !sourceType.equals(URI_TYPE) ? false : conversionService.canConvert(STRING_TYPE, targetType);
return !sourceType.equals(URI_TYPE) ? false
: repositories.getRepositoryInformationFor(targetType.getType()) != null;
}
/*
@@ -103,8 +108,8 @@ public class UriToEntityConverter implements ConditionalGenericConverter {
PersistentEntity<?, ?> entity = entities.getPersistentEntity(targetType.getType());
if (entity == null) {
throw new ConversionFailedException(sourceType, targetType, source, new IllegalArgumentException(
"No PersistentEntity information available for " + targetType.getType()));
throw new ConversionFailedException(sourceType, targetType, source,
new IllegalArgumentException("No PersistentEntity information available for " + targetType.getType()));
}
URI uri = (URI) source;
@@ -115,6 +120,6 @@ public class UriToEntityConverter implements ConditionalGenericConverter {
"Cannot resolve URI " + uri + ". Is it local or remote? Only local URIs are resolvable."));
}
return conversionService.convert(parts[parts.length - 1], targetType.getType());
return invokerFactory.getInvokerFor(targetType.getType()).invokeFindOne(parts[parts.length - 1]);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -30,11 +30,14 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.repository.support.RepositoryInvoker;
import org.springframework.data.repository.support.RepositoryInvokerFactory;
/**
* Unit tests for {@link UriToEntityConverter}.
@@ -48,7 +51,8 @@ public class UriToEntityConverterUnitTests {
static final TypeDescriptor STRING_TYPE = TypeDescriptor.valueOf(String.class);
static final TypeDescriptor ENTITY_TYPE = TypeDescriptor.valueOf(Entity.class);
@Mock ConversionService conversionService;
@Mock Repositories repositories;
@Mock RepositoryInvokerFactory invokerFactory;
MongoMappingContext context;
UriToEntityConverter converter;
@@ -61,7 +65,8 @@ public class UriToEntityConverterUnitTests {
this.context.setInitialEntitySet(new HashSet<Class<?>>(Arrays.asList(Entity.class, NonEntity.class)));
this.context.afterPropertiesSet();
this.converter = new UriToEntityConverter(new PersistentEntities(Arrays.asList(this.context)), conversionService);
this.converter = new UriToEntityConverter(new PersistentEntities(Arrays.asList(this.context)), invokerFactory,
repositories);
}
/**
@@ -90,7 +95,7 @@ public class UriToEntityConverterUnitTests {
@Test
public void canConvertEntityWithIdPropertyAndFromStringConversionPossible() {
when(conversionService.canConvert(STRING_TYPE, ENTITY_TYPE)).thenReturn(true);
doReturn(mock(RepositoryInformation.class)).when(repositories).getRepositoryInformationFor(ENTITY_TYPE.getType());
assertThat(converter.matches(URI_TYPE, ENTITY_TYPE), is(true));
}
@@ -110,7 +115,10 @@ public class UriToEntityConverterUnitTests {
public void invokesConverterWithLastUriPathSegment() {
Entity reference = new Entity();
when(conversionService.convert("1", Entity.class)).thenReturn(reference);
RepositoryInvoker invoker = mock(RepositoryInvoker.class);
doReturn(reference).when(invoker).invokeFindOne("1");
doReturn(invoker).when(invokerFactory).getInvokerFor(ENTITY_TYPE.getType());
assertThat(converter.convert(URI.create("/foo/bar/1"), URI_TYPE, ENTITY_TYPE), is((Object) reference));
}
@@ -131,6 +139,30 @@ public class UriToEntityConverterUnitTests {
converter.convert(URI.create("1"), URI_TYPE, ENTITY_TYPE);
}
/**
* @see DATAREST-741
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullPersistentEntities() {
new UriToEntityConverter(null, invokerFactory, repositories);
}
/**
* @see DATAREST-741
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullRepositoryInvokerFactory() {
new UriToEntityConverter(mock(PersistentEntities.class), null, repositories);
}
/**
* @see DATAREST-741
*/
@Test(expected = IllegalArgumentException.class)
public void rejectsNullRepositories() {
new UriToEntityConverter(mock(PersistentEntities.class), invokerFactory, null);
}
static class Entity {
String id;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@@ -326,11 +326,12 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
factory.getEntityPathResolver());
return new QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver(repositories(),
repositoryInvokerFactory(), resourceMetadataHandlerMethodArgumentResolver(), predicateBuilder, factory);
repositoryInvokerFactory(defaultConversionService()), resourceMetadataHandlerMethodArgumentResolver(),
predicateBuilder, factory);
}
return new RootResourceInformationHandlerMethodArgumentResolver(repositories(), repositoryInvokerFactory(),
resourceMetadataHandlerMethodArgumentResolver());
return new RootResourceInformationHandlerMethodArgumentResolver(repositories(),
repositoryInvokerFactory(defaultConversionService()), resourceMetadataHandlerMethodArgumentResolver());
}
@Bean
@@ -595,12 +596,8 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
uriToEntityConverter(defaultConversionService()), selfLinkProvider());
}
/**
* @param entities
* @return
*/
protected UriToEntityConverter uriToEntityConverter(ConversionService conversionService) {
return new UriToEntityConverter(persistentEntities(), conversionService);
return new UriToEntityConverter(persistentEntities(), repositoryInvokerFactory(conversionService), repositories());
}
/**
@@ -627,10 +624,10 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
}
@Bean
public RepositoryInvokerFactory repositoryInvokerFactory() {
public RepositoryInvokerFactory repositoryInvokerFactory(@Qualifier ConversionService defaultConversionService) {
return new UnwrappingRepositoryInvokerFactory(
new DefaultRepositoryInvokerFactory(repositories(), defaultConversionService()), getEntityLookups());
new DefaultRepositoryInvokerFactory(repositories(), defaultConversionService), getEntityLookups());
}
@Bean

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 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.
@@ -28,6 +28,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.repository.support.DefaultRepositoryInvokerFactory;
import org.springframework.data.repository.support.DomainClassConverter;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.core.UriToEntityConverter;
@@ -62,7 +63,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
/**
* @author Jon Brisbin
* @author Greg Trunquist
* @author Greg Turnquist
* @author Oliver Gierke
*/
@Configuration
@@ -119,8 +120,8 @@ public class RepositoryTestsConfig {
SelfLinkProvider selfLinkProvider = new DefaultSelfLinkProvider(persistentEntities(), entityLinks,
Collections.<EntityLookup<?>> emptyList());
return new PersistentEntityJackson2Module(mappings, persistentEntities(), config(),
new UriToEntityConverter(persistentEntities(), defaultConversionService()), selfLinkProvider);
return new PersistentEntityJackson2Module(mappings, persistentEntities(), config(), new UriToEntityConverter(
persistentEntities(), new DefaultRepositoryInvokerFactory(repositories()), repositories()), selfLinkProvider);
}
@Bean