From 3ce10774d88f4363d0a6cc73652a5cfd82fb75b6 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 6 Jan 2016 14:17:42 +0100 Subject: [PATCH] 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. --- .../data/rest/core/UriToEntityConverter.java | 37 +++++++++------- .../core/UriToEntityConverterUnitTests.java | 44 ++++++++++++++++--- .../RepositoryRestMvcConfiguration.java | 19 ++++---- .../webmvc/json/RepositoryTestsConfig.java | 9 ++-- 4 files changed, 72 insertions(+), 37 deletions(-) diff --git a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/UriToEntityConverter.java b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/UriToEntityConverter.java index b4e74297a..0c1bdb894 100644 --- a/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/UriToEntityConverter.java +++ b/spring-data-rest-core/src/main/java/org/springframework/data/rest/core/UriToEntityConverter.java @@ -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 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 convertiblePairs = new HashSet(); @@ -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]); } } diff --git a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/UriToEntityConverterUnitTests.java b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/UriToEntityConverterUnitTests.java index f15392216..b9b1e173f 100644 --- a/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/UriToEntityConverterUnitTests.java +++ b/spring-data-rest-core/src/test/java/org/springframework/data/rest/core/UriToEntityConverterUnitTests.java @@ -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>(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; } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java index f4ff9f7d5..a386066c8 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.java @@ -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 diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java index cda4a6138..ed45748bf 100644 --- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java +++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java @@ -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.> 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