Polishing.
Imports, Javadoc, ticket references in test cases. Removed the need for MethodParameterAware… flavors of the ResourceAssemblers by keeping the MethodParameter reference in the original assemblers in the first place. Extracted common Pageable MethodParameter lookup code into PageableMethodParameterUtils. Related ticket: #1307.
This commit is contained in:
@@ -24,6 +24,7 @@ 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.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
@@ -52,7 +53,6 @@ class PagedResourcesAssemblerArgumentResolverUnitTests {
|
||||
var result = resolver.resolveArgument(new MethodParameter(method, 0), null, null, null);
|
||||
|
||||
assertThat(result).isInstanceOf(PagedResourcesAssembler.class);
|
||||
assertThat(result).isNotInstanceOf(MethodParameterAwarePagedResourcesAssembler.class);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-418
|
||||
@@ -118,10 +118,9 @@ class PagedResourcesAssemblerArgumentResolverUnitTests {
|
||||
|
||||
private static void assertMethodParameterAwarePagedResourcesAssemblerFor(Object result, MethodParameter parameter) {
|
||||
|
||||
assertThat(result).isInstanceOf(MethodParameterAwarePagedResourcesAssembler.class);
|
||||
var assembler = (MethodParameterAwarePagedResourcesAssembler<?>) result;
|
||||
|
||||
assertThat(assembler.getMethodParameter()).isEqualTo(parameter);
|
||||
assertThat(result).isInstanceOfSatisfying(PagedResourcesAssembler.class, it -> {
|
||||
assertThat(ReflectionTestUtils.getField(it, "parameter")).isEqualTo(parameter);
|
||||
});
|
||||
}
|
||||
|
||||
private void assertRejectsAmbiguity(String methodName) throws Exception {
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2022-2023 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.*;
|
||||
@@ -9,81 +24,90 @@ 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.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link SlicedResourcesAssemblerArgumentResolver}.
|
||||
*
|
||||
* @author Michael Schout
|
||||
* @author Oliver Drotbohm
|
||||
* @since 3.1
|
||||
*/
|
||||
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
|
||||
@Test // GH-1307
|
||||
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
|
||||
@Test // GH-1307
|
||||
void selectsUniquePageableParameter() throws Exception {
|
||||
|
||||
var method = Controller.class.getMethod("unique", SlicedResourcesAssembler.class, Pageable.class);
|
||||
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void selectsUniquePageableParameterForQualifiedAssembler() throws Exception {
|
||||
|
||||
var method = Controller.class.getMethod("unnecessarilyQualified", SlicedResourcesAssembler.class,
|
||||
Pageable.class);
|
||||
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void selectsUniqueQualifiedPageableParameter() throws Exception {
|
||||
|
||||
var method = Controller.class.getMethod("qualifiedUnique", SlicedResourcesAssembler.class, Pageable.class);
|
||||
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void selectsQualifiedPageableParameter() throws Exception {
|
||||
|
||||
var method = Controller.class.getMethod("qualified", SlicedResourcesAssembler.class, Pageable.class,
|
||||
Pageable.class);
|
||||
|
||||
assertSelectsParameter(method, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void rejectsAmbiguousPageableParameters() throws Exception {
|
||||
assertRejectsAmbiguity("unqualifiedAmbiguity");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void rejectsAmbiguousPageableParametersForQualifiedAssembler() throws Exception {
|
||||
assertRejectsAmbiguity("assemblerQualifiedAmbiguity");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void rejectsAmbiguityWithoutMatchingQualifiers() throws Exception {
|
||||
assertRejectsAmbiguity("noMatchingQualifiers");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void doesNotFailForTemplatedMethodMapping() throws Exception {
|
||||
|
||||
var method = Controller.class.getMethod("methodWithPathVariable", SlicedResourcesAssembler.class);
|
||||
var result = resolver.resolveArgument(new MethodParameter(method, 0), null, null, null);
|
||||
|
||||
@@ -91,6 +115,7 @@ class SlicedResourcesAssemblerArgumentResolverUnitTest {
|
||||
}
|
||||
|
||||
private void assertSelectsParameter(Method method, int expectedIndex) {
|
||||
|
||||
var parameter = new MethodParameter(method, 0);
|
||||
|
||||
var result = resolver.resolveArgument(parameter, null, null, null);
|
||||
@@ -98,13 +123,20 @@ class SlicedResourcesAssemblerArgumentResolverUnitTest {
|
||||
}
|
||||
|
||||
private void assertRejectsAmbiguity(String methodName) throws Exception {
|
||||
var method = Controller.class.getMethod(methodName, SlicedResourcesAssembler.class, Pageable.class,
|
||||
Pageable.class);
|
||||
|
||||
var method = Controller.class.getMethod(methodName, SlicedResourcesAssembler.class, Pageable.class, Pageable.class);
|
||||
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> resolver.resolveArgument(new MethodParameter(method, 0), null, null, null));
|
||||
}
|
||||
|
||||
private static void assertMethodParameterAwareSlicedResourcesAssemblerFor(Object result, MethodParameter parameter) {
|
||||
|
||||
assertThat(result).isInstanceOfSatisfying(SlicedResourcesAssembler.class, it -> {
|
||||
assertThat(ReflectionTestUtils.getField(it, "parameter")).isEqualTo(parameter);
|
||||
});
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
interface Controller {
|
||||
void noContext(SlicedResourcesAssembler<Object> resolver);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 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.
|
||||
@@ -15,8 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.web;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
@@ -26,8 +25,15 @@ 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.data.domain.AbstractPageRequest;
|
||||
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.hateoas.IanaLinkRelations;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.hateoas.RepresentationModel;
|
||||
import org.springframework.hateoas.SlicedModel;
|
||||
import org.springframework.hateoas.server.RepresentationModelAssembler;
|
||||
import org.springframework.hateoas.server.core.EmbeddedWrapper;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -38,37 +44,25 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* Unit tests for {@link SlicedResourcesAssembler}.
|
||||
*
|
||||
* @author Michael Schout
|
||||
* @author Oliver Drotbohm
|
||||
* @since 3.1
|
||||
*/
|
||||
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
|
||||
@Test // GH-1307
|
||||
void addsNextLinkForFirstSlice() {
|
||||
|
||||
var resources = assembler.toModel(createSlice(0));
|
||||
|
||||
assertThat(resources.getLink(IanaLinkRelations.PREV)).isEmpty();
|
||||
@@ -76,8 +70,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(resources.getLink(IanaLinkRelations.NEXT)).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void addsPreviousAndNextLinksForMiddleSlice() {
|
||||
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getLink(IanaLinkRelations.PREV)).isNotEmpty();
|
||||
@@ -85,8 +80,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(resources.getLink(IanaLinkRelations.NEXT)).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void addsPreviousLinkForLastSlice() {
|
||||
|
||||
var resources = assembler.toModel(createSlice(2));
|
||||
|
||||
assertThat(resources.getLink(IanaLinkRelations.PREV)).isNotEmpty();
|
||||
@@ -94,8 +90,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(resources.getLink(IanaLinkRelations.NEXT)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void usesBaseUriIfConfigured() {
|
||||
|
||||
var baseUri = UriComponentsBuilder.fromUriString("https://foo:9090").build();
|
||||
|
||||
var assembler = new SlicedResourcesAssembler<Person>(resolver, baseUri);
|
||||
@@ -106,8 +103,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.NEXT).getHref()).startsWith(baseUri.toUriString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void usesCustomLinkProvided() {
|
||||
|
||||
var link = Link.of("https://foo:9090", "rel");
|
||||
|
||||
var resources = assembler.toModel(createSlice(1), link);
|
||||
@@ -117,8 +115,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.NEXT).getHref()).startsWith(link.getHref());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void createsSlicedResourcesForOneIndexedArgumentResolver() {
|
||||
|
||||
resolver.setOneIndexedParameters(true);
|
||||
|
||||
AbstractPageRequest request = PageRequest.of(0, 1);
|
||||
@@ -127,15 +126,17 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assembler.toModel(slice);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void createsACanonicalLinkWithoutTemplateParameters() {
|
||||
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.SELF).getHref()).doesNotContain("{").doesNotContain("}");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void invokesCustomElementResourceAssembler() {
|
||||
|
||||
var personAssembler = new PersonResourceAssembler();
|
||||
|
||||
var resources = assembler.toModel(createSlice(0), personAssembler);
|
||||
@@ -148,8 +149,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(content.iterator().next().name).isEqualTo("Dave");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void createsPaginationLinksForOneIndexedArgumentResolverCorrectly() {
|
||||
|
||||
var argumentResolver = new HateoasPageableHandlerMethodArgumentResolver();
|
||||
argumentResolver.setOneIndexedParameters(true);
|
||||
|
||||
@@ -167,8 +169,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(getQueryParameters(resource.getRequiredLink("next"))).containsEntry("page", "3");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void generatedLinksShouldNotBeTemplated() {
|
||||
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.SELF).getHref()).doesNotContain("{").doesNotContain("}");
|
||||
@@ -176,8 +179,9 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.PREV).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void generatesEmptySliceResourceWithEmbeddedWrapper() {
|
||||
|
||||
var result = assembler.toEmptyModel(EMPTY_SLICE, Person.class);
|
||||
|
||||
var content = result.getContent();
|
||||
@@ -188,39 +192,40 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(((EmbeddedWrapper) element).getRelTargetType()).isEqualTo(Person.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void emptySliceCreatorRejectsSliceWithContent() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> assembler.toEmptyModel(createSlice(1), Person.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void emptySliceCreatorRejectsNullType() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> assembler.toEmptyModel(EMPTY_SLICE, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void addsFirstLinkForMultipleSlices() {
|
||||
var resources = assembler.toModel(createSlice(1));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void addsFirstLinkForFirstSlice() {
|
||||
var resources = assembler.toModel(createSlice(0));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void addsFirstLinkForLastSlice() {
|
||||
var resources = assembler.toModel(createSlice(2));
|
||||
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void alwaysAddsFirstLinkIfConfiguredTo() {
|
||||
|
||||
var assembler = new SlicedResourcesAssembler<Person>(resolver, null);
|
||||
assembler.setForceFirstRel(true);
|
||||
|
||||
@@ -229,22 +234,22 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
assertThat(resources.getRequiredLink(IanaLinkRelations.FIRST).getHref()).endsWith("?page=0&size=20");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void usesCustomSlicedResources() {
|
||||
RepresentationModelAssembler<Slice<Person>, SlicedModel<EntityModel<Person>>> assembler = new CustomSlicedResourcesAssembler<>(
|
||||
resolver, null);
|
||||
|
||||
var assembler = new CustomSlicedResourcesAssembler<Person>(resolver, null);
|
||||
|
||||
assertThat(assembler.toModel(EMPTY_SLICE)).isInstanceOf(CustomSlicedResources.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void selfLinkContainsCoordinatesForCurrentSlice() {
|
||||
var resource = assembler.toModel(createSlice(0));
|
||||
|
||||
assertThat(resource.getRequiredLink(IanaLinkRelations.SELF).getHref()).endsWith("?page=0&size=1");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-1307
|
||||
void keepsRequestParametersOfOriginalRequestUri() {
|
||||
WebTestUtils.initWebTest(new MockHttpServletRequest("GET", "/sample?foo=bar"));
|
||||
|
||||
@@ -254,6 +259,23 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
.isEqualTo("http://localhost/sample?foo=bar&page=0&size=1");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
static class Person {
|
||||
String name;
|
||||
}
|
||||
@@ -288,4 +310,4 @@ class SlicedResourcesAssemblerUnitTest {
|
||||
super(content, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user