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/.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/.stats.yml b/.stats.yml
index 20d5e414..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-ff61a38530dfae03860bceb49379e84bfc7434eeb5d2f1dc9677cb162014faf1.yml
-openapi_spec_hash: df3bdaf4acf575bb07767cae7ca24d69
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/finch%2Ffinch-46640c1b468813b828be61b1af5cb5450f9555c4c757c5a740189906a8d56672.yml
+openapi_spec_hash: 1d5845ae61d2c0a143db43d579b048c5
config_hash: 53778a0b839c4f6ad34fbba051f5e8a6
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 853a43cd..0b7df560 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/5.2.0)
-[](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0)
+[](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/5.3.0)
+[](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 also available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.2.0).
+The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/5.3.0).
@@ -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
```
@@ -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
@@ -372,6 +383,42 @@ FinchClient client = FinchOkHttpClient.builder()
.build();
```
+### Custom HTTP client
+
+The SDK consists of three artifacts:
+
+- `finch-java-core`
+ - Contains core SDK logic
+ - Does not depend on [OkHttp](https://square.github.io/okhttp)
+ - Exposes [`FinchClient`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClient.kt), [`FinchClientAsync`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsync.kt), [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt), and [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), all of which can work with any HTTP client
+- `finch-java-client-okhttp`
+ - Depends on [OkHttp](https://square.github.io/okhttp)
+ - Exposes [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) and [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), which provide a way to construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) and [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), respectively, using OkHttp
+- `finch-java`
+ - Depends on and exposes the APIs of both `finch-java-core` and `finch-java-client-okhttp`
+ - Does not have its own logic
+
+This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies.
+
+#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html)
+
+> [!TIP]
+> Try the available [network options](#network-options) before replacing the default client.
+
+To use a customized `OkHttpClient`:
+
+1. Replace your [`finch-java` dependency](#installation) with `finch-java-core`
+2. Copy `finch-java-client-okhttp`'s [`OkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/OkHttpClient.kt) class into your code and customize it
+3. Construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) or [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), similarly to [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), using your customized client
+
+### Completely custom HTTP client
+
+To use a completely custom HTTP client:
+
+1. Replace your [`finch-java` dependency](#installation) with `finch-java-core`
+2. Write a class that implements the [`HttpClient`](finch-java-core/src/main/kotlin/com/tryfinch/api/core/http/HttpClient.kt) interface
+3. Construct [`FinchClientImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt) or [`FinchClientAsyncImpl`](finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt), similarly to [`FinchOkHttpClient`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt) or [`FinchOkHttpClientAsync`](finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt), using your new client class
+
## Undocumented API functionality
The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.
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 {
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/Check.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/Check.kt
index 7ac22c66..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
@@ -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)"
@@ -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()
}
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/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))
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