Skip to content

Commit 5737bc6

Browse files
release: 7.0.0 (#548)
* feat(client): allow providing some params positionally * feat(client)!: extract auto pagination to shared classes refactor(client)!: refactor async auto-pagination refactor(client)!: rename `getNextPage{,Params}` to `nextPage{,Params}` refactor(client)!: swap `nextPage{,Params}` to return non-optional # Migration - If you were referencing the `AutoPager` class on a specific `*Page` or `*PageAsync` type, then you should instead reference the shared `AutoPager` and `AutoPagerAsync` types, under the `core` package - `AutoPagerAsync` now has different usage. You can call `.subscribe(...)` on the returned object instead to get called back each page item. You can also call `onCompleteFuture()` to get a future that completes when all items have been processed. Finally, you can call `.close()` on the returned object to stop auto-paginating early - If you were referencing `getNextPage` or `getNextPageParams`: - Swap to `nextPage()` and `nextPageParams()` - Note that these both now return non-optional types (use `hasNextPage()` before calling these, since they will throw if it's impossible to get another page) There are examples and further information about pagination in the readme. * feat(api): api update * feat(api): api update * feat(api): api update * release: 7.0.0 --------- Co-authored-by: stainless-app[bot] <142633134+stainless-app[bot]@users.noreply.github.com>
1 parent f901d77 commit 5737bc6

File tree

118 files changed

+3389
-1410
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+3389
-1410
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "6.0.0"
2+
".": "7.0.0"
33
}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 45
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-f09e5f2c555d7ee764478b7bc73e92cd21f403d6ec189be14574c8367bc131ce.yml
3-
openapi_spec_hash: bd0a8e001f14132c105992d40149909a
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-5b00a0bc705b1d5bfcb5ea79c7af544766d51ec12ccc4721825664ab397789d8.yml
3+
openapi_spec_hash: 34891659cff31395ba7683a8153b1db5
44
config_hash: 53778a0b839c4f6ad34fbba051f5e8a6

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
# Changelog
22

3+
## 7.0.0 (2025-05-16)
4+
5+
Full Changelog: [v6.0.0...v7.0.0](https://github.com/Finch-API/finch-api-java/compare/v6.0.0...v7.0.0)
6+
7+
### ⚠ BREAKING CHANGES
8+
9+
* **client:** extract auto pagination to shared classes
10+
* **client:** **Migration:** - If you were referencing the `AutoPager` class on a specific `*Page` or `*PageAsync` type, then you should instead reference the shared `AutoPager` and `AutoPagerAsync` types, under the `core` package
11+
- `AutoPagerAsync` now has different usage. You can call `.subscribe(...)` on the returned object instead to get called back each page item. You can also call `onCompleteFuture()` to get a future that completes when all items have been processed. Finally, you can call `.close()` on the returned object to stop auto-paginating early
12+
- If you were referencing `getNextPage` or `getNextPageParams`:
13+
- Swap to `nextPage()` and `nextPageParams()`
14+
- Note that these both now return non-optional types (use `hasNextPage()` before calling these, since they will throw if it's impossible to get another page)
15+
16+
### Features
17+
18+
* **api:** api update ([f226da9](https://github.com/Finch-API/finch-api-java/commit/f226da9c89911bb4f3f39f3657c2f725d0773e7a))
19+
* **api:** api update ([7932861](https://github.com/Finch-API/finch-api-java/commit/79328615d98546e904f7cf99c222f7645ecd9131))
20+
* **api:** api update ([17bd5c1](https://github.com/Finch-API/finch-api-java/commit/17bd5c172f3ef94e68846f5b18670b42560e1fce))
21+
* **client:** allow providing some params positionally ([ec6fa40](https://github.com/Finch-API/finch-api-java/commit/ec6fa40e9be337318708f336e853b3f9e551dd47))
22+
* **client:** extract auto pagination to shared classes ([c4a8874](https://github.com/Finch-API/finch-api-java/commit/c4a8874a3f7799b0d689beaafd00cf1a3727f0ea))
23+
324
## 6.0.0 (2025-05-08)
425

526
Full Changelog: [v5.5.0...v6.0.0](https://github.com/Finch-API/finch-api-java/compare/v5.5.0...v6.0.0)

README.md

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
<!-- x-release-please-start-version -->
44

5-
[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/6.0.0)
6-
[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/6.0.0/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/6.0.0)
5+
[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/7.0.0)
6+
[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/7.0.0/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.0.0)
77

88
<!-- x-release-please-end -->
99

@@ -15,7 +15,7 @@ It is generated with [Stainless](https://www.stainless.com/).
1515

1616
<!-- x-release-please-start-version -->
1717

18-
The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/6.0.0).
18+
The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.0.0).
1919

2020
<!-- x-release-please-end -->
2121

@@ -26,7 +26,7 @@ The REST API documentation can be found on [developer.tryfinch.com](https://deve
2626
### Gradle
2727

2828
```kotlin
29-
implementation("com.tryfinch.api:finch-java:6.0.0")
29+
implementation("com.tryfinch.api:finch-java:7.0.0")
3030
```
3131

3232
### Maven
@@ -35,7 +35,7 @@ implementation("com.tryfinch.api:finch-java:6.0.0")
3535
<dependency>
3636
<groupId>com.tryfinch.api</groupId>
3737
<artifactId>finch-java</artifactId>
38-
<version>6.0.0</version>
38+
<version>7.0.0</version>
3939
</dependency>
4040
```
4141

@@ -219,53 +219,101 @@ The SDK throws custom unchecked exception types:
219219

220220
## Pagination
221221

222-
For methods that return a paginated list of results, this library provides convenient ways access the results either one page at a time, or item-by-item across all pages.
222+
The SDK defines methods that return a paginated lists of results. It provides convenient ways to access the results either one page at a time or item-by-item across all pages.
223223

224224
### Auto-pagination
225225

226-
To iterate through all results across all pages, you can use `autoPager`, which automatically handles fetching more pages for you:
226+
To iterate through all results across all pages, use the `autoPager()` method, which automatically fetches more pages as needed.
227227

228-
### Synchronous
228+
When using the synchronous client, the method returns an [`Iterable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html)
229229

230230
```java
231231
import com.tryfinch.api.models.HrisDirectoryListPage;
232232
import com.tryfinch.api.models.IndividualInDirectory;
233233

234-
// As an Iterable:
235-
HrisDirectoryListPage page = client.hris().directory().list(params);
234+
HrisDirectoryListPage page = client.hris().directory().list();
235+
236+
// Process as an Iterable
236237
for (IndividualInDirectory directory : page.autoPager()) {
237238
System.out.println(directory);
238-
};
239+
}
239240

240-
// As a Stream:
241-
client.hris().directory().list(params).autoPager().stream()
241+
// Process as a Stream
242+
page.autoPager()
243+
.stream()
242244
.limit(50)
243245
.forEach(directory -> System.out.println(directory));
244246
```
245247

246-
### Asynchronous
248+
When using the asynchronous client, the method returns an [`AsyncStreamResponse`](finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/AsyncStreamResponse.kt):
247249

248250
```java
249-
// Using forEach, which returns CompletableFuture<Void>:
250-
asyncClient.hris().directory().list(params).autoPager()
251-
.forEach(directory -> System.out.println(directory), executor);
251+
import com.tryfinch.api.core.http.AsyncStreamResponse;
252+
import com.tryfinch.api.models.HrisDirectoryListPageAsync;
253+
import com.tryfinch.api.models.IndividualInDirectory;
254+
import java.util.Optional;
255+
import java.util.concurrent.CompletableFuture;
256+
257+
CompletableFuture<HrisDirectoryListPageAsync> pageFuture = client.async().hris().directory().list();
258+
259+
pageFuture.thenRun(page -> page.autoPager().subscribe(directory -> {
260+
System.out.println(directory);
261+
}));
262+
263+
// If you need to handle errors or completion of the stream
264+
pageFuture.thenRun(page -> page.autoPager().subscribe(new AsyncStreamResponse.Handler<>() {
265+
@Override
266+
public void onNext(IndividualInDirectory directory) {
267+
System.out.println(directory);
268+
}
269+
270+
@Override
271+
public void onComplete(Optional<Throwable> error) {
272+
if (error.isPresent()) {
273+
System.out.println("Something went wrong!");
274+
throw new RuntimeException(error.get());
275+
} else {
276+
System.out.println("No more!");
277+
}
278+
}
279+
}));
280+
281+
// Or use futures
282+
pageFuture.thenRun(page -> page.autoPager()
283+
.subscribe(directory -> {
284+
System.out.println(directory);
285+
})
286+
.onCompleteFuture()
287+
.whenComplete((unused, error) -> {
288+
if (error != null) {
289+
System.out.println("Something went wrong!");
290+
throw new RuntimeException(error);
291+
} else {
292+
System.out.println("No more!");
293+
}
294+
}));
252295
```
253296

254297
### Manual pagination
255298

256-
If none of the above helpers meet your needs, you can also manually request pages one-by-one. A page of results has a `data()` method to fetch the list of objects, as well as top-level `response` and other methods to fetch top-level data about the page. It also has methods `hasNextPage`, `getNextPage`, and `getNextPageParams` methods to help with pagination.
299+
To access individual page items and manually request the next page, use the `items()`,
300+
`hasNextPage()`, and `nextPage()` methods:
257301

258302
```java
259303
import com.tryfinch.api.models.HrisDirectoryListPage;
260304
import com.tryfinch.api.models.IndividualInDirectory;
261305

262-
HrisDirectoryListPage page = client.hris().directory().list(params);
263-
while (page != null) {
264-
for (IndividualInDirectory directory : page.individuals()) {
306+
HrisDirectoryListPage page = client.hris().directory().list();
307+
while (true) {
308+
for (IndividualInDirectory directory : page.items()) {
265309
System.out.println(directory);
266310
}
267311

268-
page = page.getNextPage().orElse(null);
312+
if (!page.hasNextPage()) {
313+
break;
314+
}
315+
316+
page = page.nextPage();
269317
}
270318
```
271319

@@ -343,7 +391,6 @@ To set a custom timeout, configure the method call using the `timeout` method:
343391

344392
```java
345393
import com.tryfinch.api.models.HrisDirectoryListPage;
346-
import com.tryfinch.api.models.HrisDirectoryListParams;
347394

348395
HrisDirectoryListPage page = client.hris().directory().list(RequestOptions.builder().timeout(Duration.ofSeconds(30)).build());
349396
```
@@ -573,7 +620,6 @@ Or configure the method call to validate the response using the `responseValidat
573620

574621
```java
575622
import com.tryfinch.api.models.HrisDirectoryListPage;
576-
import com.tryfinch.api.models.HrisDirectoryListParams;
577623

578624
HrisDirectoryListPage page = client.hris().directory().list(RequestOptions.builder().responseValidation(true).build());
579625
```

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repositories {
88

99
allprojects {
1010
group = "com.tryfinch.api"
11-
version = "6.0.0" // x-release-please-version
11+
version = "7.0.0" // x-release-please-version
1212
}
1313

1414
subprojects {

finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import java.net.Proxy
1313
import java.time.Clock
1414
import java.time.Duration
1515
import java.util.Optional
16+
import java.util.concurrent.Executor
1617
import kotlin.jvm.optionals.getOrNull
1718

1819
class FinchOkHttpClient private constructor() {
@@ -47,6 +48,10 @@ class FinchOkHttpClient private constructor() {
4748

4849
fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
4950

51+
fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply {
52+
clientOptions.streamHandlerExecutor(streamHandlerExecutor)
53+
}
54+
5055
fun clock(clock: Clock) = apply { clientOptions.clock(clock) }
5156

5257
fun headers(headers: Headers) = apply { clientOptions.headers(headers) }

finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import java.net.Proxy
1313
import java.time.Clock
1414
import java.time.Duration
1515
import java.util.Optional
16+
import java.util.concurrent.Executor
1617
import kotlin.jvm.optionals.getOrNull
1718

1819
class FinchOkHttpClientAsync private constructor() {
@@ -47,6 +48,10 @@ class FinchOkHttpClientAsync private constructor() {
4748

4849
fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
4950

51+
fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply {
52+
clientOptions.streamHandlerExecutor(streamHandlerExecutor)
53+
}
54+
5055
fun clock(clock: Clock) = apply { clientOptions.clock(clock) }
5156

5257
fun headers(headers: Headers) = apply { clientOptions.headers(headers) }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// File generated from our OpenAPI spec by Stainless.
2+
3+
package com.tryfinch.api.core
4+
5+
import java.util.stream.Stream
6+
import java.util.stream.StreamSupport
7+
8+
class AutoPager<T> private constructor(private val firstPage: Page<T>) : Iterable<T> {
9+
10+
companion object {
11+
12+
fun <T> from(firstPage: Page<T>): AutoPager<T> = AutoPager(firstPage)
13+
}
14+
15+
override fun iterator(): Iterator<T> =
16+
generateSequence(firstPage) { if (it.hasNextPage()) it.nextPage() else null }
17+
.flatMap { it.items() }
18+
.iterator()
19+
20+
fun stream(): Stream<T> = StreamSupport.stream(spliterator(), false)
21+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// File generated from our OpenAPI spec by Stainless.
2+
3+
package com.tryfinch.api.core
4+
5+
import com.tryfinch.api.core.http.AsyncStreamResponse
6+
import java.util.Optional
7+
import java.util.concurrent.CompletableFuture
8+
import java.util.concurrent.CompletionException
9+
import java.util.concurrent.Executor
10+
import java.util.concurrent.atomic.AtomicReference
11+
12+
class AutoPagerAsync<T>
13+
private constructor(private val firstPage: PageAsync<T>, private val defaultExecutor: Executor) :
14+
AsyncStreamResponse<T> {
15+
16+
companion object {
17+
18+
fun <T> from(firstPage: PageAsync<T>, defaultExecutor: Executor): AutoPagerAsync<T> =
19+
AutoPagerAsync(firstPage, defaultExecutor)
20+
}
21+
22+
private val onCompleteFuture = CompletableFuture<Void?>()
23+
private val state = AtomicReference(State.NEW)
24+
25+
override fun subscribe(handler: AsyncStreamResponse.Handler<T>): AsyncStreamResponse<T> =
26+
subscribe(handler, defaultExecutor)
27+
28+
override fun subscribe(
29+
handler: AsyncStreamResponse.Handler<T>,
30+
executor: Executor,
31+
): AsyncStreamResponse<T> = apply {
32+
// TODO(JDK): Use `compareAndExchange` once targeting JDK 9.
33+
check(state.compareAndSet(State.NEW, State.SUBSCRIBED)) {
34+
if (state.get() == State.SUBSCRIBED) "Cannot subscribe more than once"
35+
else "Cannot subscribe after the response is closed"
36+
}
37+
38+
fun PageAsync<T>.handle(): CompletableFuture<Void?> {
39+
if (state.get() == State.CLOSED) {
40+
return CompletableFuture.completedFuture(null)
41+
}
42+
43+
items().forEach { handler.onNext(it) }
44+
return if (hasNextPage()) nextPage().thenCompose { it.handle() }
45+
else CompletableFuture.completedFuture(null)
46+
}
47+
48+
executor.execute {
49+
firstPage.handle().whenComplete { _, error ->
50+
val actualError =
51+
if (error is CompletionException && error.cause != null) error.cause else error
52+
try {
53+
handler.onComplete(Optional.ofNullable(actualError))
54+
} finally {
55+
try {
56+
if (actualError == null) {
57+
onCompleteFuture.complete(null)
58+
} else {
59+
onCompleteFuture.completeExceptionally(actualError)
60+
}
61+
} finally {
62+
close()
63+
}
64+
}
65+
}
66+
}
67+
}
68+
69+
override fun onCompleteFuture(): CompletableFuture<Void?> = onCompleteFuture
70+
71+
override fun close() {
72+
val previousState = state.getAndSet(State.CLOSED)
73+
if (previousState == State.CLOSED) {
74+
return
75+
}
76+
77+
// When the stream is closed, we should always consider it closed. If it closed due
78+
// to an error, then we will have already completed the future earlier, and this
79+
// will be a no-op.
80+
onCompleteFuture.complete(null)
81+
}
82+
}
83+
84+
private enum class State {
85+
NEW,
86+
SUBSCRIBED,
87+
CLOSED,
88+
}

finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ package com.tryfinch.api.core
55
import com.fasterxml.jackson.core.Version
66
import com.fasterxml.jackson.core.util.VersionUtil
77

8+
fun checkRequired(name: String, condition: Boolean) =
9+
check(condition) { "`$name` is required, but was not set" }
10+
811
fun <T : Any> checkRequired(name: String, value: T?): T =
912
checkNotNull(value) { "`$name` is required, but was not set" }
1013

0 commit comments

Comments
 (0)