diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 3d2ac0b..10f3091 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0" + ".": "0.2.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 87595ec..c9a9c8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## 0.2.0 (2025-02-12) + +Full Changelog: [v0.1.0...v0.2.0](https://github.com/prelude-so/java-sdk/compare/v0.1.0...v0.2.0) + +### Features + +* **client:** send client-side timeout headers ([#64](https://github.com/prelude-so/java-sdk/issues/64)) ([0004d4b](https://github.com/prelude-so/java-sdk/commit/0004d4b736fcd0a9e5852064bb67de8bea7085d9)) + + +### Bug Fixes + +* **api:** add missing `@MustBeClosed` annotations ([#65](https://github.com/prelude-so/java-sdk/issues/65)) ([84f3ed5](https://github.com/prelude-so/java-sdk/commit/84f3ed53e37423b70ebbeeca553057dbe74a2d6c)) +* **api:** switch `CompletableFuture<Void>` to `CompletableFuture` ([84f3ed5](https://github.com/prelude-so/java-sdk/commit/84f3ed53e37423b70ebbeeca553057dbe74a2d6c)) +* **client:** add missing validation calls on response ([84f3ed5](https://github.com/prelude-so/java-sdk/commit/84f3ed53e37423b70ebbeeca553057dbe74a2d6c)) +* **client:** always provide a body for `PATCH` methods ([84f3ed5](https://github.com/prelude-so/java-sdk/commit/84f3ed53e37423b70ebbeeca553057dbe74a2d6c)) + + +### Chores + +* **internal:** codegen related update ([#61](https://github.com/prelude-so/java-sdk/issues/61)) ([20ea6d4](https://github.com/prelude-so/java-sdk/commit/20ea6d4d3000c46b6508c9049227c3121a9e81d1)) +* **internal:** minor formatting/style changes ([84f3ed5](https://github.com/prelude-so/java-sdk/commit/84f3ed53e37423b70ebbeeca553057dbe74a2d6c)) +* **internal:** refactor `PhantomReachableClosingAsyncStreamResponse` impl ([#66](https://github.com/prelude-so/java-sdk/issues/66)) ([a9c5a51](https://github.com/prelude-so/java-sdk/commit/a9c5a519d6cb4d93f50b2a19b71609686a2b6b5d)) +* **internal:** rename some tests ([84f3ed5](https://github.com/prelude-so/java-sdk/commit/84f3ed53e37423b70ebbeeca553057dbe74a2d6c)) + + +### Documentation + +* add more phantom reachability docs ([a9c5a51](https://github.com/prelude-so/java-sdk/commit/a9c5a519d6cb4d93f50b2a19b71609686a2b6b5d)) +* fix incorrect additional properties info ([#63](https://github.com/prelude-so/java-sdk/issues/63)) ([51310ff](https://github.com/prelude-so/java-sdk/commit/51310ff3fb72ba4d132da89583a0649fde298b1d)) + ## 0.1.0 (2025-02-05) Full Changelog: [v0.1.0-beta.3...v0.1.0](https://github.com/prelude-so/java-sdk/compare/v0.1.0-beta.3...v0.1.0) diff --git a/README.md b/README.md index bdb1c69..8729ba4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ -[![Maven Central](https://img.shields.io/maven-central/v/so.prelude.sdk/prelude-java)](https://central.sonatype.com/artifact/so.prelude.sdk/prelude-java/0.1.0) +[![Maven Central](https://img.shields.io/maven-central/v/so.prelude.sdk/prelude-java)](https://central.sonatype.com/artifact/so.prelude.sdk/prelude-java/0.2.0) @@ -19,7 +19,7 @@ The REST API documentation can be found on [docs.prelude.so](https://docs.prelud ### Gradle ```kotlin -implementation("so.prelude.sdk:prelude-java:0.1.0") +implementation("so.prelude.sdk:prelude-java:0.2.0") ``` ### Maven @@ -28,7 +28,7 @@ implementation("so.prelude.sdk:prelude-java:0.1.0") so.prelude.sdk prelude-java - 0.1.0 + 0.2.0 ``` @@ -101,19 +101,7 @@ VerificationCreateResponse verification = client.verification().create(params); To make a request to the Prelude API, you generally build an instance of the appropriate `Params` class. -In [Example: creating a resource](#example-creating-a-resource) above, we used the `VerificationCreateParams.builder()` to pass to the `create` method of the `verification` service. - -Sometimes, the API may support other properties that are not yet supported in the Java SDK types. In that case, you can attach them using the `putAdditionalProperty` method. - -```java -import so.prelude.sdk.core.JsonValue; -import so.prelude.sdk.models.VerificationCreateParams; - -VerificationCreateParams params = VerificationCreateParams.builder() - // ... normal properties - .putAdditionalProperty("secret_param", JsonValue.from("4242")) - .build(); -``` +See [Undocumented request params](#undocumented-request-params) for how to send arbitrary parameters. ## Responses @@ -241,18 +229,26 @@ This library is typed for convenient access to the documented API. If you need t ### Undocumented request params -To make requests using undocumented parameters, you can provide or override parameters on the params object while building it. +In [Example: creating a resource](#example-creating-a-resource) above, we used the `VerificationCreateParams.builder()` to pass to the `create` method of the `verification` service. + +Sometimes, the API may support other properties that are not yet supported in the Java SDK types. In that case, you can attach them using raw setters: ```java -FooCreateParams address = FooCreateParams.builder() - .id("my_id") - .putAdditionalProperty("secret_prop", JsonValue.from("hello")) +import so.prelude.sdk.core.JsonValue; +import so.prelude.sdk.models.VerificationCreateParams; + +VerificationCreateParams params = VerificationCreateParams.builder() + .putAdditionalHeader("Secret-Header", "42") + .putAdditionalQueryParam("secret_query_param", "42") + .putAdditionalBodyProperty("secretProperty", JsonValue.from("42")) .build(); ``` +You can also use the `putAdditionalProperty` method on nested headers, query params, or body objects. + ### Undocumented response properties -To access undocumented response properties, you can use `res._additionalProperties()` on a response object to get a map of untyped fields of type `Map`. You can then access fields like `._additionalProperties().get("secret_prop").asString()` or use other helpers defined on the `JsonValue` class to extract it to a desired type. +To access undocumented response properties, you can use `res._additionalProperties()` on a response object to get a map of untyped fields of type `Map`. You can then access fields like `res._additionalProperties().get("secret_prop").asString()` or use other helpers defined on the `JsonValue` class to extract it to a desired type. ## Logging diff --git a/build.gradle.kts b/build.gradle.kts index 2cba991..799128f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,4 @@ allprojects { group = "so.prelude.sdk" - version = "0.1.0" // x-release-please-version + version = "0.2.0" // x-release-please-version } diff --git a/prelude-java-client-okhttp/src/main/kotlin/so/prelude/sdk/client/okhttp/OkHttpClient.kt b/prelude-java-client-okhttp/src/main/kotlin/so/prelude/sdk/client/okhttp/OkHttpClient.kt index 33aaaae..ce0cae7 100644 --- a/prelude-java-client-okhttp/src/main/kotlin/so/prelude/sdk/client/okhttp/OkHttpClient.kt +++ b/prelude-java-client-okhttp/src/main/kotlin/so/prelude/sdk/client/okhttp/OkHttpClient.kt @@ -31,38 +31,11 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val baseUrl: HttpUrl) : HttpClient { - private fun getClient(requestOptions: RequestOptions): okhttp3.OkHttpClient { - val clientBuilder = okHttpClient.newBuilder() - - val logLevel = - when (System.getenv("PRELUDE_LOG")?.lowercase()) { - "info" -> HttpLoggingInterceptor.Level.BASIC - "debug" -> HttpLoggingInterceptor.Level.BODY - else -> null - } - if (logLevel != null) { - clientBuilder.addNetworkInterceptor( - HttpLoggingInterceptor().setLevel(logLevel).apply { redactHeader("Authorization") } - ) - } - - val timeout = requestOptions.timeout - if (timeout != null) { - clientBuilder - .connectTimeout(timeout) - .readTimeout(timeout) - .writeTimeout(timeout) - .callTimeout(if (timeout.seconds == 0L) timeout else timeout.plusSeconds(30)) - } - - return clientBuilder.build() - } - override fun execute( request: HttpRequest, requestOptions: RequestOptions, ): HttpResponse { - val call = getClient(requestOptions).newCall(request.toRequest()) + val call = newCall(request, requestOptions) return try { call.execute().toResponse() @@ -81,18 +54,18 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val request.body?.run { future.whenComplete { _, _ -> close() } } - val call = getClient(requestOptions).newCall(request.toRequest()) - call.enqueue( - object : Callback { - override fun onResponse(call: Call, response: Response) { - future.complete(response.toResponse()) - } + newCall(request, requestOptions) + .enqueue( + object : Callback { + override fun onResponse(call: Call, response: Response) { + future.complete(response.toResponse()) + } - override fun onFailure(call: Call, e: IOException) { - future.completeExceptionally(PreludeIoException("Request failed", e)) + override fun onFailure(call: Call, e: IOException) { + future.completeExceptionally(PreludeIoException("Request failed", e)) + } } - } - ) + ) return future } @@ -103,10 +76,37 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val okHttpClient.cache?.close() } - private fun HttpRequest.toRequest(): Request { + private fun newCall(request: HttpRequest, requestOptions: RequestOptions): Call { + val clientBuilder = okHttpClient.newBuilder() + + val logLevel = + when (System.getenv("PRELUDE_LOG")?.lowercase()) { + "info" -> HttpLoggingInterceptor.Level.BASIC + "debug" -> HttpLoggingInterceptor.Level.BODY + else -> null + } + if (logLevel != null) { + clientBuilder.addNetworkInterceptor( + HttpLoggingInterceptor().setLevel(logLevel).apply { redactHeader("Authorization") } + ) + } + + val timeout = requestOptions.timeout + if (timeout != null) { + clientBuilder + .connectTimeout(timeout) + .readTimeout(timeout) + .writeTimeout(timeout) + .callTimeout(if (timeout.seconds == 0L) timeout else timeout.plusSeconds(30)) + } + + val client = clientBuilder.build() + return client.newCall(request.toRequest(client)) + } + + private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient): Request { var body: RequestBody? = body?.toRequestBody() - // OkHttpClient always requires a request body for PUT and POST methods. - if (body == null && (method == HttpMethod.PUT || method == HttpMethod.POST)) { + if (body == null && requiresBody(method)) { body = "".toRequestBody() } @@ -115,9 +115,33 @@ private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val headers.values(name).forEach { builder.header(name, it) } } + if ( + !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0 + ) { + builder.header( + "X-Stainless-Read-Timeout", + Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString() + ) + } + if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) { + builder.header( + "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 { url?.let { return it diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/Params.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/Params.kt new file mode 100644 index 0000000..8e371ea --- /dev/null +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/Params.kt @@ -0,0 +1,16 @@ +package so.prelude.sdk.core + +import so.prelude.sdk.core.http.Headers +import so.prelude.sdk.core.http.QueryParams + +/** An interface representing parameters passed to a service method. */ +interface Params { + /** The full set of headers in the parameters, including both fixed and additional headers. */ + fun _headers(): Headers + + /** + * The full set of query params in the parameters, including both fixed and additional query + * params. + */ + fun _queryParams(): QueryParams +} diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/PrepareRequest.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/PrepareRequest.kt new file mode 100644 index 0000000..7bdf7f0 --- /dev/null +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/PrepareRequest.kt @@ -0,0 +1,24 @@ +@file:JvmName("PrepareRequest") + +package so.prelude.sdk.core + +import java.util.concurrent.CompletableFuture +import so.prelude.sdk.core.http.HttpRequest + +@JvmSynthetic +internal fun HttpRequest.prepare(clientOptions: ClientOptions, params: Params): HttpRequest = + toBuilder() + .putAllQueryParams(clientOptions.queryParams) + .replaceAllQueryParams(params._queryParams()) + .putAllHeaders(clientOptions.headers) + .replaceAllHeaders(params._headers()) + .build() + +@JvmSynthetic +internal fun HttpRequest.prepareAsync( + clientOptions: ClientOptions, + params: Params +): CompletableFuture = + // This async version exists to make it easier to add async specific preparation logic in the + // future. + CompletableFuture.completedFuture(prepare(clientOptions, params)) diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/PhantomReachableClosingHttpClient.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/PhantomReachableClosingHttpClient.kt index 925c03a..358cb6f 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/PhantomReachableClosingHttpClient.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/PhantomReachableClosingHttpClient.kt @@ -4,6 +4,11 @@ import java.util.concurrent.CompletableFuture import so.prelude.sdk.core.RequestOptions import so.prelude.sdk.core.closeWhenPhantomReachable +/** + * A delegating wrapper around an `HttpClient` that closes it once it's only phantom reachable. + * + * This class ensures the `HttpClient` is closed even if the user forgets to close it. + */ internal class PhantomReachableClosingHttpClient(private val httpClient: HttpClient) : HttpClient { init { closeWhenPhantomReachable(this, httpClient) diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/RetryingHttpClient.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/RetryingHttpClient.kt index eab26fc..5881f87 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/RetryingHttpClient.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/core/http/RetryingHttpClient.kt @@ -57,15 +57,17 @@ private constructor( } response - } catch (t: Throwable) { - if (++retries > maxRetries || !shouldRetry(t)) { - throw t + } catch (throwable: Throwable) { + if (++retries > maxRetries || !shouldRetry(throwable)) { + throw throwable } null } val backoffMillis = getRetryBackoffMillis(retries, response) + // All responses must be closed, so close the failed one before retrying. + response?.close() Thread.sleep(backoffMillis.toMillis()) } } @@ -113,6 +115,8 @@ private constructor( } val backoffMillis = getRetryBackoffMillis(retries, response) + // All responses must be closed, so close the failed one before retrying. + response?.close() return sleepAsync(backoffMillis.toMillis()).thenCompose { executeWithRetries(requestWithRetryCount, requestOptions) } @@ -223,23 +227,23 @@ private constructor( return Duration.ofNanos((TimeUnit.SECONDS.toNanos(1) * backoffSeconds * jitter).toLong()) } - private fun sleepAsync(millis: Long): CompletableFuture { - val future = CompletableFuture() - TIMER.schedule( - object : TimerTask() { - override fun run() { - future.complete(null) - } - }, - millis - ) - return future - } - 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() } diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/TransactionalSendParams.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/TransactionalSendParams.kt index 6f9da02..14b8b56 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/TransactionalSendParams.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/TransactionalSendParams.kt @@ -13,6 +13,7 @@ import so.prelude.sdk.core.JsonField import so.prelude.sdk.core.JsonMissing import so.prelude.sdk.core.JsonValue import so.prelude.sdk.core.NoAutoDetect +import so.prelude.sdk.core.Params import so.prelude.sdk.core.checkRequired import so.prelude.sdk.core.http.Headers import so.prelude.sdk.core.http.QueryParams @@ -25,7 +26,7 @@ private constructor( private val body: TransactionalSendBody, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, -) { +) : Params { /** The template identifier. */ fun templateId(): String = body.templateId() @@ -91,11 +92,11 @@ private constructor( fun _additionalQueryParams(): QueryParams = additionalQueryParams - @JvmSynthetic internal fun getBody(): TransactionalSendBody = body + @JvmSynthetic internal fun _body(): TransactionalSendBody = body - @JvmSynthetic internal fun getHeaders(): Headers = additionalHeaders + override fun _headers(): Headers = additionalHeaders - @JvmSynthetic internal fun getQueryParams(): QueryParams = additionalQueryParams + override fun _queryParams(): QueryParams = additionalQueryParams @NoAutoDetect class TransactionalSendBody diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCheckParams.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCheckParams.kt index ba21e0b..edf6eed 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCheckParams.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCheckParams.kt @@ -13,6 +13,7 @@ import so.prelude.sdk.core.JsonField import so.prelude.sdk.core.JsonMissing import so.prelude.sdk.core.JsonValue import so.prelude.sdk.core.NoAutoDetect +import so.prelude.sdk.core.Params import so.prelude.sdk.core.checkRequired import so.prelude.sdk.core.http.Headers import so.prelude.sdk.core.http.QueryParams @@ -26,7 +27,7 @@ private constructor( private val body: VerificationCheckBody, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, -) { +) : Params { /** The OTP code to validate. */ fun code(): String = body.code() @@ -46,11 +47,11 @@ private constructor( fun _additionalQueryParams(): QueryParams = additionalQueryParams - @JvmSynthetic internal fun getBody(): VerificationCheckBody = body + @JvmSynthetic internal fun _body(): VerificationCheckBody = body - @JvmSynthetic internal fun getHeaders(): Headers = additionalHeaders + override fun _headers(): Headers = additionalHeaders - @JvmSynthetic internal fun getQueryParams(): QueryParams = additionalQueryParams + override fun _queryParams(): QueryParams = additionalQueryParams @NoAutoDetect class VerificationCheckBody diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCreateParams.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCreateParams.kt index ef958ca..f288af9 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCreateParams.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/VerificationCreateParams.kt @@ -14,6 +14,7 @@ import so.prelude.sdk.core.JsonField import so.prelude.sdk.core.JsonMissing import so.prelude.sdk.core.JsonValue import so.prelude.sdk.core.NoAutoDetect +import so.prelude.sdk.core.Params import so.prelude.sdk.core.checkRequired import so.prelude.sdk.core.http.Headers import so.prelude.sdk.core.http.QueryParams @@ -31,7 +32,7 @@ private constructor( private val body: VerificationCreateBody, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, -) { +) : Params { /** The target. Currently this can only be an E.164 formatted phone number. */ fun target(): Target = body.target() @@ -75,11 +76,11 @@ private constructor( fun _additionalQueryParams(): QueryParams = additionalQueryParams - @JvmSynthetic internal fun getBody(): VerificationCreateBody = body + @JvmSynthetic internal fun _body(): VerificationCreateBody = body - @JvmSynthetic internal fun getHeaders(): Headers = additionalHeaders + override fun _headers(): Headers = additionalHeaders - @JvmSynthetic internal fun getQueryParams(): QueryParams = additionalQueryParams + override fun _queryParams(): QueryParams = additionalQueryParams @NoAutoDetect class VerificationCreateBody diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchFeedBackParams.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchFeedBackParams.kt index fc1cd84..f08a4e7 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchFeedBackParams.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchFeedBackParams.kt @@ -13,6 +13,7 @@ import so.prelude.sdk.core.JsonField import so.prelude.sdk.core.JsonMissing import so.prelude.sdk.core.JsonValue import so.prelude.sdk.core.NoAutoDetect +import so.prelude.sdk.core.Params import so.prelude.sdk.core.checkRequired import so.prelude.sdk.core.http.Headers import so.prelude.sdk.core.http.QueryParams @@ -29,7 +30,7 @@ private constructor( private val body: WatchFeedBackBody, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, -) { +) : Params { /** * You should send a feedback event back to Watch API when your user demonstrates authentic @@ -55,11 +56,11 @@ private constructor( fun _additionalQueryParams(): QueryParams = additionalQueryParams - @JvmSynthetic internal fun getBody(): WatchFeedBackBody = body + @JvmSynthetic internal fun _body(): WatchFeedBackBody = body - @JvmSynthetic internal fun getHeaders(): Headers = additionalHeaders + override fun _headers(): Headers = additionalHeaders - @JvmSynthetic internal fun getQueryParams(): QueryParams = additionalQueryParams + override fun _queryParams(): QueryParams = additionalQueryParams @NoAutoDetect class WatchFeedBackBody diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchPredictParams.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchPredictParams.kt index d86dce5..5d31397 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchPredictParams.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/models/WatchPredictParams.kt @@ -14,6 +14,7 @@ import so.prelude.sdk.core.JsonField import so.prelude.sdk.core.JsonMissing import so.prelude.sdk.core.JsonValue import so.prelude.sdk.core.NoAutoDetect +import so.prelude.sdk.core.Params import so.prelude.sdk.core.checkRequired import so.prelude.sdk.core.http.Headers import so.prelude.sdk.core.http.QueryParams @@ -31,7 +32,7 @@ private constructor( private val body: WatchPredictBody, private val additionalHeaders: Headers, private val additionalQueryParams: QueryParams, -) { +) : Params { /** The target. Currently this can only be an E.164 formatted phone number. */ fun target(): Target = body.target() @@ -51,11 +52,11 @@ private constructor( fun _additionalQueryParams(): QueryParams = additionalQueryParams - @JvmSynthetic internal fun getBody(): WatchPredictBody = body + @JvmSynthetic internal fun _body(): WatchPredictBody = body - @JvmSynthetic internal fun getHeaders(): Headers = additionalHeaders + override fun _headers(): Headers = additionalHeaders - @JvmSynthetic internal fun getQueryParams(): QueryParams = additionalQueryParams + override fun _queryParams(): QueryParams = additionalQueryParams @NoAutoDetect class WatchPredictBody diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/TransactionalServiceAsyncImpl.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/TransactionalServiceAsyncImpl.kt index e833082..4ed1515 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/TransactionalServiceAsyncImpl.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/TransactionalServiceAsyncImpl.kt @@ -12,6 +12,7 @@ import so.prelude.sdk.core.http.HttpMethod import so.prelude.sdk.core.http.HttpRequest import so.prelude.sdk.core.http.HttpResponse.Handler import so.prelude.sdk.core.json +import so.prelude.sdk.core.prepareAsync import so.prelude.sdk.errors.PreludeError import so.prelude.sdk.models.TransactionalSendParams import so.prelude.sdk.models.TransactionalSendResponse @@ -36,21 +37,19 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "transactional") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.executeAsync(request, requestOptions).thenApply { response - -> - response - .use { sendHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() + .prepareAsync(clientOptions, params) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response + .use { sendHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() + } } - } - } + } } } diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/VerificationServiceAsyncImpl.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/VerificationServiceAsyncImpl.kt index 5b74c66..5c24622 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/VerificationServiceAsyncImpl.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/VerificationServiceAsyncImpl.kt @@ -12,6 +12,7 @@ import so.prelude.sdk.core.http.HttpMethod import so.prelude.sdk.core.http.HttpRequest import so.prelude.sdk.core.http.HttpResponse.Handler import so.prelude.sdk.core.json +import so.prelude.sdk.core.prepareAsync import so.prelude.sdk.errors.PreludeError import so.prelude.sdk.models.VerificationCheckParams import so.prelude.sdk.models.VerificationCheckResponse @@ -42,22 +43,20 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "verification") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.executeAsync(request, requestOptions).thenApply { response - -> - response - .use { createHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() + .prepareAsync(clientOptions, params) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() + } } - } - } + } } private val checkHandler: Handler = @@ -73,21 +72,19 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "verification", "check") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.executeAsync(request, requestOptions).thenApply { response - -> - response - .use { checkHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() + .prepareAsync(clientOptions, params) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response + .use { checkHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() + } } - } - } + } } } diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/WatchServiceAsyncImpl.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/WatchServiceAsyncImpl.kt index f6df4d6..00cf85e 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/WatchServiceAsyncImpl.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/async/WatchServiceAsyncImpl.kt @@ -12,6 +12,7 @@ import so.prelude.sdk.core.http.HttpMethod import so.prelude.sdk.core.http.HttpRequest import so.prelude.sdk.core.http.HttpResponse.Handler import so.prelude.sdk.core.json +import so.prelude.sdk.core.prepareAsync import so.prelude.sdk.errors.PreludeError import so.prelude.sdk.models.WatchFeedBackParams import so.prelude.sdk.models.WatchFeedBackResponse @@ -40,22 +41,20 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "watch", "feedback") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.executeAsync(request, requestOptions).thenApply { response - -> - response - .use { feedBackHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() + .prepareAsync(clientOptions, params) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response + .use { feedBackHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() + } } - } - } + } } private val predictHandler: Handler = @@ -74,21 +73,19 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "watch", "predict") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.executeAsync(request, requestOptions).thenApply { response - -> - response - .use { predictHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() + .prepareAsync(clientOptions, params) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + response + .use { predictHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() + } } - } - } + } } } diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/TransactionalServiceImpl.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/TransactionalServiceImpl.kt index a3ae8cf..04341c4 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/TransactionalServiceImpl.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/TransactionalServiceImpl.kt @@ -11,6 +11,7 @@ import so.prelude.sdk.core.http.HttpMethod import so.prelude.sdk.core.http.HttpRequest import so.prelude.sdk.core.http.HttpResponse.Handler import so.prelude.sdk.core.json +import so.prelude.sdk.core.prepare import so.prelude.sdk.errors.PreludeError import so.prelude.sdk.models.TransactionalSendParams import so.prelude.sdk.models.TransactionalSendResponse @@ -35,20 +36,16 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "transactional") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.execute(request, requestOptions).let { response -> - response - .use { sendHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() - } + .prepare(clientOptions, params) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response + .use { sendHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() } - } + } } } diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/VerificationServiceImpl.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/VerificationServiceImpl.kt index e7d3463..854099f 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/VerificationServiceImpl.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/VerificationServiceImpl.kt @@ -11,6 +11,7 @@ import so.prelude.sdk.core.http.HttpMethod import so.prelude.sdk.core.http.HttpRequest import so.prelude.sdk.core.http.HttpResponse.Handler import so.prelude.sdk.core.json +import so.prelude.sdk.core.prepare import so.prelude.sdk.errors.PreludeError import so.prelude.sdk.models.VerificationCheckParams import so.prelude.sdk.models.VerificationCheckResponse @@ -41,21 +42,17 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "verification") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.execute(request, requestOptions).let { response -> - response - .use { createHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() - } + .prepare(clientOptions, params) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() } - } + } } private val checkHandler: Handler = @@ -71,20 +68,16 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "verification", "check") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.execute(request, requestOptions).let { response -> - response - .use { checkHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() - } + .prepare(clientOptions, params) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response + .use { checkHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() } - } + } } } diff --git a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/WatchServiceImpl.kt b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/WatchServiceImpl.kt index 8fd069c..462719e 100644 --- a/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/WatchServiceImpl.kt +++ b/prelude-java-core/src/main/kotlin/so/prelude/sdk/services/blocking/WatchServiceImpl.kt @@ -11,6 +11,7 @@ import so.prelude.sdk.core.http.HttpMethod import so.prelude.sdk.core.http.HttpRequest import so.prelude.sdk.core.http.HttpResponse.Handler import so.prelude.sdk.core.json +import so.prelude.sdk.core.prepare import so.prelude.sdk.errors.PreludeError import so.prelude.sdk.models.WatchFeedBackParams import so.prelude.sdk.models.WatchFeedBackResponse @@ -39,21 +40,17 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "watch", "feedback") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.execute(request, requestOptions).let { response -> - response - .use { feedBackHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() - } + .prepare(clientOptions, params) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response + .use { feedBackHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() } - } + } } private val predictHandler: Handler = @@ -72,20 +69,16 @@ internal constructor( HttpRequest.builder() .method(HttpMethod.POST) .addPathSegments("v2", "watch", "predict") - .putAllQueryParams(clientOptions.queryParams) - .replaceAllQueryParams(params.getQueryParams()) - .putAllHeaders(clientOptions.headers) - .replaceAllHeaders(params.getHeaders()) - .body(json(clientOptions.jsonMapper, params.getBody())) + .body(json(clientOptions.jsonMapper, params._body())) .build() - return clientOptions.httpClient.execute(request, requestOptions).let { response -> - response - .use { predictHandler.handle(it) } - .apply { - if (requestOptions.responseValidation ?: clientOptions.responseValidation) { - validate() - } + .prepare(clientOptions, params) + val response = clientOptions.httpClient.execute(request, requestOptions) + return response + .use { predictHandler.handle(it) } + .also { + if (requestOptions.responseValidation ?: clientOptions.responseValidation) { + it.validate() } - } + } } } diff --git a/prelude-java-core/src/test/kotlin/so/prelude/sdk/core/http/RetryingHttpClientTest.kt b/prelude-java-core/src/test/kotlin/so/prelude/sdk/core/http/RetryingHttpClientTest.kt index f196d7a..3864f52 100644 --- a/prelude-java-core/src/test/kotlin/so/prelude/sdk/core/http/RetryingHttpClientTest.kt +++ b/prelude-java-core/src/test/kotlin/so/prelude/sdk/core/http/RetryingHttpClientTest.kt @@ -4,39 +4,84 @@ import com.github.tomakehurst.wiremock.client.WireMock.* import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo import com.github.tomakehurst.wiremock.junit5.WireMockTest import com.github.tomakehurst.wiremock.stubbing.Scenario +import java.io.InputStream +import java.util.concurrent.CompletableFuture import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import so.prelude.sdk.client.okhttp.OkHttpClient +import so.prelude.sdk.core.RequestOptions @WireMockTest internal class RetryingHttpClientTest { + private var openResponseCount = 0 private lateinit var httpClient: HttpClient @BeforeEach fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { - httpClient = OkHttpClient.builder().baseUrl(wmRuntimeInfo.httpBaseUrl).build() + val okHttpClient = OkHttpClient.builder().baseUrl(wmRuntimeInfo.httpBaseUrl).build() + httpClient = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions + ): HttpResponse = trackClose(okHttpClient.execute(request, requestOptions)) + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions + ): CompletableFuture = + okHttpClient.executeAsync(request, requestOptions).thenApply { trackClose(it) } + + override fun close() = okHttpClient.close() + + private fun trackClose(response: HttpResponse): HttpResponse { + openResponseCount++ + return object : HttpResponse { + private var isClosed = false + + override fun statusCode(): Int = response.statusCode() + + override fun headers(): Headers = response.headers() + + override fun body(): InputStream = response.body() + + override fun close() { + response.close() + if (isClosed) { + return + } + openResponseCount-- + isClosed = true + } + } + } + } resetAllScenarios() } - @Test - fun byDefaultShouldNotAddIdempotencyHeaderToRequest() { - val request = - HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build() + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute(async: Boolean) { stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) val retryingClient = RetryingHttpClient.builder().httpClient(httpClient).build() - val response = retryingClient.execute(request) + + val response = + retryingClient.execute( + HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build(), + async + ) + assertThat(response.statusCode()).isEqualTo(200) verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertNoResponseLeaks() } - @Test - fun whenProvidedShouldAddIdempotencyHeaderToRequest() { - val request = - HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build() + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withIdempotencyHeader(async: Boolean) { stubFor( post(urlPathEqualTo("/something")) .withHeader("X-Some-Header", matching("stainless-java-retry-.+")) @@ -48,19 +93,25 @@ internal class RetryingHttpClientTest { .maxRetries(2) .idempotencyHeader("X-Some-Header") .build() - val response = retryingClient.execute(request) + + val response = + retryingClient.execute( + HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build(), + async + ) + assertThat(response.statusCode()).isEqualTo(200) verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertNoResponseLeaks() } @ParameterizedTest @ValueSource(booleans = [false, true]) - fun retryAfterHeader(async: Boolean) { - val request = - HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build() + fun execute_withRetryAfterHeader(async: Boolean) { stubFor( post(urlPathEqualTo("/something")) - .inScenario("foo") // first we fail with a retry after header given as a date + // First we fail with a retry after header given as a date + .inScenario("foo") .whenScenarioStateIs(Scenario.STARTED) .willReturn( serviceUnavailable().withHeader("Retry-After", "Wed, 21 Oct 2015 07:28:00 GMT") @@ -69,14 +120,16 @@ internal class RetryingHttpClientTest { ) stubFor( post(urlPathEqualTo("/something")) - .inScenario("foo") // then we fail with a retry after header given as a delay + // Then we fail with a retry after header given as a delay + .inScenario("foo") .whenScenarioStateIs("RETRY_AFTER_DATE") .willReturn(serviceUnavailable().withHeader("Retry-After", "1.234")) .willSetStateTo("RETRY_AFTER_DELAY") ) stubFor( post(urlPathEqualTo("/something")) - .inScenario("foo") // then we return a success + // Then we return a success + .inScenario("foo") .whenScenarioStateIs("RETRY_AFTER_DELAY") .willReturn(ok()) .willSetStateTo("COMPLETED") @@ -85,8 +138,10 @@ internal class RetryingHttpClientTest { RetryingHttpClient.builder().httpClient(httpClient).maxRetries(2).build() val response = - if (async) retryingClient.executeAsync(request).get() - else retryingClient.execute(request) + retryingClient.execute( + HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build(), + async + ) assertThat(response.statusCode()).isEqualTo(200) verify( @@ -104,17 +159,12 @@ internal class RetryingHttpClientTest { postRequestedFor(urlPathEqualTo("/something")) .withHeader("x-stainless-retry-count", equalTo("2")) ) + assertNoResponseLeaks() } @ParameterizedTest @ValueSource(booleans = [false, true]) - fun overwriteRetryCountHeader(async: Boolean) { - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .addPathSegment("something") - .putHeader("x-stainless-retry-count", "42") - .build() + fun execute_withOverwrittenRetryCountHeader(async: Boolean) { stubFor( post(urlPathEqualTo("/something")) .inScenario("foo") // first we fail with a retry after header given as a date @@ -135,8 +185,14 @@ internal class RetryingHttpClientTest { RetryingHttpClient.builder().httpClient(httpClient).maxRetries(2).build() val response = - if (async) retryingClient.executeAsync(request).get() - else retryingClient.execute(request) + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .addPathSegment("something") + .putHeader("x-stainless-retry-count", "42") + .build(), + async + ) assertThat(response.statusCode()).isEqualTo(200) verify( @@ -144,12 +200,12 @@ internal class RetryingHttpClientTest { postRequestedFor(urlPathEqualTo("/something")) .withHeader("x-stainless-retry-count", equalTo("42")) ) + assertNoResponseLeaks() } - @Test - fun retryAfterMsHeader() { - val request = - HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build() + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterMsHeader(async: Boolean) { stubFor( post(urlPathEqualTo("/something")) .inScenario("foo") @@ -166,8 +222,22 @@ internal class RetryingHttpClientTest { ) val retryingClient = RetryingHttpClient.builder().httpClient(httpClient).maxRetries(1).build() - val response = retryingClient.execute(request) + + val response = + retryingClient.execute( + HttpRequest.builder().method(HttpMethod.POST).addPathSegment("something").build(), + async + ) + assertThat(response.statusCode()).isEqualTo(200) verify(2, postRequestedFor(urlPathEqualTo("/something"))) + assertNoResponseLeaks() } + + private fun HttpClient.execute(request: HttpRequest, async: Boolean): HttpResponse = + if (async) executeAsync(request).get() else execute(request) + + // When retrying, all failed responses should be closed. Only the final returned response should + // be open. + private fun assertNoResponseLeaks() = assertThat(openResponseCount).isEqualTo(1) } diff --git a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/TransactionalSendParamsTest.kt b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/TransactionalSendParamsTest.kt index d9e3bf9..32eecd4 100644 --- a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/TransactionalSendParamsTest.kt +++ b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/TransactionalSendParamsTest.kt @@ -9,7 +9,7 @@ import so.prelude.sdk.core.JsonValue class TransactionalSendParamsTest { @Test - fun createTransactionalSendParams() { + fun create() { TransactionalSendParams.builder() .templateId("template_01jd1xq0cffycayqtdkdbv4d61") .to("+30123456789") @@ -27,7 +27,7 @@ class TransactionalSendParamsTest { } @Test - fun getBody() { + fun body() { val params = TransactionalSendParams.builder() .templateId("template_01jd1xq0cffycayqtdkdbv4d61") @@ -43,7 +43,7 @@ class TransactionalSendParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.templateId()).isEqualTo("template_01jd1xq0cffycayqtdkdbv4d61") assertThat(body.to()).isEqualTo("+30123456789") @@ -61,13 +61,13 @@ class TransactionalSendParamsTest { } @Test - fun getBodyWithoutOptionalFields() { + fun bodyWithoutOptionalFields() { val params = TransactionalSendParams.builder() .templateId("template_01jd1xq0cffycayqtdkdbv4d61") .to("+30123456789") .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.templateId()).isEqualTo("template_01jd1xq0cffycayqtdkdbv4d61") assertThat(body.to()).isEqualTo("+30123456789") diff --git a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCheckParamsTest.kt b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCheckParamsTest.kt index 8e7daba..5bd7498 100644 --- a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCheckParamsTest.kt +++ b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCheckParamsTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test class VerificationCheckParamsTest { @Test - fun createVerificationCheckParams() { + fun create() { VerificationCheckParams.builder() .code("12345") .target( @@ -21,7 +21,7 @@ class VerificationCheckParamsTest { } @Test - fun getBody() { + fun body() { val params = VerificationCheckParams.builder() .code("12345") @@ -32,7 +32,7 @@ class VerificationCheckParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.code()).isEqualTo("12345") assertThat(body.target()) @@ -45,7 +45,7 @@ class VerificationCheckParamsTest { } @Test - fun getBodyWithoutOptionalFields() { + fun bodyWithoutOptionalFields() { val params = VerificationCheckParams.builder() .code("12345") @@ -56,7 +56,7 @@ class VerificationCheckParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.code()).isEqualTo("12345") assertThat(body.target()) diff --git a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCreateParamsTest.kt b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCreateParamsTest.kt index 9ccbc48..7d09705 100644 --- a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCreateParamsTest.kt +++ b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/VerificationCreateParamsTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test class VerificationCreateParamsTest { @Test - fun createVerificationCreateParams() { + fun create() { VerificationCreateParams.builder() .target( VerificationCreateParams.Target.builder() @@ -50,7 +50,7 @@ class VerificationCreateParamsTest { } @Test - fun getBody() { + fun body() { val params = VerificationCreateParams.builder() .target( @@ -94,7 +94,7 @@ class VerificationCreateParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.target()) .isEqualTo( @@ -139,7 +139,7 @@ class VerificationCreateParamsTest { } @Test - fun getBodyWithoutOptionalFields() { + fun bodyWithoutOptionalFields() { val params = VerificationCreateParams.builder() .target( @@ -149,7 +149,7 @@ class VerificationCreateParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.target()) .isEqualTo( diff --git a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchFeedBackParamsTest.kt b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchFeedBackParamsTest.kt index 2f3b4df..8f66635 100644 --- a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchFeedBackParamsTest.kt +++ b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchFeedBackParamsTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test class WatchFeedBackParamsTest { @Test - fun createWatchFeedBackParams() { + fun create() { WatchFeedBackParams.builder() .feedback( WatchFeedBackParams.Feedback.builder() @@ -25,7 +25,7 @@ class WatchFeedBackParamsTest { } @Test - fun getBody() { + fun body() { val params = WatchFeedBackParams.builder() .feedback( @@ -40,7 +40,7 @@ class WatchFeedBackParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.feedback()) .isEqualTo( @@ -58,7 +58,7 @@ class WatchFeedBackParamsTest { } @Test - fun getBodyWithoutOptionalFields() { + fun bodyWithoutOptionalFields() { val params = WatchFeedBackParams.builder() .feedback( @@ -73,7 +73,7 @@ class WatchFeedBackParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.feedback()) .isEqualTo( diff --git a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchPredictParamsTest.kt b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchPredictParamsTest.kt index de8a4b0..4ceffd7 100644 --- a/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchPredictParamsTest.kt +++ b/prelude-java-core/src/test/kotlin/so/prelude/sdk/models/WatchPredictParamsTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test class WatchPredictParamsTest { @Test - fun createWatchPredictParams() { + fun create() { WatchPredictParams.builder() .target( WatchPredictParams.Target.builder() @@ -28,7 +28,7 @@ class WatchPredictParamsTest { } @Test - fun getBody() { + fun body() { val params = WatchPredictParams.builder() .target( @@ -46,7 +46,7 @@ class WatchPredictParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.target()) .isEqualTo( @@ -67,7 +67,7 @@ class WatchPredictParamsTest { } @Test - fun getBodyWithoutOptionalFields() { + fun bodyWithoutOptionalFields() { val params = WatchPredictParams.builder() .target( @@ -77,7 +77,7 @@ class WatchPredictParamsTest { .build() ) .build() - val body = params.getBody() + val body = params._body() assertThat(body).isNotNull assertThat(body.target()) .isEqualTo(