Polishing.
Allow nullable OffsetScrollPosition and add support for values wrapped within Optional. Update tests. Reformat code. Add issue tags to tests. See #2856 Original pull request: #2861
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2023 the original author or authors.
|
||||
* Copyright 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.
|
||||
@@ -17,7 +17,6 @@ package org.springframework.data.web;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
@@ -27,27 +26,31 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Argument resolver to extract a {@link OffsetScrollPosition} object from a {@link NativeWebRequest} for a particular
|
||||
* {@link MethodParameter}. A {@link OffsetScrollPositionArgumentResolver} can either resolve {@link OffsetScrollPosition} itself or wrap another
|
||||
* {@link OffsetScrollPositionArgumentResolver} to post-process {@link OffsetScrollPosition}.
|
||||
* {@link MethodParameter}. A {@link OffsetScrollPositionArgumentResolver} can either resolve
|
||||
* {@link OffsetScrollPosition} itself or wrap another {@link OffsetScrollPositionArgumentResolver} to post-process
|
||||
* {@link OffsetScrollPosition}.
|
||||
*
|
||||
* @since 3.2
|
||||
* @author Yanming Zhou
|
||||
* @see HandlerMethodArgumentResolver
|
||||
* @author Mark Paluch
|
||||
* @since 3.2
|
||||
* @see org.springframework.web.method.support.HandlerMethodArgumentResolver
|
||||
*/
|
||||
public interface OffsetScrollPositionArgumentResolver extends HandlerMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* Resolves a {@link OffsetScrollPosition} method parameter into an argument value from a given request.
|
||||
* Resolves a {@link OffsetScrollPosition} method parameter into an argument value from a given request. Supports also
|
||||
* wrapped arguments in {@link java.util.Optional}.
|
||||
*
|
||||
* @param parameter the method parameter to resolve. This parameter must have previously been passed to
|
||||
* {@link #supportsParameter} which must have returned {@code true}.
|
||||
* @param mavContainer the ModelAndViewContainer for the current request
|
||||
* @param webRequest the current request
|
||||
* @param binderFactory a factory for creating {@link WebDataBinder} instances
|
||||
* @return the resolved argument value
|
||||
* @return the resolved argument value or {@literal null} if the value cannot be resolved. The returned value
|
||||
* considers {@link MethodParameter#isOptional() Optional} wrapping by returing either the value wrapped
|
||||
* within Optional or Optional.empty().
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
OffsetScrollPosition resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
||||
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2023 the original author or authors.
|
||||
* Copyright 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,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.web;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -23,27 +25,28 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* {@link HandlerMethodArgumentResolver} to automatically create {@link OffsetScrollPosition} instances from request parameters.
|
||||
* {@link HandlerMethodArgumentResolver} to automatically create {@link OffsetScrollPosition} instances from request
|
||||
* parameters.
|
||||
*
|
||||
* @since 3.2
|
||||
* @author Yanming Zhou
|
||||
* @since 3.2
|
||||
*/
|
||||
public class OffsetScrollPositionHandlerMethodArgumentResolver extends OffsetScrollPositionHandlerMethodArgumentResolverSupport
|
||||
implements OffsetScrollPositionArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return OffsetScrollPosition.class.equals(parameter.getParameterType());
|
||||
return OffsetScrollPosition.class.equals(parameter.nestedIfOptional().getNestedParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OffsetScrollPosition resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
||||
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
||||
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
|
||||
|
||||
String[] offsetParameter = webRequest.getParameterValues(getOffsetParameter(parameter));
|
||||
return parseParameterIntoOffsetScrollPosition(offsetParameter != null ? Arrays.asList(offsetParameter) : null);
|
||||
String[] offsetParameter = webRequest.getParameterValues(getOffsetParameter(parameter.nestedIfOptional()));
|
||||
return adaptArgumentIfNecessary(
|
||||
parseParameterIntoOffsetScrollPosition(offsetParameter != null ? Arrays.asList(offsetParameter) : null),
|
||||
parameter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2023 the original author or authors.
|
||||
* Copyright 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,22 +15,23 @@
|
||||
*/
|
||||
package org.springframework.data.web;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
import org.springframework.data.domain.ScrollPosition;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Base class providing methods for handler method argument resolvers to create {@link OffsetScrollPosition} instances from request
|
||||
* parameters.
|
||||
* Base class providing methods for handler method argument resolvers to create {@link OffsetScrollPosition} instances
|
||||
* from request parameters.
|
||||
*
|
||||
* @since 3.2
|
||||
* @author Yanming Zhou
|
||||
* @since 3.2
|
||||
* @see OffsetScrollPositionHandlerMethodArgumentResolver
|
||||
* @see ReactiveOffsetScrollPositionHandlerMethodArgumentResolver
|
||||
*/
|
||||
@@ -71,7 +72,7 @@ public abstract class OffsetScrollPositionHandlerMethodArgumentResolverSupport {
|
||||
* @param parameter can be {@literal null}.
|
||||
* @return the offset parameter
|
||||
*/
|
||||
protected String getOffsetParameter(@Nullable MethodParameter parameter) {
|
||||
protected String getOffsetParameter(MethodParameter parameter) {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -89,19 +90,39 @@ public abstract class OffsetScrollPositionHandlerMethodArgumentResolverSupport {
|
||||
* Parses the given source into a {@link OffsetScrollPosition} instance.
|
||||
*
|
||||
* @param source could be {@literal null} or empty.
|
||||
* @return parsed OffsetScrollPosition
|
||||
* @return parsed OffsetScrollPosition or {@literal null} if it cannot be constructed.
|
||||
*/
|
||||
@Nullable
|
||||
OffsetScrollPosition parseParameterIntoOffsetScrollPosition(@Nullable List<String> source) {
|
||||
|
||||
// No parameter or Single empty parameter, e.g "offset="
|
||||
if (source == null || source.size() == 1 && !StringUtils.hasText(source.get(0))) {
|
||||
return ScrollPosition.offset();
|
||||
if (CollectionUtils.isEmpty(source) || (source.size() == 1 && !StringUtils.hasText(source.get(0)))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
long offset = Long.parseLong(source.get(0));
|
||||
return ScrollPosition.offset(offset);
|
||||
} catch (NumberFormatException ex) {
|
||||
return ScrollPosition.offset();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the given argument against the method parameter, if necessary.
|
||||
*
|
||||
* @param arg the resolved argument.
|
||||
* @param parameter the method parameter descriptor.
|
||||
* @return the adapted argument, or the original resolved argument as-is.
|
||||
*/
|
||||
@Nullable
|
||||
Object adaptArgumentIfNecessary(@Nullable Object arg, MethodParameter parameter) {
|
||||
|
||||
if (parameter.getParameterType() == Optional.class) {
|
||||
return arg == null ? Optional.empty() : Optional.of(arg);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2023 the original author or authors.
|
||||
* Copyright 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,37 +15,36 @@
|
||||
*/
|
||||
package org.springframework.data.web;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.data.domain.OffsetScrollPosition;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.reactive.BindingContext;
|
||||
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.reactive.result.method.SyncHandlerMethodArgumentResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Reactive {@link HandlerMethodArgumentResolver} to create {@link OffsetScrollPosition} instances from query string parameters.
|
||||
* Reactive {@link HandlerMethodArgumentResolver} to create {@link OffsetScrollPosition} instances from query string
|
||||
* parameters.
|
||||
*
|
||||
* @since 3.2
|
||||
* @author Yanming Zhou
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ReactiveOffsetScrollPositionHandlerMethodArgumentResolver extends OffsetScrollPositionHandlerMethodArgumentResolverSupport
|
||||
implements SyncHandlerMethodArgumentResolver {
|
||||
public class ReactiveOffsetScrollPositionHandlerMethodArgumentResolver
|
||||
extends OffsetScrollPositionHandlerMethodArgumentResolverSupport implements SyncHandlerMethodArgumentResolver {
|
||||
|
||||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return OffsetScrollPosition.class.equals(parameter.getParameterType());
|
||||
return OffsetScrollPosition.class.equals(parameter.nestedIfOptional().getNestedParameterType());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public OffsetScrollPosition resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
public Object resolveArgumentValue(MethodParameter parameter, BindingContext bindingContext,
|
||||
ServerWebExchange exchange) {
|
||||
|
||||
List<String> offsetParameter = exchange.getRequest().getQueryParams().get(getOffsetParameter(parameter));
|
||||
|
||||
return parseParameterIntoOffsetScrollPosition(offsetParameter);
|
||||
return adaptArgumentIfNecessary(parseParameterIntoOffsetScrollPosition(offsetParameter), parameter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2023 the original author or authors.
|
||||
* Copyright 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,12 @@
|
||||
*/
|
||||
package org.springframework.data.web;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -28,40 +32,30 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link OffsetScrollPositionHandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @since 3.2
|
||||
* @author Yanming Zhou
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class OffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
|
||||
static MethodParameter PARAMETER;
|
||||
static final MethodParameter PARAMETER = getParameterOfMethod("supportedMethod");
|
||||
static final MethodParameter OPTIONAL_PARAMETER = getOptionalParameterOfMethod("supportedMethodWithOptional");
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() throws Exception {
|
||||
PARAMETER = getParameterOfMethod("supportedMethod");
|
||||
}
|
||||
final OffsetScrollPositionHandlerMethodArgumentResolver sut = new OffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void supportsSortParameter() {
|
||||
|
||||
var resolver = new OffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
|
||||
assertThat(resolver.supportsParameter(PARAMETER)).isTrue();
|
||||
assertThat(sut.supportsParameter(PARAMETER)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fallbackToDefaultOffset() {
|
||||
|
||||
var parameter = TestUtils.getParameterOfMethod(Controller.class, "unsupportedMethod", String.class);
|
||||
var position = resolveOffset(new MockHttpServletRequest(), parameter);
|
||||
assertThat(position).isEqualTo(ScrollPosition.offset());
|
||||
@Test // GH-2856
|
||||
void supportsOptionalOffsetScrollPositionParameter() {
|
||||
assertThat(sut.supportsParameter(OPTIONAL_PARAMETER)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void discoversOffsetFromRequest() {
|
||||
|
||||
var reference = ScrollPosition.offset(5);
|
||||
@@ -69,7 +63,35 @@ class OffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, null), PARAMETER, reference);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void returnsNullIfNotSpecified() {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
var position = resolveOffset(request, PARAMETER);
|
||||
|
||||
assertThat(position).isNull();
|
||||
}
|
||||
|
||||
@Test // GH-2856
|
||||
void returnsOptionalParameterFromRequest() {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
request.addParameter("offset", "5");
|
||||
var position = resolveOffset(request, OPTIONAL_PARAMETER);
|
||||
|
||||
assertThat(position).isEqualTo(Optional.of(ScrollPosition.offset(5)));
|
||||
}
|
||||
|
||||
@Test // GH-2856
|
||||
void returnsEmptyOptionalIfNotSpecified() {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
var position = resolveOffset(request, OPTIONAL_PARAMETER);
|
||||
|
||||
assertThat(position).isEqualTo(Optional.empty());
|
||||
}
|
||||
|
||||
@Test // GH-2856
|
||||
void discoversOffsetFromRequestWithMultipleParams() {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
@@ -79,43 +101,43 @@ class OffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void discoversQualifiedOffsetFromRequest() {
|
||||
|
||||
var parameter = getParameterOfMethod("qualifiedOffset");
|
||||
var reference = ScrollPosition.offset(5);
|
||||
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, "qual"), parameter, reference);
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, "hello"), parameter, reference);
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsDefaultForOffsetParamSetToNothing() {
|
||||
@Test // GH-2856
|
||||
void returnsNullForOffsetParamSetToNothing() {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
request.addParameter("offset", (String) null);
|
||||
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset());
|
||||
assertThat(resolveOffset(request, PARAMETER)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsDefaultForEmptyOffsetParam() {
|
||||
@Test // GH-2856
|
||||
void returnsNullForEmptyOffsetParam() {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
request.addParameter("offset", "");
|
||||
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset());
|
||||
assertThat(resolveOffset(request, PARAMETER)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsDefaultForOffsetParamIsInvalidProperty() {
|
||||
@Test // GH-2856
|
||||
void returnsNullForOffsetParamIsInvalidProperty() {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
request.addParameter("offset", "invalid_number");
|
||||
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset());
|
||||
assertThat(resolveOffset(request, PARAMETER)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void emptyQualifierIsUsedInParameterLookup() {
|
||||
|
||||
var parameter = getParameterOfMethod("emptyQualifier");
|
||||
@@ -124,7 +146,7 @@ class OffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, ""), parameter, reference);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void mergedQualifierIsUsedInParameterLookup() {
|
||||
|
||||
var parameter = getParameterOfMethod("mergedQualifier");
|
||||
@@ -133,30 +155,28 @@ class OffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, "merged"), parameter, reference);
|
||||
}
|
||||
|
||||
private static OffsetScrollPosition resolveOffset(HttpServletRequest request, MethodParameter parameter) {
|
||||
|
||||
var resolver = new OffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
return resolver.resolveArgument(parameter, null, new ServletWebRequest(request), null);
|
||||
@Nullable
|
||||
private Object resolveOffset(HttpServletRequest request, MethodParameter parameter) {
|
||||
return sut.resolveArgument(parameter, null, new ServletWebRequest(request), null);
|
||||
}
|
||||
|
||||
private static void assertSupportedAndResolvedTo(NativeWebRequest request, MethodParameter parameter, OffsetScrollPosition position) {
|
||||
private void assertSupportedAndResolvedTo(NativeWebRequest request, MethodParameter parameter,
|
||||
OffsetScrollPosition position) {
|
||||
|
||||
var resolver = new OffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
assertThat(resolver.supportsParameter(parameter)).isTrue();
|
||||
|
||||
try {
|
||||
assertThat(resolver.resolveArgument(parameter, null, request, null)).isEqualTo(position);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
assertThat(sut.supportsParameter(parameter)).isTrue();
|
||||
assertThat(sut.resolveArgument(parameter, null, request, null)).isEqualTo(position);
|
||||
}
|
||||
|
||||
|
||||
private static MethodParameter getParameterOfMethod(String name) {
|
||||
return TestUtils.getParameterOfMethod(Controller.class, name, OffsetScrollPosition.class);
|
||||
}
|
||||
|
||||
private static NativeWebRequest getRequestWithOffset(@Nullable OffsetScrollPosition position, @Nullable String qualifier) {
|
||||
private static MethodParameter getOptionalParameterOfMethod(String name) {
|
||||
return TestUtils.getParameterOfMethod(Controller.class, name, Optional.class);
|
||||
}
|
||||
|
||||
private static NativeWebRequest getRequestWithOffset(@Nullable OffsetScrollPosition position,
|
||||
@Nullable String qualifier) {
|
||||
|
||||
var request = new MockHttpServletRequest();
|
||||
|
||||
@@ -174,9 +194,11 @@ class OffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
|
||||
void supportedMethod(OffsetScrollPosition offset);
|
||||
|
||||
void supportedMethodWithOptional(Optional<OffsetScrollPosition> offset);
|
||||
|
||||
void unsupportedMethod(String string);
|
||||
|
||||
void qualifiedOffset(@Qualifier("qual") OffsetScrollPosition offset);
|
||||
void qualifiedOffset(@Qualifier("hello") OffsetScrollPosition offset);
|
||||
|
||||
void emptyQualifier(@Qualifier OffsetScrollPosition offset);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2023 the original author or authors.
|
||||
* Copyright 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,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.web;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -26,41 +29,40 @@ import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ReactiveOffsetScrollPositionHandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @since 3.2
|
||||
* @author Yanming Zhou
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ReactiveOffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
|
||||
static MethodParameter PARAMETER;
|
||||
static final MethodParameter PARAMETER = getParameterOfMethod("supportedMethod");
|
||||
static final MethodParameter OPTIONAL_PARAMETER = getOptionalParameterOfMethod("supportedMethodWithOptional");
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() throws Exception {
|
||||
PARAMETER = getParameterOfMethod("supportedMethod");
|
||||
final ReactiveOffsetScrollPositionHandlerMethodArgumentResolver sut = new ReactiveOffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
|
||||
@Test // GH-2856
|
||||
void supportsOffsetScrollPositionParameter() {
|
||||
assertThat(sut.supportsParameter(PARAMETER)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void supportsSortParameter() {
|
||||
|
||||
var resolver = new ReactiveOffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
|
||||
assertThat(resolver.supportsParameter(PARAMETER)).isTrue();
|
||||
@Test // GH-2856
|
||||
void supportsOptionalOffsetScrollPositionParameter() {
|
||||
assertThat(sut.supportsParameter(OPTIONAL_PARAMETER)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fallbackToDefaultOffset() {
|
||||
@Test // GH-2856
|
||||
void fallbackToNullOffset() {
|
||||
|
||||
var parameter = TestUtils.getParameterOfMethod(Controller.class, "unsupportedMethod", String.class);
|
||||
var request = MockServerHttpRequest.get("/foo").build();
|
||||
var position = resolveOffset(request, parameter);
|
||||
assertThat(position).isEqualTo(ScrollPosition.offset());
|
||||
|
||||
assertThat(position).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void discoversOffsetFromRequest() {
|
||||
|
||||
var reference = ScrollPosition.offset(5);
|
||||
@@ -68,7 +70,34 @@ class ReactiveOffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, null), PARAMETER, reference);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void returnsNullIfNotSpecified() {
|
||||
|
||||
var request = MockServerHttpRequest.get("/foo").build();
|
||||
var position = resolveOffset(request, PARAMETER);
|
||||
|
||||
assertThat(position).isNull();
|
||||
}
|
||||
|
||||
@Test // GH-2856
|
||||
void returnsOptionalParameterFromRequest() {
|
||||
|
||||
var request = MockServerHttpRequest.get("/foo?offset=5").build();
|
||||
var position = resolveOffset(request, OPTIONAL_PARAMETER);
|
||||
|
||||
assertThat(position).isEqualTo(Optional.of(ScrollPosition.offset(5)));
|
||||
}
|
||||
|
||||
@Test // GH-2856
|
||||
void returnsEmptyOptionalIfNotSpecified() {
|
||||
|
||||
var request = MockServerHttpRequest.get("/foo").build();
|
||||
var position = resolveOffset(request, OPTIONAL_PARAMETER);
|
||||
|
||||
assertThat(position).isEqualTo(Optional.empty());
|
||||
}
|
||||
|
||||
@Test // GH-2856
|
||||
void discoversOffsetFromRequestWithMultipleParams() {
|
||||
|
||||
var request = MockServerHttpRequest.get("/foo?offset=5&offset=6").build();
|
||||
@@ -76,40 +105,32 @@ class ReactiveOffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void discoversQualifiedOffsetFromRequest() {
|
||||
|
||||
var parameter = getParameterOfMethod("qualifiedOffset");
|
||||
var reference = ScrollPosition.offset(5);
|
||||
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, "qual"), parameter, reference);
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, "hello"), parameter, reference);
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsDefaultForOffsetParamSetToNothing() {
|
||||
|
||||
var request = MockServerHttpRequest.get("/foo").build();
|
||||
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset());
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsDefaultForEmptyOffsetParam() {
|
||||
@Test // GH-2856
|
||||
void returnsNullForEmptyOffsetParam() {
|
||||
|
||||
var request = MockServerHttpRequest.get("/foo?offset=").build();
|
||||
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset());
|
||||
assertThat(resolveOffset(request, PARAMETER)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void returnsDefaultForOffsetParamIsInvalidProperty() {
|
||||
@Test // GH-2856
|
||||
void returnsNullForOffsetParamIsInvalidProperty() {
|
||||
|
||||
var request = MockServerHttpRequest.get("/foo?offset=invalid_number").build();
|
||||
|
||||
assertThat(resolveOffset(request, PARAMETER)).isEqualTo(ScrollPosition.offset());
|
||||
assertThat(resolveOffset(request, PARAMETER)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void emptyQualifierIsUsedInParameterLookup() {
|
||||
|
||||
var parameter = getParameterOfMethod("emptyQualifier");
|
||||
@@ -118,7 +139,7 @@ class ReactiveOffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, ""), parameter, reference);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test // GH-2856
|
||||
void mergedQualifierIsUsedInParameterLookup() {
|
||||
|
||||
var parameter = getParameterOfMethod("mergedQualifier");
|
||||
@@ -127,47 +148,51 @@ class ReactiveOffsetScrollPositionHandlerMethodArgumentResolverUnitTests {
|
||||
assertSupportedAndResolvedTo(getRequestWithOffset(reference, "merged"), parameter, reference);
|
||||
}
|
||||
|
||||
private static OffsetScrollPosition resolveOffset(MockServerHttpRequest request, MethodParameter parameter) {
|
||||
@Nullable
|
||||
private static Object resolveOffset(MockServerHttpRequest request, MethodParameter parameter) {
|
||||
|
||||
var resolver = new ReactiveOffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
return resolver.resolveArgumentValue(parameter, null, MockServerWebExchange.from(request));
|
||||
}
|
||||
|
||||
private static void assertSupportedAndResolvedTo(MockServerHttpRequest request, MethodParameter parameter, OffsetScrollPosition position) {
|
||||
private void assertSupportedAndResolvedTo(MockServerHttpRequest request, MethodParameter parameter,
|
||||
OffsetScrollPosition position) {
|
||||
|
||||
var resolver = new ReactiveOffsetScrollPositionHandlerMethodArgumentResolver();
|
||||
assertThat(resolver.supportsParameter(parameter)).isTrue();
|
||||
assertThat(sut.supportsParameter(parameter)).isTrue();
|
||||
|
||||
try {
|
||||
var resolved = resolveOffset(request, parameter);
|
||||
assertThat(resolved).isEqualTo(position);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
var resolved = resolveOffset(request, parameter);
|
||||
assertThat(resolved).isEqualTo(position);
|
||||
}
|
||||
|
||||
|
||||
private static MethodParameter getParameterOfMethod(String name) {
|
||||
return TestUtils.getParameterOfMethod(Controller.class, name, OffsetScrollPosition.class);
|
||||
}
|
||||
|
||||
private static MockServerHttpRequest getRequestWithOffset(@Nullable OffsetScrollPosition position, @Nullable String qualifier) {
|
||||
private static MethodParameter getOptionalParameterOfMethod(String name) {
|
||||
return TestUtils.getParameterOfMethod(Controller.class, name, Optional.class);
|
||||
}
|
||||
|
||||
private static MockServerHttpRequest getRequestWithOffset(@Nullable OffsetScrollPosition position,
|
||||
@Nullable String qualifier) {
|
||||
|
||||
if (position == null) {
|
||||
return TestUtils.getWebfluxRequest();
|
||||
}
|
||||
|
||||
String parameterName = StringUtils.hasLength(qualifier) ? qualifier + "_offset" : "offset";
|
||||
return MockServerHttpRequest.get(String.format("foo?%s=%s", parameterName, String.valueOf(position.getOffset()))).build();
|
||||
return MockServerHttpRequest.get(String.format("foo?%s=%s", parameterName, String.valueOf(position.getOffset())))
|
||||
.build();
|
||||
}
|
||||
|
||||
interface Controller {
|
||||
|
||||
void supportedMethod(OffsetScrollPosition offset);
|
||||
|
||||
void supportedMethodWithOptional(Optional<OffsetScrollPosition> offset);
|
||||
|
||||
void unsupportedMethod(String string);
|
||||
|
||||
void qualifiedOffset(@Qualifier("qual") OffsetScrollPosition offset);
|
||||
void qualifiedOffset(@Qualifier("hello") OffsetScrollPosition offset);
|
||||
|
||||
void emptyQualifier(@Qualifier OffsetScrollPosition offset);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user