diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dec625ee6..fcb9e6804 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,12 +1,14 @@
name: CI
on:
push:
- branches-ignore:
- - 'generated'
- - 'codegen/**'
- - 'integrated/**'
- - 'stl-preview-head/**'
- - 'stl-preview-base/**'
+ branches:
+ - '**'
+ - '!integrated/**'
+ - '!stl-preview-head/**'
+ - '!stl-preview-base/**'
+ - '!generated'
+ - '!codegen/**'
+ - 'codegen/stl/**'
pull_request:
branches-ignore:
- 'stl-preview-head/**'
@@ -17,7 +19,7 @@ jobs:
timeout-minutes: 15
name: lint
runs-on: ${{ github.repository == 'stainless-sdks/finch-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- uses: actions/checkout@v6
@@ -44,7 +46,7 @@ jobs:
contents: read
id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/finch-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
- if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
steps:
- uses: actions/checkout@v6
@@ -65,14 +67,18 @@ jobs:
run: ./scripts/build
- name: Get GitHub OIDC Token
- if: github.repository == 'stainless-sdks/finch-java'
+ if: |-
+ github.repository == 'stainless-sdks/finch-java' &&
+ !startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
uses: actions/github-script@v8
with:
script: core.setOutput('github_token', await core.getIDToken());
- name: Build and upload Maven artifacts
- if: github.repository == 'stainless-sdks/finch-java'
+ if: |-
+ github.repository == 'stainless-sdks/finch-java' &&
+ !startsWith(github.ref, 'refs/heads/stl/')
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
diff --git a/.gitignore b/.gitignore
index b1346e6d1..90b85e944 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.prism.log
+.stdy.log
.gradle
.idea
.kotlin
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 8e11baaee..01f6bba7b 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "7.6.0"
+ ".": "7.7.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 5157ee799..8dd833a1d 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 45
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-46f433f34d440aa1dfcc48cc8d822c598571b68be2f723ec99e1b4fba6c13b1e.yml
-openapi_spec_hash: 5b5cd728776723ac773900f7e8a32c05
-config_hash: ccdf6a5b4aaa2a0897c89ac8685d8eb0
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch/finch-c8a0d5eca390ea3ab6e8b5b7d9b46d9a22e34d81aeab444c8ce1b5a94eba0028.yml
+openapi_spec_hash: e261a3289242d3ad52542f1491a903ee
+config_hash: 429708b67ee9e80003db82611677296c
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8cf9e908a..048a7fe03 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,77 @@
# Changelog
+## 7.7.0 (2026-05-07)
+
+Full Changelog: [v7.6.0...v7.7.0](https://github.com/Finch-API/finch-api-java/compare/v7.6.0...v7.7.0)
+
+### Features
+
+* **api:** api update ([7428d26](https://github.com/Finch-API/finch-api-java/commit/7428d26b5ce8d48cc778572db6d00ea611959210))
+* **api:** api update ([e79d0da](https://github.com/Finch-API/finch-api-java/commit/e79d0da99c2159e1f123aa35f1721305dd81947a))
+* **api:** api update ([9a22d87](https://github.com/Finch-API/finch-api-java/commit/9a22d87cee43b9e72021f9c0f290135fdc5b0d88))
+* **api:** api update ([e0941b9](https://github.com/Finch-API/finch-api-java/commit/e0941b90677171c959aef86ae567e217f7ee2280))
+* **api:** api update ([b81180a](https://github.com/Finch-API/finch-api-java/commit/b81180a374d0e2d4515bbc7ced304a2155c57970))
+* **api:** change auth to npm to oidc ([22cb9c3](https://github.com/Finch-API/finch-api-java/commit/22cb9c3fec88c8f8613e4333c81acaf1574873b8))
+* **client:** add connection pooling option ([b32cb5e](https://github.com/Finch-API/finch-api-java/commit/b32cb5e30dd6c8c6e095bf12c1a35c7346adc363))
+* **client:** add more convenience service method overloads ([98dfad7](https://github.com/Finch-API/finch-api-java/commit/98dfad72afb87db1f74daf8599ddd1c4ff9b8a12))
+* **client:** improve logging ([45718e3](https://github.com/Finch-API/finch-api-java/commit/45718e35cfb88e46aafec5206b2b925eea6ef2a2))
+* **client:** more robust error parsing ([31c1298](https://github.com/Finch-API/finch-api-java/commit/31c1298070aa84ef57221071726792bc101413fe))
+* **client:** support proxy authentication ([164fca2](https://github.com/Finch-API/finch-api-java/commit/164fca2658a181686fc63bb7217a671355898912))
+* support setting headers via env ([3d5f1bb](https://github.com/Finch-API/finch-api-java/commit/3d5f1bb1b67cff6fc2e6549e554e4a92e6625457))
+
+
+### Bug Fixes
+
+* **api:** remove invalid transform config ([8e955cc](https://github.com/Finch-API/finch-api-java/commit/8e955cc8ecb96538951c34e69abcc59542d56730))
+* **client:** allow updating header/query affecting fields in `toBuilder()` ([a2bef27](https://github.com/Finch-API/finch-api-java/commit/a2bef2796c1853918e215e3181d98a8e85f755a0))
+* **client:** incorrect `Retry-After` parsing ([184ead0](https://github.com/Finch-API/finch-api-java/commit/184ead0c155c912e8be8207a200e71b05b9928a6))
+* **docs:** fix mcp installation instructions for remote servers ([2263e95](https://github.com/Finch-API/finch-api-java/commit/2263e9537df43a609878091903108ecbf216ea76))
+
+
+### Performance Improvements
+
+* **client:** create one json mapper ([fa5904f](https://github.com/Finch-API/finch-api-java/commit/fa5904fc90f522bc0685f84b0a80396ed6bb8775))
+
+
+### Chores
+
+* **api:** minor updates ([517681b](https://github.com/Finch-API/finch-api-java/commit/517681b1ab0f11cfb16e5652597a73734ed94314))
+* **ci:** skip lint on metadata-only changes ([097c2e8](https://github.com/Finch-API/finch-api-java/commit/097c2e82ecee3fc1a290a09fde1e36ef63092224))
+* **ci:** skip uploading artifacts on stainless-internal branches ([511e088](https://github.com/Finch-API/finch-api-java/commit/511e08806a8b10ff2f9f2646ba57c1b126e82929))
+* drop apache dependency ([7e8f6e8](https://github.com/Finch-API/finch-api-java/commit/7e8f6e84b6f957bae05e2f27ba9cf2c46658adcd))
+* **internal:** allow passing args to `./scripts/test` ([5a4b86a](https://github.com/Finch-API/finch-api-java/commit/5a4b86a8afa2a0940f3f751ff4927a9f7c649973))
+* **internal:** codegen related update ([7e59d99](https://github.com/Finch-API/finch-api-java/commit/7e59d9979522714a8346fed5595bc77d593edcb0))
+* **internal:** codegen related update ([4c0d39d](https://github.com/Finch-API/finch-api-java/commit/4c0d39d2d70f43e4b0050c37687b1c90ad307b9f))
+* **internal:** codegen related update ([9390e9d](https://github.com/Finch-API/finch-api-java/commit/9390e9d48290fcf34aff35c5f934f609d2afba06))
+* **internal:** expand imports ([3ee90d0](https://github.com/Finch-API/finch-api-java/commit/3ee90d078b22210aad648c55d3165b8bd225f97a))
+* **internal:** make `OkHttp` constructor internal ([4817a04](https://github.com/Finch-API/finch-api-java/commit/4817a0454a026d3f46018245dcc16bd4f2b850af))
+* **internal:** tweak CI branches ([0a639b6](https://github.com/Finch-API/finch-api-java/commit/0a639b6323addc4b26f69eb0b5538325a84d35fe))
+* **internal:** update `TestServerExtension` comment ([4f87f8c](https://github.com/Finch-API/finch-api-java/commit/4f87f8c75e35f859abd3f6bb53b0f10d9334c43a))
+* **internal:** update gitignore ([c5b3f87](https://github.com/Finch-API/finch-api-java/commit/c5b3f876b0c417adf13f12aac4eb826ff9c1cbb8))
+* **internal:** update multipart form array serialization ([971b625](https://github.com/Finch-API/finch-api-java/commit/971b625704582df1c587ddfbb0134b695b49f486))
+* **internal:** update retry delay tests ([74d13c9](https://github.com/Finch-API/finch-api-java/commit/74d13c9d830887e3bab570b94d54422d695377ba))
+* **internal:** upgrade AssertJ ([1969203](https://github.com/Finch-API/finch-api-java/commit/196920329becccc50491e2daf3ed2dc66df36476))
+* make `Properties` more resilient to `null` ([0019871](https://github.com/Finch-API/finch-api-java/commit/0019871d4b03fc59e37876ed1c6b789a26898d42))
+* redact api-key headers in debug logs ([57fb214](https://github.com/Finch-API/finch-api-java/commit/57fb2149a865153d8a1096e9bd48bfb1cc674378))
+* remove duplicated dokka setup ([a136d65](https://github.com/Finch-API/finch-api-java/commit/a136d65e7fe094dae7c7f21f6cc317736ea84d87))
+* **tests:** bump steady to v0.19.4 ([c5bf4b6](https://github.com/Finch-API/finch-api-java/commit/c5bf4b6b4e233046700aac4d7283255987cbf15f))
+* **tests:** bump steady to v0.19.5 ([c41f85e](https://github.com/Finch-API/finch-api-java/commit/c41f85ec6c0f686db96ad08c13d30f91b6f134b6))
+* **tests:** bump steady to v0.19.6 ([d0696f6](https://github.com/Finch-API/finch-api-java/commit/d0696f688ef503404bd7736173af4619ac3e542e))
+* **tests:** bump steady to v0.19.7 ([cc4b8a7](https://github.com/Finch-API/finch-api-java/commit/cc4b8a7db1e6b38a33b0d1e5e1d0f735dbba566d))
+* **tests:** bump steady to v0.20.1 ([8ea2902](https://github.com/Finch-API/finch-api-java/commit/8ea290264cdc318aea824ee16e99452395fcd182))
+* **tests:** bump steady to v0.20.2 ([62c445f](https://github.com/Finch-API/finch-api-java/commit/62c445f77dc424656b623b5317bd9bee2de78df0))
+* **tests:** bump steady to v0.22.1 ([712b749](https://github.com/Finch-API/finch-api-java/commit/712b749d0a26c855a5b4b2971e1e9f57bf86a2f7))
+
+
+### Documentation
+
+* clarify forwards compat behavior ([766609c](https://github.com/Finch-API/finch-api-java/commit/766609c4199c84faf5df9dc24bb583008a4762f3))
+
+
+### Refactors
+
+* **tests:** switch from prism to steady ([153f640](https://github.com/Finch-API/finch-api-java/commit/153f6408de564c3ac6e19ef67963f6e11889f289))
+
## 7.6.0 (2026-01-26)
Full Changelog: [v7.5.2...v7.6.0](https://github.com/Finch-API/finch-api-java/compare/v7.5.2...v7.6.0)
diff --git a/README.md b/README.md
index 3b05b9525..12df0aa64 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/7.6.0)
-[](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.6.0)
+[](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/7.7.0)
+[](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.7.0)
@@ -17,14 +17,14 @@ It is generated with [Stainless](https://www.stainless.com/).
Use the Finch MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
-[](https://cursor.com/en-US/install-mcp?name=%40tryfinch%2Ffinch-api-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkB0cnlmaW5jaC9maW5jaC1hcGktbWNwIl19)
-[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40tryfinch%2Ffinch-api-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40tryfinch%2Ffinch-api-mcp%22%5D%7D)
+[](https://cursor.com/en-US/install-mcp?name=%40tryfinch%2Ffinch-api-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkB0cnlmaW5jaC9maW5jaC1hcGktbWNwIl0sImVudiI6eyJGSU5DSF9BQ0NFU1NfVE9LRU4iOiJNeSBBY2Nlc3MgVG9rZW4iLCJGSU5DSF9DTElFTlRfSUQiOiI0YWIxNWU1MS0xMWFkLTQ5ZjQtYWNhZS1mMzQzYjc3OTQzNzUiLCJGSU5DSF9DTElFTlRfU0VDUkVUIjoiTXkgQ2xpZW50IFNlY3JldCIsIkZJTkNIX1dFQkhPT0tfU0VDUkVUIjoiTXkgV2ViaG9vayBTZWNyZXQifX0)
+[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22%40tryfinch%2Ffinch-api-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40tryfinch%2Ffinch-api-mcp%22%5D%2C%22env%22%3A%7B%22FINCH_ACCESS_TOKEN%22%3A%22My%20Access%20Token%22%2C%22FINCH_CLIENT_ID%22%3A%224ab15e51-11ad-49f4-acae-f343b7794375%22%2C%22FINCH_CLIENT_SECRET%22%3A%22My%20Client%20Secret%22%2C%22FINCH_WEBHOOK_SECRET%22%3A%22My%20Webhook%20Secret%22%7D%7D)
> Note: You may need to set environment variables in your MCP client.
-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.6.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/7.7.0).
@@ -35,7 +35,7 @@ The REST API documentation can be found on [developer.tryfinch.com](https://deve
### Gradle
```kotlin
-implementation("com.tryfinch.api:finch-java:7.6.0")
+implementation("com.tryfinch.api:finch-java:7.7.0")
```
### Maven
@@ -44,7 +44,7 @@ implementation("com.tryfinch.api:finch-java:7.6.0")
com.tryfinch.api
finch-java
- 7.6.0
+ 7.7.0
```
@@ -352,8 +352,6 @@ while (true) {
## Logging
-The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor).
-
Enable logging by setting the `FINCH_LOG` environment variable to `info`:
```sh
@@ -366,6 +364,20 @@ Or to `debug` for more verbose logging:
export FINCH_LOG=debug
```
+Or configure the client manually using the `logLevel` method:
+
+```java
+import com.tryfinch.api.client.FinchClient;
+import com.tryfinch.api.client.okhttp.FinchOkHttpClient;
+import com.tryfinch.api.core.LogLevel;
+
+FinchClient client = FinchOkHttpClient.builder()
+ .fromEnv()
+ .logLevel(LogLevel.INFO)
+ .accessToken("My Access Token")
+ .build();
+```
+
## Webhook Verification
We provide helper methods for verifying that a webhook request came from Finch, and not a malicious third party.
@@ -471,6 +483,42 @@ FinchClient client = FinchOkHttpClient.builder()
.build();
```
+If the proxy responds with `407 Proxy Authentication Required`, supply credentials by also configuring `proxyAuthenticator`:
+
+```java
+import com.tryfinch.api.client.FinchClient;
+import com.tryfinch.api.client.okhttp.FinchOkHttpClient;
+import com.tryfinch.api.core.http.ProxyAuthenticator;
+
+FinchClient client = FinchOkHttpClient.builder()
+ .fromEnv()
+ .proxy(...)
+ // Or a custom implementation of `ProxyAuthenticator`.
+ .proxyAuthenticator(ProxyAuthenticator.basic("username", "password"))
+ .accessToken("My Access Token")
+ .build();
+```
+
+### Connection pooling
+
+To customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods:
+
+```java
+import com.tryfinch.api.client.FinchClient;
+import com.tryfinch.api.client.okhttp.FinchOkHttpClient;
+import java.time.Duration;
+
+FinchClient client = FinchOkHttpClient.builder()
+ .fromEnv()
+ // If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa.
+ .maxIdleConnections(10)
+ .keepAliveDuration(Duration.ofMinutes(2))
+ .accessToken("My Access Token")
+ .build();
+```
+
+If both options are unset, OkHttp's default connection pool settings are used.
+
### HTTPS
> [!NOTE]
@@ -671,7 +719,9 @@ In rare cases, the API may return a response that doesn't match the expected typ
By default, the SDK will not throw an exception in this case. It will throw [`FinchInvalidDataException`](finch-java-core/src/main/kotlin/com/tryfinch/api/errors/FinchInvalidDataException.kt) only if you directly access the property.
-If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`:
+Validating the response is _not_ forwards compatible with new types from the API for existing fields.
+
+If you would still prefer to check that the response is completely well-typed upfront, then either call `validate()`:
```java
import com.tryfinch.api.models.CreateAccessTokenResponse;
diff --git a/build.gradle.kts b/build.gradle.kts
index f21b0c405..83fd3d1c8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "com.tryfinch.api"
- version = "7.6.0" // x-release-please-version
+ version = "7.7.0" // x-release-please-version
}
subprojects {
@@ -21,7 +21,6 @@ subprojects {
group = "Verification"
description = "Verifies all source files are formatted."
}
- apply(plugin = "org.jetbrains.dokka")
}
subprojects {
diff --git a/buildSrc/src/main/kotlin/finch.java.gradle.kts b/buildSrc/src/main/kotlin/finch.java.gradle.kts
index 70fc33f41..8f4f902a6 100644
--- a/buildSrc/src/main/kotlin/finch.java.gradle.kts
+++ b/buildSrc/src/main/kotlin/finch.java.gradle.kts
@@ -45,7 +45,7 @@ tasks.withType().configureEach {
val palantir by configurations.creating
dependencies {
- palantir("com.palantir.javaformat:palantir-java-format:2.73.0")
+ palantir("com.palantir.javaformat:palantir-java-format:2.89.0")
}
fun registerPalantir(
diff --git a/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts b/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts
index 460dbc151..0b43c8cf3 100644
--- a/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts
+++ b/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts
@@ -40,7 +40,7 @@ tasks.withType().configureEach {
val ktfmt by configurations.creating
dependencies {
- ktfmt("com.facebook:ktfmt:0.56")
+ ktfmt("com.facebook:ktfmt:0.61")
}
fun registerKtfmt(
diff --git a/finch-java-client-okhttp/build.gradle.kts b/finch-java-client-okhttp/build.gradle.kts
index 4adac97f0..9d168cf29 100644
--- a/finch-java-client-okhttp/build.gradle.kts
+++ b/finch-java-client-okhttp/build.gradle.kts
@@ -7,9 +7,8 @@ dependencies {
api(project(":finch-java-core"))
implementation("com.squareup.okhttp3:okhttp:4.12.0")
- implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
testImplementation(kotlin("test"))
- testImplementation("org.assertj:assertj-core:3.25.3")
+ testImplementation("org.assertj:assertj-core:3.27.7")
testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2")
}
diff --git a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt
index bc1394da7..d1dd65810 100644
--- a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt
+++ b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt
@@ -6,11 +6,13 @@ import com.fasterxml.jackson.databind.json.JsonMapper
import com.tryfinch.api.client.FinchClient
import com.tryfinch.api.client.FinchClientImpl
import com.tryfinch.api.core.ClientOptions
+import com.tryfinch.api.core.LogLevel
import com.tryfinch.api.core.Sleeper
import com.tryfinch.api.core.Timeout
import com.tryfinch.api.core.http.AsyncStreamResponse
import com.tryfinch.api.core.http.Headers
import com.tryfinch.api.core.http.HttpClient
+import com.tryfinch.api.core.http.ProxyAuthenticator
import com.tryfinch.api.core.http.QueryParams
import com.tryfinch.api.core.jsonMapper
import java.net.Proxy
@@ -49,6 +51,9 @@ class FinchOkHttpClient private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var proxyAuthenticator: ProxyAuthenticator? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
@@ -77,6 +82,60 @@ class FinchOkHttpClient private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication
+ * Required`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply {
+ this.proxyAuthenticator = proxyAuthenticator
+ }
+
+ /**
+ * Alias for calling [Builder.proxyAuthenticator] with `proxyAuthenticator.orElse(null)`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: Optional) =
+ proxyAuthenticator(proxyAuthenticator.getOrNull())
+
+ /**
+ * The maximum number of idle connections kept by the underlying OkHttp connection pool.
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Alias for [Builder.maxIdleConnections].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int) =
+ maxIdleConnections(maxIdleConnections as Int?)
+
+ /**
+ * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
+ */
+ fun maxIdleConnections(maxIdleConnections: Optional) =
+ maxIdleConnections(maxIdleConnections.getOrNull())
+
+ /**
+ * The keep-alive duration for idle connections in the underlying OkHttp connection pool.
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
+ /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
+ fun keepAliveDuration(keepAliveDuration: Optional) =
+ keepAliveDuration(keepAliveDuration.getOrNull())
+
/**
* The socket factory used to secure HTTPS connections.
*
@@ -188,6 +247,9 @@ class FinchOkHttpClient private constructor() {
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -229,6 +291,15 @@ class FinchOkHttpClient private constructor() {
*/
fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) }
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) }
+
fun accessToken(accessToken: String?) = apply { clientOptions.accessToken(accessToken) }
/** Alias for calling [Builder.accessToken] with `accessToken.orElse(null)`. */
@@ -351,6 +422,9 @@ class FinchOkHttpClient private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .proxyAuthenticator(proxyAuthenticator)
+ .maxIdleConnections(maxIdleConnections)
+ .keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
diff --git a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt
index 0e52b37ef..20378776f 100644
--- a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt
+++ b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt
@@ -6,11 +6,13 @@ import com.fasterxml.jackson.databind.json.JsonMapper
import com.tryfinch.api.client.FinchClientAsync
import com.tryfinch.api.client.FinchClientAsyncImpl
import com.tryfinch.api.core.ClientOptions
+import com.tryfinch.api.core.LogLevel
import com.tryfinch.api.core.Sleeper
import com.tryfinch.api.core.Timeout
import com.tryfinch.api.core.http.AsyncStreamResponse
import com.tryfinch.api.core.http.Headers
import com.tryfinch.api.core.http.HttpClient
+import com.tryfinch.api.core.http.ProxyAuthenticator
import com.tryfinch.api.core.http.QueryParams
import com.tryfinch.api.core.jsonMapper
import java.net.Proxy
@@ -49,6 +51,9 @@ class FinchOkHttpClientAsync private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var proxyAuthenticator: ProxyAuthenticator? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
@@ -77,6 +82,60 @@ class FinchOkHttpClientAsync private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication
+ * Required`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply {
+ this.proxyAuthenticator = proxyAuthenticator
+ }
+
+ /**
+ * Alias for calling [Builder.proxyAuthenticator] with `proxyAuthenticator.orElse(null)`.
+ */
+ fun proxyAuthenticator(proxyAuthenticator: Optional) =
+ proxyAuthenticator(proxyAuthenticator.getOrNull())
+
+ /**
+ * The maximum number of idle connections kept by the underlying OkHttp connection pool.
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Alias for [Builder.maxIdleConnections].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int) =
+ maxIdleConnections(maxIdleConnections as Int?)
+
+ /**
+ * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
+ */
+ fun maxIdleConnections(maxIdleConnections: Optional) =
+ maxIdleConnections(maxIdleConnections.getOrNull())
+
+ /**
+ * The keep-alive duration for idle connections in the underlying OkHttp connection pool.
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
+ /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
+ fun keepAliveDuration(keepAliveDuration: Optional) =
+ keepAliveDuration(keepAliveDuration.getOrNull())
+
/**
* The socket factory used to secure HTTPS connections.
*
@@ -188,6 +247,9 @@ class FinchOkHttpClientAsync private constructor() {
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -229,6 +291,15 @@ class FinchOkHttpClientAsync private constructor() {
*/
fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) }
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) }
+
fun accessToken(accessToken: String?) = apply { clientOptions.accessToken(accessToken) }
/** Alias for calling [Builder.accessToken] with `accessToken.orElse(null)`. */
@@ -351,6 +422,9 @@ class FinchOkHttpClientAsync private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .proxyAuthenticator(proxyAuthenticator)
+ .maxIdleConnections(maxIdleConnections)
+ .keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
diff --git a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt
index a40937a40..fc3ffd5be 100644
--- a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt
+++ b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt
@@ -8,20 +8,26 @@ import com.tryfinch.api.core.http.HttpMethod
import com.tryfinch.api.core.http.HttpRequest
import com.tryfinch.api.core.http.HttpRequestBody
import com.tryfinch.api.core.http.HttpResponse
+import com.tryfinch.api.core.http.ProxyAuthenticator
import com.tryfinch.api.errors.FinchIoException
import java.io.IOException
import java.io.InputStream
+import java.io.OutputStream
import java.net.Proxy
import java.time.Duration
import java.util.concurrent.CancellationException
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutorService
+import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
+import kotlin.jvm.optionals.getOrNull
import okhttp3.Call
import okhttp3.Callback
+import okhttp3.ConnectionPool
import okhttp3.Dispatcher
+import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
@@ -29,17 +35,18 @@ import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
-import okhttp3.logging.HttpLoggingInterceptor
import okio.BufferedSink
+import okio.buffer
+import okio.sink
class OkHttpClient
-private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient {
+internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient {
override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
val call = newCall(request, requestOptions)
return try {
- call.execute().toResponse()
+ call.execute().toHttpResponse()
} catch (e: IOException) {
throw FinchIoException("Request failed", e)
} finally {
@@ -57,7 +64,7 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
call.enqueue(
object : Callback {
override fun onResponse(call: Call, response: Response) {
- future.complete(response.toResponse())
+ future.complete(response.toHttpResponse())
}
override fun onFailure(call: Call, e: IOException) {
@@ -85,18 +92,6 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
private fun newCall(request: HttpRequest, requestOptions: RequestOptions): Call {
val clientBuilder = okHttpClient.newBuilder()
- val logLevel =
- when (System.getenv("FINCH_LOG")?.lowercase()) {
- "info" -> HttpLoggingInterceptor.Level.BASIC
- "debug" -> HttpLoggingInterceptor.Level.BODY
- else -> null
- }
- if (logLevel != null) {
- clientBuilder.addNetworkInterceptor(
- HttpLoggingInterceptor().setLevel(logLevel).apply { redactHeader("Authorization") }
- )
- }
-
requestOptions.timeout?.let {
clientBuilder
.connectTimeout(it.connect())
@@ -109,89 +104,6 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
return client.newCall(request.toRequest(client))
}
- private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient): Request {
- var body: RequestBody? = body?.toRequestBody()
- if (body == null && requiresBody(method)) {
- body = "".toRequestBody()
- }
-
- val builder = Request.Builder().url(toUrl()).method(method.name, body)
- headers.names().forEach { name ->
- headers.values(name).forEach { builder.addHeader(name, it) }
- }
-
- if (
- !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0
- ) {
- builder.addHeader(
- "X-Stainless-Read-Timeout",
- Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
- )
- }
- if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) {
- builder.addHeader(
- "X-Stainless-Timeout",
- Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
- )
- }
-
- return builder.build()
- }
-
- /** `OkHttpClient` always requires a request body for some methods. */
- private fun requiresBody(method: HttpMethod): Boolean =
- when (method) {
- HttpMethod.POST,
- HttpMethod.PUT,
- HttpMethod.PATCH -> true
- else -> false
- }
-
- private fun HttpRequest.toUrl(): String {
- val builder = baseUrl.toHttpUrl().newBuilder()
- pathSegments.forEach(builder::addPathSegment)
- queryParams.keys().forEach { key ->
- queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
- }
-
- return builder.toString()
- }
-
- private fun HttpRequestBody.toRequestBody(): RequestBody {
- val mediaType = contentType()?.toMediaType()
- val length = contentLength()
-
- return object : RequestBody() {
- override fun contentType(): MediaType? = mediaType
-
- override fun contentLength(): Long = length
-
- override fun isOneShot(): Boolean = !repeatable()
-
- override fun writeTo(sink: BufferedSink) = writeTo(sink.outputStream())
- }
- }
-
- private fun Response.toResponse(): HttpResponse {
- val headers = headers.toHeaders()
-
- return object : HttpResponse {
- override fun statusCode(): Int = code
-
- override fun headers(): Headers = headers
-
- override fun body(): InputStream = body!!.byteStream()
-
- override fun close() = body!!.close()
- }
- }
-
- private fun okhttp3.Headers.toHeaders(): Headers {
- val headersBuilder = Headers.builder()
- forEach { (name, value) -> headersBuilder.put(name, value) }
- return headersBuilder.build()
- }
-
companion object {
@JvmStatic fun builder() = Builder()
}
@@ -200,6 +112,9 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
private var timeout: Timeout = Timeout.default()
private var proxy: Proxy? = null
+ private var proxyAuthenticator: ProxyAuthenticator? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var dispatcherExecutorService: ExecutorService? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
@@ -211,6 +126,32 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
+ fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply {
+ this.proxyAuthenticator = proxyAuthenticator
+ }
+
+ /**
+ * Sets the maximum number of idle connections kept by the underlying [ConnectionPool].
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Sets the keep-alive duration for idle connections in the underlying [ConnectionPool].
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
this.dispatcherExecutorService = dispatcherExecutorService
}
@@ -238,8 +179,37 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
.callTimeout(timeout.request())
.proxy(proxy)
.apply {
+ proxyAuthenticator?.let { auth ->
+ proxyAuthenticator { route, response ->
+ auth
+ .authenticate(
+ route?.proxy ?: Proxy.NO_PROXY,
+ response.request.toHttpRequest(),
+ response.toHttpResponse(),
+ )
+ .getOrNull()
+ ?.toRequest(client = null)
+ }
+ }
+
dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
+ val maxIdleConnections = maxIdleConnections
+ val keepAliveDuration = keepAliveDuration
+ if (maxIdleConnections != null && keepAliveDuration != null) {
+ connectionPool(
+ ConnectionPool(
+ maxIdleConnections,
+ keepAliveDuration.toNanos(),
+ TimeUnit.NANOSECONDS,
+ )
+ )
+ } else {
+ check((maxIdleConnections != null) == (keepAliveDuration != null)) {
+ "Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set"
+ }
+ }
+
val sslSocketFactory = sslSocketFactory
val trustManager = trustManager
if (sslSocketFactory != null && trustManager != null) {
@@ -261,3 +231,126 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
)
}
}
+
+private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient?): Request {
+ var body: RequestBody? = body?.toRequestBody()
+ if (body == null && requiresBody(method)) {
+ body = "".toRequestBody()
+ }
+
+ val builder = Request.Builder().url(toUrl()).method(method.name, body)
+ headers.names().forEach { name -> headers.values(name).forEach { builder.addHeader(name, it) } }
+
+ if (client != null) {
+ if (
+ !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0
+ ) {
+ builder.addHeader(
+ "X-Stainless-Read-Timeout",
+ Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
+ )
+ }
+ if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) {
+ builder.addHeader(
+ "X-Stainless-Timeout",
+ Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
+ )
+ }
+ }
+
+ return builder.build()
+}
+
+/** `OkHttpClient` always requires a request body for some methods. */
+private fun requiresBody(method: HttpMethod): Boolean =
+ when (method) {
+ HttpMethod.POST,
+ HttpMethod.PUT,
+ HttpMethod.PATCH -> true
+ else -> false
+ }
+
+private fun HttpRequest.toUrl(): String {
+ val builder = baseUrl.toHttpUrl().newBuilder()
+ pathSegments.forEach(builder::addPathSegment)
+ queryParams.keys().forEach { key ->
+ queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
+ }
+
+ return builder.toString()
+}
+
+private fun HttpRequestBody.toRequestBody(): RequestBody {
+ val mediaType = contentType()?.toMediaType()
+ val length = contentLength()
+
+ return object : RequestBody() {
+ override fun contentType(): MediaType? = mediaType
+
+ override fun contentLength(): Long = length
+
+ override fun isOneShot(): Boolean = !repeatable()
+
+ override fun writeTo(sink: BufferedSink) = writeTo(sink.outputStream())
+ }
+}
+
+private fun Request.toHttpRequest(): HttpRequest {
+ val builder = HttpRequest.builder().method(HttpMethod.valueOf(method)).baseUrl(url.toBaseUrl())
+ url.pathSegments.forEach(builder::addPathSegment)
+ url.queryParameterNames.forEach { name ->
+ url.queryParameterValues(name).filterNotNull().forEach { builder.putQueryParam(name, it) }
+ }
+ headers.forEach { (name, value) -> builder.putHeader(name, value) }
+ body?.let { builder.body(it.toHttpRequestBody()) }
+ return builder.build()
+}
+
+private fun HttpUrl.toBaseUrl(): String = buildString {
+ append(scheme).append("://").append(host)
+ if (port != HttpUrl.defaultPort(scheme)) {
+ append(":").append(port)
+ }
+}
+
+private fun RequestBody.toHttpRequestBody(): HttpRequestBody {
+ val mediaType = contentType()?.toString()
+ val length = contentLength()
+ val isOneShot = isOneShot()
+ val source = this
+ return object : HttpRequestBody {
+ override fun contentType(): String? = mediaType
+
+ override fun contentLength(): Long = length
+
+ override fun repeatable(): Boolean = !isOneShot
+
+ override fun writeTo(outputStream: OutputStream) {
+ val sink = outputStream.sink().buffer()
+ source.writeTo(sink)
+ sink.flush()
+ }
+
+ override fun close() {}
+ }
+}
+
+private fun Response.toHttpResponse(): HttpResponse {
+ val headers = headers.toHeaders()
+
+ return object : HttpResponse {
+ override fun statusCode(): Int = code
+
+ override fun headers(): Headers = headers
+
+ override fun body(): InputStream = body!!.byteStream()
+
+ override fun close() = body!!.close()
+ }
+}
+
+private fun okhttp3.Headers.toHeaders(): Headers {
+ val headersBuilder = Headers.builder()
+ forEach { (name, value) -> headersBuilder.put(name, value) }
+ return headersBuilder.build()
+}
diff --git a/finch-java-core/build.gradle.kts b/finch-java-core/build.gradle.kts
index a8087c75c..d5dc8c35f 100644
--- a/finch-java-core/build.gradle.kts
+++ b/finch-java-core/build.gradle.kts
@@ -27,13 +27,11 @@ dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
- implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4")
- implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")
testImplementation(kotlin("test"))
testImplementation(project(":finch-java-client-okhttp"))
testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2")
- testImplementation("org.assertj:assertj-core:3.25.3")
+ testImplementation("org.assertj:assertj-core:3.27.7")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.3")
testImplementation("org.junit-pioneer:junit-pioneer:1.9.1")
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt
index fbecf1953..90539b872 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt
@@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper
import com.tryfinch.api.core.http.AsyncStreamResponse
import com.tryfinch.api.core.http.Headers
import com.tryfinch.api.core.http.HttpClient
+import com.tryfinch.api.core.http.LoggingHttpClient
import com.tryfinch.api.core.http.PhantomReachableClosingHttpClient
import com.tryfinch.api.core.http.QueryParams
import com.tryfinch.api.core.http.RetryingHttpClient
@@ -81,6 +82,9 @@ private constructor(
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -108,6 +112,14 @@ private constructor(
* Defaults to 2.
*/
@get:JvmName("maxRetries") val maxRetries: Int,
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ @get:JvmName("logLevel") val logLevel: LogLevel,
private val accessToken: String?,
private val clientId: String?,
private val clientSecret: String?,
@@ -174,6 +186,7 @@ private constructor(
private var responseValidation: Boolean = false
private var timeout: Timeout = Timeout.default()
private var maxRetries: Int = 2
+ private var logLevel: LogLevel = LogLevel.fromEnv()
private var accessToken: String? = null
private var clientId: String? = null
private var clientSecret: String? = null
@@ -193,6 +206,7 @@ private constructor(
responseValidation = clientOptions.responseValidation
timeout = clientOptions.timeout
maxRetries = clientOptions.maxRetries
+ logLevel = clientOptions.logLevel
accessToken = clientOptions.accessToken
clientId = clientOptions.clientId
clientSecret = clientOptions.clientSecret
@@ -276,6 +290,9 @@ private constructor(
/**
* Whether to call `validate` on every response before returning it.
*
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
* Defaults to false, which means the shape of the response will not be validated upfront.
* Instead, validation will only occur for the parts of the response that are accessed.
*/
@@ -317,6 +334,15 @@ private constructor(
*/
fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries }
+ /**
+ * The level at which to log request and response information.
+ *
+ * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv].
+ *
+ * Defaults to [LogLevel.fromEnv].
+ */
+ fun logLevel(logLevel: LogLevel) = apply { this.logLevel = logLevel }
+
fun accessToken(accessToken: String?) = apply { this.accessToken = accessToken }
/** Alias for calling [Builder.accessToken] with `accessToken.orElse(null)`. */
@@ -435,6 +461,7 @@ private constructor(
* System properties take precedence over environment variables.
*/
fun fromEnv() = apply {
+ logLevel(LogLevel.fromEnv())
(System.getProperty("finch.baseUrl") ?: System.getenv("FINCH_BASE_URL"))?.let {
baseUrl(it)
}
@@ -445,6 +472,14 @@ private constructor(
?.let { clientSecret(it) }
(System.getProperty("finch.webhookSecret") ?: System.getenv("FINCH_WEBHOOK_SECRET"))
?.let { webhookSecret(it) }
+ System.getenv("FINCH_CUSTOM_HEADERS")?.let { customHeadersEnv ->
+ for (line in customHeadersEnv.split("\n")) {
+ val colon = line.indexOf(':')
+ if (colon >= 0) {
+ putHeader(line.substring(0, colon).trim(), line.substring(colon + 1).trim())
+ }
+ }
+ }
}
/**
@@ -513,7 +548,13 @@ private constructor(
return ClientOptions(
httpClient,
RetryingHttpClient.builder()
- .httpClient(httpClient)
+ .httpClient(
+ LoggingHttpClient.builder()
+ .httpClient(httpClient)
+ .clock(clock)
+ .level(logLevel)
+ .build()
+ )
.sleeper(sleeper)
.clock(clock)
.maxRetries(maxRetries)
@@ -529,6 +570,7 @@ private constructor(
responseValidation,
timeout,
maxRetries,
+ logLevel,
accessToken,
clientId,
clientSecret,
@@ -552,4 +594,29 @@ private constructor(
(streamHandlerExecutor as? ExecutorService)?.shutdown()
sleeper.close()
}
+
+ @JvmSynthetic
+ internal fun securityHeaders(security: SecurityOptions): Headers {
+ val headers = Headers.builder()
+ if (security.bearerAuth) {
+ accessToken?.let {
+ if (!it.isEmpty()) {
+ headers.replace("Authorization", "Bearer $it")
+ }
+ }
+ }
+ if (security.basicAuth) {
+ clientId?.let { username ->
+ clientSecret?.let { password ->
+ if (!username.isEmpty() && !password.isEmpty()) {
+ headers.replace(
+ "Authorization",
+ "Basic ${Base64.getEncoder().encodeToString("$username:$password".toByteArray())}",
+ )
+ }
+ }
+ }
+ }
+ return headers.build()
+ }
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/LogLevel.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/LogLevel.kt
new file mode 100644
index 000000000..2891f6668
--- /dev/null
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/LogLevel.kt
@@ -0,0 +1,33 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.tryfinch.api.core
+
+/** The level at which to log request and response information. */
+enum class LogLevel {
+ /** No logging. */
+ OFF,
+ /** Minimal request and response summary logs. No headers or bodies are logged. */
+ INFO,
+ /** [INFO] logs plus details about request failures. */
+ ERROR,
+ /**
+ * Full request and response logs. Sensitive headers are redacted, but sensitive data in request
+ * and response bodies may still be visible.
+ */
+ DEBUG;
+
+ /** Returns whether this level is at or higher than the given [level]. */
+ fun shouldLog(level: LogLevel): Boolean = ordinal >= level.ordinal
+
+ companion object {
+
+ /** Returns a [LogLevel] based on the `FINCH_LOG` environment variable. */
+ fun fromEnv() =
+ when (System.getenv("FINCH_LOG")?.lowercase()) {
+ "info" -> INFO
+ "error" -> ERROR
+ "debug" -> DEBUG
+ else -> OFF
+ }
+ }
+}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ObjectMappers.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ObjectMappers.kt
index 5ad93de49..7a3ddcb33 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ObjectMappers.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ObjectMappers.kt
@@ -29,7 +29,9 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoField
-fun jsonMapper(): JsonMapper =
+fun jsonMapper(): JsonMapper = JSON_MAPPER
+
+private val JSON_MAPPER: JsonMapper =
JsonMapper.builder()
.addModule(kotlinModule())
.addModule(Jdk8Module())
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/PrepareRequest.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/PrepareRequest.kt
index 6e1aaafb2..d5d2a9c33 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/PrepareRequest.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/PrepareRequest.kt
@@ -6,10 +6,15 @@ import com.tryfinch.api.core.http.HttpRequest
import java.util.concurrent.CompletableFuture
@JvmSynthetic
-internal fun HttpRequest.prepare(clientOptions: ClientOptions, params: Params): HttpRequest =
+internal fun HttpRequest.prepare(
+ clientOptions: ClientOptions,
+ params: Params,
+ security: SecurityOptions = SecurityOptions.all(),
+): HttpRequest =
toBuilder()
.putAllQueryParams(clientOptions.queryParams)
.replaceAllQueryParams(params._queryParams())
+ .putAllHeaders(clientOptions.securityHeaders(security))
.putAllHeaders(clientOptions.headers)
.replaceAllHeaders(params._headers())
.build()
@@ -18,7 +23,8 @@ internal fun HttpRequest.prepare(clientOptions: ClientOptions, params: Params):
internal fun HttpRequest.prepareAsync(
clientOptions: ClientOptions,
params: Params,
+ security: SecurityOptions = SecurityOptions.all(),
): CompletableFuture =
// This async version exists to make it easier to add async specific preparation logic in the
// future.
- CompletableFuture.completedFuture(prepare(clientOptions, params))
+ CompletableFuture.completedFuture(prepare(clientOptions, params, security))
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Properties.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Properties.kt
index 29bb4994f..795814992 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Properties.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Properties.kt
@@ -34,9 +34,9 @@ fun getOsName(): String {
}
}
-fun getOsVersion(): String = System.getProperty("os.version", "unknown")
+fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown"
fun getPackageVersion(): String =
- FinchClient::class.java.`package`.implementationVersion ?: "unknown"
+ FinchClient::class.java.`package`?.implementationVersion ?: "unknown"
-fun getJavaVersion(): String = System.getProperty("java.version", "unknown")
+fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown"
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/RequestOptions.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/RequestOptions.kt
index 89fdd644c..c31a5b33e 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/RequestOptions.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/RequestOptions.kt
@@ -33,6 +33,15 @@ class RequestOptions private constructor(val responseValidation: Boolean?, val t
private var responseValidation: Boolean? = null
private var timeout: Timeout? = null
+ /**
+ * Whether to call `validate` on the response before returning it.
+ *
+ * Setting this to `true` is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * Defaults to false, which means the shape of the response will not be validated upfront.
+ * Instead, validation will only occur for the parts of the response that are accessed.
+ */
fun responseValidation(responseValidation: Boolean) = apply {
this.responseValidation = responseValidation
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/SecurityOptions.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/SecurityOptions.kt
new file mode 100644
index 000000000..d4b5d0d5b
--- /dev/null
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/SecurityOptions.kt
@@ -0,0 +1,69 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.tryfinch.api.core
+
+import java.util.Objects
+
+/** A class for configuring which security schemes are enabled for a request. */
+class SecurityOptions
+private constructor(
+ /** Whether the bearerAuth security scheme is enabled. */
+ @get:JvmName("bearerAuth") val bearerAuth: Boolean,
+ /** Whether the basicAuth security scheme is enabled. */
+ @get:JvmName("basicAuth") val basicAuth: Boolean,
+) {
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /** Returns a mutable builder for constructing an instance of [SecurityOptions]. */
+ @JvmStatic fun builder() = Builder()
+
+ /** Returns a [Security] instance with all security schemes enabled. */
+ @JvmStatic fun all(): SecurityOptions = builder().bearerAuth(true).basicAuth(true).build()
+
+ /** Returns a [Security] instance with no security schemes enabled. */
+ @JvmStatic fun none(): SecurityOptions = builder().build()
+ }
+
+ /** A builder for [SecurityOptions]. */
+ class Builder internal constructor() {
+
+ private var bearerAuth: Boolean = false
+ private var basicAuth: Boolean = false
+
+ @JvmSynthetic
+ internal fun from(securityOptions: SecurityOptions) = apply {
+ bearerAuth = securityOptions.bearerAuth
+ basicAuth = securityOptions.basicAuth
+ }
+
+ /** Whether the bearerAuth security scheme is enabled. */
+ fun bearerAuth(bearerAuth: Boolean) = apply { this.bearerAuth = bearerAuth }
+
+ /** Whether the basicAuth security scheme is enabled. */
+ fun basicAuth(basicAuth: Boolean) = apply { this.basicAuth = basicAuth }
+
+ /**
+ * Returns an immutable instance of [SecurityOptions].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): SecurityOptions = SecurityOptions(bearerAuth, basicAuth)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is SecurityOptions &&
+ bearerAuth == other.bearerAuth &&
+ basicAuth == other.basicAuth
+ }
+
+ override fun hashCode(): Int = Objects.hash(bearerAuth, basicAuth)
+
+ override fun toString() = "SecurityOptions{bearerAuth=$bearerAuth, basicAuth=$basicAuth}"
+}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Utils.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Utils.kt
index 7580ba7ba..a2604d611 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Utils.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Utils.kt
@@ -6,6 +6,7 @@ import com.tryfinch.api.core.http.Headers
import com.tryfinch.api.errors.FinchInvalidDataException
import java.util.Collections
import java.util.SortedMap
+import java.util.SortedSet
import java.util.concurrent.CompletableFuture
import java.util.concurrent.locks.Lock
@@ -17,6 +18,11 @@ internal fun T?.getOrThrow(name: String): T =
internal fun List.toImmutable(): List =
if (isEmpty()) Collections.emptyList() else Collections.unmodifiableList(toList())
+@JvmSynthetic
+internal fun > SortedSet.toImmutable(): SortedSet =
+ if (isEmpty()) Collections.emptySortedSet()
+ else Collections.unmodifiableSortedSet(toSortedSet(comparator() ?: Comparator.naturalOrder()))
+
@JvmSynthetic
internal fun Map.toImmutable(): Map =
if (isEmpty()) immutableEmptyMap() else Collections.unmodifiableMap(toMap())
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/HttpRequestBodies.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/HttpRequestBodies.kt
index 9aee7f83b..83ab12669 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/HttpRequestBodies.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/HttpRequestBodies.kt
@@ -8,13 +8,13 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.node.JsonNodeType
import com.tryfinch.api.core.MultipartField
+import com.tryfinch.api.core.toImmutable
import com.tryfinch.api.errors.FinchInvalidDataException
+import java.io.ByteArrayInputStream
import java.io.InputStream
import java.io.OutputStream
+import java.util.UUID
import kotlin.jvm.optionals.getOrNull
-import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
-import org.apache.hc.core5.http.ContentType
-import org.apache.hc.core5.http.HttpEntity
@JvmSynthetic
internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody =
@@ -37,70 +37,207 @@ internal fun multipartFormData(
jsonMapper: JsonMapper,
fields: Map>,
): HttpRequestBody =
- object : HttpRequestBody {
- private val entity: HttpEntity by lazy {
- MultipartEntityBuilder.create()
- .apply {
- fields.forEach { (name, field) ->
- val knownValue = field.value.asKnown().getOrNull()
- val parts =
- if (knownValue is InputStream) {
- // Read directly from the `InputStream` instead of reading it all
- // into memory due to the `jsonMapper` serialization below.
- sequenceOf(name to knownValue)
- } else {
- val node = jsonMapper.valueToTree(field.value)
- serializePart(name, node)
+ MultipartBody.Builder()
+ .apply {
+ fields.forEach { (name, field) ->
+ val knownValue = field.value.asKnown().getOrNull()
+ val parts =
+ if (knownValue is InputStream) {
+ // Read directly from the `InputStream` instead of reading it all
+ // into memory due to the `jsonMapper` serialization below.
+ sequenceOf(name to knownValue)
+ } else {
+ val node = jsonMapper.valueToTree(field.value)
+ serializePart(name, node)
+ }
+
+ parts.forEach { (name, bytes) ->
+ val partBody =
+ if (bytes is ByteArrayInputStream) {
+ val byteArray = bytes.readBytes()
+
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ outputStream.write(byteArray)
+ }
+
+ override fun contentType(): String = field.contentType
+
+ override fun contentLength(): Long = byteArray.size.toLong()
+
+ override fun repeatable(): Boolean = true
+
+ override fun close() {}
}
+ } else {
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ bytes.copyTo(outputStream)
+ }
+
+ override fun contentType(): String = field.contentType
- parts.forEach { (name, bytes) ->
- addBinaryBody(
- name,
- bytes,
- ContentType.parseLenient(field.contentType),
- field.filename().getOrNull(),
- )
+ override fun contentLength(): Long = -1L
+
+ override fun repeatable(): Boolean = false
+
+ override fun close() = bytes.close()
+ }
}
- }
+
+ addPart(
+ MultipartBody.Part.create(
+ name,
+ field.filename().getOrNull(),
+ field.contentType,
+ partBody,
+ )
+ )
}
- .build()
+ }
}
+ .build()
+
+private fun serializePart(name: String, node: JsonNode): Sequence> =
+ when (node.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> emptySequence()
+ JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
+ JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream())
+ JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream())
+ JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream())
+ JsonNodeType.ARRAY ->
+ node.elements().asSequence().flatMap { element -> serializePart("$name[]", element) }
+ JsonNodeType.OBJECT ->
+ node.fields().asSequence().flatMap { (key, value) ->
+ serializePart("$name[$key]", value)
+ }
+ JsonNodeType.POJO,
+ null -> throw FinchInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
+ }
- private fun serializePart(
- name: String,
- node: JsonNode,
- ): Sequence> =
- when (node.nodeType) {
- JsonNodeType.MISSING,
- JsonNodeType.NULL -> emptySequence()
- JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
- JsonNodeType.STRING -> sequenceOf(name to node.textValue().inputStream())
- JsonNodeType.BOOLEAN ->
- sequenceOf(name to node.booleanValue().toString().inputStream())
- JsonNodeType.NUMBER ->
- sequenceOf(name to node.numberValue().toString().inputStream())
- JsonNodeType.ARRAY ->
- node.elements().asSequence().flatMap { element ->
- serializePart("$name[]", element)
- }
- JsonNodeType.OBJECT ->
- node.fields().asSequence().flatMap { (key, value) ->
- serializePart("$name[$key]", value)
- }
- JsonNodeType.POJO,
- null ->
- throw FinchInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
+private class MultipartBody
+private constructor(private val boundary: String, private val parts: List) : HttpRequestBody {
+ private val boundaryBytes: ByteArray = boundary.toByteArray()
+ private val contentType = "multipart/form-data; boundary=$boundary"
+
+ // This must remain in sync with `contentLength`.
+ override fun writeTo(outputStream: OutputStream) {
+ parts.forEach { part ->
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(CRLF)
+
+ outputStream.write(CONTENT_DISPOSITION)
+ outputStream.write(part.contentDisposition.toByteArray())
+ outputStream.write(CRLF)
+
+ outputStream.write(CONTENT_TYPE)
+ outputStream.write(part.contentType.toByteArray())
+ outputStream.write(CRLF)
+
+ outputStream.write(CRLF)
+ part.body.writeTo(outputStream)
+ outputStream.write(CRLF)
+ }
+
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(DASHDASH)
+ outputStream.write(CRLF)
+ }
+
+ override fun contentType(): String = contentType
+
+ // This must remain in sync with `writeTo`.
+ override fun contentLength(): Long {
+ var byteCount = 0L
+
+ parts.forEach { part ->
+ val contentLength = part.body.contentLength()
+ if (contentLength == -1L) {
+ return -1L
}
- private fun String.inputStream(): InputStream = toByteArray().inputStream()
+ byteCount +=
+ DASHDASH.size +
+ boundaryBytes.size +
+ CRLF.size +
+ CONTENT_DISPOSITION.size +
+ part.contentDisposition.toByteArray().size +
+ CRLF.size +
+ CONTENT_TYPE.size +
+ part.contentType.toByteArray().size +
+ CRLF.size +
+ CRLF.size +
+ contentLength +
+ CRLF.size
+ }
- override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream)
+ byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size
+ return byteCount
+ }
- override fun contentType(): String = entity.contentType
+ override fun repeatable(): Boolean = parts.all { it.body.repeatable() }
- override fun contentLength(): Long = entity.contentLength
+ override fun close() {
+ parts.forEach { it.body.close() }
+ }
- override fun repeatable(): Boolean = entity.isRepeatable
+ class Builder {
+ private val boundary = UUID.randomUUID().toString()
+ private val parts: MutableList = mutableListOf()
- override fun close() = entity.close()
+ fun addPart(part: Part) = apply { parts.add(part) }
+
+ fun build() = MultipartBody(boundary, parts.toImmutable())
+ }
+
+ class Part
+ private constructor(
+ val contentDisposition: String,
+ val contentType: String,
+ val body: HttpRequestBody,
+ ) {
+ companion object {
+ fun create(
+ name: String,
+ filename: String?,
+ contentType: String,
+ body: HttpRequestBody,
+ ): Part {
+ val disposition = buildString {
+ append("form-data; name=")
+ appendQuotedString(name)
+ if (filename != null) {
+ append("; filename=")
+ appendQuotedString(filename)
+ }
+ }
+ return Part(disposition, contentType, body)
+ }
+ }
+ }
+
+ companion object {
+ private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte())
+ private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte())
+ private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray()
+ private val CONTENT_TYPE = "Content-Type: ".toByteArray()
+
+ private fun StringBuilder.appendQuotedString(key: String) {
+ append('"')
+ for (ch in key) {
+ when (ch) {
+ '\n' -> append("%0A")
+ '\r' -> append("%0D")
+ '"' -> append("%22")
+ else -> append(ch)
+ }
+ }
+ append('"')
+ }
}
+}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/LoggingHttpClient.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/LoggingHttpClient.kt
new file mode 100644
index 000000000..a71ba5388
--- /dev/null
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/LoggingHttpClient.kt
@@ -0,0 +1,628 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.tryfinch.api.core.http
+
+import com.tryfinch.api.core.LogLevel
+import com.tryfinch.api.core.RequestOptions
+import com.tryfinch.api.core.checkRequired
+import com.tryfinch.api.core.toImmutable
+import java.io.ByteArrayOutputStream
+import java.io.InputStream
+import java.io.OutputStream
+import java.nio.ByteBuffer
+import java.nio.charset.CharacterCodingException
+import java.nio.charset.Charset
+import java.nio.charset.CharsetDecoder
+import java.nio.charset.CodingErrorAction
+import java.nio.charset.StandardCharsets
+import java.time.Clock
+import java.time.Duration
+import java.time.OffsetDateTime
+import java.util.SortedSet
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.CompletionException
+import kotlin.time.toKotlinDuration
+
+/** A wrapper [HttpClient] around [httpClient] that logs request and response information. */
+class LoggingHttpClient
+private constructor(
+ /** The underlying [HttpClient] for making requests. */
+ @get:JvmName("httpClient") val httpClient: HttpClient,
+ /**
+ * Sensitive headers to redact from logs.
+ *
+ * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie")`.
+ */
+ @get:JvmName("redactedHeaders") val redactedHeaders: SortedSet,
+ /**
+ * The clock to use for measuring request and response durations.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ @get:JvmName("clock") val clock: Clock,
+ /**
+ * The log level to use.
+ *
+ * Pass [LogLevel.fromEnv] to read from environment variables.
+ */
+ @get:JvmName("level") val level: LogLevel,
+) : HttpClient {
+
+ override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
+ val loggingRequest = logRequest(request)
+
+ val before = OffsetDateTime.now(clock)
+ val response =
+ try {
+ httpClient.execute(loggingRequest, requestOptions)
+ } catch (e: Throwable) {
+ logFailure(e, Duration.between(before, OffsetDateTime.now(clock)))
+ throw e
+ }
+
+ val took = Duration.between(before, OffsetDateTime.now(clock))
+ return logResponse(response, took)
+ }
+
+ override fun executeAsync(
+ request: HttpRequest,
+ requestOptions: RequestOptions,
+ ): CompletableFuture {
+ val loggingRequest = logRequest(request)
+
+ val before = OffsetDateTime.now(clock)
+ val future =
+ try {
+ httpClient.executeAsync(loggingRequest, requestOptions)
+ } catch (e: Throwable) {
+ logFailure(e, Duration.between(before, OffsetDateTime.now(clock)))
+ throw e
+ }
+ return future.handle { response, error ->
+ val took = Duration.between(before, OffsetDateTime.now(clock))
+ if (error != null) {
+ logFailure(unwrapCompletionException(error), took)
+ throw error
+ }
+ logResponse(response, took)
+ }
+ }
+
+ private fun logRequest(request: HttpRequest): HttpRequest {
+ if (!level.shouldLog(LogLevel.INFO)) {
+ return request
+ }
+
+ System.err.println(
+ buildString {
+ append("--> ${request.method} ${request.url()}")
+ request.body?.let {
+ val length = it.contentLength()
+ append(if (length >= 0) " ($length-byte body)" else " (unknown-length body)")
+ }
+ }
+ )
+
+ if (!level.shouldLog(LogLevel.DEBUG)) {
+ return request
+ }
+
+ logHeaders(request.headers)
+
+ if (request.body == null) {
+ System.err.println("--> END ${request.method}")
+ System.err.println()
+ return request
+ }
+
+ return request
+ .toBuilder()
+ .body(LoggingHttpRequestBody(request.method, request.body))
+ .build()
+ }
+
+ private fun logResponse(response: HttpResponse, took: Duration): HttpResponse {
+ if (!level.shouldLog(LogLevel.INFO)) {
+ return response
+ }
+
+ val contentLength = response.headers().values("Content-Length").firstOrNull()?.toIntOrNull()
+ System.err.println(
+ "<-- ${response.statusCode()} (${
+ buildString {
+ append(took.format())
+ contentLength?.let { append(", $contentLength-byte body") }
+ }
+ })"
+ )
+
+ if (!level.shouldLog(LogLevel.DEBUG)) {
+ return response
+ }
+
+ logHeaders(response.headers())
+ return LoggingHttpResponse(response)
+ }
+
+ private fun logFailure(error: Throwable, took: Duration) {
+ if (!level.shouldLog(LogLevel.ERROR)) {
+ return
+ }
+
+ System.err.println(
+ buildString {
+ append("<-- !! ${error.javaClass.simpleName}")
+ error.message?.let { append(": $it") }
+ append(" (${took.format()})")
+ }
+ )
+ }
+
+ private fun unwrapCompletionException(error: Throwable): Throwable =
+ if (error is CompletionException && error.cause != null) error.cause!! else error
+
+ private fun logHeaders(headers: Headers) =
+ headers.names().forEach { name ->
+ headers.values(name).forEach { value ->
+ System.err.println("$name: ${if (redactedHeaders.contains(name)) "██" else value}")
+ }
+ }
+
+ override fun close() = httpClient.close()
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [LoggingHttpClient].
+ *
+ * The following fields are required:
+ * ```java
+ * .httpClient()
+ * .level()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [LoggingHttpClient]. */
+ class Builder internal constructor() {
+
+ private var httpClient: HttpClient? = null
+ private var redactedHeaders: Set =
+ setOf("authorization", "api-key", "x-api-key", "cookie", "set-cookie")
+ private var clock: Clock = Clock.systemUTC()
+ private var level: LogLevel? = null
+
+ @JvmSynthetic
+ internal fun from(loggingHttpClient: LoggingHttpClient) = apply {
+ httpClient = loggingHttpClient.httpClient
+ redactedHeaders = loggingHttpClient.redactedHeaders
+ clock = loggingHttpClient.clock
+ level = loggingHttpClient.level
+ }
+
+ /** The underlying [HttpClient] for making requests. */
+ fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient }
+
+ /**
+ * Sensitive headers to redact from logs.
+ *
+ * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie")`.
+ */
+ fun redactedHeaders(redactedHeaders: Set) = apply {
+ this.redactedHeaders = redactedHeaders
+ }
+
+ /**
+ * The clock to use for measuring request and response durations.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ fun clock(clock: Clock) = apply { this.clock = clock }
+
+ /**
+ * The log level to use.
+ *
+ * Pass [LogLevel.fromEnv] to read from environment variables.
+ */
+ fun level(level: LogLevel) = apply { this.level = level }
+
+ /**
+ * Returns an immutable instance of [LoggingHttpClient].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .httpClient()
+ * .level()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): LoggingHttpClient =
+ LoggingHttpClient(
+ checkRequired("httpClient", httpClient),
+ redactedHeaders.toSortedSet(String.CASE_INSENSITIVE_ORDER).toImmutable(),
+ clock,
+ checkRequired("level", level),
+ )
+ }
+}
+
+/**
+ * An [HttpRequestBody] wrapper that delegates to [body] while also logging line by line as it's
+ * written.
+ *
+ * The logging occurs in a streaming manner with minimal buffering.
+ */
+private class LoggingHttpRequestBody(
+ private val method: HttpMethod,
+ private val body: HttpRequestBody,
+) : HttpRequestBody {
+
+ private val charset by lazy { parseCharset(body.contentType()) }
+
+ override fun writeTo(outputStream: OutputStream) {
+ val loggingOutputStream = LoggingOutputStream(outputStream, charset)
+ body.writeTo(loggingOutputStream)
+
+ loggingOutputStream.flush()
+ System.err.println("--> END $method (${loggingOutputStream.writeCount()}-byte body)")
+ System.err.println()
+ }
+
+ override fun contentType(): String? = body.contentType()
+
+ override fun contentLength(): Long = body.contentLength()
+
+ override fun repeatable(): Boolean = body.repeatable()
+
+ override fun close() = body.close()
+}
+
+/**
+ * An [OutputStream] wrapper that delegates to [outputStream] while also logging bytes line by line
+ * as it's written to.
+ *
+ * The written content is assumed to be in the given [charset] and the logging occurs in a streaming
+ * manner with minimal buffering.
+ */
+private class LoggingOutputStream(private val outputStream: OutputStream, charset: Charset?) :
+ OutputStream() {
+
+ private val buffer = LoggingBuffer(charset)
+
+ fun writeCount() = buffer.writeCount()
+
+ override fun write(b: Int) {
+ outputStream.write(b)
+ buffer.write(b)
+ }
+
+ override fun write(b: ByteArray, off: Int, len: Int) {
+ outputStream.write(b, off, len)
+ for (i in off until off + len) {
+ buffer.write(b[i].toInt() and 0xFF)
+ }
+ }
+
+ /** Prints any currently buffered content. */
+ override fun flush() {
+ buffer.flush()
+ outputStream.flush()
+ }
+
+ override fun close() = outputStream.close()
+}
+
+/**
+ * An [HttpResponse] wrapper that delegates to [response] while also logging line-by-line as it's
+ * read.
+ *
+ * The logging occurs in a streaming manner with minimal buffering.
+ */
+private class LoggingHttpResponse(private val response: HttpResponse) : HttpResponse {
+
+ private val loggingBody: Lazy = lazy {
+ LoggingInputStream(
+ response.body(),
+ parseCharset(response.headers().values("Content-Type").firstOrNull()),
+ )
+ }
+
+ override fun statusCode(): Int = response.statusCode()
+
+ override fun headers(): Headers = response.headers()
+
+ override fun body(): InputStream = loggingBody.value
+
+ override fun close() {
+ if (loggingBody.isInitialized()) {
+ loggingBody.value.close()
+ }
+ response.close()
+ }
+}
+
+/**
+ * An [InputStream] wrapper that delegates to [inputStream] while also logging bytes line by line as
+ * it's read.
+ *
+ * The contents of [inputStream] are assumed to be in the given [charset] and the logging occurs in
+ * a streaming manner with minimal buffering.
+ */
+private class LoggingInputStream(private val inputStream: InputStream, charset: Charset?) :
+ InputStream() {
+
+ private var isDone = false
+ private val buffer = LoggingBuffer(charset)
+
+ override fun read(): Int {
+ if (isDone) {
+ return -1
+ }
+
+ val b = inputStream.read()
+
+ if (b == -1) {
+ markDone()
+ return b
+ }
+
+ buffer.write(b)
+ return b
+ }
+
+ override fun read(b: ByteArray, off: Int, len: Int): Int {
+ if (isDone) {
+ return -1
+ }
+
+ val bytesRead = inputStream.read(b, off, len)
+
+ if (bytesRead == -1) {
+ markDone()
+ return bytesRead
+ }
+
+ for (i in off until off + bytesRead) {
+ buffer.write(b[i].toInt() and 0xFF)
+ }
+ return bytesRead
+ }
+
+ override fun close() {
+ if (!isDone) {
+ markDone(closedEarly = true)
+ }
+ inputStream.close()
+ }
+
+ private fun markDone(closedEarly: Boolean = false) {
+ isDone = true
+ buffer.flush()
+ val suffix = if (closedEarly) ", closed early" else ""
+ System.err.println("<-- END HTTP (${buffer.writeCount()}-byte body$suffix)")
+ System.err.println()
+ }
+}
+
+/**
+ * A byte buffer that prints line by line, using the given [charset], as bytes are written to it.
+ *
+ * When [charset] is `null`, the buffer performs an upfront check to detect binary content. If
+ * non-whitespace ISO control characters are found in the first [PROBABLY_UTF8_CODE_POINT_LIMIT]
+ * code points, body logging is suppressed entirely.
+ */
+private class LoggingBuffer(charset: Charset?) {
+
+ private val charset = charset ?: StandardCharsets.UTF_8
+
+ private val decoder: CharsetDecoder =
+ this.charset
+ .newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ private var writeCount = 0
+ private val buffer = ByteArrayOutputStream(128)
+
+ /**
+ * Whether logging has been suppressed because the content doesn't appear to be readable text.
+ *
+ * This is only set when [charset] is `null` and the content fails the [isProbablyUtf8] check.
+ */
+ private var suppressed = false
+
+ /**
+ * Bytes accumulated for the [isProbablyUtf8] check before any lines are printed.
+ *
+ * Once the check passes (or [charset] is non-null), this is set to `null` and bytes flow
+ * directly to [buffer].
+ */
+ private var prefetchBuffer: ByteArrayOutputStream? =
+ if (charset != null) null else ByteArrayOutputStream(128)
+
+ fun writeCount() = writeCount
+
+ fun write(b: Int) {
+ if (writeCount == 0) {
+ // Print a newline before we start printing anything to separate the printed content
+ // from previous content.
+ System.err.println()
+ }
+
+ writeCount++
+
+ if (suppressed) {
+ return
+ }
+
+ val prefetch = prefetchBuffer
+ if (prefetch != null) {
+ prefetch.write(b)
+ // Continue accumulating until we have enough bytes to decide.
+ if (prefetch.size() < PROBABLY_UTF8_BYTE_LIMIT && b != '\n'.code) {
+ return
+ }
+ // We have enough bytes. Check if the content is probably UTF-8.
+ prefetchBuffer = null
+ val bytes = prefetch.toByteArray()
+ if (!isProbablyUtf8(bytes)) {
+ suppressed = true
+ System.err.println("(binary body omitted)")
+ return
+ }
+ // Content looks like UTF-8. Feed the accumulated bytes into the normal buffer.
+ for (byte in bytes) {
+ writeToBuffer(byte.toInt() and 0xFF)
+ }
+ return
+ }
+
+ writeToBuffer(b)
+ }
+
+ private fun writeToBuffer(b: Int) {
+ if (b == '\n'.code) {
+ flush()
+ return
+ }
+
+ buffer.write(b)
+ }
+
+ /** Prints any currently buffered content. */
+ fun flush() {
+ if (suppressed) {
+ return
+ }
+
+ // If we still have a prefetch buffer when flush is called (body was shorter than the
+ // limit), run the check now.
+ val prefetch = prefetchBuffer
+ if (prefetch != null) {
+ prefetchBuffer = null
+ val bytes = prefetch.toByteArray()
+ if (bytes.isEmpty()) {
+ return
+ }
+ if (!isProbablyUtf8(bytes)) {
+ suppressed = true
+ System.err.println("(binary body omitted)")
+ return
+ }
+ for (byte in bytes) {
+ writeToBuffer(byte.toInt() and 0xFF)
+ }
+ }
+
+ if (buffer.size() == 0) {
+ return
+ }
+
+ val line =
+ try {
+ decoder.decode(ByteBuffer.wrap(buffer.toByteArray()))
+ } catch (e: CharacterCodingException) {
+ "(omitted line is not valid $charset)"
+ }
+ buffer.reset()
+ System.err.println(line)
+ }
+}
+
+/** The maximum number of code points to sample when checking if content is probably UTF-8. */
+private const val PROBABLY_UTF8_CODE_POINT_LIMIT = 64
+
+/**
+ * The maximum number of bytes to accumulate before running the [isProbablyUtf8] check. UTF-8 code
+ * points are at most 4 bytes, so this accommodates [PROBABLY_UTF8_CODE_POINT_LIMIT] code points.
+ */
+private const val PROBABLY_UTF8_BYTE_LIMIT = PROBABLY_UTF8_CODE_POINT_LIMIT * 4
+
+/**
+ * Returns `true` if the given [bytes] probably contain human-readable UTF-8 text.
+ *
+ * Decodes up to [PROBABLY_UTF8_CODE_POINT_LIMIT] code points and returns `false` if any
+ * non-whitespace ISO control characters are found, or if the bytes are not valid UTF-8.
+ */
+private fun isProbablyUtf8(bytes: ByteArray): Boolean {
+ try {
+ val decoder =
+ StandardCharsets.UTF_8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT)
+ val charBuffer = decoder.decode(ByteBuffer.wrap(bytes))
+ var codePointCount = 0
+ var i = 0
+ while (i < charBuffer.length && codePointCount < PROBABLY_UTF8_CODE_POINT_LIMIT) {
+ val codePoint = Character.codePointAt(charBuffer, i)
+ if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
+ return false
+ }
+ i += Character.charCount(codePoint)
+ codePointCount++
+ }
+ return true
+ } catch (e: CharacterCodingException) {
+ return false
+ }
+}
+
+/** Returns the [Charset] in the given [contentType] string, or `null` if unspecified. */
+private fun parseCharset(contentType: String?): Charset? =
+ contentType
+ ?.split(";")
+ ?.drop(1)
+ ?.map { it.trim() }
+ ?.firstOrNull { it.startsWith("charset=", ignoreCase = true) }
+ ?.substringAfter("=")
+ ?.trim()
+ ?.removeSurrounding("\"")
+ ?.let { runCatching { charset(it) }.getOrNull() }
+
+/** Formats the [Duration] into a string like "1m 40s 467ms". */
+private fun Duration.format(): String =
+ toKotlinDuration().toComponents { days, hours, minutes, seconds, nanoseconds ->
+ buildString {
+ val milliseconds = nanoseconds / 1_000_000
+ if (days > 0) {
+ append("${days}d")
+ }
+ if (hours > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${hours}h")
+ }
+ if (minutes > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${minutes}m")
+ }
+ if (seconds > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${seconds}s")
+ }
+ if (milliseconds > 0) {
+ if (isNotEmpty()) {
+ append(" ")
+ }
+ append("${milliseconds}ms")
+ }
+
+ if (isEmpty()) {
+ append("0s")
+ }
+ }
+ }
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/ProxyAuthenticator.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/ProxyAuthenticator.kt
new file mode 100644
index 000000000..7f4bcb93c
--- /dev/null
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/ProxyAuthenticator.kt
@@ -0,0 +1,59 @@
+package com.tryfinch.api.core.http
+
+import java.net.Proxy
+import java.nio.charset.Charset
+import java.nio.charset.StandardCharsets
+import java.util.Base64
+import java.util.Optional
+
+/**
+ * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication Required`.
+ *
+ * Implementations inspect the 407 [response] (typically its `Proxy-Authenticate` header) and return
+ * the request to retry with a `Proxy-Authorization` header set, or [Optional.empty] to abandon
+ * authentication and surface the 407 to the caller.
+ *
+ * Implementations must be thread-safe; they may be invoked concurrently from multiple HTTP calls.
+ */
+fun interface ProxyAuthenticator {
+
+ /**
+ * @param proxy the proxy that produced the challenge, or [Proxy.NO_PROXY] if the route is not
+ * yet established
+ * @param request the request that produced [response]
+ * @param response the 407 challenge response
+ * @return the retry request to send (typically [request] with a `Proxy-Authorization` header
+ * added), or [Optional.empty] to abandon authentication
+ */
+ fun authenticate(
+ proxy: Proxy,
+ request: HttpRequest,
+ response: HttpResponse,
+ ): Optional
+
+ companion object {
+
+ /**
+ * A [ProxyAuthenticator] that uses RFC 7617 Basic authentication with the ISO-8859-1
+ * charset.
+ */
+ @JvmStatic
+ fun basic(username: String, password: String): ProxyAuthenticator =
+ basic(username, password, StandardCharsets.ISO_8859_1)
+
+ /**
+ * A [ProxyAuthenticator] that uses RFC 7617 Basic authentication with the given [charset].
+ */
+ @JvmStatic
+ fun basic(username: String, password: String, charset: Charset): ProxyAuthenticator {
+ val token =
+ Base64.getEncoder().encodeToString("$username:$password".toByteArray(charset))
+ val headerValue = "Basic $token"
+ return ProxyAuthenticator { _, request, _ ->
+ Optional.of(
+ request.toBuilder().putHeader("Proxy-Authorization", headerValue).build()
+ )
+ }
+ }
+ }
+}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/RetryingHttpClient.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/RetryingHttpClient.kt
index 65179d582..a2b202750 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/RetryingHttpClient.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/RetryingHttpClient.kt
@@ -1,3 +1,5 @@
+// File generated from our OpenAPI spec by Stainless.
+
package com.tryfinch.api.core.http
import com.tryfinch.api.core.DefaultSleeper
@@ -199,7 +201,7 @@ private constructor(
?: headers.values("Retry-After").getOrNull(0)?.let { retryAfter ->
retryAfter.toFloatOrNull()?.times(TimeUnit.SECONDS.toNanos(1))
?: try {
- ChronoUnit.MILLIS.between(
+ ChronoUnit.NANOS.between(
OffsetDateTime.now(clock),
OffsetDateTime.parse(
retryAfter,
@@ -212,13 +214,8 @@ private constructor(
}
}
?.let { retryAfterNanos ->
- // If the API asks us to wait a certain amount of time (and it's a reasonable
- // amount), just
- // do what it says.
- val retryAfter = Duration.ofNanos(retryAfterNanos.toLong())
- if (retryAfter in Duration.ofNanos(0)..Duration.ofMinutes(1)) {
- return retryAfter
- }
+ // If the API asks us to wait a certain amount of time, do what it says.
+ return Duration.ofNanos(retryAfterNanos.toLong())
}
// Apply exponential backoff, but not more than the max.
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/BadRequestException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/BadRequestException.kt
index 6b42dcad7..afff15699 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/BadRequestException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/BadRequestException.kt
@@ -5,12 +5,16 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class BadRequestException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- FinchServiceException("400: $body", cause) {
+ FinchServiceException(
+ "400: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 400
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/InternalServerException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/InternalServerException.kt
index 0e432de35..b6fed6e6d 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/InternalServerException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/InternalServerException.kt
@@ -5,6 +5,7 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
@@ -14,7 +15,11 @@ private constructor(
private val headers: Headers,
private val body: JsonValue,
cause: Throwable?,
-) : FinchServiceException("$statusCode: $body", cause) {
+) :
+ FinchServiceException(
+ "$statusCode: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = statusCode
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/NotFoundException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/NotFoundException.kt
index 78219c0c6..62c4dbaa3 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/NotFoundException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/NotFoundException.kt
@@ -5,12 +5,16 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class NotFoundException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- FinchServiceException("404: $body", cause) {
+ FinchServiceException(
+ "404: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 404
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/PermissionDeniedException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/PermissionDeniedException.kt
index b43c0875e..c796d3d8e 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/PermissionDeniedException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/PermissionDeniedException.kt
@@ -5,12 +5,16 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class PermissionDeniedException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- FinchServiceException("403: $body", cause) {
+ FinchServiceException(
+ "403: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 403
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/RateLimitException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/RateLimitException.kt
index 02907cfc4..4e44f0148 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/RateLimitException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/RateLimitException.kt
@@ -5,12 +5,16 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class RateLimitException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- FinchServiceException("429: $body", cause) {
+ FinchServiceException(
+ "429: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 429
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnauthorizedException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnauthorizedException.kt
index 91143b9dc..f85192d08 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnauthorizedException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnauthorizedException.kt
@@ -5,12 +5,16 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class UnauthorizedException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- FinchServiceException("401: $body", cause) {
+ FinchServiceException(
+ "401: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 401
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnexpectedStatusCodeException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnexpectedStatusCodeException.kt
index 5d89680bd..ec90a1a69 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnexpectedStatusCodeException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnexpectedStatusCodeException.kt
@@ -5,6 +5,7 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
@@ -14,7 +15,11 @@ private constructor(
private val headers: Headers,
private val body: JsonValue,
cause: Throwable?,
-) : FinchServiceException("$statusCode: $body", cause) {
+) :
+ FinchServiceException(
+ "$statusCode: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = statusCode
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnprocessableEntityException.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnprocessableEntityException.kt
index c937818a1..3963339db 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnprocessableEntityException.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/errors/UnprocessableEntityException.kt
@@ -5,12 +5,16 @@ package com.tryfinch.api.errors
import com.tryfinch.api.core.JsonValue
import com.tryfinch.api.core.checkRequired
import com.tryfinch.api.core.http.Headers
+import com.tryfinch.api.core.jsonMapper
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class UnprocessableEntityException
private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
- FinchServiceException("422: $body", cause) {
+ FinchServiceException(
+ "422: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}",
+ cause,
+ ) {
override fun statusCode(): Int = 422
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccessTokenCreateParams.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccessTokenCreateParams.kt
index 75ba86fdd..a77964ea2 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccessTokenCreateParams.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccessTokenCreateParams.kt
@@ -554,6 +554,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): CreateAccessTokenRequest = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountCreateResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountCreateResponse.kt
index abb928369..372f29732 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountCreateResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountCreateResponse.kt
@@ -420,6 +420,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): AccountCreateResponse = apply {
if (validated) {
return@apply
@@ -563,6 +571,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AuthenticationType = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateEvent.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateEvent.kt
index 1faf0298f..21788eca4 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateEvent.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateEvent.kt
@@ -24,6 +24,7 @@ private constructor(
private val accountId: JsonField,
private val companyId: JsonField,
private val connectionId: JsonField,
+ private val entityId: JsonField,
private val data: JsonField,
private val eventType: JsonField,
private val additionalProperties: MutableMap,
@@ -36,17 +37,19 @@ private constructor(
@JsonProperty("connection_id")
@ExcludeMissing
connectionId: JsonField = JsonMissing.of(),
+ @JsonProperty("entity_id") @ExcludeMissing entityId: JsonField = JsonMissing.of(),
@JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(),
@JsonProperty("event_type")
@ExcludeMissing
eventType: JsonField = JsonMissing.of(),
- ) : this(accountId, companyId, connectionId, data, eventType, mutableMapOf())
+ ) : this(accountId, companyId, connectionId, entityId, data, eventType, mutableMapOf())
fun toBaseWebhookEvent(): BaseWebhookEvent =
BaseWebhookEvent.builder()
.accountId(accountId)
.companyId(companyId)
.connectionId(connectionId)
+ .entityId(entityId)
.build()
/**
@@ -75,6 +78,14 @@ private constructor(
*/
fun connectionId(): Optional = connectionId.getOptional("connection_id")
+ /**
+ * Unique Finch id of the entity for which data has been updated.
+ *
+ * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun entityId(): Optional = entityId.getOptional("entity_id")
+
/**
* @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
@@ -116,6 +127,13 @@ private constructor(
@ExcludeMissing
fun _connectionId(): JsonField = connectionId
+ /**
+ * Returns the raw JSON value of [entityId].
+ *
+ * Unlike [entityId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("entity_id") @ExcludeMissing fun _entityId(): JsonField = entityId
+
/**
* Returns the raw JSON value of [data].
*
@@ -162,6 +180,7 @@ private constructor(
private var accountId: JsonField? = null
private var companyId: JsonField? = null
private var connectionId: JsonField = JsonMissing.of()
+ private var entityId: JsonField = JsonMissing.of()
private var data: JsonField = JsonMissing.of()
private var eventType: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -171,6 +190,7 @@ private constructor(
accountId = accountUpdateEvent.accountId
companyId = accountUpdateEvent.companyId
connectionId = accountUpdateEvent.connectionId
+ entityId = accountUpdateEvent.entityId
data = accountUpdateEvent.data
eventType = accountUpdateEvent.eventType
additionalProperties = accountUpdateEvent.additionalProperties.toMutableMap()
@@ -224,6 +244,17 @@ private constructor(
this.connectionId = connectionId
}
+ /** Unique Finch id of the entity for which data has been updated. */
+ fun entityId(entityId: String) = entityId(JsonField.of(entityId))
+
+ /**
+ * Sets [Builder.entityId] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.entityId] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun entityId(entityId: JsonField) = apply { this.entityId = entityId }
+
fun data(data: Data) = data(JsonField.of(data))
/**
@@ -282,6 +313,7 @@ private constructor(
checkRequired("accountId", accountId),
checkRequired("companyId", companyId),
connectionId,
+ entityId,
data,
eventType,
additionalProperties.toMutableMap(),
@@ -290,6 +322,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): AccountUpdateEvent = apply {
if (validated) {
return@apply
@@ -298,6 +338,7 @@ private constructor(
accountId()
companyId()
connectionId()
+ entityId()
data().ifPresent { it.validate() }
eventType().ifPresent { it.validate() }
validated = true
@@ -321,6 +362,7 @@ private constructor(
(if (accountId.asKnown().isPresent) 1 else 0) +
(if (companyId.asKnown().isPresent) 1 else 0) +
(if (connectionId.asKnown().isPresent) 1 else 0) +
+ (if (entityId.asKnown().isPresent) 1 else 0) +
(data.asKnown().getOrNull()?.validity() ?: 0) +
(eventType.asKnown().getOrNull()?.validity() ?: 0)
@@ -482,6 +524,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Data = apply {
if (validated) {
return@apply
@@ -716,6 +767,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AuthenticationMethod = apply {
if (validated) {
return@apply
@@ -1095,6 +1156,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): SupportedFields = apply {
if (validated) {
return@apply
@@ -1543,6 +1614,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): SupportedCompanyFields = apply {
if (validated) {
return@apply
@@ -1860,6 +1941,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Accounts = apply {
if (validated) {
return@apply
@@ -2067,6 +2158,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Departments = apply {
if (validated) {
return@apply
@@ -2207,6 +2308,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API
+ * for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Parent = apply {
if (validated) {
return@apply
@@ -2415,6 +2526,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Entity = apply {
if (validated) {
return@apply
@@ -2750,6 +2871,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Locations = apply {
if (validated) {
return@apply
@@ -3010,6 +3141,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): SupportedDirectoryFields = apply {
if (validated) {
return@apply
@@ -3377,6 +3518,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Individuals = apply {
if (validated) {
return@apply
@@ -3525,6 +3676,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API
+ * for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Manager = apply {
if (validated) {
return@apply
@@ -3747,6 +3908,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Paging = apply {
if (validated) {
return@apply
@@ -4522,6 +4693,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): SupportedEmploymentFields = apply {
if (validated) {
return@apply
@@ -4689,6 +4870,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Department = apply {
if (validated) {
return@apply
@@ -4878,6 +5069,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Employment = apply {
if (validated) {
return@apply
@@ -5102,6 +5303,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Income = apply {
if (validated) {
return@apply
@@ -5439,6 +5650,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Location = apply {
if (validated) {
return@apply
@@ -5614,6 +5835,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Manager = apply {
if (validated) {
return@apply
@@ -6265,6 +6496,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): SupportedIndividualFields = apply {
if (validated) {
return@apply
@@ -6453,6 +6694,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Emails = apply {
if (validated) {
return@apply
@@ -6642,6 +6893,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): PhoneNumbers = apply {
if (validated) {
return@apply
@@ -6977,6 +7238,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Residence = apply {
if (validated) {
return@apply
@@ -7313,6 +7584,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): SupportedPayGroupFields = apply {
if (validated) {
return@apply
@@ -7521,6 +7802,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): SupportedPayStatementFields = apply {
if (validated) {
return@apply
@@ -7711,6 +8002,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Paging = apply {
if (validated) {
return@apply
@@ -8221,6 +8522,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): PayStatements = apply {
if (validated) {
return@apply
@@ -8486,6 +8797,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API
+ * for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Earnings = apply {
if (validated) {
return@apply
@@ -8798,6 +9119,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API
+ * for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): EmployeeDeductions = apply {
if (validated) {
return@apply
@@ -9052,6 +9383,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API
+ * for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): EmployerContributions = apply {
if (validated) {
return@apply
@@ -9360,6 +9701,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API
+ * for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): Taxes = apply {
if (validated) {
return@apply
@@ -10015,6 +10366,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected
+ * types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't
+ * match its expected type.
+ */
fun validate(): SupportedPaymentFields = apply {
if (validated) {
return@apply
@@ -10210,6 +10571,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their
+ * expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for
+ * existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object
+ * doesn't match its expected type.
+ */
fun validate(): PayPeriod = apply {
if (validated) {
return@apply
@@ -10451,6 +10822,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
@@ -10614,6 +10995,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): EventType = apply {
if (validated) {
return@apply
@@ -10661,17 +11051,26 @@ private constructor(
accountId == other.accountId &&
companyId == other.companyId &&
connectionId == other.connectionId &&
+ entityId == other.entityId &&
data == other.data &&
eventType == other.eventType &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(accountId, companyId, connectionId, data, eventType, additionalProperties)
+ Objects.hash(
+ accountId,
+ companyId,
+ connectionId,
+ entityId,
+ data,
+ eventType,
+ additionalProperties,
+ )
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "AccountUpdateEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, data=$data, eventType=$eventType, additionalProperties=$additionalProperties}"
+ "AccountUpdateEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, entityId=$entityId, data=$data, eventType=$eventType, additionalProperties=$additionalProperties}"
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateResponse.kt
index bfff64e8c..366c1f01d 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AccountUpdateResponse.kt
@@ -384,6 +384,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): AccountUpdateResponse = apply {
if (validated) {
return@apply
@@ -525,6 +533,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AuthenticationType = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedAsyncJob.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedAsyncJob.kt
index cdcb6c77b..59260e253 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedAsyncJob.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedAsyncJob.kt
@@ -456,6 +456,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): AutomatedAsyncJob = apply {
if (validated) {
return@apply
@@ -604,6 +612,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Params = apply {
if (validated) {
return@apply
@@ -758,6 +775,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Status = apply {
if (validated) {
return@apply
@@ -883,6 +909,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedCreateResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedCreateResponse.kt
index a01a4d5c0..77f6a78f8 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedCreateResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedCreateResponse.kt
@@ -275,6 +275,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): AutomatedCreateResponse = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedListResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedListResponse.kt
index b9a02fdcc..619c0cae6 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedListResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/AutomatedListResponse.kt
@@ -178,6 +178,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): AutomatedListResponse = apply {
if (validated) {
return@apply
@@ -310,6 +318,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Meta = apply {
if (validated) {
return@apply
@@ -445,6 +462,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Quotas = apply {
if (validated) {
return@apply
@@ -620,6 +647,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): DataSyncAll = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BaseWebhookEvent.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BaseWebhookEvent.kt
index a3c5d0cd9..edbf92245 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BaseWebhookEvent.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BaseWebhookEvent.kt
@@ -22,6 +22,7 @@ private constructor(
private val accountId: JsonField,
private val companyId: JsonField,
private val connectionId: JsonField,
+ private val entityId: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -32,7 +33,8 @@ private constructor(
@JsonProperty("connection_id")
@ExcludeMissing
connectionId: JsonField = JsonMissing.of(),
- ) : this(accountId, companyId, connectionId, mutableMapOf())
+ @JsonProperty("entity_id") @ExcludeMissing entityId: JsonField = JsonMissing.of(),
+ ) : this(accountId, companyId, connectionId, entityId, mutableMapOf())
/**
* [DEPRECATED] Unique Finch ID of the employer account used to make this connection. Use
@@ -60,6 +62,14 @@ private constructor(
*/
fun connectionId(): Optional = connectionId.getOptional("connection_id")
+ /**
+ * Unique Finch id of the entity for which data has been updated.
+ *
+ * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun entityId(): Optional = entityId.getOptional("entity_id")
+
/**
* Returns the raw JSON value of [accountId].
*
@@ -89,6 +99,13 @@ private constructor(
@ExcludeMissing
fun _connectionId(): JsonField = connectionId
+ /**
+ * Returns the raw JSON value of [entityId].
+ *
+ * Unlike [entityId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("entity_id") @ExcludeMissing fun _entityId(): JsonField = entityId
+
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -121,6 +138,7 @@ private constructor(
private var accountId: JsonField? = null
private var companyId: JsonField? = null
private var connectionId: JsonField = JsonMissing.of()
+ private var entityId: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
@@ -128,6 +146,7 @@ private constructor(
accountId = baseWebhookEvent.accountId
companyId = baseWebhookEvent.companyId
connectionId = baseWebhookEvent.connectionId
+ entityId = baseWebhookEvent.entityId
additionalProperties = baseWebhookEvent.additionalProperties.toMutableMap()
}
@@ -179,6 +198,17 @@ private constructor(
this.connectionId = connectionId
}
+ /** Unique Finch id of the entity for which data has been updated. */
+ fun entityId(entityId: String) = entityId(JsonField.of(entityId))
+
+ /**
+ * Sets [Builder.entityId] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.entityId] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun entityId(entityId: JsonField) = apply { this.entityId = entityId }
+
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -216,12 +246,21 @@ private constructor(
checkRequired("accountId", accountId),
checkRequired("companyId", companyId),
connectionId,
+ entityId,
additionalProperties.toMutableMap(),
)
}
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): BaseWebhookEvent = apply {
if (validated) {
return@apply
@@ -230,6 +269,7 @@ private constructor(
accountId()
companyId()
connectionId()
+ entityId()
validated = true
}
@@ -250,7 +290,8 @@ private constructor(
internal fun validity(): Int =
(if (accountId.asKnown().isPresent) 1 else 0) +
(if (companyId.asKnown().isPresent) 1 else 0) +
- (if (connectionId.asKnown().isPresent) 1 else 0)
+ (if (connectionId.asKnown().isPresent) 1 else 0) +
+ (if (entityId.asKnown().isPresent) 1 else 0)
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -261,15 +302,16 @@ private constructor(
accountId == other.accountId &&
companyId == other.companyId &&
connectionId == other.connectionId &&
+ entityId == other.entityId &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(accountId, companyId, connectionId, additionalProperties)
+ Objects.hash(accountId, companyId, connectionId, entityId, additionalProperties)
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "BaseWebhookEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, additionalProperties=$additionalProperties}"
+ "BaseWebhookEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, entityId=$entityId, additionalProperties=$additionalProperties}"
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitContribution.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitContribution.kt
index b2f29efaa..0f4883eee 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitContribution.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitContribution.kt
@@ -35,42 +35,79 @@ import kotlin.jvm.optionals.getOrNull
@JsonSerialize(using = BenefitContribution.Serializer::class)
class BenefitContribution
private constructor(
- private val unionMember0: UnionMember0? = null,
- private val unionMember1: UnionMember1? = null,
- private val unionMember2: UnionMember2? = null,
+ private val fixed: BenefitContributionFixed? = null,
+ private val percent: BenefitContributionPercent? = null,
+ private val tiered: BenefitContributionTiered? = null,
private val _json: JsonValue? = null,
) {
- fun unionMember0(): Optional = Optional.ofNullable(unionMember0)
+ fun fixed(): Optional = Optional.ofNullable(fixed)
- fun unionMember1(): Optional = Optional.ofNullable(unionMember1)
+ fun percent(): Optional = Optional.ofNullable(percent)
- fun unionMember2(): Optional = Optional.ofNullable(unionMember2)
+ fun tiered(): Optional = Optional.ofNullable(tiered)
- fun isUnionMember0(): Boolean = unionMember0 != null
+ fun isFixed(): Boolean = fixed != null
- fun isUnionMember1(): Boolean = unionMember1 != null
+ fun isPercent(): Boolean = percent != null
- fun isUnionMember2(): Boolean = unionMember2 != null
+ fun isTiered(): Boolean = tiered != null
- fun asUnionMember0(): UnionMember0 = unionMember0.getOrThrow("unionMember0")
+ fun asFixed(): BenefitContributionFixed = fixed.getOrThrow("fixed")
- fun asUnionMember1(): UnionMember1 = unionMember1.getOrThrow("unionMember1")
+ fun asPercent(): BenefitContributionPercent = percent.getOrThrow("percent")
- fun asUnionMember2(): UnionMember2 = unionMember2.getOrThrow("unionMember2")
+ fun asTiered(): BenefitContributionTiered = tiered.getOrThrow("tiered")
fun _json(): Optional = Optional.ofNullable(_json)
+ /**
+ * Maps this instance's current variant to a value of type [T] using the given [visitor].
+ *
+ * Note that this method is _not_ forwards compatible with new variants from the API, unless
+ * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the
+ * SDK gracefully, consider overriding [Visitor.unknown]:
+ * ```java
+ * import com.tryfinch.api.core.JsonValue;
+ * import java.util.Optional;
+ *
+ * Optional result = benefitContribution.accept(new BenefitContribution.Visitor>() {
+ * @Override
+ * public Optional visitFixed(BenefitContributionFixed fixed) {
+ * return Optional.of(fixed.toString());
+ * }
+ *
+ * // ...
+ *
+ * @Override
+ * public Optional unknown(JsonValue json) {
+ * // Or inspect the `json`.
+ * return Optional.empty();
+ * }
+ * });
+ * ```
+ *
+ * @throws FinchInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and the
+ * current variant is unknown.
+ */
fun accept(visitor: Visitor): T =
when {
- unionMember0 != null -> visitor.visitUnionMember0(unionMember0)
- unionMember1 != null -> visitor.visitUnionMember1(unionMember1)
- unionMember2 != null -> visitor.visitUnionMember2(unionMember2)
+ fixed != null -> visitor.visitFixed(fixed)
+ percent != null -> visitor.visitPercent(percent)
+ tiered != null -> visitor.visitTiered(tiered)
else -> visitor.unknown(_json)
}
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): BenefitContribution = apply {
if (validated) {
return@apply
@@ -78,16 +115,16 @@ private constructor(
accept(
object : Visitor {
- override fun visitUnionMember0(unionMember0: UnionMember0) {
- unionMember0.validate()
+ override fun visitFixed(fixed: BenefitContributionFixed) {
+ fixed.validate()
}
- override fun visitUnionMember1(unionMember1: UnionMember1) {
- unionMember1.validate()
+ override fun visitPercent(percent: BenefitContributionPercent) {
+ percent.validate()
}
- override fun visitUnionMember2(unionMember2: UnionMember2) {
- unionMember2.validate()
+ override fun visitTiered(tiered: BenefitContributionTiered) {
+ tiered.validate()
}
}
)
@@ -111,11 +148,11 @@ private constructor(
internal fun validity(): Int =
accept(
object : Visitor {
- override fun visitUnionMember0(unionMember0: UnionMember0) = unionMember0.validity()
+ override fun visitFixed(fixed: BenefitContributionFixed) = fixed.validity()
- override fun visitUnionMember1(unionMember1: UnionMember1) = unionMember1.validity()
+ override fun visitPercent(percent: BenefitContributionPercent) = percent.validity()
- override fun visitUnionMember2(unionMember2: UnionMember2) = unionMember2.validity()
+ override fun visitTiered(tiered: BenefitContributionTiered) = tiered.validity()
override fun unknown(json: JsonValue?) = 0
}
@@ -127,35 +164,31 @@ private constructor(
}
return other is BenefitContribution &&
- unionMember0 == other.unionMember0 &&
- unionMember1 == other.unionMember1 &&
- unionMember2 == other.unionMember2
+ fixed == other.fixed &&
+ percent == other.percent &&
+ tiered == other.tiered
}
- override fun hashCode(): Int = Objects.hash(unionMember0, unionMember1, unionMember2)
+ override fun hashCode(): Int = Objects.hash(fixed, percent, tiered)
override fun toString(): String =
when {
- unionMember0 != null -> "BenefitContribution{unionMember0=$unionMember0}"
- unionMember1 != null -> "BenefitContribution{unionMember1=$unionMember1}"
- unionMember2 != null -> "BenefitContribution{unionMember2=$unionMember2}"
+ fixed != null -> "BenefitContribution{fixed=$fixed}"
+ percent != null -> "BenefitContribution{percent=$percent}"
+ tiered != null -> "BenefitContribution{tiered=$tiered}"
_json != null -> "BenefitContribution{_unknown=$_json}"
else -> throw IllegalStateException("Invalid BenefitContribution")
}
companion object {
- @JvmStatic
- fun ofUnionMember0(unionMember0: UnionMember0) =
- BenefitContribution(unionMember0 = unionMember0)
+ @JvmStatic fun ofFixed(fixed: BenefitContributionFixed) = BenefitContribution(fixed = fixed)
@JvmStatic
- fun ofUnionMember1(unionMember1: UnionMember1) =
- BenefitContribution(unionMember1 = unionMember1)
+ fun ofPercent(percent: BenefitContributionPercent) = BenefitContribution(percent = percent)
@JvmStatic
- fun ofUnionMember2(unionMember2: UnionMember2) =
- BenefitContribution(unionMember2 = unionMember2)
+ fun ofTiered(tiered: BenefitContributionTiered) = BenefitContribution(tiered = tiered)
}
/**
@@ -164,11 +197,11 @@ private constructor(
*/
interface Visitor {
- fun visitUnionMember0(unionMember0: UnionMember0): T
+ fun visitFixed(fixed: BenefitContributionFixed): T
- fun visitUnionMember1(unionMember1: UnionMember1): T
+ fun visitPercent(percent: BenefitContributionPercent): T
- fun visitUnionMember2(unionMember2: UnionMember2): T
+ fun visitTiered(tiered: BenefitContributionTiered): T
/**
* Maps an unknown variant of [BenefitContribution] to a value of type [T].
@@ -193,14 +226,14 @@ private constructor(
val bestMatches =
sequenceOf(
- tryDeserialize(node, jacksonTypeRef())?.let {
- BenefitContribution(unionMember0 = it, _json = json)
+ tryDeserialize(node, jacksonTypeRef())?.let {
+ BenefitContribution(fixed = it, _json = json)
},
- tryDeserialize(node, jacksonTypeRef())?.let {
- BenefitContribution(unionMember1 = it, _json = json)
+ tryDeserialize(node, jacksonTypeRef())?.let {
+ BenefitContribution(percent = it, _json = json)
},
- tryDeserialize(node, jacksonTypeRef())?.let {
- BenefitContribution(unionMember2 = it, _json = json)
+ tryDeserialize(node, jacksonTypeRef())?.let {
+ BenefitContribution(tiered = it, _json = json)
},
)
.filterNotNull()
@@ -226,16 +259,16 @@ private constructor(
provider: SerializerProvider,
) {
when {
- value.unionMember0 != null -> generator.writeObject(value.unionMember0)
- value.unionMember1 != null -> generator.writeObject(value.unionMember1)
- value.unionMember2 != null -> generator.writeObject(value.unionMember2)
+ value.fixed != null -> generator.writeObject(value.fixed)
+ value.percent != null -> generator.writeObject(value.percent)
+ value.tiered != null -> generator.writeObject(value.tiered)
value._json != null -> generator.writeObject(value._json)
else -> throw IllegalStateException("Invalid BenefitContribution")
}
}
}
- class UnionMember0
+ class BenefitContributionFixed
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val amount: JsonField,
@@ -294,7 +327,7 @@ private constructor(
companion object {
/**
- * Returns a mutable builder for constructing an instance of [UnionMember0].
+ * Returns a mutable builder for constructing an instance of [BenefitContributionFixed].
*
* The following fields are required:
* ```java
@@ -305,7 +338,7 @@ private constructor(
@JvmStatic fun builder() = Builder()
}
- /** A builder for [UnionMember0]. */
+ /** A builder for [BenefitContributionFixed]. */
class Builder internal constructor() {
private var amount: JsonField? = null
@@ -313,10 +346,10 @@ private constructor(
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
- internal fun from(unionMember0: UnionMember0) = apply {
- amount = unionMember0.amount
- type = unionMember0.type
- additionalProperties = unionMember0.additionalProperties.toMutableMap()
+ internal fun from(benefitContributionFixed: BenefitContributionFixed) = apply {
+ amount = benefitContributionFixed.amount
+ type = benefitContributionFixed.type
+ additionalProperties = benefitContributionFixed.additionalProperties.toMutableMap()
}
/** Contribution amount in cents. */
@@ -363,7 +396,7 @@ private constructor(
}
/**
- * Returns an immutable instance of [UnionMember0].
+ * Returns an immutable instance of [BenefitContributionFixed].
*
* Further updates to this [Builder] will not mutate the returned instance.
*
@@ -375,8 +408,8 @@ private constructor(
*
* @throws IllegalStateException if any required field is unset.
*/
- fun build(): UnionMember0 =
- UnionMember0(
+ fun build(): BenefitContributionFixed =
+ BenefitContributionFixed(
checkRequired("amount", amount),
checkRequired("type", type),
additionalProperties.toMutableMap(),
@@ -385,7 +418,16 @@ private constructor(
private var validated: Boolean = false
- fun validate(): UnionMember0 = apply {
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
+ fun validate(): BenefitContributionFixed = apply {
if (validated) {
return@apply
}
@@ -498,6 +540,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
@@ -541,7 +593,7 @@ private constructor(
return true
}
- return other is UnionMember0 &&
+ return other is BenefitContributionFixed &&
amount == other.amount &&
type == other.type &&
additionalProperties == other.additionalProperties
@@ -552,10 +604,10 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "UnionMember0{amount=$amount, type=$type, additionalProperties=$additionalProperties}"
+ "BenefitContributionFixed{amount=$amount, type=$type, additionalProperties=$additionalProperties}"
}
- class UnionMember1
+ class BenefitContributionPercent
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val amount: JsonField,
@@ -614,7 +666,8 @@ private constructor(
companion object {
/**
- * Returns a mutable builder for constructing an instance of [UnionMember1].
+ * Returns a mutable builder for constructing an instance of
+ * [BenefitContributionPercent].
*
* The following fields are required:
* ```java
@@ -625,7 +678,7 @@ private constructor(
@JvmStatic fun builder() = Builder()
}
- /** A builder for [UnionMember1]. */
+ /** A builder for [BenefitContributionPercent]. */
class Builder internal constructor() {
private var amount: JsonField? = null
@@ -633,10 +686,11 @@ private constructor(
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
- internal fun from(unionMember1: UnionMember1) = apply {
- amount = unionMember1.amount
- type = unionMember1.type
- additionalProperties = unionMember1.additionalProperties.toMutableMap()
+ internal fun from(benefitContributionPercent: BenefitContributionPercent) = apply {
+ amount = benefitContributionPercent.amount
+ type = benefitContributionPercent.type
+ additionalProperties =
+ benefitContributionPercent.additionalProperties.toMutableMap()
}
/** Contribution amount in basis points (1/100th of a percent). */
@@ -683,7 +737,7 @@ private constructor(
}
/**
- * Returns an immutable instance of [UnionMember1].
+ * Returns an immutable instance of [BenefitContributionPercent].
*
* Further updates to this [Builder] will not mutate the returned instance.
*
@@ -695,8 +749,8 @@ private constructor(
*
* @throws IllegalStateException if any required field is unset.
*/
- fun build(): UnionMember1 =
- UnionMember1(
+ fun build(): BenefitContributionPercent =
+ BenefitContributionPercent(
checkRequired("amount", amount),
checkRequired("type", type),
additionalProperties.toMutableMap(),
@@ -705,7 +759,16 @@ private constructor(
private var validated: Boolean = false
- fun validate(): UnionMember1 = apply {
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
+ fun validate(): BenefitContributionPercent = apply {
if (validated) {
return@apply
}
@@ -818,6 +881,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
@@ -861,7 +934,7 @@ private constructor(
return true
}
- return other is UnionMember1 &&
+ return other is BenefitContributionPercent &&
amount == other.amount &&
type == other.type &&
additionalProperties == other.additionalProperties
@@ -872,10 +945,10 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "UnionMember1{amount=$amount, type=$type, additionalProperties=$additionalProperties}"
+ "BenefitContributionPercent{amount=$amount, type=$type, additionalProperties=$additionalProperties}"
}
- class UnionMember2
+ class BenefitContributionTiered
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val tiers: JsonField>,
@@ -935,7 +1008,8 @@ private constructor(
companion object {
/**
- * Returns a mutable builder for constructing an instance of [UnionMember2].
+ * Returns a mutable builder for constructing an instance of
+ * [BenefitContributionTiered].
*
* The following fields are required:
* ```java
@@ -946,7 +1020,7 @@ private constructor(
@JvmStatic fun builder() = Builder()
}
- /** A builder for [UnionMember2]. */
+ /** A builder for [BenefitContributionTiered]. */
class Builder internal constructor() {
private var tiers: JsonField>? = null
@@ -954,10 +1028,10 @@ private constructor(
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
- internal fun from(unionMember2: UnionMember2) = apply {
- tiers = unionMember2.tiers.map { it.toMutableList() }
- type = unionMember2.type
- additionalProperties = unionMember2.additionalProperties.toMutableMap()
+ internal fun from(benefitContributionTiered: BenefitContributionTiered) = apply {
+ tiers = benefitContributionTiered.tiers.map { it.toMutableList() }
+ type = benefitContributionTiered.type
+ additionalProperties = benefitContributionTiered.additionalProperties.toMutableMap()
}
/**
@@ -1021,7 +1095,7 @@ private constructor(
}
/**
- * Returns an immutable instance of [UnionMember2].
+ * Returns an immutable instance of [BenefitContributionTiered].
*
* Further updates to this [Builder] will not mutate the returned instance.
*
@@ -1033,8 +1107,8 @@ private constructor(
*
* @throws IllegalStateException if any required field is unset.
*/
- fun build(): UnionMember2 =
- UnionMember2(
+ fun build(): BenefitContributionTiered =
+ BenefitContributionTiered(
checkRequired("tiers", tiers).map { it.toImmutable() },
checkRequired("type", type),
additionalProperties.toMutableMap(),
@@ -1043,7 +1117,16 @@ private constructor(
private var validated: Boolean = false
- fun validate(): UnionMember2 = apply {
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
+ fun validate(): BenefitContributionTiered = apply {
if (validated) {
return@apply
}
@@ -1224,6 +1307,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Tier = apply {
if (validated) {
return@apply
@@ -1358,6 +1451,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
@@ -1401,7 +1504,7 @@ private constructor(
return true
}
- return other is UnionMember2 &&
+ return other is BenefitContributionTiered &&
tiers == other.tiers &&
type == other.type &&
additionalProperties == other.additionalProperties
@@ -1412,6 +1515,6 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "UnionMember2{tiers=$tiers, type=$type, additionalProperties=$additionalProperties}"
+ "BenefitContributionTiered{tiers=$tiers, type=$type, additionalProperties=$additionalProperties}"
}
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFeaturesAndOperations.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFeaturesAndOperations.kt
index 665a11a54..cec233cc8 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFeaturesAndOperations.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFeaturesAndOperations.kt
@@ -164,6 +164,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): BenefitFeaturesAndOperations = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFrequency.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFrequency.kt
index e6bdb44f8..9892ac835 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFrequency.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitFrequency.kt
@@ -102,6 +102,14 @@ class BenefitFrequency @JsonCreator private constructor(private val value: JsonF
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): BenefitFrequency = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitType.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitType.kt
index e7edd4239..693069ac0 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitType.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitType.kt
@@ -195,6 +195,14 @@ class BenefitType @JsonCreator private constructor(private val value: JsonField<
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): BenefitType = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitsSupport.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitsSupport.kt
index afd9845f0..e62d66db6 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitsSupport.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/BenefitsSupport.kt
@@ -579,6 +579,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): BenefitsSupport = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/Company.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/Company.kt
index 96420583e..805544392 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/Company.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/Company.kt
@@ -513,6 +513,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): Company = apply {
if (validated) {
return@apply
@@ -873,6 +881,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Account = apply {
if (validated) {
return@apply
@@ -1002,6 +1019,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AccountType = apply {
if (validated) {
return@apply
@@ -1227,6 +1254,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Department = apply {
if (validated) {
return@apply
@@ -1376,6 +1412,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Parent = apply {
if (validated) {
return@apply
@@ -1595,6 +1641,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Entity = apply {
if (validated) {
return@apply
@@ -1723,6 +1778,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Subtype = apply {
if (validated) {
return@apply
@@ -1881,6 +1946,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyBenefit.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyBenefit.kt
index 911be02d4..4fe83d284 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyBenefit.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyBenefit.kt
@@ -299,6 +299,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): CompanyBenefit = apply {
if (validated) {
return@apply
@@ -495,6 +503,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): BenefitCompanyMatchContribution = apply {
if (validated) {
return@apply
@@ -676,6 +693,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Tier = apply {
if (validated) {
return@apply
@@ -809,6 +836,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyEvent.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyEvent.kt
index e7b74a609..35806ffc0 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyEvent.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyEvent.kt
@@ -25,6 +25,7 @@ private constructor(
private val accountId: JsonField,
private val companyId: JsonField,
private val connectionId: JsonField,
+ private val entityId: JsonField,
private val data: JsonField,
private val eventType: JsonField,
private val additionalProperties: MutableMap,
@@ -37,17 +38,19 @@ private constructor(
@JsonProperty("connection_id")
@ExcludeMissing
connectionId: JsonField = JsonMissing.of(),
+ @JsonProperty("entity_id") @ExcludeMissing entityId: JsonField = JsonMissing.of(),
@JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(),
@JsonProperty("event_type")
@ExcludeMissing
eventType: JsonField = JsonMissing.of(),
- ) : this(accountId, companyId, connectionId, data, eventType, mutableMapOf())
+ ) : this(accountId, companyId, connectionId, entityId, data, eventType, mutableMapOf())
fun toBaseWebhookEvent(): BaseWebhookEvent =
BaseWebhookEvent.builder()
.accountId(accountId)
.companyId(companyId)
.connectionId(connectionId)
+ .entityId(entityId)
.build()
/**
@@ -76,6 +79,14 @@ private constructor(
*/
fun connectionId(): Optional = connectionId.getOptional("connection_id")
+ /**
+ * Unique Finch id of the entity for which data has been updated.
+ *
+ * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun entityId(): Optional = entityId.getOptional("entity_id")
+
/**
* @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
@@ -117,6 +128,13 @@ private constructor(
@ExcludeMissing
fun _connectionId(): JsonField = connectionId
+ /**
+ * Returns the raw JSON value of [entityId].
+ *
+ * Unlike [entityId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("entity_id") @ExcludeMissing fun _entityId(): JsonField = entityId
+
/**
* Returns the raw JSON value of [data].
*
@@ -163,6 +181,7 @@ private constructor(
private var accountId: JsonField? = null
private var companyId: JsonField? = null
private var connectionId: JsonField = JsonMissing.of()
+ private var entityId: JsonField = JsonMissing.of()
private var data: JsonField = JsonMissing.of()
private var eventType: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -172,6 +191,7 @@ private constructor(
accountId = companyEvent.accountId
companyId = companyEvent.companyId
connectionId = companyEvent.connectionId
+ entityId = companyEvent.entityId
data = companyEvent.data
eventType = companyEvent.eventType
additionalProperties = companyEvent.additionalProperties.toMutableMap()
@@ -225,6 +245,17 @@ private constructor(
this.connectionId = connectionId
}
+ /** Unique Finch id of the entity for which data has been updated. */
+ fun entityId(entityId: String) = entityId(JsonField.of(entityId))
+
+ /**
+ * Sets [Builder.entityId] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.entityId] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun entityId(entityId: JsonField) = apply { this.entityId = entityId }
+
fun data(data: Data?) = data(JsonField.ofNullable(data))
/** Alias for calling [Builder.data] with `data.orElse(null)`. */
@@ -286,6 +317,7 @@ private constructor(
checkRequired("accountId", accountId),
checkRequired("companyId", companyId),
connectionId,
+ entityId,
data,
eventType,
additionalProperties.toMutableMap(),
@@ -294,6 +326,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): CompanyEvent = apply {
if (validated) {
return@apply
@@ -302,6 +342,7 @@ private constructor(
accountId()
companyId()
connectionId()
+ entityId()
data().ifPresent { it.validate() }
eventType().ifPresent { it.validate() }
validated = true
@@ -325,6 +366,7 @@ private constructor(
(if (accountId.asKnown().isPresent) 1 else 0) +
(if (companyId.asKnown().isPresent) 1 else 0) +
(if (connectionId.asKnown().isPresent) 1 else 0) +
+ (if (entityId.asKnown().isPresent) 1 else 0) +
(data.asKnown().getOrNull()?.validity() ?: 0) +
(eventType.asKnown().getOrNull()?.validity() ?: 0)
@@ -386,6 +428,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Data = apply {
if (validated) {
return@apply
@@ -509,6 +560,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): EventType = apply {
if (validated) {
return@apply
@@ -556,17 +616,26 @@ private constructor(
accountId == other.accountId &&
companyId == other.companyId &&
connectionId == other.connectionId &&
+ entityId == other.entityId &&
data == other.data &&
eventType == other.eventType &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(accountId, companyId, connectionId, data, eventType, additionalProperties)
+ Objects.hash(
+ accountId,
+ companyId,
+ connectionId,
+ entityId,
+ data,
+ eventType,
+ additionalProperties,
+ )
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "CompanyEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, data=$data, eventType=$eventType, additionalProperties=$additionalProperties}"
+ "CompanyEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, entityId=$entityId, data=$data, eventType=$eventType, additionalProperties=$additionalProperties}"
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyUpdateResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyUpdateResponse.kt
index 96ed910cf..8cc68b505 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyUpdateResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CompanyUpdateResponse.kt
@@ -479,6 +479,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): CompanyUpdateResponse = apply {
if (validated) {
return@apply
@@ -815,6 +823,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Account = apply {
if (validated) {
return@apply
@@ -944,6 +961,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AccountType = apply {
if (validated) {
return@apply
@@ -1148,6 +1175,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Department = apply {
if (validated) {
return@apply
@@ -1282,6 +1318,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Parent = apply {
if (validated) {
return@apply
@@ -1480,6 +1526,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Entity = apply {
if (validated) {
return@apply
@@ -1608,6 +1663,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Subtype = apply {
if (validated) {
return@apply
@@ -1766,6 +1831,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionNewParams.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionNewParams.kt
index 3522aae4b..9aba999db 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionNewParams.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionNewParams.kt
@@ -48,7 +48,9 @@ private constructor(
fun customerName(): String = body.customerName()
/**
- * The Finch products to request access to
+ * The Finch products to request access to. Use `benefits` to access deductions endpoints —
+ * `deduction` is a deprecated alias that is still accepted but should not be combined with
+ * `benefits`.
*
* @throws FinchInvalidDataException if the JSON field has an unexpected type or is unexpectedly
* missing or null (e.g. if the server responded with an unexpected value).
@@ -246,7 +248,11 @@ private constructor(
body.customerName(customerName)
}
- /** The Finch products to request access to */
+ /**
+ * The Finch products to request access to. Use `benefits` to access deductions endpoints —
+ * `deduction` is a deprecated alias that is still accepted but should not be combined with
+ * `benefits`.
+ */
fun products(products: List) = apply { body.products(products) }
/**
@@ -594,7 +600,9 @@ private constructor(
fun customerName(): String = customerName.getRequired("customer_name")
/**
- * The Finch products to request access to
+ * The Finch products to request access to. Use `benefits` to access deductions endpoints —
+ * `deduction` is a deprecated alias that is still accepted but should not be combined with
+ * `benefits`.
*
* @throws FinchInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
@@ -812,7 +820,11 @@ private constructor(
this.customerName = customerName
}
- /** The Finch products to request access to */
+ /**
+ * The Finch products to request access to. Use `benefits` to access deductions
+ * endpoints — `deduction` is a deprecated alias that is still accepted but should not
+ * be combined with `benefits`.
+ */
fun products(products: List) = products(JsonField.of(products))
/**
@@ -1010,6 +1022,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): CreateConnectSessionRequest = apply {
if (validated) {
return@apply
@@ -1092,7 +1113,6 @@ private constructor(
"CreateConnectSessionRequest{customerId=$customerId, customerName=$customerName, products=$products, customerEmail=$customerEmail, integration=$integration, manual=$manual, minutesToExpire=$minutesToExpire, redirectUri=$redirectUri, sandbox=$sandbox, additionalProperties=$additionalProperties}"
}
- /** The Finch products that can be requested during the Connect flow. */
class ConnectProducts @JsonCreator private constructor(private val value: JsonField) :
Enum {
@@ -1231,6 +1251,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ConnectProducts = apply {
if (validated) {
return@apply
@@ -1429,6 +1458,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Integration = apply {
if (validated) {
return@apply
@@ -1564,6 +1602,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AuthMethod = apply {
if (validated) {
return@apply
@@ -1710,6 +1758,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Sandbox = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionReauthenticateParams.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionReauthenticateParams.kt
index 78984af0d..1dbaad120 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionReauthenticateParams.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectSessionReauthenticateParams.kt
@@ -48,7 +48,9 @@ private constructor(
fun minutesToExpire(): Optional = body.minutesToExpire()
/**
- * The products to request access to (optional for reauthentication)
+ * The products to request access to (optional for reauthentication). Use `benefits` to access
+ * deductions endpoints — `deduction` is a deprecated alias that is still accepted but should
+ * not be combined with `benefits`.
*
* @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
@@ -173,7 +175,11 @@ private constructor(
body.minutesToExpire(minutesToExpire)
}
- /** The products to request access to (optional for reauthentication) */
+ /**
+ * The products to request access to (optional for reauthentication). Use `benefits` to
+ * access deductions endpoints — `deduction` is a deprecated alias that is still accepted
+ * but should not be combined with `benefits`.
+ */
fun products(products: List?) = apply { body.products(products) }
/** Alias for calling [Builder.products] with `products.orElse(null)`. */
@@ -396,7 +402,9 @@ private constructor(
fun minutesToExpire(): Optional = minutesToExpire.getOptional("minutes_to_expire")
/**
- * The products to request access to (optional for reauthentication)
+ * The products to request access to (optional for reauthentication). Use `benefits` to
+ * access deductions endpoints — `deduction` is a deprecated alias that is still accepted
+ * but should not be combined with `benefits`.
*
* @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
@@ -524,7 +532,11 @@ private constructor(
this.minutesToExpire = minutesToExpire
}
- /** The products to request access to (optional for reauthentication) */
+ /**
+ * The products to request access to (optional for reauthentication). Use `benefits` to
+ * access deductions endpoints — `deduction` is a deprecated alias that is still
+ * accepted but should not be combined with `benefits`.
+ */
fun products(products: List?) =
products(JsonField.ofNullable(products))
@@ -614,6 +626,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ReauthenticateRequest = apply {
if (validated) {
return@apply
@@ -670,7 +691,6 @@ private constructor(
"ReauthenticateRequest{connectionId=$connectionId, minutesToExpire=$minutesToExpire, products=$products, redirectUri=$redirectUri, additionalProperties=$additionalProperties}"
}
- /** The Finch products that can be requested during the Connect flow. */
class ConnectProducts @JsonCreator private constructor(private val value: JsonField) :
Enum {
@@ -809,6 +829,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ConnectProducts = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionCreateResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionCreateResponse.kt
index 6845fb4dc..2b1f38d26 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionCreateResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionCreateResponse.kt
@@ -451,6 +451,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): ConnectionCreateResponse = apply {
if (validated) {
return@apply
@@ -596,6 +604,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): AuthenticationType = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionStatusType.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionStatusType.kt
index d6b4b286d..b87bf4727 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionStatusType.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/ConnectionStatusType.kt
@@ -120,6 +120,14 @@ class ConnectionStatusType @JsonCreator private constructor(private val value: J
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): ConnectionStatusType = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateAccessTokenResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateAccessTokenResponse.kt
index 64a9d8b98..f96a1d983 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateAccessTokenResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateAccessTokenResponse.kt
@@ -34,6 +34,7 @@ private constructor(
private val accountId: JsonField,
private val companyId: JsonField,
private val customerId: JsonField,
+ private val customerName: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -66,6 +67,9 @@ private constructor(
@JsonProperty("customer_id")
@ExcludeMissing
customerId: JsonField = JsonMissing.of(),
+ @JsonProperty("customer_name")
+ @ExcludeMissing
+ customerName: JsonField = JsonMissing.of(),
) : this(
accessToken,
clientType,
@@ -78,6 +82,7 @@ private constructor(
accountId,
companyId,
customerId,
+ customerName,
mutableMapOf(),
)
@@ -174,6 +179,15 @@ private constructor(
*/
fun customerId(): Optional = customerId.getOptional("customer_id")
+ /**
+ * The name of your customer you provided to Finch when a connect session was created for this
+ * connection
+ *
+ * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun customerName(): Optional = customerName.getOptional("customer_name")
+
/**
* Returns the raw JSON value of [accessToken].
*
@@ -267,6 +281,15 @@ private constructor(
*/
@JsonProperty("customer_id") @ExcludeMissing fun _customerId(): JsonField = customerId
+ /**
+ * Returns the raw JSON value of [customerName].
+ *
+ * Unlike [customerName], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("customer_name")
+ @ExcludeMissing
+ fun _customerName(): JsonField = customerName
+
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -313,6 +336,7 @@ private constructor(
private var accountId: JsonField = JsonMissing.of()
private var companyId: JsonField = JsonMissing.of()
private var customerId: JsonField = JsonMissing.of()
+ private var customerName: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
@@ -328,6 +352,7 @@ private constructor(
accountId = createAccessTokenResponse.accountId
companyId = createAccessTokenResponse.companyId
customerId = createAccessTokenResponse.customerId
+ customerName = createAccessTokenResponse.customerName
additionalProperties = createAccessTokenResponse.additionalProperties.toMutableMap()
}
@@ -514,6 +539,26 @@ private constructor(
*/
fun customerId(customerId: JsonField) = apply { this.customerId = customerId }
+ /**
+ * The name of your customer you provided to Finch when a connect session was created for
+ * this connection
+ */
+ fun customerName(customerName: String?) = customerName(JsonField.ofNullable(customerName))
+
+ /** Alias for calling [Builder.customerName] with `customerName.orElse(null)`. */
+ fun customerName(customerName: Optional) = customerName(customerName.getOrNull())
+
+ /**
+ * Sets [Builder.customerName] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.customerName] with a well-typed [String] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun customerName(customerName: JsonField) = apply {
+ this.customerName = customerName
+ }
+
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -565,12 +610,21 @@ private constructor(
accountId,
companyId,
customerId,
+ customerName,
additionalProperties.toMutableMap(),
)
}
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): CreateAccessTokenResponse = apply {
if (validated) {
return@apply
@@ -587,6 +641,7 @@ private constructor(
accountId()
companyId()
customerId()
+ customerName()
validated = true
}
@@ -615,7 +670,8 @@ private constructor(
(if (tokenType.asKnown().isPresent) 1 else 0) +
(if (accountId.asKnown().isPresent) 1 else 0) +
(if (companyId.asKnown().isPresent) 1 else 0) +
- (if (customerId.asKnown().isPresent) 1 else 0)
+ (if (customerId.asKnown().isPresent) 1 else 0) +
+ (if (customerName.asKnown().isPresent) 1 else 0)
/** The type of application associated with a token. */
class ClientType @JsonCreator private constructor(private val value: JsonField) : Enum {
@@ -712,6 +768,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ClientType = apply {
if (validated) {
return@apply
@@ -845,6 +910,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): ConnectionType = apply {
if (validated) {
return@apply
@@ -900,6 +974,7 @@ private constructor(
accountId == other.accountId &&
companyId == other.companyId &&
customerId == other.customerId &&
+ customerName == other.customerName &&
additionalProperties == other.additionalProperties
}
@@ -916,6 +991,7 @@ private constructor(
accountId,
companyId,
customerId,
+ customerName,
additionalProperties,
)
}
@@ -923,5 +999,5 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "CreateAccessTokenResponse{accessToken=$accessToken, clientType=$clientType, connectionId=$connectionId, connectionType=$connectionType, entityIds=$entityIds, products=$products, providerId=$providerId, tokenType=$tokenType, accountId=$accountId, companyId=$companyId, customerId=$customerId, additionalProperties=$additionalProperties}"
+ "CreateAccessTokenResponse{accessToken=$accessToken, clientType=$clientType, connectionId=$connectionId, connectionType=$connectionType, entityIds=$entityIds, products=$products, providerId=$providerId, tokenType=$tokenType, accountId=$accountId, companyId=$companyId, customerId=$customerId, customerName=$customerName, additionalProperties=$additionalProperties}"
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateCompanyBenefitsResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateCompanyBenefitsResponse.kt
index 5e4f0d487..b0620064e 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateCompanyBenefitsResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/CreateCompanyBenefitsResponse.kt
@@ -162,6 +162,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): CreateCompanyBenefitsResponse = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryCreateResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryCreateResponse.kt
index f5c0cf3c9..1eb23ce77 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryCreateResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryCreateResponse.kt
@@ -75,6 +75,14 @@ private constructor(private val additionalProperties: MutableMap,
private val companyId: JsonField,
private val connectionId: JsonField,
+ private val entityId: JsonField,
private val data: JsonField,
private val eventType: JsonField,
private val additionalProperties: MutableMap,
@@ -36,17 +37,19 @@ private constructor(
@JsonProperty("connection_id")
@ExcludeMissing
connectionId: JsonField = JsonMissing.of(),
+ @JsonProperty("entity_id") @ExcludeMissing entityId: JsonField = JsonMissing.of(),
@JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(),
@JsonProperty("event_type")
@ExcludeMissing
eventType: JsonField = JsonMissing.of(),
- ) : this(accountId, companyId, connectionId, data, eventType, mutableMapOf())
+ ) : this(accountId, companyId, connectionId, entityId, data, eventType, mutableMapOf())
fun toBaseWebhookEvent(): BaseWebhookEvent =
BaseWebhookEvent.builder()
.accountId(accountId)
.companyId(companyId)
.connectionId(connectionId)
+ .entityId(entityId)
.build()
/**
@@ -75,6 +78,14 @@ private constructor(
*/
fun connectionId(): Optional = connectionId.getOptional("connection_id")
+ /**
+ * Unique Finch id of the entity for which data has been updated.
+ *
+ * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun entityId(): Optional = entityId.getOptional("entity_id")
+
/**
* @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
@@ -116,6 +127,13 @@ private constructor(
@ExcludeMissing
fun _connectionId(): JsonField = connectionId
+ /**
+ * Returns the raw JSON value of [entityId].
+ *
+ * Unlike [entityId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("entity_id") @ExcludeMissing fun _entityId(): JsonField = entityId
+
/**
* Returns the raw JSON value of [data].
*
@@ -162,6 +180,7 @@ private constructor(
private var accountId: JsonField? = null
private var companyId: JsonField? = null
private var connectionId: JsonField = JsonMissing.of()
+ private var entityId: JsonField = JsonMissing.of()
private var data: JsonField = JsonMissing.of()
private var eventType: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -171,6 +190,7 @@ private constructor(
accountId = directoryEvent.accountId
companyId = directoryEvent.companyId
connectionId = directoryEvent.connectionId
+ entityId = directoryEvent.entityId
data = directoryEvent.data
eventType = directoryEvent.eventType
additionalProperties = directoryEvent.additionalProperties.toMutableMap()
@@ -224,6 +244,17 @@ private constructor(
this.connectionId = connectionId
}
+ /** Unique Finch id of the entity for which data has been updated. */
+ fun entityId(entityId: String) = entityId(JsonField.of(entityId))
+
+ /**
+ * Sets [Builder.entityId] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.entityId] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun entityId(entityId: JsonField) = apply { this.entityId = entityId }
+
fun data(data: Data) = data(JsonField.of(data))
/**
@@ -282,6 +313,7 @@ private constructor(
checkRequired("accountId", accountId),
checkRequired("companyId", companyId),
connectionId,
+ entityId,
data,
eventType,
additionalProperties.toMutableMap(),
@@ -290,6 +322,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): DirectoryEvent = apply {
if (validated) {
return@apply
@@ -298,6 +338,7 @@ private constructor(
accountId()
companyId()
connectionId()
+ entityId()
data().ifPresent { it.validate() }
eventType().ifPresent { it.validate() }
validated = true
@@ -321,6 +362,7 @@ private constructor(
(if (accountId.asKnown().isPresent) 1 else 0) +
(if (companyId.asKnown().isPresent) 1 else 0) +
(if (connectionId.asKnown().isPresent) 1 else 0) +
+ (if (entityId.asKnown().isPresent) 1 else 0) +
(data.asKnown().getOrNull()?.validity() ?: 0) +
(eventType.asKnown().getOrNull()?.validity() ?: 0)
@@ -429,6 +471,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Data = apply {
if (validated) {
return@apply
@@ -567,6 +618,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): EventType = apply {
if (validated) {
return@apply
@@ -614,17 +674,26 @@ private constructor(
accountId == other.accountId &&
companyId == other.companyId &&
connectionId == other.connectionId &&
+ entityId == other.entityId &&
data == other.data &&
eventType == other.eventType &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(accountId, companyId, connectionId, data, eventType, additionalProperties)
+ Objects.hash(
+ accountId,
+ companyId,
+ connectionId,
+ entityId,
+ data,
+ eventType,
+ additionalProperties,
+ )
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "DirectoryEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, data=$data, eventType=$eventType, additionalProperties=$additionalProperties}"
+ "DirectoryEvent{accountId=$accountId, companyId=$companyId, connectionId=$connectionId, entityId=$entityId, data=$data, eventType=$eventType, additionalProperties=$additionalProperties}"
}
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryListIndividualsResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryListIndividualsResponse.kt
index 123a2cb5f..806d599f1 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryListIndividualsResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DirectoryListIndividualsResponse.kt
@@ -187,6 +187,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): DirectoryListIndividualsResponse = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DisconnectResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DisconnectResponse.kt
index 4115c896e..06391e0f8 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DisconnectResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DisconnectResponse.kt
@@ -127,6 +127,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): DisconnectResponse = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentListResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentListResponse.kt
index a4d67b183..13bcb80ac 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentListResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentListResponse.kt
@@ -179,6 +179,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): DocumentListResponse = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentResponse.kt
index 7e7025608..a84246dc0 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentResponse.kt
@@ -283,6 +283,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): DocumentResponse = apply {
if (validated) {
return@apply
@@ -404,6 +412,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentRetreiveResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentRetreiveResponse.kt
index 59bee092a..1834a1d1d 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentRetreiveResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/DocumentRetreiveResponse.kt
@@ -61,6 +61,35 @@ private constructor(
fun _json(): Optional = Optional.ofNullable(_json)
+ /**
+ * Maps this instance's current variant to a value of type [T] using the given [visitor].
+ *
+ * Note that this method is _not_ forwards compatible with new variants from the API, unless
+ * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the
+ * SDK gracefully, consider overriding [Visitor.unknown]:
+ * ```java
+ * import com.tryfinch.api.core.JsonValue;
+ * import java.util.Optional;
+ *
+ * Optional result = documentRetreiveResponse.accept(new DocumentRetreiveResponse.Visitor>() {
+ * @Override
+ * public Optional visitW42020(W42020 w42020) {
+ * return Optional.of(w42020.toString());
+ * }
+ *
+ * // ...
+ *
+ * @Override
+ * public Optional unknown(JsonValue json) {
+ * // Or inspect the `json`.
+ * return Optional.empty();
+ * }
+ * });
+ * ```
+ *
+ * @throws FinchInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and the
+ * current variant is unknown.
+ */
fun accept(visitor: Visitor): T =
when {
w42020 != null -> visitor.visitW42020(w42020)
@@ -70,6 +99,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): DocumentRetreiveResponse = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentData.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentData.kt
index 2156ba87d..0e62683fe 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentData.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentData.kt
@@ -35,34 +35,71 @@ import kotlin.jvm.optionals.getOrNull
@JsonSerialize(using = EmploymentData.Serializer::class)
class EmploymentData
private constructor(
- private val unionMember0: UnionMember0? = null,
+ private val employmentData: InnerEmploymentData? = null,
private val batchError: BatchError? = null,
private val _json: JsonValue? = null,
) {
- fun unionMember0(): Optional = Optional.ofNullable(unionMember0)
+ fun employmentData(): Optional = Optional.ofNullable(employmentData)
fun batchError(): Optional = Optional.ofNullable(batchError)
- fun isUnionMember0(): Boolean = unionMember0 != null
+ fun isEmploymentData(): Boolean = employmentData != null
fun isBatchError(): Boolean = batchError != null
- fun asUnionMember0(): UnionMember0 = unionMember0.getOrThrow("unionMember0")
+ fun asEmploymentData(): InnerEmploymentData = employmentData.getOrThrow("employmentData")
fun asBatchError(): BatchError = batchError.getOrThrow("batchError")
fun _json(): Optional = Optional.ofNullable(_json)
+ /**
+ * Maps this instance's current variant to a value of type [T] using the given [visitor].
+ *
+ * Note that this method is _not_ forwards compatible with new variants from the API, unless
+ * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the
+ * SDK gracefully, consider overriding [Visitor.unknown]:
+ * ```java
+ * import com.tryfinch.api.core.JsonValue;
+ * import java.util.Optional;
+ *
+ * Optional result = employmentData.accept(new EmploymentData.Visitor>() {
+ * @Override
+ * public Optional visitEmploymentData(InnerEmploymentData employmentData) {
+ * return Optional.of(employmentData.toString());
+ * }
+ *
+ * // ...
+ *
+ * @Override
+ * public Optional unknown(JsonValue json) {
+ * // Or inspect the `json`.
+ * return Optional.empty();
+ * }
+ * });
+ * ```
+ *
+ * @throws FinchInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and the
+ * current variant is unknown.
+ */
fun accept(visitor: Visitor): T =
when {
- unionMember0 != null -> visitor.visitUnionMember0(unionMember0)
+ employmentData != null -> visitor.visitEmploymentData(employmentData)
batchError != null -> visitor.visitBatchError(batchError)
else -> visitor.unknown(_json)
}
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): EmploymentData = apply {
if (validated) {
return@apply
@@ -70,8 +107,8 @@ private constructor(
accept(
object : Visitor {
- override fun visitUnionMember0(unionMember0: UnionMember0) {
- unionMember0.validate()
+ override fun visitEmploymentData(employmentData: InnerEmploymentData) {
+ employmentData.validate()
}
override fun visitBatchError(batchError: BatchError) {
@@ -99,7 +136,8 @@ private constructor(
internal fun validity(): Int =
accept(
object : Visitor {
- override fun visitUnionMember0(unionMember0: UnionMember0) = unionMember0.validity()
+ override fun visitEmploymentData(employmentData: InnerEmploymentData) =
+ employmentData.validity()
override fun visitBatchError(batchError: BatchError) = batchError.validity()
@@ -113,15 +151,15 @@ private constructor(
}
return other is EmploymentData &&
- unionMember0 == other.unionMember0 &&
+ employmentData == other.employmentData &&
batchError == other.batchError
}
- override fun hashCode(): Int = Objects.hash(unionMember0, batchError)
+ override fun hashCode(): Int = Objects.hash(employmentData, batchError)
override fun toString(): String =
when {
- unionMember0 != null -> "EmploymentData{unionMember0=$unionMember0}"
+ employmentData != null -> "EmploymentData{employmentData=$employmentData}"
batchError != null -> "EmploymentData{batchError=$batchError}"
_json != null -> "EmploymentData{_unknown=$_json}"
else -> throw IllegalStateException("Invalid EmploymentData")
@@ -130,7 +168,8 @@ private constructor(
companion object {
@JvmStatic
- fun ofUnionMember0(unionMember0: UnionMember0) = EmploymentData(unionMember0 = unionMember0)
+ fun ofEmploymentData(employmentData: InnerEmploymentData) =
+ EmploymentData(employmentData = employmentData)
@JvmStatic
fun ofBatchError(batchError: BatchError) = EmploymentData(batchError = batchError)
@@ -141,7 +180,7 @@ private constructor(
*/
interface Visitor {
- fun visitUnionMember0(unionMember0: UnionMember0): T
+ fun visitEmploymentData(employmentData: InnerEmploymentData): T
fun visitBatchError(batchError: BatchError): T
@@ -167,8 +206,8 @@ private constructor(
val bestMatches =
sequenceOf(
- tryDeserialize(node, jacksonTypeRef())?.let {
- EmploymentData(unionMember0 = it, _json = json)
+ tryDeserialize(node, jacksonTypeRef())?.let {
+ EmploymentData(employmentData = it, _json = json)
},
tryDeserialize(node, jacksonTypeRef())?.let {
EmploymentData(batchError = it, _json = json)
@@ -197,7 +236,7 @@ private constructor(
provider: SerializerProvider,
) {
when {
- value.unionMember0 != null -> generator.writeObject(value.unionMember0)
+ value.employmentData != null -> generator.writeObject(value.employmentData)
value.batchError != null -> generator.writeObject(value.batchError)
value._json != null -> generator.writeObject(value._json)
else -> throw IllegalStateException("Invalid EmploymentData")
@@ -205,7 +244,7 @@ private constructor(
}
}
- class UnionMember0
+ class InnerEmploymentData
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val id: JsonField,
@@ -215,6 +254,7 @@ private constructor(
private val employmentStatus: JsonField,
private val endDate: JsonField,
private val firstName: JsonField,
+ private val flsaStatus: JsonField,
private val isActive: JsonField,
private val lastName: JsonField,
private val latestRehireDate: JsonField,
@@ -250,6 +290,9 @@ private constructor(
@JsonProperty("first_name")
@ExcludeMissing
firstName: JsonField = JsonMissing.of(),
+ @JsonProperty("flsa_status")
+ @ExcludeMissing
+ flsaStatus: JsonField = JsonMissing.of(),
@JsonProperty("is_active")
@ExcludeMissing
isActive: JsonField = JsonMissing.of(),
@@ -289,6 +332,7 @@ private constructor(
employmentStatus,
endDate,
firstName,
+ flsaStatus,
isActive,
lastName,
latestRehireDate,
@@ -360,6 +404,14 @@ private constructor(
*/
fun firstName(): Optional = firstName.getOptional("first_name")
+ /**
+ * The FLSA status of the individual. Available options: `exempt`, `non_exempt`, `unknown`.
+ *
+ * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun flsaStatus(): Optional = flsaStatus.getOptional("flsa_status")
+
/**
* `true` if the individual an an active employee or contractor at the company.
*
@@ -518,6 +570,15 @@ private constructor(
*/
@JsonProperty("first_name") @ExcludeMissing fun _firstName(): JsonField = firstName
+ /**
+ * Returns the raw JSON value of [flsaStatus].
+ *
+ * Unlike [flsaStatus], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("flsa_status")
+ @ExcludeMissing
+ fun _flsaStatus(): JsonField = flsaStatus
+
/**
* Returns the raw JSON value of [isActive].
*
@@ -638,7 +699,7 @@ private constructor(
companion object {
/**
- * Returns a mutable builder for constructing an instance of [UnionMember0].
+ * Returns a mutable builder for constructing an instance of [InnerEmploymentData].
*
* The following fields are required:
* ```java
@@ -649,6 +710,7 @@ private constructor(
* .employmentStatus()
* .endDate()
* .firstName()
+ * .flsaStatus()
* .isActive()
* .lastName()
* .latestRehireDate()
@@ -662,7 +724,7 @@ private constructor(
@JvmStatic fun builder() = Builder()
}
- /** A builder for [UnionMember0]. */
+ /** A builder for [InnerEmploymentData]. */
class Builder internal constructor() {
private var id: JsonField? = null
@@ -672,6 +734,7 @@ private constructor(
private var employmentStatus: JsonField? = null
private var endDate: JsonField? = null
private var firstName: JsonField? = null
+ private var flsaStatus: JsonField? = null
private var isActive: JsonField? = null
private var lastName: JsonField? = null
private var latestRehireDate: JsonField? = null
@@ -688,28 +751,29 @@ private constructor(
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
- internal fun from(unionMember0: UnionMember0) = apply {
- id = unionMember0.id
- classCode = unionMember0.classCode
- department = unionMember0.department
- employment = unionMember0.employment
- employmentStatus = unionMember0.employmentStatus
- endDate = unionMember0.endDate
- firstName = unionMember0.firstName
- isActive = unionMember0.isActive
- lastName = unionMember0.lastName
- latestRehireDate = unionMember0.latestRehireDate
- location = unionMember0.location
- manager = unionMember0.manager
- middleName = unionMember0.middleName
- startDate = unionMember0.startDate
- title = unionMember0.title
- customFields = unionMember0.customFields.map { it.toMutableList() }
- income = unionMember0.income
- incomeHistory = unionMember0.incomeHistory.map { it.toMutableList() }
- sourceId = unionMember0.sourceId
- workId = unionMember0.workId
- additionalProperties = unionMember0.additionalProperties.toMutableMap()
+ internal fun from(innerEmploymentData: InnerEmploymentData) = apply {
+ id = innerEmploymentData.id
+ classCode = innerEmploymentData.classCode
+ department = innerEmploymentData.department
+ employment = innerEmploymentData.employment
+ employmentStatus = innerEmploymentData.employmentStatus
+ endDate = innerEmploymentData.endDate
+ firstName = innerEmploymentData.firstName
+ flsaStatus = innerEmploymentData.flsaStatus
+ isActive = innerEmploymentData.isActive
+ lastName = innerEmploymentData.lastName
+ latestRehireDate = innerEmploymentData.latestRehireDate
+ location = innerEmploymentData.location
+ manager = innerEmploymentData.manager
+ middleName = innerEmploymentData.middleName
+ startDate = innerEmploymentData.startDate
+ title = innerEmploymentData.title
+ customFields = innerEmploymentData.customFields.map { it.toMutableList() }
+ income = innerEmploymentData.income
+ incomeHistory = innerEmploymentData.incomeHistory.map { it.toMutableList() }
+ sourceId = innerEmploymentData.sourceId
+ workId = innerEmploymentData.workId
+ additionalProperties = innerEmploymentData.additionalProperties.toMutableMap()
}
/** A stable Finch `id` (UUID v4) for an individual in the company. */
@@ -823,6 +887,26 @@ private constructor(
*/
fun firstName(firstName: JsonField) = apply { this.firstName = firstName }
+ /**
+ * The FLSA status of the individual. Available options: `exempt`, `non_exempt`,
+ * `unknown`.
+ */
+ fun flsaStatus(flsaStatus: FlsaStatus?) = flsaStatus(JsonField.ofNullable(flsaStatus))
+
+ /** Alias for calling [Builder.flsaStatus] with `flsaStatus.orElse(null)`. */
+ fun flsaStatus(flsaStatus: Optional) = flsaStatus(flsaStatus.getOrNull())
+
+ /**
+ * Sets [Builder.flsaStatus] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.flsaStatus] with a well-typed [FlsaStatus] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun flsaStatus(flsaStatus: JsonField) = apply {
+ this.flsaStatus = flsaStatus
+ }
+
/** `true` if the individual an an active employee or contractor at the company. */
fun isActive(isActive: Boolean?) = isActive(JsonField.ofNullable(isActive))
@@ -1090,7 +1174,7 @@ private constructor(
}
/**
- * Returns an immutable instance of [UnionMember0].
+ * Returns an immutable instance of [InnerEmploymentData].
*
* Further updates to this [Builder] will not mutate the returned instance.
*
@@ -1103,6 +1187,7 @@ private constructor(
* .employmentStatus()
* .endDate()
* .firstName()
+ * .flsaStatus()
* .isActive()
* .lastName()
* .latestRehireDate()
@@ -1115,8 +1200,8 @@ private constructor(
*
* @throws IllegalStateException if any required field is unset.
*/
- fun build(): UnionMember0 =
- UnionMember0(
+ fun build(): InnerEmploymentData =
+ InnerEmploymentData(
checkRequired("id", id),
checkRequired("classCode", classCode),
checkRequired("department", department),
@@ -1124,6 +1209,7 @@ private constructor(
checkRequired("employmentStatus", employmentStatus),
checkRequired("endDate", endDate),
checkRequired("firstName", firstName),
+ checkRequired("flsaStatus", flsaStatus),
checkRequired("isActive", isActive),
checkRequired("lastName", lastName),
checkRequired("latestRehireDate", latestRehireDate),
@@ -1143,7 +1229,16 @@ private constructor(
private var validated: Boolean = false
- fun validate(): UnionMember0 = apply {
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
+ fun validate(): InnerEmploymentData = apply {
if (validated) {
return@apply
}
@@ -1155,6 +1250,7 @@ private constructor(
employmentStatus().ifPresent { it.validate() }
endDate()
firstName()
+ flsaStatus().ifPresent { it.validate() }
isActive()
lastName()
latestRehireDate()
@@ -1194,6 +1290,7 @@ private constructor(
(employmentStatus.asKnown().getOrNull()?.validity() ?: 0) +
(if (endDate.asKnown().isPresent) 1 else 0) +
(if (firstName.asKnown().isPresent) 1 else 0) +
+ (flsaStatus.asKnown().getOrNull()?.validity() ?: 0) +
(if (isActive.asKnown().isPresent) 1 else 0) +
(if (lastName.asKnown().isPresent) 1 else 0) +
(if (latestRehireDate.asKnown().isPresent) 1 else 0) +
@@ -1329,6 +1426,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Department = apply {
if (validated) {
return@apply
@@ -1538,6 +1645,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Employment = apply {
if (validated) {
return@apply
@@ -1688,6 +1805,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Subtype = apply {
if (validated) {
return@apply
@@ -1819,6 +1946,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Type = apply {
if (validated) {
return@apply
@@ -2002,6 +2139,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): EmploymentStatus = apply {
if (validated) {
return@apply
@@ -2040,6 +2187,156 @@ private constructor(
override fun toString() = value.toString()
}
+ /**
+ * The FLSA status of the individual. Available options: `exempt`, `non_exempt`, `unknown`.
+ */
+ class FlsaStatus @JsonCreator private constructor(private val value: JsonField) :
+ Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is
+ * on an older version than the API, then the API may respond with new members that the
+ * SDK is unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val EXEMPT = of("exempt")
+
+ @JvmField val NON_EXEMPT = of("non_exempt")
+
+ @JvmField val UNKNOWN = of("unknown")
+
+ @JvmStatic fun of(value: String) = FlsaStatus(JsonField.of(value))
+ }
+
+ /** An enum containing [FlsaStatus]'s known values. */
+ enum class Known {
+ EXEMPT,
+ NON_EXEMPT,
+ UNKNOWN,
+ }
+
+ /**
+ * An enum containing [FlsaStatus]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [FlsaStatus] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if
+ * the SDK is on an older version than the API, then the API may respond with new
+ * members that the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ EXEMPT,
+ NON_EXEMPT,
+ UNKNOWN,
+ /**
+ * An enum member indicating that [FlsaStatus] was instantiated with an unknown
+ * value.
+ */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or
+ * [Value._UNKNOWN] if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you
+ * want to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ EXEMPT -> Value.EXEMPT
+ NON_EXEMPT -> Value.NON_EXEMPT
+ UNKNOWN -> Value.UNKNOWN
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and
+ * don't want to throw for the unknown case.
+ *
+ * @throws FinchInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ EXEMPT -> Known.EXEMPT
+ NON_EXEMPT -> Known.NON_EXEMPT
+ UNKNOWN -> Known.UNKNOWN
+ else -> throw FinchInvalidDataException("Unknown FlsaStatus: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * This differs from the [toString] method because that method is primarily for
+ * debugging and generally doesn't throw.
+ *
+ * @throws FinchInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asString(): String =
+ _value().asString().orElseThrow {
+ FinchInvalidDataException("Value is not a String")
+ }
+
+ private var validated: Boolean = false
+
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
+ fun validate(): FlsaStatus = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: FinchInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is FlsaStatus && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
/** The manager object representing the manager of the individual within the org. */
class Manager
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
@@ -2158,6 +2455,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): Manager = apply {
if (validated) {
return@apply
@@ -2349,6 +2656,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): CustomField = apply {
if (validated) {
return@apply
@@ -2422,6 +2739,36 @@ private constructor(
fun _json(): Optional = Optional.ofNullable(_json)
+ /**
+ * Maps this instance's current variant to a value of type [T] using the given
+ * [visitor].
+ *
+ * Note that this method is _not_ forwards compatible with new variants from the
+ * API, unless [visitor] overrides [Visitor.unknown]. To handle variants not known
+ * to this version of the SDK gracefully, consider overriding [Visitor.unknown]:
+ * ```java
+ * import com.tryfinch.api.core.JsonValue;
+ * import java.util.Optional;
+ *
+ * Optional result = value.accept(new Value.Visitor>() {
+ * @Override
+ * public Optional visitString(String string) {
+ * return Optional.of(string.toString());
+ * }
+ *
+ * // ...
+ *
+ * @Override
+ * public Optional unknown(JsonValue json) {
+ * // Or inspect the `json`.
+ * return Optional.empty();
+ * }
+ * });
+ * ```
+ *
+ * @throws FinchInvalidDataException if [Visitor.unknown] is not overridden in
+ * [visitor] and the current variant is unknown.
+ */
fun accept(visitor: Visitor): T =
when {
string != null -> visitor.visitString(string)
@@ -2434,6 +2781,16 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing
+ * fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match
+ * its expected type.
+ */
fun validate(): Value = apply {
if (validated) {
return@apply
@@ -2643,7 +3000,7 @@ private constructor(
return true
}
- return other is UnionMember0 &&
+ return other is InnerEmploymentData &&
id == other.id &&
classCode == other.classCode &&
department == other.department &&
@@ -2651,6 +3008,7 @@ private constructor(
employmentStatus == other.employmentStatus &&
endDate == other.endDate &&
firstName == other.firstName &&
+ flsaStatus == other.flsaStatus &&
isActive == other.isActive &&
lastName == other.lastName &&
latestRehireDate == other.latestRehireDate &&
@@ -2676,6 +3034,7 @@ private constructor(
employmentStatus,
endDate,
firstName,
+ flsaStatus,
isActive,
lastName,
latestRehireDate,
@@ -2696,7 +3055,7 @@ private constructor(
override fun hashCode(): Int = hashCode
override fun toString() =
- "UnionMember0{id=$id, classCode=$classCode, department=$department, employment=$employment, employmentStatus=$employmentStatus, endDate=$endDate, firstName=$firstName, isActive=$isActive, lastName=$lastName, latestRehireDate=$latestRehireDate, location=$location, manager=$manager, middleName=$middleName, startDate=$startDate, title=$title, customFields=$customFields, income=$income, incomeHistory=$incomeHistory, sourceId=$sourceId, workId=$workId, additionalProperties=$additionalProperties}"
+ "InnerEmploymentData{id=$id, classCode=$classCode, department=$department, employment=$employment, employmentStatus=$employmentStatus, endDate=$endDate, firstName=$firstName, flsaStatus=$flsaStatus, isActive=$isActive, lastName=$lastName, latestRehireDate=$latestRehireDate, location=$location, manager=$manager, middleName=$middleName, startDate=$startDate, title=$title, customFields=$customFields, income=$income, incomeHistory=$incomeHistory, sourceId=$sourceId, workId=$workId, additionalProperties=$additionalProperties}"
}
class BatchError
@@ -2905,6 +3264,15 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types
+ * recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its
+ * expected type.
+ */
fun validate(): BatchError = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentDataResponse.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentDataResponse.kt
index 1c1250448..d308bf8bc 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentDataResponse.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentDataResponse.kt
@@ -131,9 +131,9 @@ private constructor(
*/
fun body(body: JsonField) = apply { this.body = body }
- /** Alias for calling [body] with `EmploymentData.ofUnionMember0(unionMember0)`. */
- fun body(unionMember0: EmploymentData.UnionMember0) =
- body(EmploymentData.ofUnionMember0(unionMember0))
+ /** Alias for calling [body] with `EmploymentData.ofEmploymentData(employmentData)`. */
+ fun body(employmentData: EmploymentData.InnerEmploymentData) =
+ body(EmploymentData.ofEmploymentData(employmentData))
/** Alias for calling [body] with `EmploymentData.ofBatchError(batchError)`. */
fun body(batchError: EmploymentData.BatchError) =
@@ -207,6 +207,14 @@ private constructor(
private var validated: Boolean = false
+ /**
+ * Validates that the types of all values in this object match their expected types recursively.
+ *
+ * This method is _not_ forwards compatible with new types from the API for existing fields.
+ *
+ * @throws FinchInvalidDataException if any value type in this object doesn't match its expected
+ * type.
+ */
fun validate(): EmploymentDataResponse = apply {
if (validated) {
return@apply
diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentEvent.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentEvent.kt
index b09dd927a..aef62c9c5 100644
--- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentEvent.kt
+++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/EmploymentEvent.kt
@@ -24,6 +24,7 @@ private constructor(
private val accountId: JsonField,
private val companyId: JsonField,
private val connectionId: JsonField,
+ private val entityId: JsonField,
private val data: JsonField,
private val eventType: JsonField,
private val additionalProperties: MutableMap,
@@ -36,17 +37,19 @@ private constructor(
@JsonProperty("connection_id")
@ExcludeMissing
connectionId: JsonField = JsonMissing.of(),
+ @JsonProperty("entity_id") @ExcludeMissing entityId: JsonField = JsonMissing.of(),
@JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(),
@JsonProperty("event_type")
@ExcludeMissing
eventType: JsonField = JsonMissing.of(),
- ) : this(accountId, companyId, connectionId, data, eventType, mutableMapOf())
+ ) : this(accountId, companyId, connectionId, entityId, data, eventType, mutableMapOf())
fun toBaseWebhookEvent(): BaseWebhookEvent =
BaseWebhookEvent.builder()
.accountId(accountId)
.companyId(companyId)
.connectionId(connectionId)
+ .entityId(entityId)
.build()
/**
@@ -75,6 +78,14 @@ private constructor(
*/
fun connectionId(): Optional = connectionId.getOptional("connection_id")
+ /**
+ * Unique Finch id of the entity for which data has been updated.
+ *
+ * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun entityId(): Optional