From 184534f3eda9aeffde671073fcd6b140620da87e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 17:44:28 +0000 Subject: [PATCH 1/8] docs: update documentation links to be more uniform --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 853a43cd..59df7954 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It is generated with [Stainless](https://www.stainless.com/). -The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are also available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0). +The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0). From 3966f0eb05820ed6359991b877d3599727b295d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 19:23:10 +0000 Subject: [PATCH 2/8] docs(client): update jackson compat error message --- finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt index 7ac22c66..8a8929b1 100644 --- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt +++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt @@ -62,7 +62,7 @@ internal fun checkJacksonVersionCompatibility() { } check(incompatibleJacksonVersions.isEmpty()) { """ -This SDK depends on Jackson version $MINIMUM_JACKSON_VERSION, but the following incompatible Jackson versions were detected at runtime: +This SDK requires a minimum Jackson version of $MINIMUM_JACKSON_VERSION, but the following incompatible Jackson versions were detected at runtime: ${incompatibleJacksonVersions.asSequence().map { (version, incompatibilityReason) -> "- `${version.toFullString().replace("/", ":")}` ($incompatibilityReason)" From acd26d9181ec31200b4c820bffde37761aa18d52 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 14:14:22 +0000 Subject: [PATCH 3/8] docs: explain jackson compat in readme --- README.md | 11 +++++++++++ .../src/main/kotlin/com/tryfinch/api/core/Check.kt | 2 ++ 2 files changed, 13 insertions(+) diff --git a/README.md b/README.md index 59df7954..46242b3e 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,17 @@ both of which will raise an error if the signature is invalid. Note that the "body" parameter must be the raw JSON string sent from the server (do not parse it first). The `.unwrap()` method can parse this JSON for you. +## Jackson + +The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default. + +The SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config). + +If the SDK threw an exception, but you're _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt). + +> [!CAUTION] +> We make no guarantee that the SDK works correctly when the Jackson version check is disabled. + ## Network options ### Retries diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt index 8a8929b1..b228e69d 100644 --- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt +++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt @@ -73,6 +73,8 @@ This can happen if you are either: 2. Depending on some library that depends on different Jackson versions, potentially transitively Double-check that you are depending on compatible Jackson versions. + +See https://www.github.com/Finch-API/finch-api-java#jackson for more information. """ .trimIndent() } From 7ce9aa1115f40676d990dabb5fbeb365b834cdcc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 15:29:26 +0000 Subject: [PATCH 4/8] perf(internal): improve compilation+test speed --- .github/workflows/publish-sonatype.yml | 2 +- .../src/main/kotlin/finch.kotlin.gradle.kts | 9 +-- .../api/core/http/RetryingHttpClient.kt | 56 ++++++++++++------- .../tryfinch/api/core/PhantomReachableTest.kt | 2 +- .../com/tryfinch/api/core/http/HeadersTest.kt | 32 ----------- .../tryfinch/api/core/http/QueryParamsTest.kt | 32 ----------- .../api/core/http/RetryingHttpClientTest.kt | 36 ++++++++---- .../api/services/ErrorHandlingTest.kt | 2 + .../api/services/ServiceParamsTest.kt | 2 + gradle.properties | 16 +++++- 10 files changed, 85 insertions(+), 104 deletions(-) diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml index 518dad07..a180cc9d 100644 --- a/.github/workflows/publish-sonatype.yml +++ b/.github/workflows/publish-sonatype.yml @@ -33,7 +33,7 @@ jobs: export -- GPG_SIGNING_KEY_ID printenv -- GPG_SIGNING_KEY | gpg --batch --passphrase-fd 3 --import 3<<< "$GPG_SIGNING_PASSWORD" GPG_SIGNING_KEY_ID="$(gpg --with-colons --list-keys | awk -F : -- '/^pub:/ { getline; print "0x" substr($10, length($10) - 7) }')" - ./gradlew publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD" + ./gradlew publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD" --no-configuration-cache env: SONATYPE_USERNAME: ${{ secrets.FINCH_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} SONATYPE_PASSWORD: ${{ secrets.FINCH_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} diff --git a/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts b/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts index 2fcf511b..f274b11f 100644 --- a/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/finch.kotlin.gradle.kts @@ -9,7 +9,7 @@ plugins { kotlin { jvmToolchain { - languageVersion.set(JavaLanguageVersion.of(17)) + languageVersion.set(JavaLanguageVersion.of(21)) } compilerOptions { @@ -19,6 +19,8 @@ kotlin { // Suppress deprecation warnings because we may still reference and test deprecated members. // TODO: Replace with `-Xsuppress-warning=DEPRECATION` once we use Kotlin compiler 2.1.0+. "-nowarn", + // Use as many threads as there are CPU cores on the machine for compilation. + "-Xbackend-threads=0", ) jvmTarget.set(JvmTarget.JVM_1_8) languageVersion.set(KotlinVersion.KOTLIN_1_8) @@ -34,8 +36,7 @@ configure { } } -// Run tests in parallel to some degree. tasks.withType().configureEach { - maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) - forkEvery = 100 + systemProperty("junit.jupiter.execution.parallel.enabled", true) + systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") } 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 77823c9b..c195f8aa 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 @@ -23,6 +23,7 @@ import kotlin.math.pow class RetryingHttpClient private constructor( private val httpClient: HttpClient, + private val sleeper: Sleeper, private val clock: Clock, private val maxRetries: Int, private val idempotencyHeader: String?, @@ -62,10 +63,10 @@ private constructor( null } - val backoffMillis = getRetryBackoffMillis(retries, response) + val backoffDuration = getRetryBackoffDuration(retries, response) // All responses must be closed, so close the failed one before retrying. response?.close() - Thread.sleep(backoffMillis.toMillis()) + sleeper.sleep(backoffDuration) } } @@ -111,10 +112,10 @@ private constructor( } } - val backoffMillis = getRetryBackoffMillis(retries, response) + val backoffDuration = getRetryBackoffDuration(retries, response) // All responses must be closed, so close the failed one before retrying. response?.close() - return sleepAsync(backoffMillis.toMillis()).thenCompose { + return sleeper.sleepAsync(backoffDuration).thenCompose { executeWithRetries(requestWithRetryCount, requestOptions) } } @@ -179,7 +180,7 @@ private constructor( // retried. throwable is IOException || throwable is FinchIoException - private fun getRetryBackoffMillis(retries: Int, response: HttpResponse?): Duration { + private fun getRetryBackoffDuration(retries: Int, response: HttpResponse?): Duration { // About the Retry-After header: // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After response @@ -226,33 +227,40 @@ private constructor( companion object { - private val TIMER = Timer("RetryingHttpClient", true) - - private fun sleepAsync(millis: Long): CompletableFuture { - val future = CompletableFuture() - TIMER.schedule( - object : TimerTask() { - override fun run() { - future.complete(null) - } - }, - millis, - ) - return future - } - @JvmStatic fun builder() = Builder() } class Builder internal constructor() { private var httpClient: HttpClient? = null + private var sleeper: Sleeper = + object : Sleeper { + + private val timer = Timer("RetryingHttpClient", true) + + override fun sleep(duration: Duration) = Thread.sleep(duration.toMillis()) + + override fun sleepAsync(duration: Duration): CompletableFuture { + val future = CompletableFuture() + timer.schedule( + object : TimerTask() { + override fun run() { + future.complete(null) + } + }, + duration.toMillis(), + ) + return future + } + } private var clock: Clock = Clock.systemUTC() private var maxRetries: Int = 2 private var idempotencyHeader: String? = null fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient } + @JvmSynthetic internal fun sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper } + fun clock(clock: Clock) = apply { this.clock = clock } fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } @@ -262,9 +270,17 @@ private constructor( fun build(): HttpClient = RetryingHttpClient( checkRequired("httpClient", httpClient), + sleeper, clock, maxRetries, idempotencyHeader, ) } + + internal interface Sleeper { + + fun sleep(duration: Duration) + + fun sleepAsync(duration: Duration): CompletableFuture + } } diff --git a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/PhantomReachableTest.kt b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/PhantomReachableTest.kt index 02dbac71..be5a9040 100644 --- a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/PhantomReachableTest.kt +++ b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/PhantomReachableTest.kt @@ -20,7 +20,7 @@ internal class PhantomReachableTest { assertThat(closed).isFalse() System.gc() - Thread.sleep(3000) + Thread.sleep(100) assertThat(closed).isTrue() } diff --git a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/HeadersTest.kt b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/HeadersTest.kt index f1611438..104c7d98 100644 --- a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/HeadersTest.kt +++ b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/HeadersTest.kt @@ -1,8 +1,6 @@ package com.tryfinch.api.core.http import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.catchThrowable -import org.assertj.core.api.Assumptions.assumeThat import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource @@ -241,34 +239,4 @@ internal class HeadersTest { assertThat(size).isEqualTo(testCase.expectedSize) } - - @ParameterizedTest - @EnumSource - fun namesAreImmutable(testCase: TestCase) { - val headers = testCase.headers - val headerNamesCopy = headers.names().toSet() - - val throwable = catchThrowable { - (headers.names() as MutableSet).add("another name") - } - - assertThat(throwable).isInstanceOf(UnsupportedOperationException::class.java) - assertThat(headers.names()).isEqualTo(headerNamesCopy) - } - - @ParameterizedTest - @EnumSource - fun valuesAreImmutable(testCase: TestCase) { - val headers = testCase.headers - assumeThat(headers.size).isNotEqualTo(0) - val name = headers.names().first() - val headerValuesCopy = headers.values(name).toList() - - val throwable = catchThrowable { - (headers.values(name) as MutableList).add("another value") - } - - assertThat(throwable).isInstanceOf(UnsupportedOperationException::class.java) - assertThat(headers.values(name)).isEqualTo(headerValuesCopy) - } } diff --git a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/QueryParamsTest.kt b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/QueryParamsTest.kt index 2002ca28..c2f9a6df 100644 --- a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/QueryParamsTest.kt +++ b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/QueryParamsTest.kt @@ -1,8 +1,6 @@ package com.tryfinch.api.core.http import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.catchThrowable -import org.assertj.core.api.Assumptions.assumeThat import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource @@ -179,34 +177,4 @@ internal class QueryParamsTest { assertThat(size).isEqualTo(testCase.expectedSize) } - - @ParameterizedTest - @EnumSource - fun keysAreImmutable(testCase: TestCase) { - val queryParams = testCase.queryParams - val queryParamKeysCopy = queryParams.keys().toSet() - - val throwable = catchThrowable { - (queryParams.keys() as MutableSet).add("another key") - } - - assertThat(throwable).isInstanceOf(UnsupportedOperationException::class.java) - assertThat(queryParams.keys()).isEqualTo(queryParamKeysCopy) - } - - @ParameterizedTest - @EnumSource - fun valuesAreImmutable(testCase: TestCase) { - val queryParams = testCase.queryParams - assumeThat(queryParams.size).isNotEqualTo(0) - val key = queryParams.keys().first() - val queryParamValuesCopy = queryParams.values(key).toList() - - val throwable = catchThrowable { - (queryParams.values(key) as MutableList).add("another value") - } - - assertThat(throwable).isInstanceOf(UnsupportedOperationException::class.java) - assertThat(queryParams.values(key)).isEqualTo(queryParamValuesCopy) - } } diff --git a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/RetryingHttpClientTest.kt b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/RetryingHttpClientTest.kt index ae5c8211..b20e1b90 100644 --- a/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/RetryingHttpClientTest.kt +++ b/finch-java-core/src/test/kotlin/com/tryfinch/api/core/http/RetryingHttpClientTest.kt @@ -7,13 +7,16 @@ import com.github.tomakehurst.wiremock.stubbing.Scenario import com.tryfinch.api.client.okhttp.OkHttpClient import com.tryfinch.api.core.RequestOptions import java.io.InputStream +import java.time.Duration import java.util.concurrent.CompletableFuture import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.parallel.ResourceLock import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") internal class RetryingHttpClientTest { private var openResponseCount = 0 @@ -24,6 +27,7 @@ internal class RetryingHttpClientTest { val okHttpClient = OkHttpClient.builder().baseUrl(wmRuntimeInfo.httpBaseUrl).build() httpClient = object : HttpClient { + override fun execute( request: HttpRequest, requestOptions: RequestOptions, @@ -40,6 +44,7 @@ internal class RetryingHttpClientTest { private fun trackClose(response: HttpResponse): HttpResponse { openResponseCount++ return object : HttpResponse { + private var isClosed = false override fun statusCode(): Int = response.statusCode() @@ -66,7 +71,7 @@ internal class RetryingHttpClientTest { @ValueSource(booleans = [false, true]) fun execute(async: Boolean) { stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) - val retryingClient = RetryingHttpClient.builder().httpClient(httpClient).build() + val retryingClient = retryingHttpClientBuilder().build() val response = retryingClient.execute( @@ -88,11 +93,7 @@ internal class RetryingHttpClientTest { .willReturn(ok()) ) val retryingClient = - RetryingHttpClient.builder() - .httpClient(httpClient) - .maxRetries(2) - .idempotencyHeader("X-Some-Header") - .build() + retryingHttpClientBuilder().maxRetries(2).idempotencyHeader("X-Some-Header").build() val response = retryingClient.execute( @@ -134,8 +135,7 @@ internal class RetryingHttpClientTest { .willReturn(ok()) .willSetStateTo("COMPLETED") ) - val retryingClient = - RetryingHttpClient.builder().httpClient(httpClient).maxRetries(2).build() + val retryingClient = retryingHttpClientBuilder().maxRetries(2).build() val response = retryingClient.execute( @@ -181,8 +181,7 @@ internal class RetryingHttpClientTest { .willReturn(ok()) .willSetStateTo("COMPLETED") ) - val retryingClient = - RetryingHttpClient.builder().httpClient(httpClient).maxRetries(2).build() + val retryingClient = retryingHttpClientBuilder().maxRetries(2).build() val response = retryingClient.execute( @@ -220,8 +219,7 @@ internal class RetryingHttpClientTest { .willReturn(ok()) .willSetStateTo("COMPLETED") ) - val retryingClient = - RetryingHttpClient.builder().httpClient(httpClient).maxRetries(1).build() + val retryingClient = retryingHttpClientBuilder().maxRetries(1).build() val response = retryingClient.execute( @@ -234,6 +232,20 @@ internal class RetryingHttpClientTest { assertNoResponseLeaks() } + private fun retryingHttpClientBuilder() = + RetryingHttpClient.builder() + .httpClient(httpClient) + // Use a no-op `Sleeper` to make the test fast. + .sleeper( + object : RetryingHttpClient.Sleeper { + + override fun sleep(duration: Duration) {} + + override fun sleepAsync(duration: Duration): CompletableFuture = + CompletableFuture.completedFuture(null) + } + ) + private fun HttpClient.execute(request: HttpRequest, async: Boolean): HttpResponse = if (async) executeAsync(request).get() else execute(request) diff --git a/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ErrorHandlingTest.kt b/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ErrorHandlingTest.kt index 59b76d6a..51beb3bf 100644 --- a/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ErrorHandlingTest.kt +++ b/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ErrorHandlingTest.kt @@ -27,8 +27,10 @@ import org.assertj.core.api.Assertions.entry import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.parallel.ResourceLock @WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") internal class ErrorHandlingTest { companion object { diff --git a/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ServiceParamsTest.kt b/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ServiceParamsTest.kt index fcded4b6..690e57b9 100644 --- a/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ServiceParamsTest.kt +++ b/finch-java-core/src/test/kotlin/com/tryfinch/api/services/ServiceParamsTest.kt @@ -16,8 +16,10 @@ import com.tryfinch.api.client.okhttp.FinchOkHttpClient import com.tryfinch.api.models.HrisDirectoryListParams import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock @WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") internal class ServiceParamsTest { private lateinit var client: FinchClient diff --git a/gradle.properties b/gradle.properties index 0c8d4ded..ff76593f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,17 @@ org.gradle.caching=true +org.gradle.configuration-cache=true org.gradle.parallel=true org.gradle.daemon=false -org.gradle.jvmargs=-Xmx4g -kotlin.daemon.jvmargs=-Xmx4g +# These options improve our compilation and test performance. They are inherited by the Kotlin daemon. +org.gradle.jvmargs=\ + -Xms1g \ + -Xmx4g \ + -XX:+UseParallelGC \ + -XX:InitialCodeCacheSize=256m \ + -XX:ReservedCodeCacheSize=1G \ + -XX:MetaspaceSize=256m \ + -XX:TieredStopAtLevel=1 \ + -XX:GCTimeRatio=4 \ + -XX:CICompilerCount=4 \ + -XX:+OptimizeStringConcat \ + -XX:+UseStringDeduplication From 09d6b33bc87d1232cc855d0e9621a4f594985878 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:11:37 +0000 Subject: [PATCH 5/8] docs: explain http client customization --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index 46242b3e..96888026 100644 --- a/README.md +++ b/README.md @@ -383,6 +383,42 @@ FinchClient client = FinchOkHttpClient.builder() .build(); ``` +### Custom HTTP client + +The SDK consists of three artifacts: + +- `finch-java-core` + - Contains core SDK logic + - Does not depend on [OkHttp](https://square.github.io/okhttp) + - Exposes [`FinchClient`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClient.kt), [`FinchClientAsync`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsync.kt), [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt), and [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), all of which can work with any HTTP client +- `finch-java-client-okhttp` + - Depends on [OkHttp](https://square.github.io/okhttp) + - Exposes [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) and [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), which provide a way to construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) and [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), respectively, using OkHttp +- `finch-java` + - Depends on and exposes the APIs of both `finch-java-core` and `finch-java-client-okhttp` + - Does not have its own logic + +This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies. + +#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html) + +> [!TIP] +> Try the available [network options](#network-options) before replacing the default client. + +To use a customized `OkHttpClient`: + +1. Replace your [`finch-java` dependency](#installation) with `finch-java-core` +2. Copy `finch-java-client-okhttp`'s [`OkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt) class into your code and customize it +3. Construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) or [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), similarly to [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), using your customized client + +### Completely custom HTTP client + +To use a completely custom HTTP client: + +1. Replace your [`finch-java` dependency](#installation) with `finch-java-core` +2. Write a class that implements the [`HttpClient`](finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/HttpClient.kt) interface +3. Construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) or [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), similarly to [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), using your new client class + ## Undocumented API functionality The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API. From 8a943e843922b45684c633f2ef1d0e158b04c6d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 22:29:16 +0000 Subject: [PATCH 6/8] feat(api): api update --- .stats.yml | 4 ++-- .../kotlin/com/tryfinch/api/models/Company.kt | 8 ++++++-- .../tryfinch/api/models/CompanyUpdateResponse.kt | 8 ++++++-- .../api/models/SandboxCompanyUpdateParams.kt | 16 ++++++++++++---- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/.stats.yml b/.stats.yml index 20d5e414..0acb73ad 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-ff61a38530dfae03860bceb49379e84bfc7434eeb5d2f1dc9677cb162014faf1.yml -openapi_spec_hash: df3bdaf4acf575bb07767cae7ca24d69 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-388b6200908d48b0f759495ec489b5f7ffda61549b3faedad9a537104223ecdc.yml +openapi_spec_hash: c47f17807131ac97532dfd69c1970104 config_hash: 53778a0b839c4f6ad34fbba051f5e8a6 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 d77d890c..19928fb7 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 @@ -131,7 +131,8 @@ private constructor( fun primaryEmail(): Optional = primaryEmail.getOptional("primary_email") /** - * The phone number of the main administrator on the account. Format: `XXXXXXXXXX` + * The phone number of the main administrator on the account. Format: E.164, with extension + * where applicable, e.g. `+NNNNNNNNNNN xExtension` * * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -431,7 +432,10 @@ private constructor( this.primaryEmail = primaryEmail } - /** The phone number of the main administrator on the account. Format: `XXXXXXXXXX` */ + /** + * The phone number of the main administrator on the account. Format: E.164, with extension + * where applicable, e.g. `+NNNNNNNNNNN xExtension` + */ fun primaryPhoneNumber(primaryPhoneNumber: String?) = primaryPhoneNumber(JsonField.ofNullable(primaryPhoneNumber)) 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 802688bc..7b15b8dd 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 @@ -120,7 +120,8 @@ private constructor( fun primaryEmail(): Optional = primaryEmail.getOptional("primary_email") /** - * The phone number of the main administrator on the account. Format: `XXXXXXXXXX` + * The phone number of the main administrator on the account. Format: E.164, with extension + * where applicable, e.g. `+NNNNNNNNNNN xExtension` * * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -399,7 +400,10 @@ private constructor( this.primaryEmail = primaryEmail } - /** The phone number of the main administrator on the account. Format: `XXXXXXXXXX` */ + /** + * The phone number of the main administrator on the account. Format: E.164, with extension + * where applicable, e.g. `+NNNNNNNNNNN xExtension` + */ fun primaryPhoneNumber(primaryPhoneNumber: String?) = primaryPhoneNumber(JsonField.ofNullable(primaryPhoneNumber)) diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/SandboxCompanyUpdateParams.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/SandboxCompanyUpdateParams.kt index 4c71017d..96b53822 100644 --- a/finch-java-core/src/main/kotlin/com/tryfinch/api/models/SandboxCompanyUpdateParams.kt +++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/models/SandboxCompanyUpdateParams.kt @@ -86,7 +86,8 @@ private constructor( fun primaryEmail(): Optional = body.primaryEmail() /** - * The phone number of the main administrator on the account. Format: `XXXXXXXXXX` + * The phone number of the main administrator on the account. Format: E.164, with extension + * where applicable, e.g. `+NNNNNNNNNNN xExtension` * * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -334,7 +335,10 @@ private constructor( body.primaryEmail(primaryEmail) } - /** The phone number of the main administrator on the account. Format: `XXXXXXXXXX` */ + /** + * The phone number of the main administrator on the account. Format: E.164, with extension + * where applicable, e.g. `+NNNNNNNNNNN xExtension` + */ fun primaryPhoneNumber(primaryPhoneNumber: String?) = apply { body.primaryPhoneNumber(primaryPhoneNumber) } @@ -608,7 +612,8 @@ private constructor( fun primaryEmail(): Optional = primaryEmail.getOptional("primary_email") /** - * The phone number of the main administrator on the account. Format: `XXXXXXXXXX` + * The phone number of the main administrator on the account. Format: E.164, with extension + * where applicable, e.g. `+NNNNNNNNNNN xExtension` * * @throws FinchInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). @@ -894,7 +899,10 @@ private constructor( this.primaryEmail = primaryEmail } - /** The phone number of the main administrator on the account. Format: `XXXXXXXXXX` */ + /** + * The phone number of the main administrator on the account. Format: E.164, with + * extension where applicable, e.g. `+NNNNNNNNNNN xExtension` + */ fun primaryPhoneNumber(primaryPhoneNumber: String?) = primaryPhoneNumber(JsonField.ofNullable(primaryPhoneNumber)) From 4676dff9a9987c87afc865501e27928770be9815 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:44:15 +0000 Subject: [PATCH 7/8] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 0acb73ad..6650a5fe 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-388b6200908d48b0f759495ec489b5f7ffda61549b3faedad9a537104223ecdc.yml -openapi_spec_hash: c47f17807131ac97532dfd69c1970104 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-46640c1b468813b828be61b1af5cb5450f9555c4c757c5a740189906a8d56672.yml +openapi_spec_hash: 1d5845ae61d2c0a143db43d579b048c5 config_hash: 53778a0b839c4f6ad34fbba051f5e8a6 From a956c943de1f3e66a7839ec9b83970f4e123e29a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:44:46 +0000 Subject: [PATCH 8/8] release: 5.3.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 21 +++++++++++++++++++++ README.md | 10 +++++----- build.gradle.kts | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b29b3b64..41010749 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "5.2.0" + ".": "5.3.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 787dc0a3..bdf08445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## 5.3.0 (2025-04-18) + +Full Changelog: [v5.2.0...v5.3.0](https://github.com/Finch-API/finch-api-java/compare/v5.2.0...v5.3.0) + +### Features + +* **api:** api update ([8a943e8](https://github.com/Finch-API/finch-api-java/commit/8a943e843922b45684c633f2ef1d0e158b04c6d7)) + + +### Performance Improvements + +* **internal:** improve compilation+test speed ([7ce9aa1](https://github.com/Finch-API/finch-api-java/commit/7ce9aa1115f40676d990dabb5fbeb365b834cdcc)) + + +### Documentation + +* **client:** update jackson compat error message ([3966f0e](https://github.com/Finch-API/finch-api-java/commit/3966f0eb05820ed6359991b877d3599727b295d7)) +* explain http client customization ([09d6b33](https://github.com/Finch-API/finch-api-java/commit/09d6b33bc87d1232cc855d0e9621a4f594985878)) +* explain jackson compat in readme ([acd26d9](https://github.com/Finch-API/finch-api-java/commit/acd26d9181ec31200b4c820bffde37761aa18d52)) +* update documentation links to be more uniform ([184534f](https://github.com/Finch-API/finch-api-java/commit/184534f3eda9aeffde671073fcd6b140620da87e)) + ## 5.2.0 (2025-04-12) Full Changelog: [v5.1.0...v5.2.0](https://github.com/Finch-API/finch-api-java/compare/v5.1.0...v5.2.0) diff --git a/README.md b/README.md index 96888026..0b7df560 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/5.2.0) -[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/5.2.0/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0) +[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/5.3.0) +[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/5.3.0/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.3.0) @@ -15,7 +15,7 @@ It is generated with [Stainless](https://www.stainless.com/). -The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0). +The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.3.0). @@ -26,7 +26,7 @@ The REST API documentation can be found on [developer.tryfinch.com](https://deve ### Gradle ```kotlin -implementation("com.tryfinch.api:finch-java:5.2.0") +implementation("com.tryfinch.api:finch-java:5.3.0") ``` ### Maven @@ -35,7 +35,7 @@ implementation("com.tryfinch.api:finch-java:5.2.0") com.tryfinch.api finch-java - 5.2.0 + 5.3.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 35c07cc3..9d32ca9a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.tryfinch.api" - version = "5.2.0" // x-release-please-version + version = "5.3.0" // x-release-please-version } subprojects {