Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
c1e942f
feat: add pagination interfaces
ricellis Jan 28, 2025
710384f
fix: client in ctors
ricellis Feb 10, 2025
4a1ba8d
fix: add limit getters
ricellis Feb 10, 2025
cb4f1ed
feat: base pager implementation
ricellis Feb 10, 2025
896e1b9
feat: KeyPager nextRequest override
ricellis Feb 10, 2025
13ce6da
test: use Java 11 source version for test compile
ricellis Feb 12, 2025
f027b45
test: extract MockCloudant
ricellis Feb 11, 2025
103273a
test: BasePager tests
ricellis Feb 12, 2025
543f564
fix: update default page size to 20
ricellis Feb 26, 2025
ad3323f
test: add testGetNextUntilSmaller case
ricellis Feb 26, 2025
1141b46
test: move test helpers for re-use
ricellis Mar 5, 2025
5d66784
test: fix incorrect comments
ricellis Mar 11, 2025
8371d36
fix: make new instances for Iterable
ricellis Mar 7, 2025
07a76b7
feat: add DesignDocsPager outline
ricellis Mar 7, 2025
8ba21fa
fix: SQ complaint about volatile Object
ricellis Mar 10, 2025
7a02b4c
feat: KeyPager implementation
ricellis Mar 10, 2025
3b7e9d8
feat: add getRows stream
ricellis Mar 13, 2025
028cd05
test: add tests for getNext & getAll exceptions
ricellis Mar 13, 2025
4b922fb
fix: ensure getAll/getRows uses new iterator
ricellis Mar 13, 2025
78f3f8b
test: fix KeyPager options builder
ricellis Mar 14, 2025
63bb5a6
test: add bookmark pager tests
ricellis Mar 17, 2025
8f78a87
feat: update default page size to 200
ricellis Mar 17, 2025
9cd4528
test: fix incorrect comment
ricellis Mar 19, 2025
dd91548
refactor: factory API
ricellis Mar 19, 2025
ce1206c
refactor: renames
ricellis Mar 25, 2025
fdf776f
feat: add design docs pagination factory
ricellis Mar 25, 2025
103df94
docs: fix function -> method
ricellis Apr 1, 2025
8eccb68
feat: add option validators outline
ricellis Apr 28, 2025
02092f2
feat: add page size validation
ricellis Apr 28, 2025
887b3a1
feat: add opt validation for view-like operations
ricellis Apr 28, 2025
5cfaead
feat: query option validation
ricellis Apr 29, 2025
55bc9d7
feat: search option validation
ricellis Apr 29, 2025
ec547ae
test: fix search facet options validation test
ricellis May 8, 2025
946adac
test: refactor pagination test helper
ricellis May 13, 2025
ea7434f
test: fix method sigs to make tests run
ricellis May 12, 2025
bae814e
test: add tests for all docs pagination
ricellis May 13, 2025
e074f57
test: fix mock document
ricellis May 13, 2025
1379d60
test: add remaining pagination operation tests
ricellis May 13, 2025
7749837
test: reduce duplication
ricellis May 16, 2025
b7856a9
test: move parameterized errors to MockCloudant
ricellis May 16, 2025
066ebd6
test: add pagination error cases
ricellis May 16, 2025
3e02444
test: add count assertions on error tests
ricellis May 23, 2025
06a8b4e
docs: pagination examples
ricellis Jun 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* © Copyright IBM Corporation 2025. All Rights Reserved.
*
* 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 com.ibm.cloud.cloudant.features.pagination;

import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.DocsResultRow;

abstract class AllDocsBasePageIterator<B, O> extends KeyPageIterator<String, B, O, AllDocsResult, DocsResultRow> {

AllDocsBasePageIterator(Cloudant client, O options, OptionsHandler<B, O> optsHandler) {
super(client, options, optsHandler);
}

@Override
final Function<AllDocsResult, List<DocsResultRow>> itemsGetter() {
return AllDocsResult::getRows;
}

@Override
final Function<DocsResultRow, String> nextKeyGetter() {
return DocsResultRow::getKey;
}

@Override
final Function<DocsResultRow, String> nextKeyIdGetter() {
return DocsResultRow::getId;
}

/**
* Setting start key doc ID is a no-op for all_docs based paging where
* key is the same as id.
*/
@Override
final Optional<BiFunction<B, String, B>> nextKeyIdSetter() {
return Optional.empty();
}

@Override
final Optional<String> checkBoundary(DocsResultRow penultimateItem, DocsResultRow lastItem) {
// AllDocs and DesignDocs pagers always have unique keys (because they are document IDs)
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* © Copyright IBM Corporation 2025. All Rights Reserved.
*
* 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 com.ibm.cloud.cloudant.features.pagination;

import java.util.function.BiFunction;
import java.util.function.Function;
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.PostAllDocsOptions;
import com.ibm.cloud.cloudant.v1.model.PostAllDocsOptions.Builder;
import com.ibm.cloud.sdk.core.http.ServiceCall;

final class AllDocsPageIterator extends AllDocsBasePageIterator<Builder, PostAllDocsOptions> {

AllDocsPageIterator(Cloudant client, PostAllDocsOptions options) {
super(client, options, OptionsHandler.POST_ALL_DOCS);
}

@Override
Function<PostAllDocsOptions, Builder> optionsToBuilderFunction() {
return PostAllDocsOptions::newBuilder;
}

@Override
Function<Builder, PostAllDocsOptions> builderToOptionsFunction() {
return Builder::build;
}

@Override
BiFunction<Cloudant, PostAllDocsOptions, ServiceCall<AllDocsResult>> nextRequestFunction() {
return Cloudant::postAllDocs;
}

@Override
BiFunction<Builder, String, Builder> nextKeySetter() {
return Builder::startKey;
}

@Override
Function<PostAllDocsOptions, Long> limitGetter() {
return PostAllDocsOptions::limit;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* © Copyright IBM Corporation 2025. All Rights Reserved.
*
* 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 com.ibm.cloud.cloudant.features.pagination;

import java.util.function.BiFunction;
import java.util.function.Function;
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.PostPartitionAllDocsOptions;
import com.ibm.cloud.cloudant.v1.model.PostPartitionAllDocsOptions.Builder;
import com.ibm.cloud.sdk.core.http.ServiceCall;

final class AllDocsPartitionPageIterator extends AllDocsBasePageIterator<PostPartitionAllDocsOptions.Builder, PostPartitionAllDocsOptions> {

AllDocsPartitionPageIterator(Cloudant client, PostPartitionAllDocsOptions options) {
super(client, options, OptionsHandler.POST_PARTITION_ALL_DOCS);
}

@Override
Function<PostPartitionAllDocsOptions, Builder> optionsToBuilderFunction() {
return PostPartitionAllDocsOptions::newBuilder;
}

@Override
Function<Builder, PostPartitionAllDocsOptions> builderToOptionsFunction() {
return Builder::build;
}

@Override
BiFunction<Cloudant, PostPartitionAllDocsOptions, ServiceCall<AllDocsResult>> nextRequestFunction() {
return Cloudant::postPartitionAllDocs;
}


@Override
BiFunction<Builder, String, Builder> nextKeySetter() {
return Builder::startKey;
}

@Override
Function<PostPartitionAllDocsOptions, Long> limitGetter() {
return PostPartitionAllDocsOptions::limit;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* © Copyright IBM Corporation 2025. All Rights Reserved.
*
* 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 com.ibm.cloud.cloudant.features.pagination;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Function;
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.sdk.core.http.ServiceCall;

abstract class BasePageIterator<B, O, R, I> implements Iterator<List<I>> {

protected final Cloudant client;
protected final long pageSize;
protected final OptionsHandler<B, O> optsHandler;
protected final AtomicReference<O> nextPageOptionsRef = new AtomicReference<>();
protected volatile boolean hasNext = true;

BasePageIterator(Cloudant client, O options, OptionsHandler<B, O> optsHandler) {
this.client = client;
this.optsHandler = optsHandler;
// Set the page size from the supplied options limit
this.pageSize = getPageSizeFromOptionsLimit(options);
// Set the first page options
buildAndSetOptions(optsHandler.builderFromOptions(options));
}

@Override
public final boolean hasNext() {
return this.hasNext;
}

@Override
public List<I> next() {
if (this.hasNext()) {
return Collections.unmodifiableList(this.nextRequest());
} else {
throw new NoSuchElementException();
}
}

List<I> nextRequest() {
ServiceCall<R> request = nextRequestFunction().apply(this.client, this.nextPageOptionsRef.get());
R result = request.execute().getResult();
List<I> items = itemsGetter().apply(result);
if (items.size() < this.pageSize) {
this.hasNext = false;
} else {
B optionsBuilder = optsHandler.builderFromOptions(this.nextPageOptionsRef.get());
setNextPageOptions(optionsBuilder, result);
buildAndSetOptions(optionsBuilder);
}
return items;
}

private void buildAndSetOptions(B optionsBuilder) {
this.nextPageOptionsRef.set(optsHandler.optionsFromBuilder(optionsBuilder));
}

abstract Function<O, B> optionsToBuilderFunction();

abstract Function<B, O> builderToOptionsFunction();

abstract Function<R, List<I>> itemsGetter();

abstract void setNextPageOptions(B builder, R result);

abstract BiFunction<Cloudant, O, ServiceCall<R>> nextRequestFunction();

abstract Function<O, Long> limitGetter();

Long getPageSizeFromOptionsLimit(O opts) {
return Optional.ofNullable(limitGetter().apply(opts)).orElse(200L);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* © Copyright IBM Corporation 2025. All Rights Reserved.
*
* 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 com.ibm.cloud.cloudant.features.pagination;

import java.util.function.BiFunction;
import java.util.function.Function;
import com.ibm.cloud.cloudant.v1.Cloudant;

abstract class BookmarkPageIterator<B, O, R, I> extends BasePageIterator<B, O, R, I> {

BookmarkPageIterator(Cloudant client, O options, OptionsHandler<B, O> optsHandler) {
super(client, options, optsHandler);
}

abstract Function<R, String> bookmarkGetter();

abstract BiFunction<B, String, B> bookmarkSetter();

@Override
final void setNextPageOptions(B builder, R result) {
String bookmark = bookmarkGetter().apply(result);
bookmarkSetter().apply(builder, bookmark);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* © Copyright IBM Corporation 2025. All Rights Reserved.
*
* 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 com.ibm.cloud.cloudant.features.pagination;

import java.util.function.BiFunction;
import java.util.function.Function;
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.AllDocsResult;
import com.ibm.cloud.cloudant.v1.model.PostDesignDocsOptions;
import com.ibm.cloud.cloudant.v1.model.PostDesignDocsOptions.Builder;
import com.ibm.cloud.sdk.core.http.ServiceCall;

public class DesignDocsPageIterator extends AllDocsBasePageIterator<Builder, PostDesignDocsOptions> {

DesignDocsPageIterator(Cloudant client, PostDesignDocsOptions options) {
super(client, options, OptionsHandler.POST_DESIGN_DOCS);
}

@Override
BiFunction<Builder, String, Builder> nextKeySetter() {
return Builder::key;
}

@Override
Function<PostDesignDocsOptions, Builder> optionsToBuilderFunction() {
return PostDesignDocsOptions::newBuilder;
}

@Override
Function<Builder, PostDesignDocsOptions> builderToOptionsFunction() {
return Builder::build;
}

@Override
BiFunction<Cloudant, PostDesignDocsOptions, ServiceCall<AllDocsResult>> nextRequestFunction() {
return Cloudant::postDesignDocs;
}

@Override
Function<PostDesignDocsOptions, Long> limitGetter() {
return PostDesignDocsOptions::limit;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* © Copyright IBM Corporation 2025. All Rights Reserved.
*
* 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 com.ibm.cloud.cloudant.features.pagination;

import java.util.List;
import java.util.function.Function;
import com.ibm.cloud.cloudant.v1.Cloudant;
import com.ibm.cloud.cloudant.v1.model.Document;
import com.ibm.cloud.cloudant.v1.model.FindResult;

abstract class FindBasePageIterator<B, O> extends BookmarkPageIterator<B, O, FindResult, Document> {

FindBasePageIterator(Cloudant client, O options, OptionsHandler<B, O> optsHandler) {
super(client, options, optsHandler);
}

@Override
final Function<FindResult, List<Document>> itemsGetter() {
return FindResult::getDocs;
}

@Override
final Function<FindResult, String> bookmarkGetter() {
return FindResult::getBookmark;
}

}
Loading