DATAREST-925 - Invoke ResourceProcessors for ProjectionResources.
The serializer for projection resources now also invokes ResourceProcessor instances registered for that particular projection. Original pull request: #238.
This commit is contained in:
committed by
Oliver Gierke
parent
080d1b0dab
commit
b66c8272ee
@@ -42,7 +42,6 @@ import org.springframework.data.rest.core.support.SelfLinkProvider;
|
||||
import org.springframework.data.rest.webmvc.EmbeddedResourcesAssembler;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.LookupObjectSerializer;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.NestedEntitySerializer;
|
||||
import org.springframework.data.rest.webmvc.mapping.Associations;
|
||||
import org.springframework.data.rest.webmvc.mapping.LinkCollector;
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
|
||||
@@ -123,12 +122,10 @@ public class RepositoryTestsConfig {
|
||||
Associations associations = new Associations(mappings, config());
|
||||
LinkCollector collector = new LinkCollector(persistentEntities(), selfLinkProvider, associations);
|
||||
|
||||
NestedEntitySerializer nestedEntitySerializer = new NestedEntitySerializer(persistentEntities(),
|
||||
new EmbeddedResourcesAssembler(persistentEntities(), associations, mock(ExcerptProjector.class)),
|
||||
new ResourceProcessorInvoker(Collections.<ResourceProcessor<?>> emptyList()));
|
||||
|
||||
return new PersistentEntityJackson2Module(associations, persistentEntities(), uriToEntityConverter, collector,
|
||||
invokerFactory, nestedEntitySerializer, mock(LookupObjectSerializer.class));
|
||||
invokerFactory, mock(LookupObjectSerializer.class),
|
||||
new ResourceProcessorInvoker(Collections.<ResourceProcessor<?>> emptyList()),
|
||||
new EmbeddedResourcesAssembler(persistentEntities(), associations, mock(ExcerptProjector.class)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -43,7 +43,6 @@ import org.springframework.data.rest.webmvc.EmbeddedResourcesAssembler;
|
||||
import org.springframework.data.rest.webmvc.jpa.Person;
|
||||
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.LookupObjectSerializer;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.NestedEntitySerializer;
|
||||
import org.springframework.data.rest.webmvc.mapping.Associations;
|
||||
import org.springframework.data.rest.webmvc.mapping.LinkCollector;
|
||||
import org.springframework.data.rest.webmvc.spi.BackendIdConverter;
|
||||
@@ -131,12 +130,10 @@ public class RepositoryTestsConfig {
|
||||
Associations associations = new Associations(mappings, config());
|
||||
LinkCollector collector = new LinkCollector(persistentEntities(), selfLinkProvider, associations);
|
||||
|
||||
NestedEntitySerializer nestedEntitySerializer = new NestedEntitySerializer(persistentEntities(),
|
||||
new EmbeddedResourcesAssembler(persistentEntities(), associations, mock(ExcerptProjector.class)),
|
||||
new ResourceProcessorInvoker(Collections.<ResourceProcessor<?>> emptyList()));
|
||||
|
||||
return new PersistentEntityJackson2Module(associations, persistentEntities(), uriToEntityConverter, collector,
|
||||
invokerFactory, nestedEntitySerializer, mock(LookupObjectSerializer.class));
|
||||
invokerFactory, mock(LookupObjectSerializer.class),
|
||||
new ResourceProcessorInvoker(Collections.<ResourceProcessor<?>> emptyList()),
|
||||
new EmbeddedResourcesAssembler(persistentEntities(), associations, mock(ExcerptProjector.class)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.UUID;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.rest.core.config.Projection;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
@@ -33,6 +34,11 @@ import org.springframework.data.annotation.Reference;
|
||||
@EqualsAndHashCode(of = "id")
|
||||
public class LineItem {
|
||||
|
||||
@Projection(name = "productsOnly", types = { LineItem.class })
|
||||
public interface LineItemProductsOnlyProjection {
|
||||
List<Product.ProductNameOnlyProjection> getProducts();
|
||||
}
|
||||
|
||||
private final @Id UUID id = UUID.randomUUID();
|
||||
private final String description;
|
||||
private final BigDecimal price;
|
||||
|
||||
@@ -23,12 +23,18 @@ import java.util.UUID;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.rest.core.config.Projection;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Value
|
||||
public class Order {
|
||||
|
||||
@Projection(name = "itemsOnly", types = { Order.class })
|
||||
public interface OrderItemsOnlyProjection {
|
||||
List<LineItem.LineItemProductsOnlyProjection> getItems();
|
||||
}
|
||||
|
||||
private final @Id UUID id = UUID.randomUUID();
|
||||
private final List<LineItem> items = new ArrayList<>();
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.rest.core.config.Projection;
|
||||
|
||||
/**
|
||||
* @author Oliver Gierke
|
||||
@@ -31,6 +32,11 @@ import org.springframework.data.annotation.Id;
|
||||
@RequiredArgsConstructor
|
||||
public class Product {
|
||||
|
||||
@Projection(name = "nameOnly", types = { Product.class })
|
||||
public interface ProductNameOnlyProjection {
|
||||
String getName();
|
||||
}
|
||||
|
||||
private final @Id UUID id = UUID.randomUUID();
|
||||
private final String name;
|
||||
private final BigDecimal price;
|
||||
|
||||
@@ -58,6 +58,22 @@ public class ShopConfiguration {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceProcessor<Resource<Product.ProductNameOnlyProjection>> productNameOnlyProjectionResourceProcessor() {
|
||||
return new ResourceProcessor<Resource<Product.ProductNameOnlyProjection>>() {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.hateoas.ResourceProcessor#process(org.springframework.hateoas.ResourceSupport)
|
||||
*/
|
||||
@Override
|
||||
public Resource<Product.ProductNameOnlyProjection> process(Resource<Product.ProductNameOnlyProjection> resource) {
|
||||
resource.add(new Link("alpha", "beta"));
|
||||
return resource;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ package org.springframework.data.rest.tests.shop;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.rest.tests.AbstractWebIntegrationTests;
|
||||
@@ -67,4 +70,36 @@ public class ShopIntegrationTests extends AbstractWebIntegrationTests {
|
||||
actions.andExpect(jsonPath(prefix.concat("_links.").concat(suffix)).exists());
|
||||
actions.andExpect(jsonPath(prefix.concat("_embedded.").concat(suffix)).exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderProductNameOnlyProjection() throws Exception {
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
arguments.put("projection", "nameOnly");
|
||||
client.follow(client.discoverUnique("products").expand(arguments))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.products[0].name", notNullValue()))
|
||||
.andExpect(jsonPath("$._embedded.products[0].price").doesNotExist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderProductNameOnlyProjectionResourceProcessor() throws Exception {
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
arguments.put("projection", "nameOnly");
|
||||
client.follow(client.discoverUnique("products").expand(arguments))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.products[0]._links.beta").exists());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAREST-221
|
||||
*/
|
||||
@Test
|
||||
public void renderOrderItemsOnlyProjectionResourceProcessor() throws Exception {
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
arguments.put("projection", "itemsOnly");
|
||||
client.follow(client.discoverUnique("orders").expand(arguments))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$._embedded.orders[0].items[0].products[0].name").exists())
|
||||
.andExpect(jsonPath("$._embedded.orders[0].items[0].products[0]._links.beta").exists());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ import org.springframework.data.rest.webmvc.json.MappingAwarePageableArgumentRes
|
||||
import org.springframework.data.rest.webmvc.json.MappingAwareSortArgumentResolver;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.LookupObjectSerializer;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.NestedEntitySerializer;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter;
|
||||
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter.ValueTypeSchemaPropertyCustomizerFactory;
|
||||
import org.springframework.data.rest.webmvc.mapping.Associations;
|
||||
@@ -637,12 +636,11 @@ public class RepositoryRestMvcConfiguration extends HateoasAwareSpringDataWebCon
|
||||
|
||||
EmbeddedResourcesAssembler assembler = new EmbeddedResourcesAssembler(entities, associationLinks(),
|
||||
excerptProjector());
|
||||
NestedEntitySerializer serializer = new NestedEntitySerializer(entities, assembler, resourceProcessorInvoker());
|
||||
LookupObjectSerializer lookupObjectSerializer = new LookupObjectSerializer(
|
||||
OrderAwarePluginRegistry.create(getEntityLookups()));
|
||||
|
||||
return new PersistentEntityJackson2Module(associationLinks(), entities, uriToEntityConverter, linkCollector(),
|
||||
repositoryInvokerFactory, serializer, lookupObjectSerializer);
|
||||
repositoryInvokerFactory, lookupObjectSerializer, resourceProcessorInvoker(), assembler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -116,7 +116,8 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
*/
|
||||
public PersistentEntityJackson2Module(Associations associations, PersistentEntities entities,
|
||||
UriToEntityConverter converter, LinkCollector collector, RepositoryInvokerFactory factory,
|
||||
NestedEntitySerializer serializer, LookupObjectSerializer lookupObjectSerializer) {
|
||||
LookupObjectSerializer lookupObjectSerializer, ResourceProcessorInvoker resourceProcessorInvoker,
|
||||
EmbeddedResourcesAssembler assembler) {
|
||||
|
||||
super(new Version(2, 0, 0, null, "org.springframework.data.rest", "jackson-module"));
|
||||
|
||||
@@ -125,8 +126,9 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
Assert.notNull(converter, "UriToEntityConverter must not be null!");
|
||||
Assert.notNull(collector, "LinkCollector must not be null!");
|
||||
|
||||
NestedEntitySerializer serializer = new NestedEntitySerializer(entities, assembler, resourceProcessorInvoker);
|
||||
addSerializer(new PersistentEntityResourceSerializer(collector));
|
||||
addSerializer(new ProjectionSerializer(collector, associations, false));
|
||||
addSerializer(new ProjectionSerializer(collector, associations, resourceProcessorInvoker, false));
|
||||
addSerializer(new ProjectionResourceContentSerializer(false));
|
||||
|
||||
setSerializerModifier(
|
||||
@@ -321,7 +323,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
* @author Alex Leigh
|
||||
* @since 2.5
|
||||
*/
|
||||
public static class NestedEntitySerializer extends StdSerializer<Object> {
|
||||
static class NestedEntitySerializer extends StdSerializer<Object> {
|
||||
|
||||
private static final long serialVersionUID = -2327469118972125954L;
|
||||
|
||||
@@ -538,6 +540,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
|
||||
private final LinkCollector collector;
|
||||
private final Associations associations;
|
||||
private final ResourceProcessorInvoker resourceProcessorInvoker;
|
||||
private final boolean unwrapping;
|
||||
|
||||
/**
|
||||
@@ -548,12 +551,13 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
* @param mappings must not be {@literal null}.
|
||||
* @param unwrapping
|
||||
*/
|
||||
private ProjectionSerializer(LinkCollector collector, Associations mappings, boolean unwrapping) {
|
||||
private ProjectionSerializer(LinkCollector collector, Associations mappings, ResourceProcessorInvoker resourceProcessorInvoker, boolean unwrapping) {
|
||||
|
||||
super(TargetAware.class);
|
||||
|
||||
this.collector = collector;
|
||||
this.associations = mappings;
|
||||
this.resourceProcessorInvoker = resourceProcessorInvoker;
|
||||
this.unwrapping = unwrapping;
|
||||
}
|
||||
|
||||
@@ -565,10 +569,6 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
public void serialize(TargetAware value, JsonGenerator jgen, SerializerProvider provider)
|
||||
throws IOException, JsonGenerationException {
|
||||
|
||||
Object target = value.getTarget();
|
||||
Links links = associations.getMetadataFor(value.getTargetClass()).isExported() ? collector.getLinksFor(target)
|
||||
: new Links();
|
||||
|
||||
if (!unwrapping) {
|
||||
jgen.writeStartObject();
|
||||
}
|
||||
@@ -576,13 +576,21 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
provider.//
|
||||
findValueSerializer(ProjectionResource.class, null).//
|
||||
unwrappingSerializer(null).//
|
||||
serialize(new ProjectionResource(value, links), jgen, provider);
|
||||
serialize(toResource(value), jgen, provider);
|
||||
|
||||
if (!unwrapping) {
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
private ProjectionResource toResource(TargetAware value) {
|
||||
Object target = value.getTarget();
|
||||
Links links = associations.getMetadataFor(value.getTargetClass()).isExported() ? collector.getLinksFor(target)
|
||||
: new Links();
|
||||
Resource<TargetAware> resource = resourceProcessorInvoker.invokeProcessorsFor(new Resource<TargetAware>(value, links));
|
||||
return new ProjectionResource(resource.getContent(), resource.getLinks());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.fasterxml.jackson.databind.JsonSerializer#isUnwrappingSerializer()
|
||||
@@ -598,7 +606,7 @@ public class PersistentEntityJackson2Module extends SimpleModule {
|
||||
*/
|
||||
@Override
|
||||
public JsonSerializer<TargetAware> unwrappingSerializer(NameTransformer unwrapper) {
|
||||
return new ProjectionSerializer(collector, associations, true);
|
||||
return new ProjectionSerializer(collector, associations, resourceProcessorInvoker, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user