Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/publish-sonatype.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
export -- GPG_SIGNING_KEY_ID
printenv -- GPG_SIGNING_KEY | gpg --batch --passphrase-fd 3 --import 3<<< "$GPG_SIGNING_PASSWORD"
GPG_SIGNING_KEY_ID="$(gpg --with-colons --list-keys | awk -F : -- '/^pub:/ { getline; print "0x" substr($10, length($10) - 7) }')"
./gradlew publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD"
./gradlew publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD" --no-configuration-cache
env:
SONATYPE_USERNAME: ${{ secrets.FINCH_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.FINCH_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }}
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "5.2.0"
".": "5.3.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 45
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-ff61a38530dfae03860bceb49379e84bfc7434eeb5d2f1dc9677cb162014faf1.yml
openapi_spec_hash: df3bdaf4acf575bb07767cae7ca24d69
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-46640c1b468813b828be61b1af5cb5450f9555c4c757c5a740189906a8d56672.yml
openapi_spec_hash: 1d5845ae61d2c0a143db43d579b048c5
config_hash: 53778a0b839c4f6ad34fbba051f5e8a6
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## 5.3.0 (2025-04-18)

Full Changelog: [v5.2.0...v5.3.0](https://github.com/Finch-API/finch-api-java/compare/v5.2.0...v5.3.0)

### Features

* **api:** api update ([8a943e8](https://github.com/Finch-API/finch-api-java/commit/8a943e843922b45684c633f2ef1d0e158b04c6d7))


### Performance Improvements

* **internal:** improve compilation+test speed ([7ce9aa1](https://github.com/Finch-API/finch-api-java/commit/7ce9aa1115f40676d990dabb5fbeb365b834cdcc))


### Documentation

* **client:** update jackson compat error message ([3966f0e](https://github.com/Finch-API/finch-api-java/commit/3966f0eb05820ed6359991b877d3599727b295d7))
* explain http client customization ([09d6b33](https://github.com/Finch-API/finch-api-java/commit/09d6b33bc87d1232cc855d0e9621a4f594985878))
* explain jackson compat in readme ([acd26d9](https://github.com/Finch-API/finch-api-java/commit/acd26d9181ec31200b4c820bffde37761aa18d52))
* update documentation links to be more uniform ([184534f](https://github.com/Finch-API/finch-api-java/commit/184534f3eda9aeffde671073fcd6b140620da87e))

## 5.2.0 (2025-04-12)

Full Changelog: [v5.1.0...v5.2.0](https://github.com/Finch-API/finch-api-java/compare/v5.1.0...v5.2.0)
Expand Down
57 changes: 52 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

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

[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/5.2.0)
[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/5.2.0/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0)
[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/5.3.0)
[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/5.3.0/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.3.0)

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

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

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

The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are also available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0).
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/5.3.0).

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

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

```kotlin
implementation("com.tryfinch.api:finch-java:5.2.0")
implementation("com.tryfinch.api:finch-java:5.3.0")
```

### Maven
Expand All @@ -35,7 +35,7 @@ implementation("com.tryfinch.api:finch-java:5.2.0")
<dependency>
<groupId>com.tryfinch.api</groupId>
<artifactId>finch-java</artifactId>
<version>5.2.0</version>
<version>5.3.0</version>
</dependency>
```

Expand Down Expand Up @@ -295,6 +295,17 @@ both of which will raise an error if the signature is invalid.
Note that the "body" parameter must be the raw JSON string sent from the server (do not parse it first).
The `.unwrap()` method can parse this JSON for you.

## Jackson

The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default.

The SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config).

If the SDK threw an exception, but you're _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt).

> [!CAUTION]
> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.

## Network options

### Retries
Expand Down Expand Up @@ -372,6 +383,42 @@ FinchClient client = FinchOkHttpClient.builder()
.build();
```

### Custom HTTP client

The SDK consists of three artifacts:

- `finch-java-core`
- Contains core SDK logic
- Does not depend on [OkHttp](https://square.github.io/okhttp)
- Exposes [`FinchClient`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClient.kt), [`FinchClientAsync`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsync.kt), [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt), and [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), all of which can work with any HTTP client
- `finch-java-client-okhttp`
- Depends on [OkHttp](https://square.github.io/okhttp)
- Exposes [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) and [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), which provide a way to construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) and [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), respectively, using OkHttp
- `finch-java`
- Depends on and exposes the APIs of both `finch-java-core` and `finch-java-client-okhttp`
- Does not have its own logic

This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies.

#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html)

> [!TIP]
> Try the available [network options](#network-options) before replacing the default client.

To use a customized `OkHttpClient`:

1. Replace your [`finch-java` dependency](#installation) with `finch-java-core`
2. Copy `finch-java-client-okhttp`'s [`OkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt) class into your code and customize it
3. Construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) or [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), similarly to [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), using your customized client

### Completely custom HTTP client

To use a completely custom HTTP client:

1. Replace your [`finch-java` dependency](#installation) with `finch-java-core`
2. Write a class that implements the [`HttpClient`](finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/HttpClient.kt) interface
3. Construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) or [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), similarly to [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), using your new client class

## Undocumented API functionality

The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repositories {

allprojects {
group = "com.tryfinch.api"
version = "5.2.0" // x-release-please-version
version = "5.3.0" // x-release-please-version
}

subprojects {
Expand Down
9 changes: 5 additions & 4 deletions buildSrc/src/main/kotlin/finch.kotlin.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {

kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
languageVersion.set(JavaLanguageVersion.of(21))
}

compilerOptions {
Expand All @@ -19,6 +19,8 @@ kotlin {
// Suppress deprecation warnings because we may still reference and test deprecated members.
// TODO: Replace with `-Xsuppress-warning=DEPRECATION` once we use Kotlin compiler 2.1.0+.
"-nowarn",
// Use as many threads as there are CPU cores on the machine for compilation.
"-Xbackend-threads=0",
)
jvmTarget.set(JvmTarget.JVM_1_8)
languageVersion.set(KotlinVersion.KOTLIN_1_8)
Expand All @@ -34,8 +36,7 @@ configure<SpotlessExtension> {
}
}

// Run tests in parallel to some degree.
tasks.withType<Test>().configureEach {
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1)
forkEvery = 100
systemProperty("junit.jupiter.execution.parallel.enabled", true)
systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent")
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ internal fun checkJacksonVersionCompatibility() {
}
check(incompatibleJacksonVersions.isEmpty()) {
"""
This SDK depends on Jackson version $MINIMUM_JACKSON_VERSION, but the following incompatible Jackson versions were detected at runtime:
This SDK requires a minimum Jackson version of $MINIMUM_JACKSON_VERSION, but the following incompatible Jackson versions were detected at runtime:

${incompatibleJacksonVersions.asSequence().map { (version, incompatibilityReason) ->
"- `${version.toFullString().replace("/", ":")}` ($incompatibilityReason)"
Expand All @@ -73,6 +73,8 @@ This can happen if you are either:
2. Depending on some library that depends on different Jackson versions, potentially transitively

Double-check that you are depending on compatible Jackson versions.

See https://www.github.com/Finch-API/finch-api-java#jackson for more information.
"""
.trimIndent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kotlin.math.pow
class RetryingHttpClient
private constructor(
private val httpClient: HttpClient,
private val sleeper: Sleeper,
private val clock: Clock,
private val maxRetries: Int,
private val idempotencyHeader: String?,
Expand Down Expand Up @@ -62,10 +63,10 @@ private constructor(
null
}

val backoffMillis = getRetryBackoffMillis(retries, response)
val backoffDuration = getRetryBackoffDuration(retries, response)
// All responses must be closed, so close the failed one before retrying.
response?.close()
Thread.sleep(backoffMillis.toMillis())
sleeper.sleep(backoffDuration)
}
}

Expand Down Expand Up @@ -111,10 +112,10 @@ private constructor(
}
}

val backoffMillis = getRetryBackoffMillis(retries, response)
val backoffDuration = getRetryBackoffDuration(retries, response)
// All responses must be closed, so close the failed one before retrying.
response?.close()
return sleepAsync(backoffMillis.toMillis()).thenCompose {
return sleeper.sleepAsync(backoffDuration).thenCompose {
executeWithRetries(requestWithRetryCount, requestOptions)
}
}
Expand Down Expand Up @@ -179,7 +180,7 @@ private constructor(
// retried.
throwable is IOException || throwable is FinchIoException

private fun getRetryBackoffMillis(retries: Int, response: HttpResponse?): Duration {
private fun getRetryBackoffDuration(retries: Int, response: HttpResponse?): Duration {
// About the Retry-After header:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
response
Expand Down Expand Up @@ -226,33 +227,40 @@ private constructor(

companion object {

private val TIMER = Timer("RetryingHttpClient", true)

private fun sleepAsync(millis: Long): CompletableFuture<Void> {
val future = CompletableFuture<Void>()
TIMER.schedule(
object : TimerTask() {
override fun run() {
future.complete(null)
}
},
millis,
)
return future
}

@JvmStatic fun builder() = Builder()
}

class Builder internal constructor() {

private var httpClient: HttpClient? = null
private var sleeper: Sleeper =
object : Sleeper {

private val timer = Timer("RetryingHttpClient", true)

override fun sleep(duration: Duration) = Thread.sleep(duration.toMillis())

override fun sleepAsync(duration: Duration): CompletableFuture<Void> {
val future = CompletableFuture<Void>()
timer.schedule(
object : TimerTask() {
override fun run() {
future.complete(null)
}
},
duration.toMillis(),
)
return future
}
}
private var clock: Clock = Clock.systemUTC()
private var maxRetries: Int = 2
private var idempotencyHeader: String? = null

fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient }

@JvmSynthetic internal fun sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper }

fun clock(clock: Clock) = apply { this.clock = clock }

fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries }
Expand All @@ -262,9 +270,17 @@ private constructor(
fun build(): HttpClient =
RetryingHttpClient(
checkRequired("httpClient", httpClient),
sleeper,
clock,
maxRetries,
idempotencyHeader,
)
}

internal interface Sleeper {

fun sleep(duration: Duration)

fun sleepAsync(duration: Duration): CompletableFuture<Void>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ private constructor(
fun primaryEmail(): Optional<String> = primaryEmail.getOptional("primary_email")

/**
* The phone number of the main administrator on the account. Format: `XXXXXXXXXX`
* The phone number of the main administrator on the account. Format: E.164, with extension
* where applicable, e.g. `+NNNNNNNNNNN xExtension`
*
* @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
Expand Down Expand Up @@ -431,7 +432,10 @@ private constructor(
this.primaryEmail = primaryEmail
}

/** The phone number of the main administrator on the account. Format: `XXXXXXXXXX` */
/**
* The phone number of the main administrator on the account. Format: E.164, with extension
* where applicable, e.g. `+NNNNNNNNNNN xExtension`
*/
fun primaryPhoneNumber(primaryPhoneNumber: String?) =
primaryPhoneNumber(JsonField.ofNullable(primaryPhoneNumber))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ private constructor(
fun primaryEmail(): Optional<String> = primaryEmail.getOptional("primary_email")

/**
* The phone number of the main administrator on the account. Format: `XXXXXXXXXX`
* The phone number of the main administrator on the account. Format: E.164, with extension
* where applicable, e.g. `+NNNNNNNNNNN xExtension`
*
* @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
Expand Down Expand Up @@ -399,7 +400,10 @@ private constructor(
this.primaryEmail = primaryEmail
}

/** The phone number of the main administrator on the account. Format: `XXXXXXXXXX` */
/**
* The phone number of the main administrator on the account. Format: E.164, with extension
* where applicable, e.g. `+NNNNNNNNNNN xExtension`
*/
fun primaryPhoneNumber(primaryPhoneNumber: String?) =
primaryPhoneNumber(JsonField.ofNullable(primaryPhoneNumber))

Expand Down
Loading