Add SlicedResourcesAssembler for web integration.
Added SlicedResourcesAssembler to esaily convert Slice instances into SlicedResource instances and automatically build the required previous/next link based on PageableHandlerMethodArgumentResolver present in the MVC configuration. The assembler can either be injected into a Spring MVC controller or a controller method. The latter will then assume the controller methods URI to be used as pagination link base. Added necessary SlicedResourcesAssemblerArgumentResolver and MethodParameterAwareSlicedResourcesAssembler classes and wire up HateoasAwareSpringDataWebConfiguration configuration beans to that SlicedResourcesAssembler's can be auto-injected into controllers. Closes #1307
This commit is contained in:
committed by
Oliver Drotbohm
parent
83655663ea
commit
70f21bda9f
@@ -0,0 +1,137 @@
|
||||
package org.springframework.data.web;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
class SlicedResourcesAssemblerArgumentResolverUnitTest {
|
||||
SlicedResourcesAssemblerArgumentResolver resolver;
|
||||
|
||||
private static void assertMethodParameterAwareSlicedResourcesAssemblerFor(Object result,
|
||||
MethodParameter parameter) {
|
||||
assertThat(result).isInstanceOf(MethodParameterAwareSlicedResourcesAssembler.class);
|
||||
|
||||
var assembler = (MethodParameterAwareSlicedResourcesAssembler<?>) result;
|
||||
|
||||
assertThat(assembler.getMethodParameter()).isEqualTo(parameter);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
WebTestUtils.initWebTest();
|
||||
|
||||
var hateoasPageableHandlerMethodArgumentResolver = new HateoasPageableHandlerMethodArgumentResolver();
|
||||
this.resolver = new SlicedResourcesAssemblerArgumentResolver(hateoasPageableHandlerMethodArgumentResolver);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsPlainAssemblerWithoutContext() throws Exception {
|
||||
var method = Controller.class.getMethod("noContext", SlicedResourcesAssembler.class);
|
||||
var result = resolver.resolveArgument(new MethodParameter(method, 0), null, null, null);
|
||||
|
||||
assertThat(result).isInstanceOf(SlicedResourcesAssembler.class);
|
||||
assertThat(result).isNotInstanceOf(MethodParameterAwareSlicedResourcesAssembler.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectsUniquePageableParameter() throws Exception {
|
||||
var method = Controller.class.getMethod("unique", SlicedResourcesAssembler.class, Pageable.class);
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectsUniquePageableParameterForQualifiedAssembler() throws Exception {
|
||||
var method = Controller.class.getMethod("unnecessarilyQualified", SlicedResourcesAssembler.class,
|
||||
Pageable.class);
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectsUniqueQualifiedPageableParameter() throws Exception {
|
||||
|
||||
var method = Controller.class.getMethod("qualifiedUnique", SlicedResourcesAssembler.class, Pageable.class);
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selectsQualifiedPageableParameter() throws Exception {
|
||||
var method = Controller.class.getMethod("qualified", SlicedResourcesAssembler.class, Pageable.class,
|
||||
Pageable.class);
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsAmbiguousPageableParameters() throws Exception {
|
||||
assertRejectsAmbiguity("unqualifiedAmbiguity");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsAmbiguousPageableParametersForQualifiedAssembler() throws Exception {
|
||||
assertRejectsAmbiguity("assemblerQualifiedAmbiguity");
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsAmbiguityWithoutMatchingQualifiers() throws Exception {
|
||||
assertRejectsAmbiguity("noMatchingQualifiers");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doesNotFailForTemplatedMethodMapping() throws Exception {
|
||||
var method = Controller.class.getMethod("methodWithPathVariable", SlicedResourcesAssembler.class);
|
||||
var result = resolver.resolveArgument(new MethodParameter(method, 0), null, null, null);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
}
|
||||
|
||||
private void assertSelectsParameter(Method method, int expectedIndex) {
|
||||
var parameter = new MethodParameter(method, 0);
|
||||
|
||||
var result = resolver.resolveArgument(parameter, null, null, null);
|
||||
assertMethodParameterAwareSlicedResourcesAssemblerFor(result, new MethodParameter(method, expectedIndex));
|
||||
}
|
||||
|
||||
private void assertRejectsAmbiguity(String methodName) throws Exception {
|
||||
var method = Controller.class.getMethod(methodName, SlicedResourcesAssembler.class, Pageable.class,
|
||||
Pageable.class);
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> resolver.resolveArgument(new MethodParameter(method, 0), null, null, null));
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
interface Controller {
|
||||
void noContext(SlicedResourcesAssembler<Object> resolver);
|
||||
|
||||
void unique(SlicedResourcesAssembler<Object> assembler, Pageable pageable);
|
||||
|
||||
void unnecessarilyQualified(@Qualifier("qualified") SlicedResourcesAssembler<Object> assembler,
|
||||
Pageable pageable);
|
||||
|
||||
void qualifiedUnique(@Qualifier("qualified") SlicedResourcesAssembler<Object> assembler,
|
||||
@Qualifier("qualified") Pageable pageable);
|
||||
|
||||
void qualified(@Qualifier("qualified") SlicedResourcesAssembler<Object> resolver,
|
||||
@Qualifier("qualified") Pageable pageable, Pageable unqualified);
|
||||
|
||||
void unqualifiedAmbiguity(SlicedResourcesAssembler<Object> assembler, Pageable pageable, Pageable unqualified);
|
||||
|
||||
void assemblerQualifiedAmbiguity(@Qualifier("qualified") SlicedResourcesAssembler<Object> assembler,
|
||||
Pageable pageable, Pageable unqualified);
|
||||
|
||||
void noMatchingQualifiers(@Qualifier("qualified") SlicedResourcesAssembler<Object> assembler, Pageable pageable,
|
||||
@Qualifier("qualified2") Pageable unqualified);
|
||||
|
||||
@RequestMapping("/{variable}/foo")
|
||||
void methodWithPathVariable(SlicedResourcesAssembler<Object> assembler);
|
||||
|
||||
@RequestMapping("/mapping")
|
||||
Object methodWithMapping(SlicedResourcesAssembler<Object> pageable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright 2022 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.data.web;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.*;
|
||||
import org.springframework.hateoas.*;
|
||||
import org.springframework.hateoas.server.RepresentationModelAssembler;
|
||||
import org.springframework.hateoas.server.core.EmbeddedWrapper;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SlicedResourcesAssembler}.
|
||||
*
|
||||
* @author Michael Schout
|
||||
*/
|
||||
class SlicedResourcesAssemblerUnitTest {
|
||||
static final Pageable PAGEABLE = PageRequest.of(0, 20);
|
||||
static final Slice<Person> EMPTY_SLICE = new SliceImpl<>(Collections.emptyList(), PAGEABLE, false);
|
||||
|
||||
HateoasPageableHandlerMethodArgumentResolver resolver = new HateoasPageableHandlerMethodArgumentResolver();
|
||||
SlicedResourcesAssembler<Person> assembler = new SlicedResourcesAssembler<>(resolver, null);
|
||||
|
||||
private static Slice<Person> createSlice(int index) {
|
||||
Pageable request = PageRequest.of(index, 1);
|
||||
|
||||
var person = new Person();
|
||||
person.name = "Dave";
|
||||
|
||||
boolean hasNext = index < 2;
|
||||
|
||||
return new SliceImpl<>(Collections.singletonList(person), request, hasNext);
|
||||
}
|
||||
|
||||
private static Map<String, String> getQueryParameters(Link link) {
|
||||
var uriComponents = UriComponentsBuilder.fromUri(URI.create(link.expand().getHref())).build();
|
||||
return uriComponents.getQueryParams().toSingleValueMap();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
WebTestUtils.initWebTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addsNextLinkForFirstSlice() {
|
||||
var resources = assembler.toModel(createSlice(0));
|
||||
|
||||
assertThat(resources.getLink(IanaLinkRelations.PREV)).isEmpty();
|
||||
assertThat(resources.getLink(IanaLinkRelations.SELF)).isNotEmpty();
|
||||
assertThat(resources.getLink(IanaLinkRelations.NEXT)).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addsPreviousAndNextLinksForMiddleSlice() {
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getLink(IanaLinkRelations.PREV)).isNotEmpty();
|
||||
assertThat(resources.getLink(IanaLinkRelations.SELF)).isNotEmpty();
|
||||
assertThat(resources.getLink(IanaLinkRelations.NEXT)).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void addsPreviousLinkForLastSlice() {
|
||||
var resources = assembler.toModel(createSlice(2));
|
||||
|
||||
assertThat(resources.getLink(IanaLinkRelations.PREV)).isNotEmpty();
|
||||
assertThat(resources.getLink(IanaLinkRelations.SELF)).isNotEmpty();
|
||||
assertThat(resources.getLink(IanaLinkRelations.NEXT)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesBaseUriIfConfigured() {
|
||||
var baseUri = UriComponentsBuilder.fromUriString("https://foo:9090").build();
|
||||
|
||||
var assembler = new SlicedResourcesAssembler<Person>(resolver, baseUri);
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.PREV).getHref()).startsWith(baseUri.toUriString());
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.SELF)).isNotNull();
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.NEXT).getHref()).startsWith(baseUri.toUriString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesCustomLinkProvided() {
|
||||
var link = Link.of("https://foo:9090", "rel");
|
||||
|
||||
var resources = assembler.toModel(createSlice(1), link);
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.PREV).getHref()).startsWith(link.getHref());
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.SELF)).isEqualTo(link.withSelfRel());
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.NEXT).getHref()).startsWith(link.getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsSlicedResourcesForOneIndexedArgumentResolver() {
|
||||
resolver.setOneIndexedParameters(true);
|
||||
|
||||
AbstractPageRequest request = PageRequest.of(0, 1);
|
||||
Slice<Person> slice = new SliceImpl<>(Collections.emptyList(), request, true);
|
||||
|
||||
assembler.toModel(slice);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsACanonicalLinkWithoutTemplateParameters() {
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.SELF).getHref()).doesNotContain("{").doesNotContain("}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void invokesCustomElementResourceAssembler() {
|
||||
var personAssembler = new PersonResourceAssembler();
|
||||
|
||||
var resources = assembler.toModel(createSlice(0), personAssembler);
|
||||
|
||||
assertThat(resources.hasLink(IanaLinkRelations.SELF)).isTrue();
|
||||
assertThat(resources.hasLink(IanaLinkRelations.NEXT)).isTrue();
|
||||
|
||||
var content = resources.getContent();
|
||||
assertThat(content).hasSize(1);
|
||||
assertThat(content.iterator().next().name).isEqualTo("Dave");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsPaginationLinksForOneIndexedArgumentResolverCorrectly() {
|
||||
var argumentResolver = new HateoasPageableHandlerMethodArgumentResolver();
|
||||
argumentResolver.setOneIndexedParameters(true);
|
||||
|
||||
var assembler = new SlicedResourcesAssembler<Person>(argumentResolver, null);
|
||||
var resource = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resource.hasLink("prev")).isTrue();
|
||||
assertThat(resource.hasLink("next")).isTrue();
|
||||
|
||||
// We expect 2 as the created slice has index 1. slices are always 0 indexed, so we
|
||||
// created page 2 above.
|
||||
assertThat(resource.getMetadata().getNumber()).isEqualTo(2);
|
||||
|
||||
assertThat(getQueryParameters(resource.getRequiredLink("prev"))).containsEntry("page", "1");
|
||||
assertThat(getQueryParameters(resource.getRequiredLink("next"))).containsEntry("page", "3");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatedLinksShouldNotBeTemplated() {
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.SELF).getHref()).doesNotContain("{").doesNotContain("}");
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.NEXT).getHref()).endsWith("?page=2&size=1");
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.PREV).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesEmptySliceResourceWithEmbeddedWrapper() {
|
||||
var result = assembler.toEmptyModel(EMPTY_SLICE, Person.class);
|
||||
|
||||
var content = result.getContent();
|
||||
assertThat(content).hasSize(1);
|
||||
|
||||
var element = content.iterator().next();
|
||||
assertThat(element).isInstanceOf(EmbeddedWrapper.class);
|
||||
assertThat(((EmbeddedWrapper) element).getRelTargetType()).isEqualTo(Person.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptySliceCreatorRejectsSliceWithContent() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> assembler.toEmptyModel(createSlice(1), Person.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void emptySliceCreatorRejectsNullType() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> assembler.toEmptyModel(EMPTY_SLICE, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addsFirstLinkForMultipleSlices() {
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addsFirstLinkForFirstSlice() {
|
||||
var resources = assembler.toModel(createSlice(0));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void addsFirstLinkForLastSlice() {
|
||||
var resources = assembler.toModel(createSlice(2));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void alwaysAddsFirstLinkIfConfiguredTo() {
|
||||
var assembler = new SlicedResourcesAssembler<Person>(resolver, null);
|
||||
assembler.setForceFirstRel(true);
|
||||
|
||||
var resources = assembler.toModel(EMPTY_SLICE);
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=20");
|
||||
}
|
||||
|
||||
@Test
|
||||
void usesCustomSlicedResources() {
|
||||
RepresentationModelAssembler<Slice<Person>, SlicedModel<EntityModel<Person>>> assembler = new CustomSlicedResourcesAssembler<>(
|
||||
resolver, null);
|
||||
|
||||
assertThat(assembler.toModel(EMPTY_SLICE)).isInstanceOf(CustomSlicedResources.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void selfLinkContainsCoordinatesForCurrentSlice() {
|
||||
var resource = assembler.toModel(createSlice(0));
|
||||
|
||||
assertThat(resource.getRequiredLink(IanaLinkRelations.SELF).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepsRequestParametersOfOriginalRequestUri() {
|
||||
WebTestUtils.initWebTest(new MockHttpServletRequest("GET", "/sample?foo=bar"));
|
||||
|
||||
var model = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(model.getRequiredLink(IanaLinkRelations.FIRST).getHref())
|
||||
.isEqualTo("http://localhost/sample?foo=bar&page=0&size=1");
|
||||
}
|
||||
|
||||
static class Person {
|
||||
String name;
|
||||
}
|
||||
|
||||
static class PersonResource extends RepresentationModel<PersonResource> {
|
||||
String name;
|
||||
}
|
||||
|
||||
static class PersonResourceAssembler implements RepresentationModelAssembler<Person, PersonResource> {
|
||||
@Override
|
||||
public PersonResource toModel(Person entity) {
|
||||
var resource = new PersonResource();
|
||||
resource.name = entity.name;
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomSlicedResourcesAssembler<T> extends SlicedResourcesAssembler<T> {
|
||||
CustomSlicedResourcesAssembler(HateoasPageableHandlerMethodArgumentResolver resolver, UriComponents baseUri) {
|
||||
super(resolver, baseUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <R extends RepresentationModel<?>, S> SlicedModel<R> createSlicedModel(List<R> resources,
|
||||
SlicedModel.SliceMetadata metadata, Slice<S> slice) {
|
||||
return new CustomSlicedResources<>(resources, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
static class CustomSlicedResources<R extends RepresentationModel> extends SlicedModel<R> {
|
||||
CustomSlicedResources(Collection<R> content, SliceMetadata metadata) {
|
||||
super(content, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.springframework.data.web.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.data.web.SlicedResourcesAssembler;
|
||||
import org.springframework.data.web.WebTestUtils;
|
||||
import org.springframework.hateoas.EntityModel;
|
||||
import org.springframework.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.SlicedModel;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
public class SliceableResourcesAssemblerIntegrationTests {
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
WebTestUtils.initWebTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void injectsSlicedResourcesAssembler() {
|
||||
var context = WebTestUtils.createApplicationContext(Config.class);
|
||||
var controller = context.getBean(SampleController.class);
|
||||
|
||||
assertThat(controller.assembler).isNotNull();
|
||||
|
||||
var resources = controller.sample(PageRequest.of(1, 1));
|
||||
|
||||
assertThat(resources.getLink(IanaLinkRelations.PREV)).isNotNull();
|
||||
assertThat(resources.getLink(IanaLinkRelations.NEXT)).isNotNull();
|
||||
assertThat(resources.getLink(IanaLinkRelations.SELF)).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void setsUpSlicedResourcesAssemblerFromManualXmlConfig() {
|
||||
var context = new ClassPathXmlApplicationContext("manual.xml", getClass());
|
||||
assertThat(context.getBean(SlicedResourcesAssembler.class)).isNotNull();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void setsUpPagedResourcesAssemblerFromJavaConfigXmlConfig() {
|
||||
var context = new ClassPathXmlApplicationContext("via-config-class.xml", getClass());
|
||||
assertThat(context.getBean(SlicedResourcesAssembler.class)).isNotNull();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableSpringDataWebSupport
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
SampleController controller() {
|
||||
return new SampleController();
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class SampleController {
|
||||
@Autowired
|
||||
SlicedResourcesAssembler<Person> assembler;
|
||||
|
||||
@RequestMapping("/persons")
|
||||
SlicedModel<EntityModel<Person>> sample(Pageable pageable) {
|
||||
|
||||
Slice<Person> page = new SliceImpl<>(Collections.singletonList(new Person()), pageable, true);
|
||||
|
||||
return assembler.toModel(page);
|
||||
}
|
||||
}
|
||||
|
||||
static class Person {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user