/*
* Copyright 2013-2017 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.data.web;
import static org.assertj.core.api.Assertions.*;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.AbstractPageRequest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.PagedResources.PageMetadata;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceAssembler;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.core.EmbeddedWrapper;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Unit tests for {@link PagedResourcesAssembler}.
*
* @author Oliver Gierke
* @author Nick Williams
*/
public class PagedResourcesAssemblerUnitTests {
static final Pageable PAGEABLE = PageRequest.of(0, 20);
static final Page<Person> EMPTY_PAGE = new PageImpl<>(Collections.emptyList(), PAGEABLE, 0);
HateoasPageableHandlerMethodArgumentResolver resolver = new HateoasPageableHandlerMethodArgumentResolver();
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<>(resolver, null);
@Before
public void setUp() {
WebTestUtils.initWebTest();
}
@Test
public void addsNextLinkForFirstPage() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(0));
assertThat(resources.getLink(Link.REL_PREVIOUS)).isNull();
assertThat(resources.getLink(Link.REL_SELF)).isNotNull();
assertThat(resources.getLink(Link.REL_NEXT)).isNotNull();
}
@Test
public void addsPreviousAndNextLinksForMiddlePage() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1));
assertThat(resources.getLink(Link.REL_PREVIOUS)).isNotNull();
assertThat(resources.getLink(Link.REL_SELF)).isNotNull();
assertThat(resources.getLink(Link.REL_NEXT)).isNotNull();
}
@Test
public void addsPreviousLinkForLastPage() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(2));
assertThat(resources.getLink(Link.REL_PREVIOUS)).isNotNull();
assertThat(resources.getLink(Link.REL_SELF)).isNotNull();
assertThat(resources.getLink(Link.REL_NEXT)).isNull();
}
@Test
public void usesBaseUriIfConfigured() {
UriComponents baseUri = UriComponentsBuilder.fromUriString("http://foo:9090").build();
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<>(resolver, baseUri);
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1));
assertThat(resources.getLink(Link.REL_PREVIOUS).getHref()).startsWith(baseUri.toUriString());
assertThat(resources.getLink(Link.REL_SELF)).isNotNull();
assertThat(resources.getLink(Link.REL_NEXT).getHref()).startsWith(baseUri.toUriString());
}
@Test
public void usesCustomLinkProvided() {
Link link = new Link("http://foo:9090", "rel");
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1), link);
assertThat(resources.getLink(Link.REL_PREVIOUS).getHref()).startsWith(link.getHref());
assertThat(resources.getLink(Link.REL_SELF)).isEqualTo(link.withSelfRel());
assertThat(resources.getLink(Link.REL_NEXT).getHref()).startsWith(link.getHref());
}
@Test // DATACMNS-358
public void createsPagedResourcesForOneIndexedArgumentResolver() {
resolver.setOneIndexedParameters(true);
AbstractPageRequest request = PageRequest.of(0, 1);
Page<Person> page = new PageImpl<>(Collections.emptyList(), request, 0);
assembler.toResource(page);
}
@Test // DATACMNS-418, DATACMNS-515
public void createsACanonicalLinkWithoutTemplateParameters() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1));
assertThat(resources.getLink(Link.REL_SELF).getHref()).doesNotContain("{").doesNotContain("}");
}
@Test // DATACMNS-418
public void invokesCustomElementResourceAssembler() {
PersonResourceAssembler personAssembler = new PersonResourceAssembler();
PagedResources<PersonResource> resources = assembler.toResource(createPage(0), personAssembler);
assertThat(resources.hasLink(Link.REL_SELF)).isTrue();
assertThat(resources.hasLink(Link.REL_NEXT)).isTrue();
Collection<PersonResource> content = resources.getContent();
assertThat(content).hasSize(1);
assertThat(content.iterator().next().name).isEqualTo("Dave");
}
@Test // DATAMCNS-563
public void createsPaginationLinksForOneIndexedArgumentResolverCorrectly() {
HateoasPageableHandlerMethodArgumentResolver argumentResolver = new HateoasPageableHandlerMethodArgumentResolver();
argumentResolver.setOneIndexedParameters(true);
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<>(argumentResolver, null);
PagedResources<Resource<Person>> resource = assembler.toResource(createPage(1));
assertThat(resource.hasLink("prev")).isTrue();
assertThat(resource.hasLink("next")).isTrue();
assertThat(getQueryParameters(resource.getLink("prev"))).containsEntry("page", "1");
assertThat(getQueryParameters(resource.getLink("next"))).containsEntry("page", "3");
}
@Test // DATACMNS-515
public void generatedLinksShouldNotBeTemplated() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1));
assertThat(resources.getLink(Link.REL_SELF).getHref()).doesNotContain("{").doesNotContain("}");
assertThat(resources.getLink(Link.REL_NEXT).getHref()).endsWith("?page=2&size=1");
assertThat(resources.getLink(Link.REL_PREVIOUS).getHref()).endsWith("?page=0&size=1");
}
@Test // DATACMNS-699
public void generatesEmptyPagedResourceWithEmbeddedWrapper() {
PagedResources<?> result = assembler.toEmptyResource(EMPTY_PAGE, Person.class);
Collection<?> content = result.getContent();
assertThat(content).hasSize(1);
Object element = content.iterator().next();
assertThat(element).isInstanceOf(EmbeddedWrapper.class);
assertThat(((EmbeddedWrapper) element).getRelTargetType()).isEqualTo(Person.class);
}
@Test(expected = IllegalArgumentException.class) // DATACMNS-699
public void emptyPageCreatorRejectsPageWithContent() {
assembler.toEmptyResource(createPage(1), Person.class);
}
@Test(expected = IllegalArgumentException.class) // DATACMNS-699
public void emptyPageCreatorRejectsNullType() {
assembler.toEmptyResource(EMPTY_PAGE, null);
}
@Test // DATACMNS-701
public void addsFirstAndLastLinksForMultiplePages() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(1));
assertThat(resources.getLink(Link.REL_FIRST).getHref()).endsWith("?page=0&size=1");
assertThat(resources.getLink(Link.REL_LAST).getHref()).endsWith("?page=2&size=1");
}
@Test // DATACMNS-701
public void addsFirstAndLastLinksForFirstPage() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(0));
assertThat(resources.getLink(Link.REL_FIRST).getHref()).endsWith("?page=0&size=1");
assertThat(resources.getLink(Link.REL_LAST).getHref()).endsWith("?page=2&size=1");
}
@Test // DATACMNS-701
public void addsFirstAndLastLinksForLastPage() {
PagedResources<Resource<Person>> resources = assembler.toResource(createPage(2));
assertThat(resources.getLink(Link.REL_FIRST).getHref()).endsWith("?page=0&size=1");
assertThat(resources.getLink(Link.REL_LAST).getHref()).endsWith("?page=2&size=1");
}
@Test // DATACMNS-701
public void alwaysAddsFirstAndLastLinkIfConfiguredTo() {
PagedResourcesAssembler<Person> assembler = new PagedResourcesAssembler<>(resolver, null);
assembler.setForceFirstAndLastRels(true);
PagedResources<Resource<Person>> resources = assembler.toResource(EMPTY_PAGE);
assertThat(resources.getLink(Link.REL_FIRST).getHref()).endsWith("?page=0&size=20");
assertThat(resources.getLink(Link.REL_LAST).getHref()).endsWith("?page=0&size=20");
}
@Test // DATACMNS-802
public void usesCustomPagedResources() {
ResourceAssembler<Page<Person>, PagedResources<Resource<Person>>> assembler = new CustomPagedResourcesAssembler<>(
resolver, null);
assertThat(assembler.toResource(EMPTY_PAGE)).isInstanceOf(CustomPagedResources.class);
}
@Test // DATACMNS-1042
public void selfLinkContainsCoordinatesForCurrentPage() {
PagedResources<Resource<Person>> resource = assembler.toResource(createPage(0));
assertThat(resource.getLink(Link.REL_SELF).getHref()).endsWith("?page=0&size=1");
}
private static Page<Person> createPage(int index) {
Pageable request = PageRequest.of(index, 1);
Person person = new Person();
person.name = "Dave";
return new PageImpl<>(Collections.singletonList(person), request, 3);
}
private static Map<String, String> getQueryParameters(Link link) {
UriComponents uriComponents = UriComponentsBuilder.fromUri(URI.create(link.expand().getHref())).build();
return uriComponents.getQueryParams().toSingleValueMap();
}
static class Person {
String name;
}
static class PersonResource extends ResourceSupport {
String name;
}
static class PersonResourceAssembler implements ResourceAssembler<Person, PersonResource> {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.ResourceAssembler#toResource(java.lang.Object)
*/
@Override
public PersonResource toResource(Person entity) {
PersonResource resource = new PersonResource();
resource.name = entity.name;
return resource;
}
}
static class CustomPagedResourcesAssembler<T> extends PagedResourcesAssembler<T> {
public CustomPagedResourcesAssembler(HateoasPageableHandlerMethodArgumentResolver resolver, UriComponents baseUri) {
super(resolver, baseUri);
}
@Override
protected <R extends ResourceSupport, S> PagedResources<R> createPagedResource(List<R> resources,
PageMetadata metadata, Page<S> page) {
return new CustomPagedResources<>(resources, metadata);
}
}
static class CustomPagedResources<R extends ResourceSupport> extends PagedResources<R> {
public CustomPagedResources(Collection<R> content, PageMetadata metadata) {
super(content, metadata);
}
}
}