diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index eedd0cf..30ff679 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,10 +20,10 @@ jobs:
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up Java
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
@@ -40,14 +40,17 @@ jobs:
build:
timeout-minutes: 15
name: build
+ permissions:
+ contents: read
+ id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/cas-parser-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up Java
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
@@ -61,16 +64,31 @@ jobs:
- name: Build SDK
run: ./scripts/build
+ - name: Get GitHub OIDC Token
+ if: github.repository == 'stainless-sdks/cas-parser-java'
+ id: github-oidc
+ uses: actions/github-script@v6
+ with:
+ script: core.setOutput('github_token', await core.getIDToken());
+
+ - name: Build and upload Maven artifacts
+ if: github.repository == 'stainless-sdks/cas-parser-java'
+ env:
+ URL: https://pkg.stainless.com/s
+ AUTH: ${{ steps.github-oidc.outputs.github_token }}
+ SHA: ${{ github.sha }}
+ PROJECT: cas-parser-java
+ run: ./scripts/upload-artifacts
test:
timeout-minutes: 15
name: test
runs-on: ${{ github.repository == 'stainless-sdks/cas-parser-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up Java
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml
index 1b11f45..2bfe3c5 100644
--- a/.github/workflows/publish-sonatype.yml
+++ b/.github/workflows/publish-sonatype.yml
@@ -14,10 +14,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Set up Java
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v5
with:
distribution: temurin
java-version: |
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 87d49ed..a636804 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -12,7 +12,7 @@ jobs:
if: github.repository == 'CASParser/cas-parser-java' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Check release environment
run: |
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 2ed3b71..3d2ac0b 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.0.4"
+ ".": "0.1.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 92721c7..968a0a4 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 5
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-b7fdba3d3f97c7debc22c7ca30b828bce81bcd64648df8c94029b27a3321ebb9.yml
-openapi_spec_hash: 03f1315f1d32ada42445ca920f047dff
+configured_endpoints: 4
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-ce2296c4b14d27c141bb2745607d2456c923fdca3ae0a0a0800c26e564333850.yml
+openapi_spec_hash: 8eb586ccf16b534c0c15ff6a22274c7d
config_hash: cb5d75abef6264b5d86448caf7295afa
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f21b326..2b43336 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,29 @@
# Changelog
+## 0.1.0 (2026-01-20)
+
+Full Changelog: [v0.0.4...v0.1.0](https://github.com/CASParser/cas-parser-java/compare/v0.0.4...v0.1.0)
+
+### Features
+
+* **api:** api update ([1c7a4f1](https://github.com/CASParser/cas-parser-java/commit/1c7a4f1eebb4b336dc85f73a0319b48d568bf06c))
+* **api:** api update ([2e6c34f](https://github.com/CASParser/cas-parser-java/commit/2e6c34fbe30670c8ae1a74eb7f0f8fc5e8bb1890))
+* **api:** api update ([91a54b7](https://github.com/CASParser/cas-parser-java/commit/91a54b7acbf164ad459293fe697a4b0c9a795758))
+* **api:** api update ([557721f](https://github.com/CASParser/cas-parser-java/commit/557721f0775228db057005e912d8f820f512fcca))
+* **client:** expose sleeper option ([482319f](https://github.com/CASParser/cas-parser-java/commit/482319f0e19023df4e3ba5414e38914f6ad037fa))
+
+
+### Bug Fixes
+
+* **client:** deserialization of empty objects ([ce29632](https://github.com/CASParser/cas-parser-java/commit/ce29632f7f03c172ad042bbaa68e431ab2fd7fa7))
+* **client:** ensure single timer is created per client ([482319f](https://github.com/CASParser/cas-parser-java/commit/482319f0e19023df4e3ba5414e38914f6ad037fa))
+* **client:** incorrect `getPackageVersion` impl ([6772c5e](https://github.com/CASParser/cas-parser-java/commit/6772c5eeb5557a1e494852713cc55ec722b75145))
+
+
+### Chores
+
+* improve formatter performance ([692db64](https://github.com/CASParser/cas-parser-java/commit/692db64a36ef927e9c0ea23bd57192af99aaaf02))
+
## 0.0.4 (2025-09-13)
Full Changelog: [v0.0.3...v0.0.4](https://github.com/CASParser/cas-parser-java/compare/v0.0.3...v0.0.4)
diff --git a/LICENSE b/LICENSE
index f1756ce..6bbb512 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2025 Cas Parser
+ Copyright 2026 Cas Parser
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index eb1c0bb..bea2ccb 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.cas_parser.api/cas-parser-java/0.0.4)
-[](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.0.4)
+[](https://central.sonatype.com/artifact/com.cas_parser.api/cas-parser-java/0.1.0)
+[](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.1.0)
@@ -11,9 +11,18 @@ The Cas Parser Java SDK provides convenient access to the [Cas Parser REST API](
It is generated with [Stainless](https://www.stainless.com/).
+## MCP Server
+
+Use the Cas Parser MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application.
+
+[](https://cursor.com/en-US/install-mcp?name=cas-parser-node-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImNhcy1wYXJzZXItbm9kZS1tY3AiXX0)
+[](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22cas-parser-node-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22cas-parser-node-mcp%22%5D%7D)
+
+> Note: You may need to set environment variables in your MCP client.
+
-The REST API documentation can be found on [docs.casparser.in](https://docs.casparser.in/reference). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.0.4).
+The REST API documentation can be found on [docs.casparser.in](https://docs.casparser.in/reference). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.1.0).
@@ -24,7 +33,7 @@ The REST API documentation can be found on [docs.casparser.in](https://docs.casp
### Gradle
```kotlin
-implementation("com.cas_parser.api:cas-parser-java:0.0.4")
+implementation("com.cas_parser.api:cas-parser-java:0.1.0")
```
### Maven
@@ -33,7 +42,7 @@ implementation("com.cas_parser.api:cas-parser-java:0.0.4")
com.cas_parser.api
cas-parser-java
- 0.0.4
+ 0.1.0
```
@@ -248,13 +257,13 @@ The SDK uses the standard [OkHttp logging interceptor](https://github.com/square
Enable logging by setting the `CAS_PARSER_LOG` environment variable to `info`:
```sh
-$ export CAS_PARSER_LOG=info
+export CAS_PARSER_LOG=info
```
Or to `debug` for more verbose logging:
```sh
-$ export CAS_PARSER_LOG=debug
+export CAS_PARSER_LOG=debug
```
## ProGuard and R8
@@ -274,6 +283,8 @@ If the SDK threw an exception, but you're _certain_ the version is compatible, t
> [!CAUTION]
> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.
+Also note that there are bugs in older Jackson versions that can affect the SDK. We don't work around all Jackson bugs ([example](https://github.com/FasterXML/jackson-databind/issues/3240)) and expect users to upgrade Jackson for those instead.
+
## Network options
### Retries
@@ -476,23 +487,6 @@ JsonValue complexValue = JsonValue.from(Map.of(
));
```
-Normally a `Builder` class's `build` method will throw [`IllegalStateException`](https://docs.oracle.com/javase/8/docs/api/java/lang/IllegalStateException.html) if any required parameter or property is unset.
-
-To forcibly omit a required parameter or property, pass [`JsonMissing`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Values.kt):
-
-```java
-import com.cas_parser.api.core.JsonMissing;
-import com.cas_parser.api.models.casgenerator.CasGeneratorGenerateCasParams;
-import com.cas_parser.api.models.casparser.CasParserSmartParseParams;
-
-CasParserSmartParseParams params = CasGeneratorGenerateCasParams.builder()
- .fromDate("2023-01-01")
- .password("Abcdefghi12$")
- .toDate("2023-12-31")
- .email(JsonMissing.of())
- .build();
-```
-
### Response properties
To access undocumented response properties, call the `_additionalProperties()` method:
diff --git a/build.gradle.kts b/build.gradle.kts
index d58d78e..30b3cae 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,7 +9,7 @@ repositories {
allprojects {
group = "com.cas_parser.api"
- version = "0.0.4" // x-release-please-version
+ version = "0.1.0" // x-release-please-version
}
subprojects {
diff --git a/buildSrc/src/main/kotlin/cas-parser.publish.gradle.kts b/buildSrc/src/main/kotlin/cas-parser.publish.gradle.kts
index ef287d8..26aec10 100644
--- a/buildSrc/src/main/kotlin/cas-parser.publish.gradle.kts
+++ b/buildSrc/src/main/kotlin/cas-parser.publish.gradle.kts
@@ -40,6 +40,14 @@ configure {
}
}
}
+ repositories {
+ if (project.hasProperty("publishLocal")) {
+ maven {
+ name = "LocalFileSystem"
+ url = uri("${rootProject.layout.buildDirectory.get()}/local-maven-repo")
+ }
+ }
+ }
}
signing {
diff --git a/cas-parser-java-client-okhttp/build.gradle.kts b/cas-parser-java-client-okhttp/build.gradle.kts
index 115d28c..91df2a9 100644
--- a/cas-parser-java-client-okhttp/build.gradle.kts
+++ b/cas-parser-java-client-okhttp/build.gradle.kts
@@ -11,4 +11,5 @@ dependencies {
testImplementation(kotlin("test"))
testImplementation("org.assertj:assertj-core:3.25.3")
+ testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2")
}
diff --git a/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt
index 687527a..93a7aeb 100644
--- a/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt
+++ b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt
@@ -5,6 +5,7 @@ package com.cas_parser.api.client.okhttp
import com.cas_parser.api.client.CasParserClient
import com.cas_parser.api.client.CasParserClientImpl
import com.cas_parser.api.core.ClientOptions
+import com.cas_parser.api.core.Sleeper
import com.cas_parser.api.core.Timeout
import com.cas_parser.api.core.http.Headers
import com.cas_parser.api.core.http.HttpClient
@@ -15,6 +16,7 @@ import java.net.Proxy
import java.time.Clock
import java.time.Duration
import java.util.Optional
+import java.util.concurrent.ExecutorService
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
@@ -43,11 +45,31 @@ class CasParserOkHttpClient private constructor() {
class Builder internal constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
+ private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
+ /**
+ * The executor service to use for running HTTP requests.
+ *
+ * Defaults to OkHttp's
+ * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
+ *
+ * This class takes ownership of the executor service and shuts it down when closed.
+ */
+ fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
+ this.dispatcherExecutorService = dispatcherExecutorService
+ }
+
+ /**
+ * Alias for calling [Builder.dispatcherExecutorService] with
+ * `dispatcherExecutorService.orElse(null)`.
+ */
+ fun dispatcherExecutorService(dispatcherExecutorService: Optional) =
+ dispatcherExecutorService(dispatcherExecutorService.getOrNull())
+
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
@@ -120,6 +142,17 @@ class CasParserOkHttpClient private constructor() {
*/
fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) }
+
/**
* The clock to use for operations that require timing, like retries.
*
@@ -285,6 +318,7 @@ class CasParserOkHttpClient private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
.hostnameVerifier(hostnameVerifier)
diff --git a/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt
index 186bbe4..f4858a0 100644
--- a/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt
+++ b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt
@@ -5,6 +5,7 @@ package com.cas_parser.api.client.okhttp
import com.cas_parser.api.client.CasParserClientAsync
import com.cas_parser.api.client.CasParserClientAsyncImpl
import com.cas_parser.api.core.ClientOptions
+import com.cas_parser.api.core.Sleeper
import com.cas_parser.api.core.Timeout
import com.cas_parser.api.core.http.Headers
import com.cas_parser.api.core.http.HttpClient
@@ -15,6 +16,7 @@ import java.net.Proxy
import java.time.Clock
import java.time.Duration
import java.util.Optional
+import java.util.concurrent.ExecutorService
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
@@ -43,11 +45,31 @@ class CasParserOkHttpClientAsync private constructor() {
class Builder internal constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
+ private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
+ /**
+ * The executor service to use for running HTTP requests.
+ *
+ * Defaults to OkHttp's
+ * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
+ *
+ * This class takes ownership of the executor service and shuts it down when closed.
+ */
+ fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
+ this.dispatcherExecutorService = dispatcherExecutorService
+ }
+
+ /**
+ * Alias for calling [Builder.dispatcherExecutorService] with
+ * `dispatcherExecutorService.orElse(null)`.
+ */
+ fun dispatcherExecutorService(dispatcherExecutorService: Optional) =
+ dispatcherExecutorService(dispatcherExecutorService.getOrNull())
+
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
@@ -120,6 +142,17 @@ class CasParserOkHttpClientAsync private constructor() {
*/
fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) }
+
/**
* The clock to use for operations that require timing, like retries.
*
@@ -285,6 +318,7 @@ class CasParserOkHttpClientAsync private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
.hostnameVerifier(hostnameVerifier)
diff --git a/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/OkHttpClient.kt b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/OkHttpClient.kt
index 281e1c4..7079d3c 100644
--- a/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/OkHttpClient.kt
+++ b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/OkHttpClient.kt
@@ -13,12 +13,15 @@ import java.io.IOException
import java.io.InputStream
import java.net.Proxy
import java.time.Duration
+import java.util.concurrent.CancellationException
import java.util.concurrent.CompletableFuture
+import java.util.concurrent.ExecutorService
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Call
import okhttp3.Callback
+import okhttp3.Dispatcher
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
@@ -29,8 +32,8 @@ import okhttp3.Response
import okhttp3.logging.HttpLoggingInterceptor
import okio.BufferedSink
-class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpClient) :
- HttpClient {
+class OkHttpClient
+private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient {
override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
val call = newCall(request, requestOptions)
@@ -50,20 +53,25 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC
): CompletableFuture {
val future = CompletableFuture()
- request.body?.run { future.whenComplete { _, _ -> close() } }
-
- newCall(request, requestOptions)
- .enqueue(
- object : Callback {
- override fun onResponse(call: Call, response: Response) {
- future.complete(response.toResponse())
- }
+ val call = newCall(request, requestOptions)
+ call.enqueue(
+ object : Callback {
+ override fun onResponse(call: Call, response: Response) {
+ future.complete(response.toResponse())
+ }
- override fun onFailure(call: Call, e: IOException) {
- future.completeExceptionally(CasParserIoException("Request failed", e))
- }
+ override fun onFailure(call: Call, e: IOException) {
+ future.completeExceptionally(CasParserIoException("Request failed", e))
}
- )
+ }
+ )
+
+ future.whenComplete { _, e ->
+ if (e is CancellationException) {
+ call.cancel()
+ }
+ request.body?.close()
+ }
return future
}
@@ -109,19 +117,19 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC
val builder = Request.Builder().url(toUrl()).method(method.name, body)
headers.names().forEach { name ->
- headers.values(name).forEach { builder.header(name, it) }
+ headers.values(name).forEach { builder.addHeader(name, it) }
}
if (
!headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0
) {
- builder.header(
+ builder.addHeader(
"X-Stainless-Read-Timeout",
Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
)
}
if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) {
- builder.header(
+ builder.addHeader(
"X-Stainless-Timeout",
Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
)
@@ -192,6 +200,7 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC
private var timeout: Timeout = Timeout.default()
private var proxy: Proxy? = null
+ private var dispatcherExecutorService: ExecutorService? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
@@ -202,6 +211,10 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
+ fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
+ this.dispatcherExecutorService = dispatcherExecutorService
+ }
+
fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
this.sslSocketFactory = sslSocketFactory
}
@@ -217,12 +230,16 @@ class OkHttpClient private constructor(private val okHttpClient: okhttp3.OkHttpC
fun build(): OkHttpClient =
OkHttpClient(
okhttp3.OkHttpClient.Builder()
+ // `RetryingHttpClient` handles retries if the user enabled them.
+ .retryOnConnectionFailure(false)
.connectTimeout(timeout.connect())
.readTimeout(timeout.read())
.writeTimeout(timeout.write())
.callTimeout(timeout.request())
.proxy(proxy)
.apply {
+ dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
+
val sslSocketFactory = sslSocketFactory
val trustManager = trustManager
if (sslSocketFactory != null && trustManager != null) {
diff --git a/cas-parser-java-client-okhttp/src/test/kotlin/com/cas_parser/api/client/okhttp/OkHttpClientTest.kt b/cas-parser-java-client-okhttp/src/test/kotlin/com/cas_parser/api/client/okhttp/OkHttpClientTest.kt
new file mode 100644
index 0000000..457b40c
--- /dev/null
+++ b/cas-parser-java-client-okhttp/src/test/kotlin/com/cas_parser/api/client/okhttp/OkHttpClientTest.kt
@@ -0,0 +1,44 @@
+package com.cas_parser.api.client.okhttp
+
+import com.cas_parser.api.core.http.HttpMethod
+import com.cas_parser.api.core.http.HttpRequest
+import com.github.tomakehurst.wiremock.client.WireMock.*
+import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo
+import com.github.tomakehurst.wiremock.junit5.WireMockTest
+import org.assertj.core.api.Assertions.assertThat
+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 OkHttpClientTest {
+
+ private lateinit var baseUrl: String
+ private lateinit var httpClient: OkHttpClient
+
+ @BeforeEach
+ fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) {
+ baseUrl = wmRuntimeInfo.httpBaseUrl
+ httpClient = OkHttpClient.builder().build()
+ }
+
+ @Test
+ fun executeAsync_whenFutureCancelled_cancelsUnderlyingCall() {
+ stubFor(post(urlPathEqualTo("/something")).willReturn(ok()))
+ val responseFuture =
+ httpClient.executeAsync(
+ HttpRequest.builder()
+ .method(HttpMethod.POST)
+ .baseUrl(baseUrl)
+ .addPathSegment("something")
+ .build()
+ )
+ val call = httpClient.okHttpClient.dispatcher.runningCalls().single()
+
+ responseFuture.cancel(false)
+
+ // Should have cancelled the underlying call
+ assertThat(call.isCanceled()).isTrue()
+ }
+}
diff --git a/cas-parser-java-core/build.gradle.kts b/cas-parser-java-core/build.gradle.kts
index 9861d5b..7d1eeed 100644
--- a/cas-parser-java-core/build.gradle.kts
+++ b/cas-parser-java-core/build.gradle.kts
@@ -5,14 +5,16 @@ plugins {
configurations.all {
resolutionStrategy {
- // Compile and test against a lower Jackson version to ensure we're compatible with it.
- // We publish with a higher version (see below) to ensure users depend on a secure version by default.
- force("com.fasterxml.jackson.core:jackson-core:2.13.4")
- force("com.fasterxml.jackson.core:jackson-databind:2.13.4")
- force("com.fasterxml.jackson.core:jackson-annotations:2.13.4")
- force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4")
- force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4")
- force("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4")
+ // Compile and test against a lower Jackson version to ensure we're compatible with it. Note that
+ // we generally support 2.13.4, but test against 2.14.0 because 2.13.4 has some annoying (but
+ // niche) bugs (users should upgrade if they encounter them). We publish with a higher version
+ // (see below) to ensure users depend on a secure version by default.
+ force("com.fasterxml.jackson.core:jackson-core:2.14.0")
+ force("com.fasterxml.jackson.core:jackson-databind:2.14.0")
+ force("com.fasterxml.jackson.core:jackson-annotations:2.14.0")
+ force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.0")
+ force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0")
+ force("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0")
}
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ClientOptions.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ClientOptions.kt
index d330bc7..f5195cd 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ClientOptions.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ClientOptions.kt
@@ -40,6 +40,16 @@ private constructor(
* rarely needs to be overridden.
*/
@get:JvmName("jsonMapper") val jsonMapper: JsonMapper,
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ @get:JvmName("sleeper") val sleeper: Sleeper,
/**
* The clock to use for operations that require timing, like retries.
*
@@ -131,6 +141,7 @@ private constructor(
private var httpClient: HttpClient? = null
private var checkJacksonVersionCompatibility: Boolean = true
private var jsonMapper: JsonMapper = jsonMapper()
+ private var sleeper: Sleeper? = null
private var clock: Clock = Clock.systemUTC()
private var baseUrl: String? = null
private var headers: Headers.Builder = Headers.builder()
@@ -145,6 +156,7 @@ private constructor(
httpClient = clientOptions.originalHttpClient
checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility
jsonMapper = clientOptions.jsonMapper
+ sleeper = clientOptions.sleeper
clock = clientOptions.clock
baseUrl = clientOptions.baseUrl
headers = clientOptions.headers.toBuilder()
@@ -185,6 +197,17 @@ private constructor(
*/
fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper }
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ fun sleeper(sleeper: Sleeper) = apply { this.sleeper = PhantomReachableSleeper(sleeper) }
+
/**
* The clock to use for operations that require timing, like retries.
*
@@ -369,6 +392,7 @@ private constructor(
*/
fun build(): ClientOptions {
val httpClient = checkRequired("httpClient", httpClient)
+ val sleeper = sleeper ?: PhantomReachableSleeper(DefaultSleeper())
val apiKey = checkRequired("apiKey", apiKey)
val headers = Headers.builder()
@@ -392,11 +416,13 @@ private constructor(
httpClient,
RetryingHttpClient.builder()
.httpClient(httpClient)
+ .sleeper(sleeper)
.clock(clock)
.maxRetries(maxRetries)
.build(),
checkJacksonVersionCompatibility,
jsonMapper,
+ sleeper,
clock,
baseUrl,
headers.build(),
@@ -421,5 +447,6 @@ private constructor(
*/
fun close() {
httpClient.close()
+ sleeper.close()
}
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/DefaultSleeper.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/DefaultSleeper.kt
new file mode 100644
index 0000000..5e092e2
--- /dev/null
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/DefaultSleeper.kt
@@ -0,0 +1,28 @@
+package com.cas_parser.api.core
+
+import java.time.Duration
+import java.util.Timer
+import java.util.TimerTask
+import java.util.concurrent.CompletableFuture
+
+class DefaultSleeper : Sleeper {
+
+ private val timer = Timer("DefaultSleeper", 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
+ }
+
+ override fun close() = timer.cancel()
+}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ObjectMappers.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ObjectMappers.kt
index 85d0b7e..9096f88 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ObjectMappers.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ObjectMappers.kt
@@ -24,6 +24,7 @@ import java.io.InputStream
import java.time.DateTimeException
import java.time.LocalDate
import java.time.LocalDateTime
+import java.time.OffsetDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoField
@@ -36,7 +37,7 @@ fun jsonMapper(): JsonMapper =
.addModule(
SimpleModule()
.addSerializer(InputStreamSerializer)
- .addDeserializer(LocalDateTime::class.java, LenientLocalDateTimeDeserializer())
+ .addDeserializer(OffsetDateTime::class.java, LenientOffsetDateTimeDeserializer())
)
.withCoercionConfig(LogicalType.Boolean) {
it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
@@ -47,6 +48,7 @@ fun jsonMapper(): JsonMapper =
}
.withCoercionConfig(LogicalType.Integer) {
it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
.setCoercion(CoercionInputShape.String, CoercionAction.Fail)
.setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
.setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
@@ -64,6 +66,12 @@ fun jsonMapper(): JsonMapper =
.setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
.setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
}
+ .withCoercionConfig(LogicalType.DateTime) {
+ it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
.withCoercionConfig(LogicalType.Array) {
it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
@@ -124,10 +132,10 @@ private object InputStreamSerializer : BaseSerializer(InputStream::
}
/**
- * A deserializer that can deserialize [LocalDateTime] from datetimes, dates, and zoned datetimes.
+ * A deserializer that can deserialize [OffsetDateTime] from datetimes, dates, and zoned datetimes.
*/
-private class LenientLocalDateTimeDeserializer :
- StdDeserializer(LocalDateTime::class.java) {
+private class LenientOffsetDateTimeDeserializer :
+ StdDeserializer(OffsetDateTime::class.java) {
companion object {
@@ -141,7 +149,7 @@ private class LenientLocalDateTimeDeserializer :
override fun logicalType(): LogicalType = LogicalType.DateTime
- override fun deserialize(p: JsonParser, context: DeserializationContext?): LocalDateTime {
+ override fun deserialize(p: JsonParser, context: DeserializationContext): OffsetDateTime {
val exceptions = mutableListOf()
for (formatter in DATE_TIME_FORMATTERS) {
@@ -149,18 +157,20 @@ private class LenientLocalDateTimeDeserializer :
val temporal = formatter.parse(p.text)
return when {
- !temporal.isSupported(ChronoField.HOUR_OF_DAY) ->
- LocalDate.from(temporal).atStartOfDay()
- !temporal.isSupported(ChronoField.OFFSET_SECONDS) ->
- LocalDateTime.from(temporal)
- else -> ZonedDateTime.from(temporal).toLocalDateTime()
- }
+ !temporal.isSupported(ChronoField.HOUR_OF_DAY) ->
+ LocalDate.from(temporal).atStartOfDay()
+ !temporal.isSupported(ChronoField.OFFSET_SECONDS) ->
+ LocalDateTime.from(temporal)
+ else -> ZonedDateTime.from(temporal).toLocalDateTime()
+ }
+ .atZone(context.timeZone.toZoneId())
+ .toOffsetDateTime()
} catch (e: DateTimeException) {
exceptions.add(e)
}
}
- throw JsonParseException(p, "Cannot parse `LocalDateTime` from value: ${p.text}").apply {
+ throw JsonParseException(p, "Cannot parse `OffsetDateTime` from value: ${p.text}").apply {
exceptions.forEach { addSuppressed(it) }
}
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachableSleeper.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachableSleeper.kt
new file mode 100644
index 0000000..2b3afd5
--- /dev/null
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachableSleeper.kt
@@ -0,0 +1,23 @@
+package com.cas_parser.api.core
+
+import java.time.Duration
+import java.util.concurrent.CompletableFuture
+
+/**
+ * A delegating wrapper around a [Sleeper] that closes it once it's only phantom reachable.
+ *
+ * This class ensures the [Sleeper] is closed even if the user forgets to do it.
+ */
+internal class PhantomReachableSleeper(private val sleeper: Sleeper) : Sleeper {
+
+ init {
+ closeWhenPhantomReachable(this, sleeper)
+ }
+
+ override fun sleep(duration: Duration) = sleeper.sleep(duration)
+
+ override fun sleepAsync(duration: Duration): CompletableFuture =
+ sleeper.sleepAsync(duration)
+
+ override fun close() = sleeper.close()
+}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt
index c715f1a..dc9a375 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt
@@ -2,7 +2,7 @@
package com.cas_parser.api.core
-import java.util.Properties
+import com.cas_parser.api.client.CasParserClient
fun getOsArch(): String {
val osArch = System.getProperty("os.arch")
@@ -16,7 +16,7 @@ fun getOsArch(): String {
"x86_64" -> "x64"
"arm" -> "arm"
"aarch64" -> "arm64"
- else -> "other:${osArch}"
+ else -> "other:$osArch"
}
}
@@ -30,13 +30,13 @@ fun getOsName(): String {
osName.startsWith("Linux") -> "Linux"
osName.startsWith("Mac OS") -> "MacOS"
osName.startsWith("Windows") -> "Windows"
- else -> "Other:${osName}"
+ else -> "Other:$osName"
}
}
fun getOsVersion(): String = System.getProperty("os.version", "unknown")
fun getPackageVersion(): String =
- Properties::class.java.`package`.implementationVersion ?: "unknown"
+ CasParserClient::class.java.`package`.implementationVersion ?: "unknown"
fun getJavaVersion(): String = System.getProperty("java.version", "unknown")
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Sleeper.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Sleeper.kt
new file mode 100644
index 0000000..5a93d9a
--- /dev/null
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Sleeper.kt
@@ -0,0 +1,21 @@
+package com.cas_parser.api.core
+
+import java.time.Duration
+import java.util.concurrent.CompletableFuture
+
+/**
+ * An interface for delaying execution for a specified amount of time.
+ *
+ * Useful for testing and cleaning up resources.
+ */
+interface Sleeper : AutoCloseable {
+
+ /** Synchronously pauses execution for the given [duration]. */
+ fun sleep(duration: Duration)
+
+ /** Asynchronously pauses execution for the given [duration]. */
+ fun sleepAsync(duration: Duration): CompletableFuture
+
+ /** Overridden from [AutoCloseable] to not have a checked exception in its signature. */
+ override fun close()
+}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequest.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequest.kt
index 7301769..9679caa 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequest.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequest.kt
@@ -2,6 +2,7 @@ package com.cas_parser.api.core.http
import com.cas_parser.api.core.checkRequired
import com.cas_parser.api.core.toImmutable
+import java.net.URLEncoder
class HttpRequest
private constructor(
@@ -13,6 +14,35 @@ private constructor(
@get:JvmName("body") val body: HttpRequestBody?,
) {
+ fun url(): String = buildString {
+ append(baseUrl)
+
+ pathSegments.forEach { segment ->
+ if (!endsWith("/")) {
+ append("/")
+ }
+ append(URLEncoder.encode(segment, "UTF-8"))
+ }
+
+ if (queryParams.isEmpty()) {
+ return@buildString
+ }
+
+ append("?")
+ var isFirst = true
+ queryParams.keys().forEach { key ->
+ queryParams.values(key).forEach { value ->
+ if (!isFirst) {
+ append("&")
+ }
+ append(URLEncoder.encode(key, "UTF-8"))
+ append("=")
+ append(URLEncoder.encode(value, "UTF-8"))
+ isFirst = false
+ }
+ }
+ }
+
fun toBuilder(): Builder = Builder().from(this)
override fun toString(): String =
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt
index 569b1aa..3f34f6e 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt
@@ -1,6 +1,8 @@
package com.cas_parser.api.core.http
+import com.cas_parser.api.core.DefaultSleeper
import com.cas_parser.api.core.RequestOptions
+import com.cas_parser.api.core.Sleeper
import com.cas_parser.api.core.checkRequired
import com.cas_parser.api.errors.CasParserIoException
import com.cas_parser.api.errors.CasParserRetryableException
@@ -11,8 +13,6 @@ import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
import java.time.temporal.ChronoUnit
-import java.util.Timer
-import java.util.TimerTask
import java.util.UUID
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ThreadLocalRandom
@@ -31,10 +31,6 @@ private constructor(
) : HttpClient {
override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
- if (!isRetryable(request) || maxRetries <= 0) {
- return httpClient.execute(request, requestOptions)
- }
-
var modifiedRequest = maybeAddIdempotencyHeader(request)
// Don't send the current retry count in the headers if the caller set their own value.
@@ -48,6 +44,10 @@ private constructor(
modifiedRequest = setRetryCountHeader(modifiedRequest, retries)
}
+ if (!isRetryable(modifiedRequest)) {
+ return httpClient.execute(modifiedRequest, requestOptions)
+ }
+
val response =
try {
val response = httpClient.execute(modifiedRequest, requestOptions)
@@ -75,10 +75,6 @@ private constructor(
request: HttpRequest,
requestOptions: RequestOptions,
): CompletableFuture {
- if (!isRetryable(request) || maxRetries <= 0) {
- return httpClient.executeAsync(request, requestOptions)
- }
-
val modifiedRequest = maybeAddIdempotencyHeader(request)
// Don't send the current retry count in the headers if the caller set their own value.
@@ -94,8 +90,12 @@ private constructor(
val requestWithRetryCount =
if (shouldSendRetryCount) setRetryCountHeader(request, retries) else request
- return httpClient
- .executeAsync(requestWithRetryCount, requestOptions)
+ val responseFuture = httpClient.executeAsync(requestWithRetryCount, requestOptions)
+ if (!isRetryable(requestWithRetryCount)) {
+ return responseFuture
+ }
+
+ return responseFuture
.handleAsync(
fun(
response: HttpResponse?,
@@ -130,7 +130,10 @@ private constructor(
return executeWithRetries(modifiedRequest, requestOptions)
}
- override fun close() = httpClient.close()
+ override fun close() {
+ httpClient.close()
+ sleeper.close()
+ }
private fun isRetryable(request: HttpRequest): Boolean =
// Some requests, such as when a request body is being streamed, cannot be retried because
@@ -235,33 +238,14 @@ private constructor(
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 sleeper: Sleeper? = null
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 sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper }
fun clock(clock: Clock) = apply { this.clock = clock }
@@ -272,17 +256,10 @@ private constructor(
fun build(): HttpClient =
RetryingHttpClient(
checkRequired("httpClient", httpClient),
- sleeper,
+ sleeper ?: DefaultSleeper(),
clock,
maxRetries,
idempotencyHeader,
)
}
-
- internal interface Sleeper {
-
- fun sleep(duration: Duration)
-
- fun sleepAsync(duration: Duration): CompletableFuture
- }
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casgenerator/CasGeneratorGenerateCasParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casgenerator/CasGeneratorGenerateCasParams.kt
deleted file mode 100644
index eb7213c..0000000
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casgenerator/CasGeneratorGenerateCasParams.kt
+++ /dev/null
@@ -1,918 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.cas_parser.api.models.casgenerator
-
-import com.cas_parser.api.core.Enum
-import com.cas_parser.api.core.ExcludeMissing
-import com.cas_parser.api.core.JsonField
-import com.cas_parser.api.core.JsonMissing
-import com.cas_parser.api.core.JsonValue
-import com.cas_parser.api.core.Params
-import com.cas_parser.api.core.checkRequired
-import com.cas_parser.api.core.http.Headers
-import com.cas_parser.api.core.http.QueryParams
-import com.cas_parser.api.errors.CasParserInvalidDataException
-import com.fasterxml.jackson.annotation.JsonAnyGetter
-import com.fasterxml.jackson.annotation.JsonAnySetter
-import com.fasterxml.jackson.annotation.JsonCreator
-import com.fasterxml.jackson.annotation.JsonProperty
-import java.util.Collections
-import java.util.Objects
-import java.util.Optional
-import kotlin.jvm.optionals.getOrNull
-
-/**
- * This endpoint generates CAS (Consolidated Account Statement) documents by submitting a mailback
- * request to the specified CAS authority. Currently only supports KFintech, with plans to support
- * CAMS, CDSL, and NSDL in the future.
- */
-class CasGeneratorGenerateCasParams
-private constructor(
- private val body: Body,
- private val additionalHeaders: Headers,
- private val additionalQueryParams: QueryParams,
-) : Params {
-
- /**
- * Email address to receive the CAS document
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun email(): String = body.email()
-
- /**
- * Start date for the CAS period (format YYYY-MM-DD)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun fromDate(): String = body.fromDate()
-
- /**
- * Password to protect the generated CAS PDF
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun password(): String = body.password()
-
- /**
- * End date for the CAS period (format YYYY-MM-DD)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun toDate(): String = body.toDate()
-
- /**
- * CAS authority to generate the document from (currently only kfintech is supported)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun casAuthority(): Optional = body.casAuthority()
-
- /**
- * PAN number (optional for some CAS authorities)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun panNo(): Optional = body.panNo()
-
- /**
- * Returns the raw JSON value of [email].
- *
- * Unlike [email], this method doesn't throw if the JSON field has an unexpected type.
- */
- fun _email(): JsonField = body._email()
-
- /**
- * Returns the raw JSON value of [fromDate].
- *
- * Unlike [fromDate], this method doesn't throw if the JSON field has an unexpected type.
- */
- fun _fromDate(): JsonField = body._fromDate()
-
- /**
- * Returns the raw JSON value of [password].
- *
- * Unlike [password], this method doesn't throw if the JSON field has an unexpected type.
- */
- fun _password(): JsonField = body._password()
-
- /**
- * Returns the raw JSON value of [toDate].
- *
- * Unlike [toDate], this method doesn't throw if the JSON field has an unexpected type.
- */
- fun _toDate(): JsonField = body._toDate()
-
- /**
- * Returns the raw JSON value of [casAuthority].
- *
- * Unlike [casAuthority], this method doesn't throw if the JSON field has an unexpected type.
- */
- fun _casAuthority(): JsonField = body._casAuthority()
-
- /**
- * Returns the raw JSON value of [panNo].
- *
- * Unlike [panNo], this method doesn't throw if the JSON field has an unexpected type.
- */
- fun _panNo(): JsonField = body._panNo()
-
- fun _additionalBodyProperties(): Map = body._additionalProperties()
-
- /** Additional headers to send with the request. */
- fun _additionalHeaders(): Headers = additionalHeaders
-
- /** Additional query param to send with the request. */
- fun _additionalQueryParams(): QueryParams = additionalQueryParams
-
- fun toBuilder() = Builder().from(this)
-
- companion object {
-
- /**
- * Returns a mutable builder for constructing an instance of
- * [CasGeneratorGenerateCasParams].
- *
- * The following fields are required:
- * ```java
- * .email()
- * .fromDate()
- * .password()
- * .toDate()
- * ```
- */
- @JvmStatic fun builder() = Builder()
- }
-
- /** A builder for [CasGeneratorGenerateCasParams]. */
- class Builder internal constructor() {
-
- private var body: Body.Builder = Body.builder()
- private var additionalHeaders: Headers.Builder = Headers.builder()
- private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
-
- @JvmSynthetic
- internal fun from(casGeneratorGenerateCasParams: CasGeneratorGenerateCasParams) = apply {
- body = casGeneratorGenerateCasParams.body.toBuilder()
- additionalHeaders = casGeneratorGenerateCasParams.additionalHeaders.toBuilder()
- additionalQueryParams = casGeneratorGenerateCasParams.additionalQueryParams.toBuilder()
- }
-
- /**
- * Sets the entire request body.
- *
- * This is generally only useful if you are already constructing the body separately.
- * Otherwise, it's more convenient to use the top-level setters instead:
- * - [email]
- * - [fromDate]
- * - [password]
- * - [toDate]
- * - [casAuthority]
- * - etc.
- */
- fun body(body: Body) = apply { this.body = body.toBuilder() }
-
- /** Email address to receive the CAS document */
- fun email(email: String) = apply { body.email(email) }
-
- /**
- * Sets [Builder.email] to an arbitrary JSON value.
- *
- * You should usually call [Builder.email] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun email(email: JsonField) = apply { body.email(email) }
-
- /** Start date for the CAS period (format YYYY-MM-DD) */
- fun fromDate(fromDate: String) = apply { body.fromDate(fromDate) }
-
- /**
- * Sets [Builder.fromDate] to an arbitrary JSON value.
- *
- * You should usually call [Builder.fromDate] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun fromDate(fromDate: JsonField) = apply { body.fromDate(fromDate) }
-
- /** Password to protect the generated CAS PDF */
- fun password(password: String) = apply { body.password(password) }
-
- /**
- * Sets [Builder.password] to an arbitrary JSON value.
- *
- * You should usually call [Builder.password] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun password(password: JsonField) = apply { body.password(password) }
-
- /** End date for the CAS period (format YYYY-MM-DD) */
- fun toDate(toDate: String) = apply { body.toDate(toDate) }
-
- /**
- * Sets [Builder.toDate] to an arbitrary JSON value.
- *
- * You should usually call [Builder.toDate] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun toDate(toDate: JsonField) = apply { body.toDate(toDate) }
-
- /** CAS authority to generate the document from (currently only kfintech is supported) */
- fun casAuthority(casAuthority: CasAuthority) = apply { body.casAuthority(casAuthority) }
-
- /**
- * Sets [Builder.casAuthority] to an arbitrary JSON value.
- *
- * You should usually call [Builder.casAuthority] with a well-typed [CasAuthority] value
- * instead. This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun casAuthority(casAuthority: JsonField) = apply {
- body.casAuthority(casAuthority)
- }
-
- /** PAN number (optional for some CAS authorities) */
- fun panNo(panNo: String) = apply { body.panNo(panNo) }
-
- /**
- * Sets [Builder.panNo] to an arbitrary JSON value.
- *
- * You should usually call [Builder.panNo] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun panNo(panNo: JsonField) = apply { body.panNo(panNo) }
-
- fun additionalBodyProperties(additionalBodyProperties: Map) = apply {
- body.additionalProperties(additionalBodyProperties)
- }
-
- fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply {
- body.putAdditionalProperty(key, value)
- }
-
- fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) =
- apply {
- body.putAllAdditionalProperties(additionalBodyProperties)
- }
-
- fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) }
-
- fun removeAllAdditionalBodyProperties(keys: Set) = apply {
- body.removeAllAdditionalProperties(keys)
- }
-
- fun additionalHeaders(additionalHeaders: Headers) = apply {
- this.additionalHeaders.clear()
- putAllAdditionalHeaders(additionalHeaders)
- }
-
- fun additionalHeaders(additionalHeaders: Map>) = apply {
- this.additionalHeaders.clear()
- putAllAdditionalHeaders(additionalHeaders)
- }
-
- fun putAdditionalHeader(name: String, value: String) = apply {
- additionalHeaders.put(name, value)
- }
-
- fun putAdditionalHeaders(name: String, values: Iterable) = apply {
- additionalHeaders.put(name, values)
- }
-
- fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
- this.additionalHeaders.putAll(additionalHeaders)
- }
-
- fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
- this.additionalHeaders.putAll(additionalHeaders)
- }
-
- fun replaceAdditionalHeaders(name: String, value: String) = apply {
- additionalHeaders.replace(name, value)
- }
-
- fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
- additionalHeaders.replace(name, values)
- }
-
- fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
- this.additionalHeaders.replaceAll(additionalHeaders)
- }
-
- fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
- this.additionalHeaders.replaceAll(additionalHeaders)
- }
-
- fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
-
- fun removeAllAdditionalHeaders(names: Set) = apply {
- additionalHeaders.removeAll(names)
- }
-
- fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
- this.additionalQueryParams.clear()
- putAllAdditionalQueryParams(additionalQueryParams)
- }
-
- fun additionalQueryParams(additionalQueryParams: Map>) = apply {
- this.additionalQueryParams.clear()
- putAllAdditionalQueryParams(additionalQueryParams)
- }
-
- fun putAdditionalQueryParam(key: String, value: String) = apply {
- additionalQueryParams.put(key, value)
- }
-
- fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
- additionalQueryParams.put(key, values)
- }
-
- fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
- this.additionalQueryParams.putAll(additionalQueryParams)
- }
-
- fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
- apply {
- this.additionalQueryParams.putAll(additionalQueryParams)
- }
-
- fun replaceAdditionalQueryParams(key: String, value: String) = apply {
- additionalQueryParams.replace(key, value)
- }
-
- fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
- additionalQueryParams.replace(key, values)
- }
-
- fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
- this.additionalQueryParams.replaceAll(additionalQueryParams)
- }
-
- fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
- apply {
- this.additionalQueryParams.replaceAll(additionalQueryParams)
- }
-
- fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
-
- fun removeAllAdditionalQueryParams(keys: Set) = apply {
- additionalQueryParams.removeAll(keys)
- }
-
- /**
- * Returns an immutable instance of [CasGeneratorGenerateCasParams].
- *
- * Further updates to this [Builder] will not mutate the returned instance.
- *
- * The following fields are required:
- * ```java
- * .email()
- * .fromDate()
- * .password()
- * .toDate()
- * ```
- *
- * @throws IllegalStateException if any required field is unset.
- */
- fun build(): CasGeneratorGenerateCasParams =
- CasGeneratorGenerateCasParams(
- body.build(),
- additionalHeaders.build(),
- additionalQueryParams.build(),
- )
- }
-
- fun _body(): Body = body
-
- override fun _headers(): Headers = additionalHeaders
-
- override fun _queryParams(): QueryParams = additionalQueryParams
-
- class Body
- private constructor(
- private val email: JsonField,
- private val fromDate: JsonField,
- private val password: JsonField,
- private val toDate: JsonField,
- private val casAuthority: JsonField,
- private val panNo: JsonField,
- private val additionalProperties: MutableMap,
- ) {
-
- @JsonCreator
- private constructor(
- @JsonProperty("email") @ExcludeMissing email: JsonField = JsonMissing.of(),
- @JsonProperty("from_date")
- @ExcludeMissing
- fromDate: JsonField = JsonMissing.of(),
- @JsonProperty("password")
- @ExcludeMissing
- password: JsonField = JsonMissing.of(),
- @JsonProperty("to_date") @ExcludeMissing toDate: JsonField = JsonMissing.of(),
- @JsonProperty("cas_authority")
- @ExcludeMissing
- casAuthority: JsonField = JsonMissing.of(),
- @JsonProperty("pan_no") @ExcludeMissing panNo: JsonField = JsonMissing.of(),
- ) : this(email, fromDate, password, toDate, casAuthority, panNo, mutableMapOf())
-
- /**
- * Email address to receive the CAS document
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun email(): String = email.getRequired("email")
-
- /**
- * Start date for the CAS period (format YYYY-MM-DD)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun fromDate(): String = fromDate.getRequired("from_date")
-
- /**
- * Password to protect the generated CAS PDF
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun password(): String = password.getRequired("password")
-
- /**
- * End date for the CAS period (format YYYY-MM-DD)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type or is
- * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
- */
- fun toDate(): String = toDate.getRequired("to_date")
-
- /**
- * CAS authority to generate the document from (currently only kfintech is supported)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if
- * the server responded with an unexpected value).
- */
- fun casAuthority(): Optional = casAuthority.getOptional("cas_authority")
-
- /**
- * PAN number (optional for some CAS authorities)
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if
- * the server responded with an unexpected value).
- */
- fun panNo(): Optional = panNo.getOptional("pan_no")
-
- /**
- * Returns the raw JSON value of [email].
- *
- * Unlike [email], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("email") @ExcludeMissing fun _email(): JsonField = email
-
- /**
- * Returns the raw JSON value of [fromDate].
- *
- * Unlike [fromDate], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("from_date") @ExcludeMissing fun _fromDate(): JsonField = fromDate
-
- /**
- * Returns the raw JSON value of [password].
- *
- * Unlike [password], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("password") @ExcludeMissing fun _password(): JsonField = password
-
- /**
- * Returns the raw JSON value of [toDate].
- *
- * Unlike [toDate], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("to_date") @ExcludeMissing fun _toDate(): JsonField = toDate
-
- /**
- * Returns the raw JSON value of [casAuthority].
- *
- * Unlike [casAuthority], this method doesn't throw if the JSON field has an unexpected
- * type.
- */
- @JsonProperty("cas_authority")
- @ExcludeMissing
- fun _casAuthority(): JsonField = casAuthority
-
- /**
- * Returns the raw JSON value of [panNo].
- *
- * Unlike [panNo], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("pan_no") @ExcludeMissing fun _panNo(): JsonField = panNo
-
- @JsonAnySetter
- private fun putAdditionalProperty(key: String, value: JsonValue) {
- additionalProperties.put(key, value)
- }
-
- @JsonAnyGetter
- @ExcludeMissing
- fun _additionalProperties(): Map =
- Collections.unmodifiableMap(additionalProperties)
-
- fun toBuilder() = Builder().from(this)
-
- companion object {
-
- /**
- * Returns a mutable builder for constructing an instance of [Body].
- *
- * The following fields are required:
- * ```java
- * .email()
- * .fromDate()
- * .password()
- * .toDate()
- * ```
- */
- @JvmStatic fun builder() = Builder()
- }
-
- /** A builder for [Body]. */
- class Builder internal constructor() {
-
- private var email: JsonField? = null
- private var fromDate: JsonField? = null
- private var password: JsonField? = null
- private var toDate: JsonField? = null
- private var casAuthority: JsonField = JsonMissing.of()
- private var panNo: JsonField = JsonMissing.of()
- private var additionalProperties: MutableMap = mutableMapOf()
-
- @JvmSynthetic
- internal fun from(body: Body) = apply {
- email = body.email
- fromDate = body.fromDate
- password = body.password
- toDate = body.toDate
- casAuthority = body.casAuthority
- panNo = body.panNo
- additionalProperties = body.additionalProperties.toMutableMap()
- }
-
- /** Email address to receive the CAS document */
- fun email(email: String) = email(JsonField.of(email))
-
- /**
- * Sets [Builder.email] to an arbitrary JSON value.
- *
- * You should usually call [Builder.email] with a well-typed [String] value instead.
- * This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun email(email: JsonField) = apply { this.email = email }
-
- /** Start date for the CAS period (format YYYY-MM-DD) */
- fun fromDate(fromDate: String) = fromDate(JsonField.of(fromDate))
-
- /**
- * Sets [Builder.fromDate] to an arbitrary JSON value.
- *
- * You should usually call [Builder.fromDate] with a well-typed [String] value instead.
- * This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun fromDate(fromDate: JsonField) = apply { this.fromDate = fromDate }
-
- /** Password to protect the generated CAS PDF */
- fun password(password: String) = password(JsonField.of(password))
-
- /**
- * Sets [Builder.password] to an arbitrary JSON value.
- *
- * You should usually call [Builder.password] with a well-typed [String] value instead.
- * This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun password(password: JsonField) = apply { this.password = password }
-
- /** End date for the CAS period (format YYYY-MM-DD) */
- fun toDate(toDate: String) = toDate(JsonField.of(toDate))
-
- /**
- * Sets [Builder.toDate] to an arbitrary JSON value.
- *
- * You should usually call [Builder.toDate] with a well-typed [String] value instead.
- * This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun toDate(toDate: JsonField) = apply { this.toDate = toDate }
-
- /**
- * CAS authority to generate the document from (currently only kfintech is supported)
- */
- fun casAuthority(casAuthority: CasAuthority) = casAuthority(JsonField.of(casAuthority))
-
- /**
- * Sets [Builder.casAuthority] to an arbitrary JSON value.
- *
- * You should usually call [Builder.casAuthority] with a well-typed [CasAuthority] value
- * instead. This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun casAuthority(casAuthority: JsonField) = apply {
- this.casAuthority = casAuthority
- }
-
- /** PAN number (optional for some CAS authorities) */
- fun panNo(panNo: String) = panNo(JsonField.of(panNo))
-
- /**
- * Sets [Builder.panNo] to an arbitrary JSON value.
- *
- * You should usually call [Builder.panNo] with a well-typed [String] value instead.
- * This method is primarily for setting the field to an undocumented or not yet
- * supported value.
- */
- fun panNo(panNo: JsonField) = apply { this.panNo = panNo }
-
- fun additionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.clear()
- putAllAdditionalProperties(additionalProperties)
- }
-
- fun putAdditionalProperty(key: String, value: JsonValue) = apply {
- additionalProperties.put(key, value)
- }
-
- fun putAllAdditionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.putAll(additionalProperties)
- }
-
- fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
-
- fun removeAllAdditionalProperties(keys: Set) = apply {
- keys.forEach(::removeAdditionalProperty)
- }
-
- /**
- * Returns an immutable instance of [Body].
- *
- * Further updates to this [Builder] will not mutate the returned instance.
- *
- * The following fields are required:
- * ```java
- * .email()
- * .fromDate()
- * .password()
- * .toDate()
- * ```
- *
- * @throws IllegalStateException if any required field is unset.
- */
- fun build(): Body =
- Body(
- checkRequired("email", email),
- checkRequired("fromDate", fromDate),
- checkRequired("password", password),
- checkRequired("toDate", toDate),
- casAuthority,
- panNo,
- additionalProperties.toMutableMap(),
- )
- }
-
- private var validated: Boolean = false
-
- fun validate(): Body = apply {
- if (validated) {
- return@apply
- }
-
- email()
- fromDate()
- password()
- toDate()
- casAuthority().ifPresent { it.validate() }
- panNo()
- validated = true
- }
-
- fun isValid(): Boolean =
- try {
- validate()
- true
- } catch (e: CasParserInvalidDataException) {
- false
- }
-
- /**
- * Returns a score indicating how many valid values are contained in this object
- * recursively.
- *
- * Used for best match union deserialization.
- */
- @JvmSynthetic
- internal fun validity(): Int =
- (if (email.asKnown().isPresent) 1 else 0) +
- (if (fromDate.asKnown().isPresent) 1 else 0) +
- (if (password.asKnown().isPresent) 1 else 0) +
- (if (toDate.asKnown().isPresent) 1 else 0) +
- (casAuthority.asKnown().getOrNull()?.validity() ?: 0) +
- (if (panNo.asKnown().isPresent) 1 else 0)
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
- }
-
- return other is Body &&
- email == other.email &&
- fromDate == other.fromDate &&
- password == other.password &&
- toDate == other.toDate &&
- casAuthority == other.casAuthority &&
- panNo == other.panNo &&
- additionalProperties == other.additionalProperties
- }
-
- private val hashCode: Int by lazy {
- Objects.hash(
- email,
- fromDate,
- password,
- toDate,
- casAuthority,
- panNo,
- additionalProperties,
- )
- }
-
- override fun hashCode(): Int = hashCode
-
- override fun toString() =
- "Body{email=$email, fromDate=$fromDate, password=$password, toDate=$toDate, casAuthority=$casAuthority, panNo=$panNo, additionalProperties=$additionalProperties}"
- }
-
- /** CAS authority to generate the document from (currently only kfintech is supported) */
- class CasAuthority @JsonCreator private constructor(private val value: JsonField) :
- Enum {
-
- /**
- * Returns this class instance's raw value.
- *
- * This is usually only useful if this instance was deserialized from data that doesn't
- * match any known member, and you want to know that value. For example, if the SDK is on an
- * older version than the API, then the API may respond with new members that the SDK is
- * unaware of.
- */
- @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
-
- companion object {
-
- @JvmField val KFINTECH = of("kfintech")
-
- @JvmField val CAMS = of("cams")
-
- @JvmField val CDSL = of("cdsl")
-
- @JvmField val NSDL = of("nsdl")
-
- @JvmStatic fun of(value: String) = CasAuthority(JsonField.of(value))
- }
-
- /** An enum containing [CasAuthority]'s known values. */
- enum class Known {
- KFINTECH,
- CAMS,
- CDSL,
- NSDL,
- }
-
- /**
- * An enum containing [CasAuthority]'s known values, as well as an [_UNKNOWN] member.
- *
- * An instance of [CasAuthority] can contain an unknown value in a couple of cases:
- * - It was deserialized from data that doesn't match any known member. For example, if the
- * SDK is on an older version than the API, then the API may respond with new members that
- * the SDK is unaware of.
- * - It was constructed with an arbitrary value using the [of] method.
- */
- enum class Value {
- KFINTECH,
- CAMS,
- CDSL,
- NSDL,
- /**
- * An enum member indicating that [CasAuthority] was instantiated with an unknown value.
- */
- _UNKNOWN,
- }
-
- /**
- * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
- * if the class was instantiated with an unknown value.
- *
- * Use the [known] method instead if you're certain the value is always known or if you want
- * to throw for the unknown case.
- */
- fun value(): Value =
- when (this) {
- KFINTECH -> Value.KFINTECH
- CAMS -> Value.CAMS
- CDSL -> Value.CDSL
- NSDL -> Value.NSDL
- else -> Value._UNKNOWN
- }
-
- /**
- * Returns an enum member corresponding to this class instance's value.
- *
- * Use the [value] method instead if you're uncertain the value is always known and don't
- * want to throw for the unknown case.
- *
- * @throws CasParserInvalidDataException if this class instance's value is a not a known
- * member.
- */
- fun known(): Known =
- when (this) {
- KFINTECH -> Known.KFINTECH
- CAMS -> Known.CAMS
- CDSL -> Known.CDSL
- NSDL -> Known.NSDL
- else -> throw CasParserInvalidDataException("Unknown CasAuthority: $value")
- }
-
- /**
- * Returns this class instance's primitive wire representation.
- *
- * This differs from the [toString] method because that method is primarily for debugging
- * and generally doesn't throw.
- *
- * @throws CasParserInvalidDataException if this class instance's value does not have the
- * expected primitive type.
- */
- fun asString(): String =
- _value().asString().orElseThrow {
- CasParserInvalidDataException("Value is not a String")
- }
-
- private var validated: Boolean = false
-
- fun validate(): CasAuthority = apply {
- if (validated) {
- return@apply
- }
-
- known()
- validated = true
- }
-
- fun isValid(): Boolean =
- try {
- validate()
- true
- } catch (e: CasParserInvalidDataException) {
- false
- }
-
- /**
- * Returns a score indicating how many valid values are contained in this object
- * recursively.
- *
- * Used for best match union deserialization.
- */
- @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
- }
-
- return other is CasAuthority && value == other.value
- }
-
- override fun hashCode() = value.hashCode()
-
- override fun toString() = value.toString()
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
- }
-
- return other is CasGeneratorGenerateCasParams &&
- body == other.body &&
- additionalHeaders == other.additionalHeaders &&
- additionalQueryParams == other.additionalQueryParams
- }
-
- override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams)
-
- override fun toString() =
- "CasGeneratorGenerateCasParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
-}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casgenerator/CasGeneratorGenerateCasResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casgenerator/CasGeneratorGenerateCasResponse.kt
deleted file mode 100644
index 09df4cd..0000000
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casgenerator/CasGeneratorGenerateCasResponse.kt
+++ /dev/null
@@ -1,188 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.cas_parser.api.models.casgenerator
-
-import com.cas_parser.api.core.ExcludeMissing
-import com.cas_parser.api.core.JsonField
-import com.cas_parser.api.core.JsonMissing
-import com.cas_parser.api.core.JsonValue
-import com.cas_parser.api.errors.CasParserInvalidDataException
-import com.fasterxml.jackson.annotation.JsonAnyGetter
-import com.fasterxml.jackson.annotation.JsonAnySetter
-import com.fasterxml.jackson.annotation.JsonCreator
-import com.fasterxml.jackson.annotation.JsonProperty
-import java.util.Collections
-import java.util.Objects
-import java.util.Optional
-
-class CasGeneratorGenerateCasResponse
-private constructor(
- private val msg: JsonField,
- private val status: JsonField,
- private val additionalProperties: MutableMap,
-) {
-
- @JsonCreator
- private constructor(
- @JsonProperty("msg") @ExcludeMissing msg: JsonField = JsonMissing.of(),
- @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(),
- ) : this(msg, status, mutableMapOf())
-
- /**
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun msg(): Optional = msg.getOptional("msg")
-
- /**
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun status(): Optional = status.getOptional("status")
-
- /**
- * Returns the raw JSON value of [msg].
- *
- * Unlike [msg], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("msg") @ExcludeMissing fun _msg(): JsonField = msg
-
- /**
- * Returns the raw JSON value of [status].
- *
- * Unlike [status], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status
-
- @JsonAnySetter
- private fun putAdditionalProperty(key: String, value: JsonValue) {
- additionalProperties.put(key, value)
- }
-
- @JsonAnyGetter
- @ExcludeMissing
- fun _additionalProperties(): Map =
- Collections.unmodifiableMap(additionalProperties)
-
- fun toBuilder() = Builder().from(this)
-
- companion object {
-
- /**
- * Returns a mutable builder for constructing an instance of
- * [CasGeneratorGenerateCasResponse].
- */
- @JvmStatic fun builder() = Builder()
- }
-
- /** A builder for [CasGeneratorGenerateCasResponse]. */
- class Builder internal constructor() {
-
- private var msg: JsonField = JsonMissing.of()
- private var status: JsonField = JsonMissing.of()
- private var additionalProperties: MutableMap = mutableMapOf()
-
- @JvmSynthetic
- internal fun from(casGeneratorGenerateCasResponse: CasGeneratorGenerateCasResponse) =
- apply {
- msg = casGeneratorGenerateCasResponse.msg
- status = casGeneratorGenerateCasResponse.status
- additionalProperties =
- casGeneratorGenerateCasResponse.additionalProperties.toMutableMap()
- }
-
- fun msg(msg: String) = msg(JsonField.of(msg))
-
- /**
- * Sets [Builder.msg] to an arbitrary JSON value.
- *
- * You should usually call [Builder.msg] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun msg(msg: JsonField) = apply { this.msg = msg }
-
- fun status(status: String) = status(JsonField.of(status))
-
- /**
- * Sets [Builder.status] to an arbitrary JSON value.
- *
- * You should usually call [Builder.status] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun status(status: JsonField) = apply { this.status = status }
-
- fun additionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.clear()
- putAllAdditionalProperties(additionalProperties)
- }
-
- fun putAdditionalProperty(key: String, value: JsonValue) = apply {
- additionalProperties.put(key, value)
- }
-
- fun putAllAdditionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.putAll(additionalProperties)
- }
-
- fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
-
- fun removeAllAdditionalProperties(keys: Set) = apply {
- keys.forEach(::removeAdditionalProperty)
- }
-
- /**
- * Returns an immutable instance of [CasGeneratorGenerateCasResponse].
- *
- * Further updates to this [Builder] will not mutate the returned instance.
- */
- fun build(): CasGeneratorGenerateCasResponse =
- CasGeneratorGenerateCasResponse(msg, status, additionalProperties.toMutableMap())
- }
-
- private var validated: Boolean = false
-
- fun validate(): CasGeneratorGenerateCasResponse = apply {
- if (validated) {
- return@apply
- }
-
- msg()
- status()
- validated = true
- }
-
- fun isValid(): Boolean =
- try {
- validate()
- true
- } catch (e: CasParserInvalidDataException) {
- false
- }
-
- /**
- * Returns a score indicating how many valid values are contained in this object recursively.
- *
- * Used for best match union deserialization.
- */
- @JvmSynthetic
- internal fun validity(): Int =
- (if (msg.asKnown().isPresent) 1 else 0) + (if (status.asKnown().isPresent) 1 else 0)
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
- }
-
- return other is CasGeneratorGenerateCasResponse &&
- msg == other.msg &&
- status == other.status &&
- additionalProperties == other.additionalProperties
- }
-
- private val hashCode: Int by lazy { Objects.hash(msg, status, additionalProperties) }
-
- override fun hashCode(): Int = hashCode
-
- override fun toString() =
- "CasGeneratorGenerateCasResponse{msg=$msg, status=$status, additionalProperties=$additionalProperties}"
-}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casparser/UnifiedResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casparser/UnifiedResponse.kt
index 2c18576..e64ebfb 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casparser/UnifiedResponse.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/casparser/UnifiedResponse.kt
@@ -22,12 +22,14 @@ import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class UnifiedResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val dematAccounts: JsonField>,
private val insurance: JsonField,
private val investor: JsonField,
private val meta: JsonField,
private val mutualFunds: JsonField>,
+ private val nps: JsonField>,
private val summary: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -45,8 +47,9 @@ private constructor(
@JsonProperty("mutual_funds")
@ExcludeMissing
mutualFunds: JsonField> = JsonMissing.of(),
+ @JsonProperty("nps") @ExcludeMissing nps: JsonField> = JsonMissing.of(),
@JsonProperty("summary") @ExcludeMissing summary: JsonField = JsonMissing.of(),
- ) : this(dematAccounts, insurance, investor, meta, mutualFunds, summary, mutableMapOf())
+ ) : this(dematAccounts, insurance, investor, meta, mutualFunds, nps, summary, mutableMapOf())
/**
* @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the
@@ -78,6 +81,14 @@ private constructor(
*/
fun mutualFunds(): Optional> = mutualFunds.getOptional("mutual_funds")
+ /**
+ * List of NPS accounts
+ *
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun nps(): Optional> = nps.getOptional("nps")
+
/**
* @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
@@ -123,6 +134,13 @@ private constructor(
@ExcludeMissing
fun _mutualFunds(): JsonField> = mutualFunds
+ /**
+ * Returns the raw JSON value of [nps].
+ *
+ * Unlike [nps], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("nps") @ExcludeMissing fun _nps(): JsonField> = nps
+
/**
* Returns the raw JSON value of [summary].
*
@@ -156,6 +174,7 @@ private constructor(
private var investor: JsonField = JsonMissing.of()
private var meta: JsonField = JsonMissing.of()
private var mutualFunds: JsonField>? = null
+ private var nps: JsonField>? = null
private var summary: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -166,6 +185,7 @@ private constructor(
investor = unifiedResponse.investor
meta = unifiedResponse.meta
mutualFunds = unifiedResponse.mutualFunds.map { it.toMutableList() }
+ nps = unifiedResponse.nps.map { it.toMutableList() }
summary = unifiedResponse.summary
additionalProperties = unifiedResponse.additionalProperties.toMutableMap()
}
@@ -253,6 +273,26 @@ private constructor(
}
}
+ /** List of NPS accounts */
+ fun nps(nps: List) = nps(JsonField.of(nps))
+
+ /**
+ * Sets [Builder.nps] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.nps] with a well-typed `List` value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun nps(nps: JsonField>) = apply { this.nps = nps.map { it.toMutableList() } }
+
+ /**
+ * Adds a single [Np] to [nps].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addNp(np: Np) = apply {
+ nps = (nps ?: JsonField.of(mutableListOf())).also { checkKnown("nps", it).add(np) }
+ }
+
fun summary(summary: Summary) = summary(JsonField.of(summary))
/**
@@ -294,6 +334,7 @@ private constructor(
investor,
meta,
(mutualFunds ?: JsonMissing.of()).map { it.toImmutable() },
+ (nps ?: JsonMissing.of()).map { it.toImmutable() },
summary,
additionalProperties.toMutableMap(),
)
@@ -311,6 +352,7 @@ private constructor(
investor().ifPresent { it.validate() }
meta().ifPresent { it.validate() }
mutualFunds().ifPresent { it.forEach { it.validate() } }
+ nps().ifPresent { it.forEach { it.validate() } }
summary().ifPresent { it.validate() }
validated = true
}
@@ -335,9 +377,11 @@ private constructor(
(investor.asKnown().getOrNull()?.validity() ?: 0) +
(meta.asKnown().getOrNull()?.validity() ?: 0) +
(mutualFunds.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
+ (nps.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
(summary.asKnown().getOrNull()?.validity() ?: 0)
class DematAccount
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val additionalInfo: JsonField,
private val boId: JsonField,
@@ -346,6 +390,7 @@ private constructor(
private val dpId: JsonField,
private val dpName: JsonField,
private val holdings: JsonField,
+ private val linkedHolders: JsonField>,
private val value: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -367,6 +412,9 @@ private constructor(
@JsonProperty("holdings")
@ExcludeMissing
holdings: JsonField = JsonMissing.of(),
+ @JsonProperty("linked_holders")
+ @ExcludeMissing
+ linkedHolders: JsonField> = JsonMissing.of(),
@JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(),
) : this(
additionalInfo,
@@ -376,6 +424,7 @@ private constructor(
dpId,
dpName,
holdings,
+ linkedHolders,
value,
mutableMapOf(),
)
@@ -435,6 +484,15 @@ private constructor(
*/
fun holdings(): Optional = holdings.getOptional("holdings")
+ /**
+ * List of account holders linked to this demat account
+ *
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if
+ * the server responded with an unexpected value).
+ */
+ fun linkedHolders(): Optional> =
+ linkedHolders.getOptional("linked_holders")
+
/**
* Total value of the demat account
*
@@ -497,6 +555,16 @@ private constructor(
*/
@JsonProperty("holdings") @ExcludeMissing fun _holdings(): JsonField = holdings
+ /**
+ * Returns the raw JSON value of [linkedHolders].
+ *
+ * Unlike [linkedHolders], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("linked_holders")
+ @ExcludeMissing
+ fun _linkedHolders(): JsonField> = linkedHolders
+
/**
* Returns the raw JSON value of [value].
*
@@ -532,6 +600,7 @@ private constructor(
private var dpId: JsonField = JsonMissing.of()
private var dpName: JsonField = JsonMissing.of()
private var holdings: JsonField = JsonMissing.of()
+ private var linkedHolders: JsonField>? = null
private var value: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -544,6 +613,7 @@ private constructor(
dpId = dematAccount.dpId
dpName = dematAccount.dpName
holdings = dematAccount.holdings
+ linkedHolders = dematAccount.linkedHolders.map { it.toMutableList() }
value = dematAccount.value
additionalProperties = dematAccount.additionalProperties.toMutableMap()
}
@@ -634,6 +704,33 @@ private constructor(
*/
fun holdings(holdings: JsonField) = apply { this.holdings = holdings }
+ /** List of account holders linked to this demat account */
+ fun linkedHolders(linkedHolders: List) =
+ linkedHolders(JsonField.of(linkedHolders))
+
+ /**
+ * Sets [Builder.linkedHolders] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.linkedHolders] with a well-typed
+ * `List` value instead. This method is primarily for setting the field to
+ * an undocumented or not yet supported value.
+ */
+ fun linkedHolders(linkedHolders: JsonField>) = apply {
+ this.linkedHolders = linkedHolders.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [LinkedHolder] to [linkedHolders].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addLinkedHolder(linkedHolder: LinkedHolder) = apply {
+ linkedHolders =
+ (linkedHolders ?: JsonField.of(mutableListOf())).also {
+ checkKnown("linkedHolders", it).add(linkedHolder)
+ }
+ }
+
/** Total value of the demat account */
fun value(value: Float) = value(JsonField.of(value))
@@ -679,6 +776,7 @@ private constructor(
dpId,
dpName,
holdings,
+ (linkedHolders ?: JsonMissing.of()).map { it.toImmutable() },
value,
additionalProperties.toMutableMap(),
)
@@ -698,6 +796,7 @@ private constructor(
dpId()
dpName()
holdings().ifPresent { it.validate() }
+ linkedHolders().ifPresent { it.forEach { it.validate() } }
value()
validated = true
}
@@ -725,10 +824,12 @@ private constructor(
(if (dpId.asKnown().isPresent) 1 else 0) +
(if (dpName.asKnown().isPresent) 1 else 0) +
(holdings.asKnown().getOrNull()?.validity() ?: 0) +
+ (linkedHolders.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
(if (value.asKnown().isPresent) 1 else 0)
/** Additional information specific to the demat account type */
class AdditionalInfo
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val boStatus: JsonField,
private val boSubStatus: JsonField,
@@ -1310,6 +1411,7 @@ private constructor(
}
class Holdings
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val aifs: JsonField>,
private val corporateBonds: JsonField>,
@@ -1668,10 +1770,12 @@ private constructor(
?: 0)
class Aif
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
- private val additionalInfo: JsonValue,
+ private val additionalInfo: JsonField,
private val isin: JsonField,
private val name: JsonField,
+ private val transactions: JsonField>,
private val units: JsonField,
private val value: JsonField,
private val additionalProperties: MutableMap,
@@ -1681,25 +1785,32 @@ private constructor(
private constructor(
@JsonProperty("additional_info")
@ExcludeMissing
- additionalInfo: JsonValue = JsonMissing.of(),
+ additionalInfo: JsonField = JsonMissing.of(),
@JsonProperty("isin")
@ExcludeMissing
isin: JsonField = JsonMissing.of(),
@JsonProperty("name")
@ExcludeMissing
name: JsonField = JsonMissing.of(),
+ @JsonProperty("transactions")
+ @ExcludeMissing
+ transactions: JsonField> = JsonMissing.of(),
@JsonProperty("units")
@ExcludeMissing
units: JsonField = JsonMissing.of(),
@JsonProperty("value")
@ExcludeMissing
value: JsonField = JsonMissing.of(),
- ) : this(additionalInfo, isin, name, units, value, mutableMapOf())
+ ) : this(additionalInfo, isin, name, transactions, units, value, mutableMapOf())
- /** Additional information specific to the AIF */
- @JsonProperty("additional_info")
- @ExcludeMissing
- fun _additionalInfo(): JsonValue = additionalInfo
+ /**
+ * Additional information specific to the AIF
+ *
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun additionalInfo(): Optional =
+ additionalInfo.getOptional("additional_info")
/**
* ISIN code of the AIF
@@ -1717,6 +1828,15 @@ private constructor(
*/
fun name(): Optional = name.getOptional("name")
+ /**
+ * List of transactions for this holding (beta)
+ *
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun transactions(): Optional> =
+ transactions.getOptional("transactions")
+
/**
* Number of units held
*
@@ -1733,6 +1853,16 @@ private constructor(
*/
fun value(): Optional = value.getOptional("value")
+ /**
+ * Returns the raw JSON value of [additionalInfo].
+ *
+ * Unlike [additionalInfo], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("additional_info")
+ @ExcludeMissing
+ fun _additionalInfo(): JsonField = additionalInfo
+
/**
* Returns the raw JSON value of [isin].
*
@@ -1749,6 +1879,16 @@ private constructor(
*/
@JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name
+ /**
+ * Returns the raw JSON value of [transactions].
+ *
+ * Unlike [transactions], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("transactions")
+ @ExcludeMissing
+ fun _transactions(): JsonField> = transactions
+
/**
* Returns the raw JSON value of [units].
*
@@ -1786,9 +1926,10 @@ private constructor(
/** A builder for [Aif]. */
class Builder internal constructor() {
- private var additionalInfo: JsonValue = JsonMissing.of()
+ private var additionalInfo: JsonField = JsonMissing.of()
private var isin: JsonField = JsonMissing.of()
private var name: JsonField = JsonMissing.of()
+ private var transactions: JsonField>? = null
private var units: JsonField = JsonMissing.of()
private var value: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -1798,13 +1939,24 @@ private constructor(
additionalInfo = aif.additionalInfo
isin = aif.isin
name = aif.name
+ transactions = aif.transactions.map { it.toMutableList() }
units = aif.units
value = aif.value
additionalProperties = aif.additionalProperties.toMutableMap()
}
/** Additional information specific to the AIF */
- fun additionalInfo(additionalInfo: JsonValue) = apply {
+ fun additionalInfo(additionalInfo: AdditionalInfo) =
+ additionalInfo(JsonField.of(additionalInfo))
+
+ /**
+ * Sets [Builder.additionalInfo] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.additionalInfo] with a well-typed
+ * [AdditionalInfo] value instead. This method is primarily for setting the
+ * field to an undocumented or not yet supported value.
+ */
+ fun additionalInfo(additionalInfo: JsonField) = apply {
this.additionalInfo = additionalInfo
}
@@ -1832,6 +1984,33 @@ private constructor(
*/
fun name(name: JsonField) = apply { this.name = name }
+ /** List of transactions for this holding (beta) */
+ fun transactions(transactions: List) =
+ transactions(JsonField.of(transactions))
+
+ /**
+ * Sets [Builder.transactions] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.transactions] with a well-typed
+ * `List` value instead. This method is primarily for setting the
+ * field to an undocumented or not yet supported value.
+ */
+ fun transactions(transactions: JsonField>) = apply {
+ this.transactions = transactions.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [Transaction] to [transactions].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addTransaction(transaction: Transaction) = apply {
+ transactions =
+ (transactions ?: JsonField.of(mutableListOf())).also {
+ checkKnown("transactions", it).add(transaction)
+ }
+ }
+
/** Number of units held */
fun units(units: Float) = units(JsonField.of(units))
@@ -1888,6 +2067,7 @@ private constructor(
additionalInfo,
isin,
name,
+ (transactions ?: JsonMissing.of()).map { it.toImmutable() },
units,
value,
additionalProperties.toMutableMap(),
@@ -1901,8 +2081,10 @@ private constructor(
return@apply
}
+ additionalInfo().ifPresent { it.validate() }
isin()
name()
+ transactions().ifPresent { it.forEach { it.validate() } }
units()
value()
validated = true
@@ -1924,373 +2106,1553 @@ private constructor(
*/
@JvmSynthetic
internal fun validity(): Int =
- (if (isin.asKnown().isPresent) 1 else 0) +
+ (additionalInfo.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (isin.asKnown().isPresent) 1 else 0) +
(if (name.asKnown().isPresent) 1 else 0) +
+ (transactions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
(if (units.asKnown().isPresent) 1 else 0) +
(if (value.asKnown().isPresent) 1 else 0)
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
- }
+ /** Additional information specific to the AIF */
+ class AdditionalInfo
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val closeUnits: JsonField,
+ private val openUnits: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("close_units")
+ @ExcludeMissing
+ closeUnits: JsonField = JsonMissing.of(),
+ @JsonProperty("open_units")
+ @ExcludeMissing
+ openUnits: JsonField = JsonMissing.of(),
+ ) : this(closeUnits, openUnits, mutableMapOf())
- return other is Aif &&
- additionalInfo == other.additionalInfo &&
- isin == other.isin &&
- name == other.name &&
- units == other.units &&
- value == other.value &&
- additionalProperties == other.additionalProperties
- }
+ /**
+ * Closing balance units for the statement period (beta)
+ *
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected
+ * type (e.g. if the server responded with an unexpected value).
+ */
+ fun closeUnits(): Optional = closeUnits.getOptional("close_units")
- private val hashCode: Int by lazy {
- Objects.hash(additionalInfo, isin, name, units, value, additionalProperties)
- }
+ /**
+ * Opening balance units for the statement period (beta)
+ *
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected
+ * type (e.g. if the server responded with an unexpected value).
+ */
+ fun openUnits(): Optional = openUnits.getOptional("open_units")
- override fun hashCode(): Int = hashCode
+ /**
+ * Returns the raw JSON value of [closeUnits].
+ *
+ * Unlike [closeUnits], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("close_units")
+ @ExcludeMissing
+ fun _closeUnits(): JsonField = closeUnits
- override fun toString() =
- "Aif{additionalInfo=$additionalInfo, isin=$isin, name=$name, units=$units, value=$value, additionalProperties=$additionalProperties}"
- }
+ /**
+ * Returns the raw JSON value of [openUnits].
+ *
+ * Unlike [openUnits], this method doesn't throw if the JSON field has an
+ * unexpected type.
+ */
+ @JsonProperty("open_units")
+ @ExcludeMissing
+ fun _openUnits(): JsonField = openUnits
- class CorporateBond
- private constructor(
- private val additionalInfo: JsonValue,
- private val isin: JsonField,
- private val name: JsonField,
- private val units: JsonField,
- private val value: JsonField,
- private val additionalProperties: MutableMap,
- ) {
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
- @JsonCreator
- private constructor(
- @JsonProperty("additional_info")
- @ExcludeMissing
- additionalInfo: JsonValue = JsonMissing.of(),
- @JsonProperty("isin")
- @ExcludeMissing
- isin: JsonField = JsonMissing.of(),
- @JsonProperty("name")
- @ExcludeMissing
- name: JsonField = JsonMissing.of(),
- @JsonProperty("units")
- @ExcludeMissing
- units: JsonField = JsonMissing.of(),
- @JsonProperty("value")
+ @JsonAnyGetter
@ExcludeMissing
- value: JsonField = JsonMissing.of(),
- ) : this(additionalInfo, isin, name, units, value, mutableMapOf())
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
- /** Additional information specific to the corporate bond */
- @JsonProperty("additional_info")
- @ExcludeMissing
- fun _additionalInfo(): JsonValue = additionalInfo
+ fun toBuilder() = Builder().from(this)
- /**
- * ISIN code of the corporate bond
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type
- * (e.g. if the server responded with an unexpected value).
- */
- fun isin(): Optional = isin.getOptional("isin")
+ companion object {
- /**
- * Name of the corporate bond
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type
- * (e.g. if the server responded with an unexpected value).
- */
- fun name(): Optional = name.getOptional("name")
+ /**
+ * Returns a mutable builder for constructing an instance of
+ * [AdditionalInfo].
+ */
+ @JvmStatic fun builder() = Builder()
+ }
- /**
- * Number of units held
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type
- * (e.g. if the server responded with an unexpected value).
- */
- fun units(): Optional = units.getOptional("units")
+ /** A builder for [AdditionalInfo]. */
+ class Builder internal constructor() {
- /**
- * Current market value of the holding
- *
- * @throws CasParserInvalidDataException if the JSON field has an unexpected type
- * (e.g. if the server responded with an unexpected value).
- */
- fun value(): Optional = value.getOptional("value")
+ private var closeUnits: JsonField = JsonMissing.of()
+ private var openUnits: JsonField = JsonMissing.of()
+ private var additionalProperties: MutableMap =
+ mutableMapOf()
- /**
- * Returns the raw JSON value of [isin].
- *
- * Unlike [isin], this method doesn't throw if the JSON field has an unexpected
- * type.
- */
- @JsonProperty("isin") @ExcludeMissing fun _isin(): JsonField = isin
+ @JvmSynthetic
+ internal fun from(additionalInfo: AdditionalInfo) = apply {
+ closeUnits = additionalInfo.closeUnits
+ openUnits = additionalInfo.openUnits
+ additionalProperties =
+ additionalInfo.additionalProperties.toMutableMap()
+ }
- /**
- * Returns the raw JSON value of [name].
- *
- * Unlike [name], this method doesn't throw if the JSON field has an unexpected
- * type.
- */
- @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name
+ /** Closing balance units for the statement period (beta) */
+ fun closeUnits(closeUnits: Float?) =
+ closeUnits(JsonField.ofNullable(closeUnits))
+
+ /**
+ * Alias for [Builder.closeUnits].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun closeUnits(closeUnits: Float) = closeUnits(closeUnits as Float?)
+
+ /**
+ * Alias for calling [Builder.closeUnits] with `closeUnits.orElse(null)`.
+ */
+ fun closeUnits(closeUnits: Optional) =
+ closeUnits(closeUnits.getOrNull())
+
+ /**
+ * Sets [Builder.closeUnits] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.closeUnits] with a well-typed [Float]
+ * value instead. This method is primarily for setting the field to an
+ * undocumented or not yet supported value.
+ */
+ fun closeUnits(closeUnits: JsonField) = apply {
+ this.closeUnits = closeUnits
+ }
- /**
- * Returns the raw JSON value of [units].
- *
- * Unlike [units], this method doesn't throw if the JSON field has an unexpected
- * type.
- */
- @JsonProperty("units") @ExcludeMissing fun _units(): JsonField = units
+ /** Opening balance units for the statement period (beta) */
+ fun openUnits(openUnits: Float?) =
+ openUnits(JsonField.ofNullable(openUnits))
+
+ /**
+ * Alias for [Builder.openUnits].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun openUnits(openUnits: Float) = openUnits(openUnits as Float?)
+
+ /** Alias for calling [Builder.openUnits] with `openUnits.orElse(null)`. */
+ fun openUnits(openUnits: Optional) = openUnits(openUnits.getOrNull())
+
+ /**
+ * Sets [Builder.openUnits] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.openUnits] with a well-typed [Float]
+ * value instead. This method is primarily for setting the field to an
+ * undocumented or not yet supported value.
+ */
+ fun openUnits(openUnits: JsonField) = apply {
+ this.openUnits = openUnits
+ }
- /**
- * Returns the raw JSON value of [value].
- *
- * Unlike [value], this method doesn't throw if the JSON field has an unexpected
- * type.
- */
- @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value
+ fun additionalProperties(additionalProperties: Map) =
+ apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
- @JsonAnySetter
- private fun putAdditionalProperty(key: String, value: JsonValue) {
- additionalProperties.put(key, value)
- }
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
- @JsonAnyGetter
- @ExcludeMissing
- fun _additionalProperties(): Map =
- Collections.unmodifiableMap(additionalProperties)
+ fun putAllAdditionalProperties(
+ additionalProperties: Map
+ ) = apply { this.additionalProperties.putAll(additionalProperties) }
- fun toBuilder() = Builder().from(this)
+ fun removeAdditionalProperty(key: String) = apply {
+ additionalProperties.remove(key)
+ }
- companion object {
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
- /**
- * Returns a mutable builder for constructing an instance of [CorporateBond].
- */
- @JvmStatic fun builder() = Builder()
- }
+ /**
+ * Returns an immutable instance of [AdditionalInfo].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): AdditionalInfo =
+ AdditionalInfo(
+ closeUnits,
+ openUnits,
+ additionalProperties.toMutableMap(),
+ )
+ }
- /** A builder for [CorporateBond]. */
- class Builder internal constructor() {
+ private var validated: Boolean = false
- private var additionalInfo: JsonValue = JsonMissing.of()
- private var isin: JsonField = JsonMissing.of()
- private var name: JsonField = JsonMissing.of()
- private var units: JsonField = JsonMissing.of()
- private var value: JsonField = JsonMissing.of()
- private var additionalProperties: MutableMap = mutableMapOf()
+ fun validate(): AdditionalInfo = apply {
+ if (validated) {
+ return@apply
+ }
- @JvmSynthetic
- internal fun from(corporateBond: CorporateBond) = apply {
- additionalInfo = corporateBond.additionalInfo
- isin = corporateBond.isin
- name = corporateBond.name
- units = corporateBond.units
- value = corporateBond.value
- additionalProperties = corporateBond.additionalProperties.toMutableMap()
+ closeUnits()
+ openUnits()
+ validated = true
}
- /** Additional information specific to the corporate bond */
- fun additionalInfo(additionalInfo: JsonValue) = apply {
- this.additionalInfo = additionalInfo
- }
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CasParserInvalidDataException) {
+ false
+ }
- /** ISIN code of the corporate bond */
- fun isin(isin: String) = isin(JsonField.of(isin))
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (closeUnits.asKnown().isPresent) 1 else 0) +
+ (if (openUnits.asKnown().isPresent) 1 else 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is AdditionalInfo &&
+ closeUnits == other.closeUnits &&
+ openUnits == other.openUnits &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(closeUnits, openUnits, additionalProperties)
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "AdditionalInfo{closeUnits=$closeUnits, openUnits=$openUnits, additionalProperties=$additionalProperties}"
+ }
+
+ /**
+ * Unified transaction schema for all holding types (MF folios, equities, bonds,
+ * etc.)
+ */
+ class Transaction
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val additionalInfo: JsonField,
+ private val amount: JsonField,
+ private val balance: JsonField,
+ private val date: JsonField,
+ private val description: JsonField,
+ private val dividendRate: JsonField,
+ private val nav: JsonField,
+ private val type: JsonField,
+ private val units: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("additional_info")
+ @ExcludeMissing
+ additionalInfo: JsonField = JsonMissing.of(),
+ @JsonProperty("amount")
+ @ExcludeMissing
+ amount: JsonField = JsonMissing.of(),
+ @JsonProperty("balance")
+ @ExcludeMissing
+ balance: JsonField = JsonMissing.of(),
+ @JsonProperty("date")
+ @ExcludeMissing
+ date: JsonField = JsonMissing.of(),
+ @JsonProperty("description")
+ @ExcludeMissing
+ description: JsonField = JsonMissing.of(),
+ @JsonProperty("dividend_rate")
+ @ExcludeMissing
+ dividendRate: JsonField = JsonMissing.of(),
+ @JsonProperty("nav")
+ @ExcludeMissing
+ nav: JsonField = JsonMissing.of(),
+ @JsonProperty("type")
+ @ExcludeMissing
+ type: JsonField = JsonMissing.of(),
+ @JsonProperty("units")
+ @ExcludeMissing
+ units: JsonField = JsonMissing.of(),
+ ) : this(
+ additionalInfo,
+ amount,
+ balance,
+ date,
+ description,
+ dividendRate,
+ nav,
+ type,
+ units,
+ mutableMapOf(),
+ )
/**
- * Sets [Builder.isin] to an arbitrary JSON value.
+ * Additional transaction-specific fields that vary by source
*
- * You should usually call [Builder.isin] with a well-typed [String] value
- * instead. This method is primarily for setting the field to an undocumented or
- * not yet supported value.
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected
+ * type (e.g. if the server responded with an unexpected value).
*/
- fun isin(isin: JsonField) = apply { this.isin = isin }
+ fun additionalInfo(): Optional =
+ additionalInfo.getOptional("additional_info")
- /** Name of the corporate bond */
- fun name(name: String) = name(JsonField.of(name))
+ /**
+ * Transaction amount in currency (computed from units × price/NAV)
+ *
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected
+ * type (e.g. if the server responded with an unexpected value).
+ */
+ fun amount(): Optional = amount.getOptional("amount")
/**
- * Sets [Builder.name] to an arbitrary JSON value.
+ * Balance units after transaction
*
- * You should usually call [Builder.name] with a well-typed [String] value
- * instead. This method is primarily for setting the field to an undocumented or
- * not yet supported value.
+ * @throws CasParserInvalidDataException if the JSON field has an unexpected
+ * type (e.g. if the server responded with an unexpected value).
*/
- fun name(name: JsonField) = apply { this.name = name }
+ fun balance(): Optional