#859 - Fix RepresentationModelProcessor to smoothly register with Spring MVC.
Some more polishing on internal of Processor and Invoker machinery regarding resource -> model, etc.
This commit is contained in:
committed by
Oliver Drotbohm
parent
0439c5f708
commit
1e621784e7
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2019 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.hateoas;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
// tag::code[]
|
||||
@Configuration
|
||||
public class PaymentProcessingApp {
|
||||
|
||||
@Bean
|
||||
PaymentProcessor paymentProcessor() {
|
||||
return new PaymentProcessor();
|
||||
}
|
||||
}
|
||||
// end::code[]
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2019 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.hateoas;
|
||||
|
||||
import org.springframework.hateoas.server.RepresentationModelProcessor;
|
||||
import org.springframework.hateoas.support.Order;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
// tag::code[]
|
||||
public class PaymentProcessor implements RepresentationModelProcessor<EntityModel<Order>> { // <1>
|
||||
|
||||
@Override
|
||||
public EntityModel<Order> process(EntityModel<Order> model) {
|
||||
|
||||
model.add( // <2>
|
||||
new Link("/payments/{orderId}").withRel(LinkRelation.of("payments")) //
|
||||
.expand(model.getContent().getOrderId()));
|
||||
|
||||
return model; // <3>
|
||||
}
|
||||
}
|
||||
// end::code[]
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2019 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.hateoas.support;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
public class Customer {
|
||||
private final long customerId;
|
||||
}
|
||||
30
src/docs/java/org/springframework/hateoas/support/Order.java
Normal file
30
src/docs/java/org/springframework/hateoas/support/Order.java
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2019 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.hateoas.support;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
public class Order {
|
||||
private final long orderId;
|
||||
private final String state;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2019 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.hateoas.support;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@Value
|
||||
@RequiredArgsConstructor
|
||||
public class Payment {
|
||||
private final Order order;
|
||||
private final double amount;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2019 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.hateoas.support;
|
||||
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@RestController
|
||||
public class PaymentController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"orderId" : "42",
|
||||
"state" : "AWAITING_PAYMENT",
|
||||
"_links" : {
|
||||
"self" : {
|
||||
"href" : "http://localhost/orders/999"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"orderId" : "42",
|
||||
"state" : "AWAITING_PAYMENT",
|
||||
"_links" : {
|
||||
"self" : {
|
||||
"href" : "http://localhost/orders/999"
|
||||
},
|
||||
"payments" : { // <1>
|
||||
"href" : "/payments/42" // <2>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,7 +319,7 @@ entityLinks.linkToItemResource(order, idExtractor); <2>
|
||||
==== TypedEntityLinks
|
||||
|
||||
As controller implementations are often grouped around entity types, you'll very often find yourself using the same extractor function (see <<server.entity-links.api>> for details) all over the controller class.
|
||||
We can centralize the identifier extraction logic even more by obtaining a `TypedEntityLinks` instance poviding the extractor once, so that the actually lookups don't have to deal with the extraction anymore at all.
|
||||
We can centralize the identifier extraction logic even more by obtaining a `TypedEntityLinks` instance providing the extractor once, so that the actually lookups don't have to deal with the extraction anymore at all.
|
||||
|
||||
.Using TypedEntityLinks
|
||||
====
|
||||
@@ -425,6 +425,66 @@ CollectionModel<PersonModel> model = assembler.toCollectionModel(people);
|
||||
----
|
||||
====
|
||||
|
||||
[[server.processors]]
|
||||
== Representation Model Processors
|
||||
|
||||
Sometimes you need to tweak and adjust hypermedia representations after they have been <<server.representation-model-assembler,assembled>>.
|
||||
|
||||
A perfect example is when you have a controller that deals with order fulfillment, but you need to add links related to making payments.
|
||||
|
||||
Imagine having your ordering system producing this type of hypermedia:
|
||||
|
||||
====
|
||||
[source, json, tabsize=2]
|
||||
----
|
||||
include::{resource-dir}/docs/order-plain.json[]
|
||||
----
|
||||
====
|
||||
|
||||
You wish to add a link so the client can make payment, but don't want to mix details about your `PaymentController` into
|
||||
the `OrderController`.
|
||||
|
||||
Instead of polluting the details of your ordering system, you can write a `RepresentationModelProcessor` like this:
|
||||
|
||||
====
|
||||
[source, java, tabsize=2]
|
||||
----
|
||||
include::{code-dir}/PaymentProcessor.java[tag=code]
|
||||
----
|
||||
<1> This processor will only be applied to `EntityModel<Order>` objects.
|
||||
<2> Manipulate the existing `EntityModel` object by adding an unconditional link.
|
||||
<3> Return the `EntityModel` so it can be serialized into the requested media type.
|
||||
====
|
||||
|
||||
Register the processor with your application:
|
||||
|
||||
====
|
||||
[source, java, tabsize=2]
|
||||
----
|
||||
include::{code-dir}/PaymentProcessingApp.java[tag=code]
|
||||
----
|
||||
====
|
||||
|
||||
Now when you issue a hypermedia respresentation of an `Order`, the client receives this:
|
||||
|
||||
====
|
||||
[source, java, tabsize=2]
|
||||
----
|
||||
include::{resource-dir}/docs/order-with-payment-link.json[]
|
||||
----
|
||||
<1> You see the `LinkRelation.of("payments")` plugged in as this link's relation.
|
||||
<2> The URI was provided by the processor.
|
||||
====
|
||||
|
||||
This example is quite simple, but you can easily:
|
||||
|
||||
* Use `WebMvcLinkBuilder` or `WebFluxLinkBuilder` to construct a dynamic link to your `PaymentController`.
|
||||
* Inject any services needed to conditionally add other links (e.g. `cancel`, `amend`) that are driven by state.
|
||||
* Leverage cross cutting services like Spring Security to add, remove, or revise links based upon the current user's context.
|
||||
|
||||
Also, in this example, the `PaymentProcessor` alters the provided `EntityModel<Order>`. You also have the power to
|
||||
_replace_ it with another object. Just be advised the API requires the return type to equal the input type.
|
||||
|
||||
[[server.rel-provider]]
|
||||
== [[spis.rel-provider]] Using the `RelProvider` API
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.hateoas.config;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -27,13 +28,18 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.server.RepresentationModelProcessor;
|
||||
import org.springframework.hateoas.server.mvc.RepresentationModelProcessorHandlerMethodReturnValueHandler;
|
||||
import org.springframework.hateoas.server.mvc.RepresentationModelProcessorInvoker;
|
||||
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||
import org.springframework.hateoas.server.mvc.UriComponentsContributor;
|
||||
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilderFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@@ -52,6 +58,13 @@ class WebMvcHateoasConfiguration {
|
||||
return new HypermediaWebMvcConfigurer(mapper.getIfAvailable(ObjectMapper::new), hypermediaTypes);
|
||||
}
|
||||
|
||||
@Bean
|
||||
HypermediaRepresentationModelBeanProcessorPostProcessor hypermediaRepresentionModelProcessorConfigurator(
|
||||
List<RepresentationModelProcessor<?>> processors) {
|
||||
|
||||
return new HypermediaRepresentationModelBeanProcessorPostProcessor(processors);
|
||||
}
|
||||
|
||||
@Bean
|
||||
static HypermediaRestTemplateBeanPostProcessor restTemplateBeanPostProcessor(
|
||||
ObjectProvider<HypermediaWebMvcConfigurer> configurer) {
|
||||
@@ -92,6 +105,34 @@ class WebMvcHateoasConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
static class HypermediaRepresentationModelBeanProcessorPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final List<RepresentationModelProcessor<?>> processors;
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
|
||||
if (RequestMappingHandlerAdapter.class.isInstance(bean)) {
|
||||
|
||||
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean;
|
||||
|
||||
HandlerMethodReturnValueHandlerComposite delegate = new HandlerMethodReturnValueHandlerComposite();
|
||||
delegate.addHandlers(adapter.getReturnValueHandlers());
|
||||
|
||||
RepresentationModelProcessorHandlerMethodReturnValueHandler handler = new RepresentationModelProcessorHandlerMethodReturnValueHandler(
|
||||
delegate, new RepresentationModelProcessorInvoker(processors));
|
||||
|
||||
adapter.setReturnValueHandlers(Collections.singletonList(handler));
|
||||
}
|
||||
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to register hypermedia support with {@link RestTemplate} instances found in the
|
||||
* application context.
|
||||
|
||||
@@ -32,8 +32,8 @@ public interface RepresentationModelProcessor<T extends RepresentationModel<?>>
|
||||
/**
|
||||
* Processes the given representation model, add links, alter the domain data etc.
|
||||
*
|
||||
* @param resource
|
||||
* @return the processed resource
|
||||
* @param model
|
||||
* @return the processed model
|
||||
*/
|
||||
T process(T resource);
|
||||
T process(T model);
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
@RequiredArgsConstructor
|
||||
public class RepresentationModelProcessorHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
|
||||
|
||||
static final ResolvableType RESOURCE_TYPE = ResolvableType.forRawClass(EntityModel.class);
|
||||
static final ResolvableType RESOURCES_TYPE = ResolvableType.forRawClass(CollectionModel.class);
|
||||
static final ResolvableType ENTITY_MODEL_TYPE = ResolvableType.forRawClass(EntityModel.class);
|
||||
static final ResolvableType COLLECTION_MODEL_TYPE = ResolvableType.forRawClass(CollectionModel.class);
|
||||
private static final ResolvableType HTTP_ENTITY_TYPE = ResolvableType.forRawClass(HttpEntity.class);
|
||||
|
||||
static final Field CONTENT_FIELD = ReflectionUtils.findField(CollectionModel.class, "content");
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* {@link RepresentationModel}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Greg Turnquist
|
||||
* @since 0.20
|
||||
* @soundtrack Doppelkopf - Die fabelhaften Vier (Von Abseits)
|
||||
*/
|
||||
@@ -64,9 +65,9 @@ public class RepresentationModelProcessorInvoker {
|
||||
|
||||
if (rawType != null) {
|
||||
if (EntityModel.class.isAssignableFrom(rawType)) {
|
||||
this.processors.add(new ResourceProcessorWrapper(processor));
|
||||
this.processors.add(new EntityModelProcessorWrapper(processor));
|
||||
} else if (CollectionModel.class.isAssignableFrom(rawType)) {
|
||||
this.processors.add(new ResourcesProcessorWrapper(processor));
|
||||
this.processors.add(new CollectionModelProcessorWrapper(processor));
|
||||
} else {
|
||||
this.processors.add(new DefaultProcessorWrapper(processor));
|
||||
}
|
||||
@@ -104,9 +105,10 @@ public class RepresentationModelProcessorInvoker {
|
||||
Assert.notNull(referenceType, "Reference type must not be null!");
|
||||
|
||||
// For Resources implementations, process elements first
|
||||
if (RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(referenceType)) {
|
||||
if (RepresentationModelProcessorHandlerMethodReturnValueHandler.COLLECTION_MODEL_TYPE
|
||||
.isAssignableFrom(referenceType)) {
|
||||
|
||||
CollectionModel<?> resources = (CollectionModel<?>) value;
|
||||
CollectionModel<?> collectionModel = (CollectionModel<?>) value;
|
||||
Class<?> rawClass = referenceType.getRawClass();
|
||||
|
||||
if (rawClass == null) {
|
||||
@@ -114,9 +116,9 @@ public class RepresentationModelProcessorInvoker {
|
||||
}
|
||||
|
||||
ResolvableType elementTargetType = ResolvableType.forClass(CollectionModel.class, rawClass).getGeneric(0);
|
||||
List<Object> result = new ArrayList<>(resources.getContent().size());
|
||||
List<Object> result = new ArrayList<>(collectionModel.getContent().size());
|
||||
|
||||
for (Object element : resources) {
|
||||
for (Object element : collectionModel) {
|
||||
|
||||
ResolvableType elementType = ResolvableType.forClass(element.getClass());
|
||||
|
||||
@@ -128,8 +130,8 @@ public class RepresentationModelProcessorInvoker {
|
||||
}
|
||||
|
||||
if (RepresentationModelProcessorHandlerMethodReturnValueHandler.CONTENT_FIELD != null) {
|
||||
ReflectionUtils.setField(RepresentationModelProcessorHandlerMethodReturnValueHandler.CONTENT_FIELD, resources,
|
||||
result);
|
||||
ReflectionUtils.setField(RepresentationModelProcessorHandlerMethodReturnValueHandler.CONTENT_FIELD,
|
||||
collectionModel, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,14 +271,14 @@ public class RepresentationModelProcessorInvoker {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
private static class ResourceProcessorWrapper extends RepresentationModelProcessorInvoker.DefaultProcessorWrapper {
|
||||
private static class EntityModelProcessorWrapper extends RepresentationModelProcessorInvoker.DefaultProcessorWrapper {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourceProcessorWrapper} for the given {@link RepresentationModelProcessor}.
|
||||
* Creates a new {@link EntityModelProcessorWrapper} for the given {@link RepresentationModelProcessor}.
|
||||
*
|
||||
* @param processor must not be {@literal null}.
|
||||
*/
|
||||
public ResourceProcessorWrapper(RepresentationModelProcessor<?> processor) {
|
||||
public EntityModelProcessorWrapper(RepresentationModelProcessor<?> processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
@@ -287,7 +289,7 @@ public class RepresentationModelProcessorInvoker {
|
||||
@Override
|
||||
public boolean supports(ResolvableType type, Object value) {
|
||||
|
||||
if (!RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCE_TYPE.isAssignableFrom(type)) {
|
||||
if (!RepresentationModelProcessorHandlerMethodReturnValueHandler.ENTITY_MODEL_TYPE.isAssignableFrom(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -298,24 +300,26 @@ public class RepresentationModelProcessorInvoker {
|
||||
* Returns whether the given {@link EntityModel} matches the given target {@link ResolvableType}. We inspect the
|
||||
* {@link EntityModel}'s value to determine the match.
|
||||
*
|
||||
* @param resource can be {@literal null}.
|
||||
* @param entityModel can be {@literal null}.
|
||||
* @param target can be {@literal null}.
|
||||
* @return whether the given {@link EntityModel} can be assigned to the given target {@link ResolvableType}
|
||||
*/
|
||||
private static boolean isValueTypeMatch(@Nullable EntityModel<?> resource, @Nullable ResolvableType target) {
|
||||
private static boolean isValueTypeMatch(@Nullable EntityModel<?> entityModel, @Nullable ResolvableType target) {
|
||||
|
||||
if (resource == null || !isRawTypeAssignable(target, resource.getClass())) {
|
||||
if (entityModel == null || !isRawTypeAssignable(target, entityModel.getClass())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object content = resource.getContent();
|
||||
Object content = entityModel.getContent();
|
||||
|
||||
if (content == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResolvableType type = findGenericType(target, EntityModel.class);
|
||||
return type != null && type.getGeneric(0).isAssignableFrom(ResolvableType.forClass(content.getClass()));
|
||||
|
||||
return target.isAssignableFrom(content.getClass()) || //
|
||||
(type != null && type.getGeneric(0).isAssignableFrom(ResolvableType.forClass(content.getClass())));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -345,14 +349,15 @@ public class RepresentationModelProcessorInvoker {
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static class ResourcesProcessorWrapper extends RepresentationModelProcessorInvoker.DefaultProcessorWrapper {
|
||||
public static class CollectionModelProcessorWrapper
|
||||
extends RepresentationModelProcessorInvoker.DefaultProcessorWrapper {
|
||||
|
||||
/**
|
||||
* Creates a new {@link ResourcesProcessorWrapper} for the given {@link RepresentationModelProcessor}.
|
||||
* Creates a new {@link CollectionModelProcessorWrapper} for the given {@link RepresentationModelProcessor}.
|
||||
*
|
||||
* @param processor must not be {@literal null}.
|
||||
*/
|
||||
public ResourcesProcessorWrapper(RepresentationModelProcessor<?> processor) {
|
||||
public CollectionModelProcessorWrapper(RepresentationModelProcessor<?> processor) {
|
||||
super(processor);
|
||||
}
|
||||
|
||||
@@ -363,28 +368,31 @@ public class RepresentationModelProcessorInvoker {
|
||||
@Override
|
||||
public boolean supports(ResolvableType type, Object value) {
|
||||
|
||||
if (!RepresentationModelProcessorHandlerMethodReturnValueHandler.RESOURCES_TYPE.isAssignableFrom(type)) {
|
||||
if (!RepresentationModelProcessorHandlerMethodReturnValueHandler.COLLECTION_MODEL_TYPE.isAssignableFrom(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.supports(type, value) && isValueTypeMatch((CollectionModel<?>) value, getTargetType());
|
||||
boolean defaultSupports = super.supports(type, value);
|
||||
boolean valueTypeMatch = isValueTypeMatch((CollectionModel<?>) value, getTargetType());
|
||||
return defaultSupports && valueTypeMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link CollectionModel} instance matches the given {@link ResolvableType}. We predict
|
||||
* this by inspecting the first element of the content of the {@link CollectionModel}.
|
||||
*
|
||||
* @param resources the {@link CollectionModel} to inspect.
|
||||
* @param collectionModel the {@link CollectionModel} to inspect.
|
||||
* @param target that target {@link ResolvableType}.
|
||||
* @return
|
||||
*/
|
||||
static boolean isValueTypeMatch(@Nullable CollectionModel<?> resources, ResolvableType target) {
|
||||
|
||||
if (resources == null) {
|
||||
static boolean isValueTypeMatch(@Nullable CollectionModel<?> collectionModel, ResolvableType target) {
|
||||
|
||||
if (collectionModel == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Collection<?> content = resources.getContent();
|
||||
Collection<?> content = collectionModel.getContent();
|
||||
|
||||
if (content.isEmpty()) {
|
||||
return false;
|
||||
@@ -392,9 +400,9 @@ public class RepresentationModelProcessorInvoker {
|
||||
|
||||
ResolvableType superType = null;
|
||||
|
||||
for (Class<?> resourcesType : Arrays.<Class<?>> asList(resources.getClass(), CollectionModel.class)) {
|
||||
for (Class<?> collectionModelType : Arrays.<Class<?>> asList(collectionModel.getClass(), CollectionModel.class)) {
|
||||
|
||||
superType = getSuperType(target, resourcesType);
|
||||
superType = getSuperType(target, collectionModelType);
|
||||
|
||||
if (superType != null) {
|
||||
break;
|
||||
@@ -409,7 +417,7 @@ public class RepresentationModelProcessorInvoker {
|
||||
ResolvableType resourceType = superType.getGeneric(0);
|
||||
|
||||
if (element instanceof EntityModel) {
|
||||
return ResourceProcessorWrapper.isValueTypeMatch((EntityModel<?>) element, resourceType);
|
||||
return EntityModelProcessorWrapper.isValueTypeMatch((EntityModel<?>) element, resourceType);
|
||||
} else if (element instanceof EmbeddedWrapper) {
|
||||
return isRawTypeAssignable(resourceType, ((EmbeddedWrapper) element).getRelTargetType());
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.hateoas.server.mvc;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.hateoas.server.RepresentationModelProcessor;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
|
||||
|
||||
/**
|
||||
* Special {@link RequestMappingHandlerAdapter} that tweaks the {@link HandlerMethodReturnValueHandlerComposite} to be
|
||||
* proxied by a {@link RepresentationModelProcessorHandlerMethodReturnValueHandler} which will invoke the
|
||||
* {@link RepresentationModelProcessor} s found in the application context and eventually delegate to the originally
|
||||
* configured {@link HandlerMethodReturnValueHandler}.
|
||||
* <p/>
|
||||
* This is a separate component as it might make sense to deploy it in a standalone SpringMVC application to enable post
|
||||
* processing. It would actually make most sense in Spring HATEOAS project.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Phil Webb
|
||||
* @since 0.20
|
||||
* @soundtrack Dopplekopf - Regen für immer (Von Abseits)
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class RepresentationModelProcessorInvokingHandlerAdapter extends RequestMappingHandlerAdapter {
|
||||
|
||||
private @NonNull final RepresentationModelProcessorInvoker invoker;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
super.afterPropertiesSet();
|
||||
|
||||
// Retrieve actual handlers to use as delegate
|
||||
HandlerMethodReturnValueHandlerComposite oldHandlers = getReturnValueHandlersComposite();
|
||||
|
||||
// Set up ResourceProcessingHandlerMethodResolver to delegate to originally configured ones
|
||||
List<HandlerMethodReturnValueHandler> newHandlers = new ArrayList<>();
|
||||
newHandlers.add(new RepresentationModelProcessorHandlerMethodReturnValueHandler(oldHandlers, invoker));
|
||||
|
||||
// Configure the new handler to be used
|
||||
this.setReturnValueHandlers(newHandlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link HandlerMethodReturnValueHandlerComposite} for return handlers, dealing with API changes introduced in
|
||||
* Spring 4.0.
|
||||
*
|
||||
* @return a HandlerMethodReturnValueHandlerComposite
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private HandlerMethodReturnValueHandlerComposite getReturnValueHandlersComposite() {
|
||||
|
||||
Object handlers = this.getReturnValueHandlers();
|
||||
|
||||
if (handlers instanceof HandlerMethodReturnValueHandlerComposite) {
|
||||
return (HandlerMethodReturnValueHandlerComposite) handlers;
|
||||
}
|
||||
|
||||
return new HandlerMethodReturnValueHandlerComposite() //
|
||||
.addHandlers((List<? extends HandlerMethodReturnValueHandler>) handlers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2019 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.hateoas.server.mvc;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForInterfaceTypes.*;
|
||||
import static org.springframework.hateoas.MediaTypes.*;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.hateoas.CollectionModel;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.LinkRelation;
|
||||
import org.springframework.hateoas.client.LinkDiscoverer;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
|
||||
import org.springframework.hateoas.mediatype.hal.HalLinkDiscoverer;
|
||||
import org.springframework.hateoas.server.RepresentationModelProcessor;
|
||||
import org.springframework.hateoas.support.Employee;
|
||||
import org.springframework.hateoas.support.WebMvcEmployeeController;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
|
||||
/**
|
||||
* @author Greg Turnquist
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@WebAppConfiguration
|
||||
@ContextConfiguration
|
||||
public class RepresentationModelProcessorIntegrationTest {
|
||||
|
||||
private static final LinkRelation COLLECTION_LINK_RELATION = LinkRelation.of("collection-processor");
|
||||
private static final LinkRelation ENTITY_LINK_RELATION = LinkRelation.of("entity-processor");
|
||||
private static final LinkRelation WILDCARD_LINK_RELATION = LinkRelation.of("wildcard-processor");
|
||||
|
||||
private static final LinkDiscoverer DISCOVERER = new HalLinkDiscoverer();
|
||||
|
||||
private @Autowired WebApplicationContext context;
|
||||
|
||||
private @Autowired CollectionModelProcessor collectionModelProcessor;
|
||||
private @Autowired EntityModelProcessor entityModelProcessor;
|
||||
private @Autowired NonSpecificDomainObjectProcessor wildcardProcessor;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
this.mockMvc = webAppContextSetup(this.context).build();
|
||||
|
||||
WebMvcEmployeeController.reset();
|
||||
|
||||
collectionModelProcessor.setTriggered(false);
|
||||
entityModelProcessor.setTriggered(false);
|
||||
wildcardProcessor.setTriggered(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void collectionModelProcessorShouldWork() throws Exception {
|
||||
|
||||
String results = this.mockMvc.perform(get("/employees").accept(HAL_JSON)) //
|
||||
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, HAL_JSON.toString() + ";charset=UTF-8")) //
|
||||
.andReturn() //
|
||||
.getResponse() //
|
||||
.getContentAsString();
|
||||
|
||||
assertThat(DISCOVERER.findRequiredLinkWithRel(COLLECTION_LINK_RELATION, results).getHref())
|
||||
.isEqualTo("/collection/link");
|
||||
|
||||
assertThat(DISCOVERER.findLinkWithRel(ENTITY_LINK_RELATION, results)).isEmpty();
|
||||
|
||||
assertThat(collectionModelProcessor.isTriggered()).isTrue();
|
||||
assertThat(entityModelProcessor.isTriggered()).isTrue();
|
||||
assertThat(wildcardProcessor.isTriggered()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entityModelProcessorShouldWork() throws Exception {
|
||||
|
||||
String results = this.mockMvc.perform(get("/employees/1").accept(HAL_JSON)) //
|
||||
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, HAL_JSON.toString() + ";charset=UTF-8")) //
|
||||
.andReturn() //
|
||||
.getResponse() //
|
||||
.getContentAsString();
|
||||
|
||||
assertThat(DISCOVERER.findRequiredLinkWithRel(ENTITY_LINK_RELATION, results).getHref()).isEqualTo("/entity/link");
|
||||
|
||||
assertThat(collectionModelProcessor.isTriggered()).isFalse();
|
||||
assertThat(entityModelProcessor.isTriggered()).isTrue();
|
||||
assertThat(wildcardProcessor.isTriggered()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wildcardProcessorShouldNotWork() throws Exception {
|
||||
|
||||
String results = this.mockMvc.perform(get("/employees").accept(HAL_JSON)) //
|
||||
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, HAL_JSON.toString() + ";charset=UTF-8")) //
|
||||
.andReturn() //
|
||||
.getResponse() //
|
||||
.getContentAsString();
|
||||
|
||||
System.out.println(results);
|
||||
|
||||
assertThat(DISCOVERER.findRequiredLinkWithRel(WILDCARD_LINK_RELATION, results).getHref())
|
||||
.isEqualTo("/non-specific-collection/link");
|
||||
|
||||
assertThat(collectionModelProcessor.isTriggered()).isTrue();
|
||||
assertThat(entityModelProcessor.isTriggered()).isTrue();
|
||||
assertThat(wildcardProcessor.isTriggered()).isTrue();
|
||||
}
|
||||
|
||||
static class EntityModelProcessor implements RepresentationModelProcessor<EntityModel<Employee>> {
|
||||
|
||||
private @Getter @Setter boolean triggered = false;
|
||||
|
||||
@Override
|
||||
public EntityModel<Employee> process(EntityModel<Employee> model) {
|
||||
|
||||
triggered = true;
|
||||
model.add(new Link("/entity/link", ENTITY_LINK_RELATION));
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
static class CollectionModelProcessor
|
||||
implements RepresentationModelProcessor<CollectionModel<EntityModel<Employee>>> {
|
||||
|
||||
private @Getter @Setter boolean triggered = false;
|
||||
|
||||
@Override
|
||||
public CollectionModel<EntityModel<Employee>> process(CollectionModel<EntityModel<Employee>> model) {
|
||||
|
||||
triggered = true;
|
||||
model.add(new Link("/collection/link", COLLECTION_LINK_RELATION));
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
static class NonSpecificDomainObjectProcessor implements RepresentationModelProcessor<CollectionModel<?>> {
|
||||
|
||||
private @Getter @Setter boolean triggered = false;
|
||||
|
||||
@Override
|
||||
public CollectionModel<?> process(CollectionModel<?> model) {
|
||||
|
||||
triggered = true;
|
||||
model.add(new Link("/non-specific-collection/link", WILDCARD_LINK_RELATION));
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@EnableHypermediaSupport(type = HypermediaType.HAL)
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
EntityModelProcessor entityModelProcessor() {
|
||||
return new EntityModelProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CollectionModelProcessor collectionModelProcessor() {
|
||||
return new CollectionModelProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
NonSpecificDomainObjectProcessor wildcardProcessor() {
|
||||
return new NonSpecificDomainObjectProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebMvcEmployeeController employeeController() {
|
||||
return new WebMvcEmployeeController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObjectMapper testMapper() {
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
return objectMapper;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ import org.springframework.hateoas.PagedModel.PageMetadata;
|
||||
import org.springframework.hateoas.server.RepresentationModelProcessor;
|
||||
import org.springframework.hateoas.server.core.EmbeddedWrappers;
|
||||
import org.springframework.hateoas.server.core.HeaderLinksResponseEntity;
|
||||
import org.springframework.hateoas.server.mvc.RepresentationModelProcessorInvoker.ResourcesProcessorWrapper;
|
||||
import org.springframework.hateoas.server.mvc.RepresentationModelProcessorInvoker.CollectionModelProcessorWrapper;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -63,19 +63,17 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
|
||||
static final EntityModel<String> FOO = new EntityModel<>("foo");
|
||||
static final CollectionModel<EntityModel<String>> FOOS = new CollectionModel<>(Collections.singletonList(FOO));
|
||||
static final PagedModel<EntityModel<String>> FOO_PAGE = new PagedModel<>(
|
||||
Collections.singleton(FOO), new PageMetadata(1, 0, 10));
|
||||
static final PagedModel<EntityModel<String>> FOO_PAGE = new PagedModel<>(Collections.singleton(FOO),
|
||||
new PageMetadata(1, 0, 10));
|
||||
static final StringResource FOO_RES = new StringResource("foo");
|
||||
static final HttpEntity<EntityModel<String>> FOO_ENTITY = new HttpEntity<>(FOO);
|
||||
static final ResponseEntity<EntityModel<String>> FOO_RESP_ENTITY = new ResponseEntity<>(FOO,
|
||||
HttpStatus.OK);
|
||||
static final ResponseEntity<EntityModel<String>> FOO_RESP_ENTITY = new ResponseEntity<>(FOO, HttpStatus.OK);
|
||||
static final HttpEntity<StringResource> FOO_RES_ENTITY = new HttpEntity<>(FOO_RES);
|
||||
static final EntityModel<String> BAR = new EntityModel<>("bar");
|
||||
static final CollectionModel<EntityModel<String>> BARS = new CollectionModel<>(Collections.singletonList(BAR));
|
||||
static final StringResource BAR_RES = new StringResource("bar");
|
||||
static final HttpEntity<EntityModel<String>> BAR_ENTITY = new HttpEntity<>(BAR);
|
||||
static final ResponseEntity<EntityModel<String>> BAR_RESP_ENTITY = new ResponseEntity<>(BAR,
|
||||
HttpStatus.OK);
|
||||
static final ResponseEntity<EntityModel<String>> BAR_RESP_ENTITY = new ResponseEntity<>(BAR, HttpStatus.OK);
|
||||
static final HttpEntity<StringResource> BAR_RES_ENTITY = new HttpEntity<>(BAR_RES);
|
||||
static final EntityModel<Long> LONG_10 = new EntityModel<>(10L);
|
||||
static final EntityModel<Long> LONG_20 = new EntityModel<>(20L);
|
||||
@@ -273,7 +271,7 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
|
||||
ResolvableType type = ResolvableType.forClass(PagedStringResources.class);
|
||||
|
||||
assertThat(ResourcesProcessorWrapper.isValueTypeMatch(FOO_PAGE, type)).isTrue();
|
||||
assertThat(CollectionModelProcessorWrapper.isValueTypeMatch(FOO_PAGE, type)).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,8 +282,8 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
|
||||
EmbeddedWrappers wrappers = new EmbeddedWrappers(false);
|
||||
CollectionModel<Object> value = new CollectionModel<>(
|
||||
Collections.singleton(wrappers.emptyCollectionOf(Object.class)));
|
||||
ResourcesProcessorWrapper wrapper = new ResourcesProcessorWrapper(new SpecialResourcesProcessor());
|
||||
Collections.singleton(wrappers.emptyCollectionOf(Object.class)));
|
||||
CollectionModelProcessorWrapper wrapper = new CollectionModelProcessorWrapper(new SpecialResourcesProcessor());
|
||||
|
||||
ResolvableType type = ResolvableType.forMethodReturnType(Controller.class.getMethod("resourcesOfObject"));
|
||||
|
||||
@@ -303,7 +301,8 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
|
||||
resourceProcessors.add((RepresentationModelProcessor<?>) factory.getProxy());
|
||||
|
||||
new RepresentationModelProcessorHandlerMethodReturnValueHandler(delegate, new RepresentationModelProcessorInvoker(resourceProcessors));
|
||||
new RepresentationModelProcessorHandlerMethodReturnValueHandler(delegate,
|
||||
new RepresentationModelProcessorInvoker(resourceProcessors));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,7 +346,7 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public EntityModel<String> process(EntityModel<String> resource) {
|
||||
public EntityModel<String> process(EntityModel<String> model) {
|
||||
return BAR;
|
||||
}
|
||||
}
|
||||
@@ -356,7 +355,7 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public EntityModel<Long> process(EntityModel<Long> resource) {
|
||||
public EntityModel<Long> process(EntityModel<Long> model) {
|
||||
return LONG_20;
|
||||
}
|
||||
}
|
||||
@@ -365,7 +364,7 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public CollectionModel<EntityModel<String>> process(CollectionModel<EntityModel<String>> resource) {
|
||||
public CollectionModel<EntityModel<String>> process(CollectionModel<EntityModel<String>> model) {
|
||||
return BARS;
|
||||
}
|
||||
}
|
||||
@@ -374,7 +373,7 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public StringResource process(StringResource resource) {
|
||||
public StringResource process(StringResource model) {
|
||||
return BAR_RES;
|
||||
}
|
||||
}
|
||||
@@ -383,7 +382,7 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public LongResource process(LongResource resource) {
|
||||
public LongResource process(LongResource model) {
|
||||
return LONG_20_RES;
|
||||
}
|
||||
}
|
||||
@@ -450,9 +449,9 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
boolean invoked = false;
|
||||
|
||||
@Override
|
||||
public EntityModel<SampleProjection> process(EntityModel<SampleProjection> resource) {
|
||||
public EntityModel<SampleProjection> process(EntityModel<SampleProjection> model) {
|
||||
this.invoked = true;
|
||||
return resource;
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,9 +466,9 @@ class ResourceProcessorHandlerMethodReturnValueHandlerUnitTest {
|
||||
boolean invoked = false;
|
||||
|
||||
@Override
|
||||
public SpecialResources process(SpecialResources resource) {
|
||||
public SpecialResources process(SpecialResources model) {
|
||||
this.invoked = true;
|
||||
return resource;
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user