Introduce Scroll API.

See: #2151
Original Pull Request: #2787
This commit is contained in:
Mark Paluch
2023-02-27 13:37:06 +01:00
committed by Christoph Strobl
parent a5de842460
commit 035965a3a2
27 changed files with 1280 additions and 134 deletions

View File

@@ -0,0 +1,43 @@
/*
* 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.
* 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.domain;
import static org.assertj.core.api.Assertions.*;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.KeysetScrollPosition.Direction;
/**
* Unit tests for {@link KeysetScrollPosition}.
*
* @author Mark Paluch
*/
class KeysetScrollPositionUnitTests {
@Test // GH-2151
void equalsAndHashCode() {
KeysetScrollPosition foo1 = KeysetScrollPosition.of(Collections.singletonMap("k", "v"));
KeysetScrollPosition foo2 = KeysetScrollPosition.of(Collections.singletonMap("k", "v"));
KeysetScrollPosition bar = KeysetScrollPosition.of(Collections.singletonMap("k", "v"), Direction.Backward);
assertThat(foo1).isEqualTo(foo2).hasSameClassAs(foo2);
assertThat(foo1).isNotEqualTo(bar).doesNotHaveSameHashCodeAs(bar);
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.
* 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.domain;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.domain.OffsetScrollPosition.*;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link OffsetScrollPosition}.
*
* @author Mark Paluch
*/
class OffsetScrollPositionUnitTests {
@Test // GH-2151
void equalsAndHashCode() {
OffsetScrollPosition foo1 = OffsetScrollPosition.of(1);
OffsetScrollPosition foo2 = OffsetScrollPosition.of(1);
OffsetScrollPosition bar = OffsetScrollPosition.of(2);
assertThat(foo1).isEqualTo(foo2).hasSameClassAs(foo2);
assertThat(foo1).isNotEqualTo(bar).doesNotHaveSameHashCodeAs(bar);
}
@Test // GH-2151
void shouldCreateCorrectIndexPosition() {
assertThat(positionFunction(0).apply(0)).isEqualTo(OffsetScrollPosition.of(1));
assertThat(positionFunction(0).apply(1)).isEqualTo(OffsetScrollPosition.of(2));
assertThat(positionFunction(100).apply(0)).isEqualTo(OffsetScrollPosition.of(101));
assertThat(positionFunction(100).apply(1)).isEqualTo(OffsetScrollPosition.of(102));
}
}

View File

@@ -19,7 +19,6 @@ import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.domain.UnitTestUtils.*;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Sort.Direction;
/**
@@ -67,4 +66,12 @@ class PageRequestUnitTests extends AbstractPageRequestUnitTests {
assertThatIllegalArgumentException() //
.isThrownBy(() -> PageRequest.of(0, 10, null));
}
@Test // GH-2151
void createsOffsetScrollPosition() {
PageRequest request = PageRequest.of(1, 10);
assertThat(request.toScrollPosition()).isEqualTo(OffsetScrollPosition.of(10));
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.
* 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.domain;
import static org.assertj.core.api.Assertions.*;
import java.util.List;
import java.util.function.IntFunction;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link Scroll}.
*
* @author Mark Paluch
*/
class ScrollUnitTests {
@Test // GH-2151
void equalsAndHashCode() {
IntFunction<OffsetScrollPosition> positionFunction = OffsetScrollPosition.positionFunction(0);
Scroll<Integer> one = Scroll.from(List.of(1, 2, 3), positionFunction);
Scroll<Integer> two = Scroll.from(List.of(1, 2, 3), positionFunction);
assertThat(one).isEqualTo(two).hasSameHashCodeAs(two);
assertThat(one.equals(two)).isTrue();
assertThat(Scroll.from(List.of(1, 2, 3), positionFunction, true)).isNotEqualTo(two).doesNotHaveSameHashCodeAs(two);
}
@Test // GH-2151
void allowsIteration() {
Scroll<Integer> scroll = Scroll.from(List.of(1, 2, 3), OffsetScrollPosition.positionFunction(0));
for (Integer integer : scroll) {
assertThat(integer).isBetween(1, 3);
}
}
@Test // GH-2151
void shouldCreateCorrectPositions() {
Scroll<Integer> scroll = Scroll.from(List.of(1, 2, 3), OffsetScrollPosition.positionFunction(0));
assertThat(scroll.firstPosition()).isEqualTo(OffsetScrollPosition.of(1));
assertThat(scroll.lastPosition()).isEqualTo(OffsetScrollPosition.of(3));
// by index
assertThat(scroll.positionAt(1)).isEqualTo(OffsetScrollPosition.of(2));
// by object
assertThat(scroll.positionAt(Integer.valueOf(1))).isEqualTo(OffsetScrollPosition.of(1));
}
}

View File

@@ -19,14 +19,17 @@ import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.ScrollPosition;
/**
* Unit tests for {@link ParametersParameterAccessor}.
*
* @author Oliver Gierke
* @author Greg Turnquist
* @author Mark Paluch
*/
class ParametersParameterAccessorUnitTests {
@@ -75,6 +78,18 @@ class ParametersParameterAccessorUnitTests {
assertThat(accessor.getBindableValue(0)).isEqualTo("Foo");
}
@Test // GH-2151
void handlesScrollPositionAsAParameterType() throws NoSuchMethodException {
var method = Sample.class.getMethod("method", ScrollPosition.class, String.class);
var parameters = new DefaultParameters(method);
var accessor = new ParametersParameterAccessor(parameters, new Object[] { OffsetScrollPosition.of(1), "Foo" });
assertThat(accessor).hasSize(1);
assertThat(accessor.getBindableValue(0)).isEqualTo("Foo");
}
@Test // #2626
void handlesPageRequestAsAParameterType() throws NoSuchMethodException {
@@ -93,6 +108,8 @@ class ParametersParameterAccessorUnitTests {
void method(Pageable pageable, String string);
void method(ScrollPosition scrollPosition, String string);
void methodWithPageRequest(PageRequest pageRequest, String string);
}
}

View File

@@ -25,9 +25,10 @@ import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Scroll;
import org.springframework.data.domain.Sort;
import org.springframework.test.util.ReflectionTestUtils;
@@ -182,6 +183,14 @@ class ParametersUnitTests {
assertThat(parameters.hasPageableParameter()).isTrue();
}
@Test // GH-2151
void acceptsScrollPositionSubtypeParameter() throws Exception {
var parameters = getParametersFor("customScrollPosition", OffsetScrollPosition.class);
assertThat(parameters.hasScrollPositionParameter()).isTrue();
}
private Parameters<?, Parameter> getParametersFor(String methodName, Class<?>... parameterTypes)
throws SecurityException, NoSuchMethodException {
@@ -221,6 +230,8 @@ class ParametersUnitTests {
void methodWithSingle(Single<String> single);
Page<Object> customPageable(SomePageable pageable);
Scroll<Object> customScrollPosition(OffsetScrollPosition request);
}
interface SomePageable extends Pageable {}

View File

@@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.*;
import io.vavr.collection.Seq;
import io.vavr.control.Option;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.Serializable;
@@ -31,6 +32,8 @@ import org.eclipse.collections.api.list.ImmutableList;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Scroll;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Slice;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -88,6 +91,48 @@ class QueryMethodUnitTests {
assertThat(queryMethod.isCollectionQuery()).isFalse();
}
@Test // GH-2151
void supportsImperativecursorQueries() throws Exception {
var method = SampleRepository.class.getMethod("cursorWindow", ScrollPosition.class);
var queryMethod = new QueryMethod(method, metadata, factory);
assertThat(queryMethod.isPageQuery()).isFalse();
assertThat(queryMethod.isScrollQuery()).isTrue();
assertThat(queryMethod.isCollectionQuery()).isFalse();
}
@Test // GH-2151
void supportsReactiveCursorQueries() throws Exception {
var method = SampleRepository.class.getMethod("reactiveCursorWindow", ScrollPosition.class);
var queryMethod = new QueryMethod(method, metadata, factory);
assertThat(queryMethod.isPageQuery()).isFalse();
assertThat(queryMethod.isScrollQuery()).isTrue();
assertThat(queryMethod.isCollectionQuery()).isFalse();
}
@Test // GH-2151
void rejectsInvalidReactiveCursorQueries() throws Exception {
var method = SampleRepository.class.getMethod("invalidReactiveCursorWindow", ScrollPosition.class);
assertThatIllegalStateException().isThrownBy(() -> new QueryMethod(method, metadata, factory));
}
@Test // GH-2151
void rejectsCursorWindowMethodWithoutPageable() throws Exception {
var method = SampleRepository.class.getMethod("cursorWindowWithoutScrollPosition");
assertThatIllegalArgumentException().isThrownBy(() -> new QueryMethod(method, metadata, factory));
}
@Test // GH-2151
void rejectsCursorWindowMethodWithInvalidReturnType() throws Exception {
var method = SampleRepository.class.getMethod("cursorWindowMethodWithInvalidReturnType", ScrollPosition.class);
assertThatIllegalStateException().isThrownBy(() -> new QueryMethod(method, metadata, factory));
}
@Test // DATACMNS-171
void detectsAnEntityBeingReturned() throws Exception {
@@ -305,6 +350,16 @@ class QueryMethodUnitTests {
Mono<Slice<User>> reactiveSlice();
ImmutableList<User> returnsEclipseCollection();
Scroll<User> cursorWindow(ScrollPosition cursorRequest);
Mono<Scroll<User>> reactiveCursorWindow(ScrollPosition cursorRequest);
Flux<Scroll<User>> invalidReactiveCursorWindow(ScrollPosition cursorRequest);
Page<User> cursorWindowMethodWithInvalidReturnType(ScrollPosition cursorRequest);
Scroll<User> cursorWindowWithoutScrollPosition();
}
class User {

View File

@@ -19,23 +19,27 @@ import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.OffsetScrollPosition;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Sort;
/**
* Unit tests for {@link ParametersParameterAccessor}.
*
* @author Oliver Gierke
* @author Mark Paluch
*/
class SimpleParameterAccessorUnitTests {
Parameters<?, ?> parameters, sortParameters, pageableParameters;
Parameters<?, ?> parameters, cursorRequestParameters, sortParameters, pageableParameters;
@BeforeEach
void setUp() throws SecurityException, NoSuchMethodException {
parameters = new DefaultParameters(Sample.class.getMethod("sample", String.class));
cursorRequestParameters = new DefaultParameters(Sample.class.getMethod("sample", ScrollPosition.class));
sortParameters = new DefaultParameters(Sample.class.getMethod("sample1", String.class, Sort.class));
pageableParameters = new DefaultParameters(Sample.class.getMethod("sample2", String.class, Pageable.class));
}
@@ -75,6 +79,16 @@ class SimpleParameterAccessorUnitTests {
assertThat(accessor.getSort().isSorted()).isFalse();
}
@Test // GH-2151
void returnsScrollPositionIfAvailable() {
var cursorRequest = OffsetScrollPosition.of(1);
ParameterAccessor accessor = new ParametersParameterAccessor(cursorRequestParameters,
new Object[] { cursorRequest });
assertThat(accessor.getScrollPosition()).isEqualTo(cursorRequest);
}
@Test
void returnsSortIfAvailable() {
@@ -110,6 +124,8 @@ class SimpleParameterAccessorUnitTests {
void sample(String firstname);
void sample(ScrollPosition scrollPosition);
void sample1(String firstname, Sort sort);
void sample2(String firstname, Pageable pageable);