diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..bd8e261 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libxkbcommon0 \ + ca-certificates \ + ca-certificates-java \ + make \ + curl \ + git \ + openjdk-17-jdk-headless \ + unzip \ + libc++1 \ + vim \ + && apt-get clean autoclean + +# Ensure UTF-8 encoding +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 + +WORKDIR /workspace + +COPY . /workspace diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..d55fc4d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,20 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/debian +{ + "name": "Debian", + "build": { + "dockerfile": "Dockerfile" + } + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..022b841 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5e1426a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,109 @@ +name: CI +on: + push: + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' + pull_request: + branches-ignore: + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 15 + name: lint + 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) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Java + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 + + - name: Run lints + run: ./scripts/lint + + 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) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Java + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 + + - name: Build SDK + run: ./scripts/build + + - name: Get GitHub OIDC Token + if: |- + github.repository == 'stainless-sdks/cas-parser-java' && + !startsWith(github.ref, 'refs/heads/stl/') + id: github-oidc + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: core.setOutput('github_token', await core.getIDToken()); + + - name: Build and upload Maven artifacts + if: |- + github.repository == 'stainless-sdks/cas-parser-java' && + !startsWith(github.ref, 'refs/heads/stl/') + 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Java + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 + + - name: Run tests + run: ./scripts/test diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml new file mode 100644 index 0000000..ed2e8c8 --- /dev/null +++ b/.github/workflows/publish-sonatype.yml @@ -0,0 +1,41 @@ +# This workflow is triggered when a GitHub release is created. +# It can also be run manually to re-publish to Sonatype in case it failed for some reason. +# You can run this workflow by navigating to https://www.github.com/CASParser/cas-parser-java/actions/workflows/publish-sonatype.yml +name: Publish Sonatype +on: + workflow_dispatch: + + release: + types: [published] + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Java + uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/gradle-build-action@a8f75513eafdebd8141bd1cd4e30fcd194af8dfa # v2.12.0 + + - name: Publish to Sonatype + run: |- + export -- GPG_SIGNING_KEY_ID + printenv -- GPG_SIGNING_KEY | gpg --batch --passphrase-fd 3 --import 3<<< "$GPG_SIGNING_PASSWORD" + GPG_SIGNING_KEY_ID="$(gpg --with-colons --list-keys | awk -F : -- '/^pub:/ { getline; print "0x" substr($10, length($10) - 7) }')" + ./gradlew publish --no-configuration-cache + env: + SONATYPE_USERNAME: ${{ secrets.CAS_PARSER_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.CAS_PARSER_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.CAS_PARSER_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.CAS_PARSER_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..6e803cf --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,24 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + SONATYPE_USERNAME: ${{ secrets.CAS_PARSER_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.CAS_PARSER_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.CAS_PARSER_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.CAS_PARSER_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90b85e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.prism.log +.stdy.log +.gradle +.idea +.kotlin +build/ +codegen.log +kls_database.db diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..1b77f50 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.7.0" +} \ No newline at end of file diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 0000000..fb087e4 --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 21 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser/cas-parser-904e3aa8081755d046016db9d84d13d140a4235c724e18e1cd7f8ebb7712883e.yml +openapi_spec_hash: 453b8e667c364b064e04352ad4deccfa +config_hash: 5509bb7a961ae2e79114b24c381606d4 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6bbb512 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index ac89b43..60011e7 100644 --- a/README.md +++ b/README.md @@ -1 +1,659 @@ -# cas-parser-java \ No newline at end of file +# Cas Parser Java API Library + + + +[![Maven Central](https://img.shields.io/maven-central/v/com.cas_parser.api/cas-parser-java)](https://central.sonatype.com/artifact/com.cas_parser.api/cas-parser-java/0.7.0) +[![javadoc](https://javadoc.io/badge2/com.cas_parser.api/cas-parser-java/0.7.0/javadoc.svg)](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.7.0) + + + +The Cas Parser Java SDK provides convenient access to the [Cas Parser REST API](https://casparser.in/docs) from applications written in Java. + +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. + +[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=cas-parser-node-mcp&config=eyJuYW1lIjoiY2FzLXBhcnNlci1ub2RlLW1jcCIsInRyYW5zcG9ydCI6Imh0dHAiLCJ1cmwiOiJodHRwczovL2Nhcy1wYXJzZXIuc3RsbWNwLmNvbSIsImhlYWRlcnMiOnsieC1hcGkta2V5IjoiTXkgQVBJIEtleSJ9fQ) +[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22cas-parser-node-mcp%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fcas-parser.stlmcp.com%22%2C%22headers%22%3A%7B%22x-api-key%22%3A%22My%20API%20Key%22%7D%7D) + +> Note: You may need to set environment variables in your MCP client. + + + +The REST API documentation can be found on [casparser.in](https://casparser.in/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.7.0). + + + +## Installation + + + +### Gradle + +```kotlin +implementation("com.cas_parser.api:cas-parser-java:0.7.0") +``` + +### Maven + +```xml + + com.cas_parser.api + cas-parser-java + 0.7.0 + +``` + + + +## Requirements + +This library requires Java 8 or later. + +## Usage + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; +import com.cas_parser.api.models.credits.CreditCheckParams; +import com.cas_parser.api.models.credits.CreditCheckResponse; + +// Configures using the `casparser.apiKey` and `casparser.baseUrl` system properties +// Or configures using the `CAS_PARSER_API_KEY` and `CAS_PARSER_BASE_URL` environment variables +CasParserClient client = CasParserOkHttpClient.fromEnv(); + +CreditCheckResponse response = client.credits().check(); +``` + +## Client configuration + +Configure the client using system properties or environment variables: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; + +// Configures using the `casparser.apiKey` and `casparser.baseUrl` system properties +// Or configures using the `CAS_PARSER_API_KEY` and `CAS_PARSER_BASE_URL` environment variables +CasParserClient client = CasParserOkHttpClient.fromEnv(); +``` + +Or manually: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; + +CasParserClient client = CasParserOkHttpClient.builder() + .apiKey("My API Key") + .build(); +``` + +Or using a combination of the two approaches: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; + +CasParserClient client = CasParserOkHttpClient.builder() + // Configures using the `casparser.apiKey` and `casparser.baseUrl` system properties + // Or configures using the `CAS_PARSER_API_KEY` and `CAS_PARSER_BASE_URL` environment variables + .fromEnv() + .apiKey("My API Key") + .build(); +``` + +See this table for the available options: + +| Setter | System property | Environment variable | Required | Default value | +| --------- | ------------------- | --------------------- | -------- | ---------------------------- | +| `apiKey` | `casparser.apiKey` | `CAS_PARSER_API_KEY` | true | - | +| `baseUrl` | `casparser.baseUrl` | `CAS_PARSER_BASE_URL` | true | `"https://api.casparser.in"` | + +System properties take precedence over environment variables. + +> [!TIP] +> Don't create more than one client in the same application. Each client has a connection pool and +> thread pools, which are more efficient to share between requests. + +### Modifying configuration + +To temporarily use a modified client configuration, while reusing the same connection and thread pools, call `withOptions()` on any client or service: + +```java +import com.cas_parser.api.client.CasParserClient; + +CasParserClient clientWithOptions = client.withOptions(optionsBuilder -> { + optionsBuilder.baseUrl("https://example.com"); + optionsBuilder.maxRetries(42); +}); +``` + +The `withOptions()` method does not affect the original client or service. + +## Requests and responses + +To send a request to the Cas Parser API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it will be deserialized into an instance of a Java class. + +For example, `client.credits().check(...)` should be called with an instance of `CreditCheckParams`, and it will return an instance of `CreditCheckResponse`. + +## Immutability + +Each class in the SDK has an associated [builder](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java) or factory method for constructing it. + +Each class is [immutable](https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html) once constructed. If the class has an associated builder, then it has a `toBuilder()` method, which can be used to convert it back to a builder for making a modified copy. + +Because each class is immutable, builder modification will _never_ affect already built class instances. + +## Asynchronous execution + +The default client is synchronous. To switch to asynchronous execution, call the `async()` method: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; +import com.cas_parser.api.models.credits.CreditCheckParams; +import com.cas_parser.api.models.credits.CreditCheckResponse; +import java.util.concurrent.CompletableFuture; + +// Configures using the `casparser.apiKey` and `casparser.baseUrl` system properties +// Or configures using the `CAS_PARSER_API_KEY` and `CAS_PARSER_BASE_URL` environment variables +CasParserClient client = CasParserOkHttpClient.fromEnv(); + +CompletableFuture response = client.async().credits().check(); +``` + +Or create an asynchronous client from the beginning: + +```java +import com.cas_parser.api.client.CasParserClientAsync; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync; +import com.cas_parser.api.models.credits.CreditCheckParams; +import com.cas_parser.api.models.credits.CreditCheckResponse; +import java.util.concurrent.CompletableFuture; + +// Configures using the `casparser.apiKey` and `casparser.baseUrl` system properties +// Or configures using the `CAS_PARSER_API_KEY` and `CAS_PARSER_BASE_URL` environment variables +CasParserClientAsync client = CasParserOkHttpClientAsync.fromEnv(); + +CompletableFuture response = client.credits().check(); +``` + +The asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s. + +## Raw responses + +The SDK defines methods that deserialize responses into instances of Java classes. However, these methods don't provide access to the response headers, status code, or the raw response body. + +To access this data, prefix any HTTP method call on a client or service with `withRawResponse()`: + +```java +import com.cas_parser.api.core.http.Headers; +import com.cas_parser.api.core.http.HttpResponseFor; +import com.cas_parser.api.models.credits.CreditCheckParams; +import com.cas_parser.api.models.credits.CreditCheckResponse; + +HttpResponseFor response = client.credits().withRawResponse().check(); + +int statusCode = response.statusCode(); +Headers headers = response.headers(); +``` + +You can still deserialize the response into an instance of a Java class if needed: + +```java +import com.cas_parser.api.models.credits.CreditCheckResponse; + +CreditCheckResponse parsedResponse = response.parse(); +``` + +## Error handling + +The SDK throws custom unchecked exception types: + +- [`CasParserServiceException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserServiceException.kt): Base class for HTTP errors. See this table for which exception subclass is thrown for each HTTP status code: + + | Status | Exception | + | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | + | 400 | [`BadRequestException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/BadRequestException.kt) | + | 401 | [`UnauthorizedException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnauthorizedException.kt) | + | 403 | [`PermissionDeniedException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/PermissionDeniedException.kt) | + | 404 | [`NotFoundException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/NotFoundException.kt) | + | 422 | [`UnprocessableEntityException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnprocessableEntityException.kt) | + | 429 | [`RateLimitException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/RateLimitException.kt) | + | 5xx | [`InternalServerException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/InternalServerException.kt) | + | others | [`UnexpectedStatusCodeException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnexpectedStatusCodeException.kt) | + +- [`CasParserIoException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserIoException.kt): I/O networking errors. + +- [`CasParserRetryableException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserRetryableException.kt): Generic error indicating a failure that could be retried by the client. + +- [`CasParserInvalidDataException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserInvalidDataException.kt): Failure to interpret successfully parsed data. For example, when accessing a property that's supposed to be required, but the API unexpectedly omitted it from the response. + +- [`CasParserException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserException.kt): Base class for all exceptions. Most errors will result in one of the previously mentioned ones, but completely generic errors may be thrown using the base class. + +## Logging + +Enable logging by setting the `CAS_PARSER_LOG` environment variable to `info`: + +```sh +export CAS_PARSER_LOG=info +``` + +Or to `debug` for more verbose logging: + +```sh +export CAS_PARSER_LOG=debug +``` + +Or configure the client manually using the `logLevel` method: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; +import com.cas_parser.api.core.LogLevel; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + .logLevel(LogLevel.INFO) + .build(); +``` + +## ProGuard and R8 + +Although the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `cas-parser-java-core` is published with a [configuration file](cas-parser-java-core/src/main/resources/META-INF/proguard/cas-parser-java-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage). + +ProGuard and R8 should automatically detect and use the published rules, but you can also manually copy the keep rules if necessary. + +## Jackson + +The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default. + +The SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config). + +If the SDK threw an exception, but you're _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`CasParserOkHttpClient`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt) or [`CasParserOkHttpClientAsync`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt). + +> [!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 + +The SDK automatically retries 2 times by default, with a short exponential backoff between requests. + +Only the following error types are retried: + +- Connection errors (for example, due to a network connectivity problem) +- 408 Request Timeout +- 409 Conflict +- 429 Rate Limit +- 5xx Internal + +The API may also explicitly instruct the SDK to retry or not retry a request. + +To set a custom number of retries, configure the client using the `maxRetries` method: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + .maxRetries(4) + .build(); +``` + +### Timeouts + +Requests time out after 1 minute by default. + +To set a custom timeout, configure the method call using the `timeout` method: + +```java +import com.cas_parser.api.models.credits.CreditCheckResponse; + +CreditCheckResponse response = client.credits().check(RequestOptions.builder().timeout(Duration.ofSeconds(30)).build()); +``` + +Or configure the default for all method calls at the client level: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; +import java.time.Duration; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + .timeout(Duration.ofSeconds(30)) + .build(); +``` + +### Proxies + +To route requests through a proxy, configure the client using the `proxy` method: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; +import java.net.InetSocketAddress; +import java.net.Proxy; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + .proxy(new Proxy( + Proxy.Type.HTTP, new InetSocketAddress( + "https://example.com", 8080 + ) + )) + .build(); +``` + +If the proxy responds with `407 Proxy Authentication Required`, supply credentials by also configuring `proxyAuthenticator`: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; +import com.cas_parser.api.core.http.ProxyAuthenticator; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + .proxy(...) + // Or a custom implementation of `ProxyAuthenticator`. + .proxyAuthenticator(ProxyAuthenticator.basic("username", "password")) + .build(); +``` + +### Connection pooling + +To customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; +import java.time.Duration; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + // If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa. + .maxIdleConnections(10) + .keepAliveDuration(Duration.ofMinutes(2)) + .build(); +``` + +If both options are unset, OkHttp's default connection pool settings are used. + +### HTTPS + +> [!NOTE] +> Most applications should not call these methods, and instead use the system defaults. The defaults include +> special optimizations that can be lost if the implementations are modified. + +To configure how HTTPS connections are secured, configure the client using the `sslSocketFactory`, `trustManager`, and `hostnameVerifier` methods: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + // If `sslSocketFactory` is set, then `trustManager` must be set, and vice versa. + .sslSocketFactory(yourSSLSocketFactory) + .trustManager(yourTrustManager) + .hostnameVerifier(yourHostnameVerifier) + .build(); +``` + +### Custom HTTP client + +The SDK consists of three artifacts: + +- `cas-parser-java-core` + - Contains core SDK logic + - Does not depend on [OkHttp](https://square.github.io/okhttp) + - Exposes [`CasParserClient`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt), [`CasParserClientAsync`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt), [`CasParserClientImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt), and [`CasParserClientAsyncImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt), all of which can work with any HTTP client +- `cas-parser-java-client-okhttp` + - Depends on [OkHttp](https://square.github.io/okhttp) + - Exposes [`CasParserOkHttpClient`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt) and [`CasParserOkHttpClientAsync`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt), which provide a way to construct [`CasParserClientImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt) and [`CasParserClientAsyncImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt), respectively, using OkHttp +- `cas-parser-java` + - Depends on and exposes the APIs of both `cas-parser-java-core` and `cas-parser-java-client-okhttp` + - Does not have its own logic + +This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies. + +#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html) + +> [!TIP] +> Try the available [network options](#network-options) before replacing the default client. + +To use a customized `OkHttpClient`: + +1. Replace your [`cas-parser-java` dependency](#installation) with `cas-parser-java-core` +2. Copy `cas-parser-java-client-okhttp`'s [`OkHttpClient`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/OkHttpClient.kt) class into your code and customize it +3. Construct [`CasParserClientImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt) or [`CasParserClientAsyncImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt), similarly to [`CasParserOkHttpClient`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt) or [`CasParserOkHttpClientAsync`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt), using your customized client + +### Completely custom HTTP client + +To use a completely custom HTTP client: + +1. Replace your [`cas-parser-java` dependency](#installation) with `cas-parser-java-core` +2. Write a class that implements the [`HttpClient`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpClient.kt) interface +3. Construct [`CasParserClientImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt) or [`CasParserClientAsyncImpl`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt), similarly to [`CasParserOkHttpClient`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt) or [`CasParserOkHttpClientAsync`](cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt), using your new client class + +## Undocumented API functionality + +The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API. + +### Parameters + +To set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class: + +```java +import com.cas_parser.api.core.JsonValue; +import com.cas_parser.api.models.credits.CreditCheckParams; + +CreditCheckParams params = CreditCheckParams.builder() + .putAdditionalHeader("Secret-Header", "42") + .putAdditionalQueryParam("secret_query_param", "42") + .putAdditionalBodyProperty("secretProperty", JsonValue.from("42")) + .build(); +``` + +These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods. + +To set a documented parameter or property to an undocumented or not yet supported _value_, pass a [`JsonValue`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Values.kt) object to its setter: + +```java +import com.cas_parser.api.models.credits.CreditCheckParams; + +CreditCheckParams params = CreditCheckParams.builder().build(); +``` + +The most straightforward way to create a [`JsonValue`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Values.kt) is using its `from(...)` method: + +```java +import com.cas_parser.api.core.JsonValue; +import java.util.List; +import java.util.Map; + +// Create primitive JSON values +JsonValue nullValue = JsonValue.from(null); +JsonValue booleanValue = JsonValue.from(true); +JsonValue numberValue = JsonValue.from(42); +JsonValue stringValue = JsonValue.from("Hello World!"); + +// Create a JSON array value equivalent to `["Hello", "World"]` +JsonValue arrayValue = JsonValue.from(List.of( + "Hello", "World" +)); + +// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }` +JsonValue objectValue = JsonValue.from(Map.of( + "a", 1, + "b", 2 +)); + +// Create an arbitrarily nested JSON equivalent to: +// { +// "a": [1, 2], +// "b": [3, 4] +// } +JsonValue complexValue = JsonValue.from(Map.of( + "a", List.of( + 1, 2 + ), + "b", List.of( + 3, 4 + ) +)); +``` + +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.cdsl.fetch.FetchRequestOtpParams; +import com.cas_parser.api.models.credits.CreditCheckParams; + +CreditCheckParams params = FetchRequestOtpParams.builder() + .dob("1990-01-15") + .pan("ABCDE1234F") + .boId(JsonMissing.of()) + .build(); +``` + +### Response properties + +To access undocumented response properties, call the `_additionalProperties()` method: + +```java +import com.cas_parser.api.core.JsonValue; +import java.util.Map; + +Map additionalProperties = client.credits().check(params)._additionalProperties(); +JsonValue secretPropertyValue = additionalProperties.get("secretProperty"); + +String result = secretPropertyValue.accept(new JsonValue.Visitor<>() { + @Override + public String visitNull() { + return "It's null!"; + } + + @Override + public String visitBoolean(boolean value) { + return "It's a boolean!"; + } + + @Override + public String visitNumber(Number value) { + return "It's a number!"; + } + + // Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject` + // The default implementation of each unimplemented method delegates to `visitDefault`, which throws by default, but can also be overridden +}); +``` + +To access a property's raw JSON value, which may be undocumented, call its `_` prefixed method: + +```java +import com.cas_parser.api.core.JsonField; +import java.util.Optional; + +JsonField field = client.credits().check(params)._field(); + +if (field.isMissing()) { + // The property is absent from the JSON response +} else if (field.isNull()) { + // The property was set to literal null +} else { + // Check if value was provided as a string + // Other methods include `asNumber()`, `asBoolean()`, etc. + Optional jsonString = field.asString(); + + // Try to deserialize into a custom type + MyClass myObject = field.asUnknown().orElseThrow().convert(MyClass.class); +} +``` + +### Response validation + +In rare cases, the API may return a response that doesn't match the expected type. For example, the SDK may expect a property to contain a `String`, but the API could return something else. + +By default, the SDK will not throw an exception in this case. It will throw [`CasParserInvalidDataException`](cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserInvalidDataException.kt) only if you directly access the property. + +Validating the response is _not_ forwards compatible with new types from the API for existing fields. + +If you would still prefer to check that the response is completely well-typed upfront, then either call `validate()`: + +```java +import com.cas_parser.api.models.credits.CreditCheckResponse; + +CreditCheckResponse response = client.credits().check(params).validate(); +``` + +Or configure the method call to validate the response using the `responseValidation` method: + +```java +import com.cas_parser.api.models.credits.CreditCheckResponse; + +CreditCheckResponse response = client.credits().check(RequestOptions.builder().responseValidation(true).build()); +``` + +Or configure the default for all method calls at the client level: + +```java +import com.cas_parser.api.client.CasParserClient; +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient; + +CasParserClient client = CasParserOkHttpClient.builder() + .fromEnv() + .responseValidation(true) + .build(); +``` + +## FAQ + +### Why don't you use plain `enum` classes? + +Java `enum` classes are not trivially [forwards compatible](https://www.stainless.com/blog/making-java-enums-forwards-compatible). Using them in the SDK could cause runtime exceptions if the API is updated to respond with a new enum value. + +### Why do you represent fields using `JsonField` instead of just plain `T`? + +Using `JsonField` enables a few features: + +- Allowing usage of [undocumented API functionality](#undocumented-api-functionality) +- Lazily [validating the API response against the expected shape](#response-validation) +- Representing absent vs explicitly null values + +### Why don't you use [`data` classes](https://kotlinlang.org/docs/data-classes.html)? + +It is not [backwards compatible to add new fields to a data class](https://kotlinlang.org/docs/api-guidelines-backward-compatibility.html#avoid-using-data-classes-in-your-api) and we don't want to introduce a breaking change every time we add a field to a class. + +### Why don't you use checked exceptions? + +Checked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason. + +Checked exceptions: + +- Are verbose to handle +- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error +- Are tedious to propagate due to the [function coloring problem](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function) +- Don't play well with lambdas (also due to the function coloring problem) + +## Semantic versioning + +This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions: + +1. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_ +2. Changes that we do not expect to impact the vast majority of users in practice. + +We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience. + +We are keen for your feedback; please open an [issue](https://www.github.com/CASParser/cas-parser-java/issues) with questions, bugs, or suggestions. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..62f7dbe --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Reporting Security Issues + +This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken. + +To report a security issue, please contact the Stainless team at security@stainless.com. + +## Responsible Disclosure + +We appreciate the efforts of security researchers and individuals who help us maintain the security of +SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible +disclosure practices by allowing us a reasonable amount of time to investigate and address the issue +before making any information public. + +## Reporting Non-SDK Related Security Issues + +If you encounter security issues that are not directly related to SDKs but pertain to the services +or products provided by Cas Parser, please follow the respective company's security reporting guidelines. + +### Cas Parser Terms and Policies + +Please contact sameer@casparser.in for any questions or concerns regarding the security of our services. + +--- + +Thank you for helping us keep the SDKs and systems they interact with secure. diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..3a6a7b4 --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${SONATYPE_USERNAME}" ]; then + errors+=("The SONATYPE_USERNAME secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${SONATYPE_PASSWORD}" ]; then + errors+=("The SONATYPE_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${GPG_SIGNING_KEY}" ]; then + errors+=("The GPG_SIGNING_KEY secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${GPG_SIGNING_PASSWORD}" ]; then + errors+=("The GPG_SIGNING_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..d0f1e15 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + id("io.github.gradle-nexus.publish-plugin") version "1.1.0" + id("org.jetbrains.dokka") version "2.0.0" +} + +repositories { + mavenCentral() +} + +allprojects { + group = "com.cas_parser.api" + version = "0.7.0" // x-release-please-version +} + +subprojects { + // These are populated with dependencies by `buildSrc` scripts. + tasks.register("format") { + group = "Verification" + description = "Formats all source files." + } + tasks.register("lint") { + group = "Verification" + description = "Verifies all source files are formatted." + } +} + +subprojects { + apply(plugin = "org.jetbrains.dokka") +} + +// Avoid race conditions between `dokkaJavadocCollector` and `dokkaJavadocJar` tasks +tasks.named("dokkaJavadocCollector").configure { + subprojects.flatMap { it.tasks } + .filter { it.project.name != "cas-parser-java" && it.name == "dokkaJavadocJar" } + .forEach { mustRunAfter(it) } +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + + username.set(System.getenv("SONATYPE_USERNAME")) + password.set(System.getenv("SONATYPE_PASSWORD")) + } + } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..0b14135 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + `kotlin-dsl` + kotlin("jvm") version "1.9.20" +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20") +} diff --git a/buildSrc/src/main/kotlin/cas-parser.java.gradle.kts b/buildSrc/src/main/kotlin/cas-parser.java.gradle.kts new file mode 100644 index 0000000..a3cfe28 --- /dev/null +++ b/buildSrc/src/main/kotlin/cas-parser.java.gradle.kts @@ -0,0 +1,136 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat + +plugins { + `java-library` +} + +repositories { + mavenCentral() +} + +configure { + withJavadocJar() + withSourcesJar() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } + + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().configureEach { + options.compilerArgs.add("-Werror") + options.release.set(8) +} + +tasks.named("javadocJar") { + setZip64(true) +} + +tasks.named("jar") { + manifest { + attributes(mapOf( + "Implementation-Title" to project.name, + "Implementation-Version" to project.version + )) + } +} + +tasks.withType().configureEach { + useJUnitPlatform() + + // Run tests in parallel to some degree. + maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1) + forkEvery = 100 + + testLogging { + exceptionFormat = TestExceptionFormat.FULL + } +} + +val palantir by configurations.creating +dependencies { + palantir("com.palantir.javaformat:palantir-java-format:2.89.0") +} + +fun registerPalantir( + name: String, + description: String, +) { + val javaName = "${name}Java" + tasks.register(javaName) { + group = "Verification" + this.description = description + + classpath = palantir + mainClass = "com.palantir.javaformat.java.Main" + + // Avoid an `IllegalAccessError` on Java 9+. + jvmArgs( + "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + ) + + // Use paths relative to the current module. + val argumentFile = + project.layout.buildDirectory.file("palantir-$name-args.txt").get().asFile + val lastRunTimeFile = + project.layout.buildDirectory.file("palantir-$name-last-run.txt").get().asFile + + // Read the time when this task was last executed for this module (if ever). + val lastRunTime = lastRunTimeFile.takeIf { it.exists() }?.readText()?.toLongOrNull() ?: 0L + + // Use a `fileTree` relative to the module's source directory. + val javaFiles = project.fileTree("src") { include("**/*.java") } + + // Determine if any files need to be formatted or linted and continue only if there is at least + // one file. + onlyIf { javaFiles.any { it.lastModified() > lastRunTime } } + + inputs.files(javaFiles) + + doFirst { + // Create the argument file and set the preferred formatting style. + argumentFile.parentFile.mkdirs() + argumentFile.writeText("--palantir\n") + + if (name == "lint") { + // For lint, do a dry run, so no files are modified. Set the exit code to 1 (instead of + // the default 0) if any files need to be formatted, indicating that linting has failed. + argumentFile.appendText("--dry-run\n") + argumentFile.appendText("--set-exit-if-changed\n") + } else { + // `--dry-run` and `--replace` (for in-place formatting) are mutually exclusive. + argumentFile.appendText("--replace\n") + } + + // Write the modified files to the argument file. + javaFiles.filter { it.lastModified() > lastRunTime } + .forEach { argumentFile.appendText("${it.absolutePath}\n") } + } + + doLast { + // Record the last execution time for later up-to-date checking. + lastRunTimeFile.writeText(System.currentTimeMillis().toString()) + } + + // Pass the argument file using the @ symbol + args = listOf("@${argumentFile.absolutePath}") + + outputs.upToDateWhen { javaFiles.none { it.lastModified() > lastRunTime } } + } + + tasks.named(name) { + dependsOn(tasks.named(javaName)) + } +} + +registerPalantir(name = "format", description = "Formats all Java source files.") +registerPalantir(name = "lint", description = "Verifies all Java source files are formatted.") diff --git a/buildSrc/src/main/kotlin/cas-parser.kotlin.gradle.kts b/buildSrc/src/main/kotlin/cas-parser.kotlin.gradle.kts new file mode 100644 index 0000000..056d328 --- /dev/null +++ b/buildSrc/src/main/kotlin/cas-parser.kotlin.gradle.kts @@ -0,0 +1,109 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion + +plugins { + id("cas-parser.java") + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + } + + compilerOptions { + freeCompilerArgs = listOf( + "-Xjvm-default=all", + "-Xjdk-release=1.8", + // Suppress deprecation warnings because we may still reference and test deprecated members. + // TODO: Replace with `-Xsuppress-warning=DEPRECATION` once we use Kotlin compiler 2.1.0+. + "-nowarn", + ) + jvmTarget.set(JvmTarget.JVM_1_8) + languageVersion.set(KotlinVersion.KOTLIN_1_8) + apiVersion.set(KotlinVersion.KOTLIN_1_8) + coreLibrariesVersion = "1.8.0" + } +} + +tasks.withType().configureEach { + systemProperty("junit.jupiter.execution.parallel.enabled", true) + systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") + + // `SKIP_MOCK_TESTS` affects which tests run so it must be added as input for proper cache invalidation. + inputs.property("skipMockTests", System.getenv("SKIP_MOCK_TESTS")).optional(true) +} + +val ktfmt by configurations.creating +dependencies { + ktfmt("com.facebook:ktfmt:0.61") +} + +fun registerKtfmt( + name: String, + description: String, +) { + val kotlinName = "${name}Kotlin" + tasks.register(kotlinName) { + group = "Verification" + this.description = description + + classpath = ktfmt + mainClass = "com.facebook.ktfmt.cli.Main" + + // Use paths relative to the current module. + val argumentFile = project.layout.buildDirectory.file("ktfmt-$name-args.txt").get().asFile + val lastRunTimeFile = + project.layout.buildDirectory.file("ktfmt-$name-last-run.txt").get().asFile + + // Read the time when this task was last executed for this module (if ever). + val lastRunTime = lastRunTimeFile.takeIf { it.exists() }?.readText()?.toLongOrNull() ?: 0L + + // Use a `fileTree` relative to the module's source directory. + val kotlinFiles = project.fileTree("src") { include("**/*.kt") } + + // Determine if any files need to be formatted or linted and continue only if there is at least + // one file (otherwise Ktfmt will fail). + onlyIf { kotlinFiles.any { it.lastModified() > lastRunTime } } + + inputs.files(kotlinFiles) + + doFirst { + // Create the argument file and set the preferred formatting style. + argumentFile.parentFile.mkdirs() + argumentFile.writeText("--kotlinlang-style\n") + + if (name == "lint") { + // For lint, do a dry run, so no files are modified. Set the exit code to 1 (instead of + // the default 0) if any files need to be formatted, indicating that linting has failed. + argumentFile.appendText("--dry-run\n") + argumentFile.appendText("--set-exit-if-changed\n") + } + + // Write the modified files to the argument file. + kotlinFiles.filter { it.lastModified() > lastRunTime } + .forEach { argumentFile.appendText("${it.absolutePath}\n") } + } + + doLast { + // Record the last execution time for later up-to-date checking. + lastRunTimeFile.writeText(System.currentTimeMillis().toString()) + } + + // Pass the argument file using the @ symbol + args = listOf("@${argumentFile.absolutePath}") + + outputs.upToDateWhen { kotlinFiles.none { it.lastModified() > lastRunTime } } + } + + tasks.named(name) { + dependsOn(tasks.named(kotlinName)) + } +} + +registerKtfmt(name = "format", description = "Formats all Kotlin source files.") +registerKtfmt(name = "lint", description = "Verifies all Kotlin source files are formatted.") diff --git a/buildSrc/src/main/kotlin/cas-parser.publish.gradle.kts b/buildSrc/src/main/kotlin/cas-parser.publish.gradle.kts new file mode 100644 index 0000000..f958b7b --- /dev/null +++ b/buildSrc/src/main/kotlin/cas-parser.publish.gradle.kts @@ -0,0 +1,69 @@ +plugins { + `maven-publish` + signing +} + +configure { + publications { + register("maven") { + from(components["java"]) + + pom { + name.set("CAS Parser - Track Portfolios from CDSL, NSDL, CAMS, KFintech") + description.set("API for parsing and analyzing CAS (Consolidated Account Statement) PDF files\nfrom NSDL, CDSL, and CAMS/KFintech, with a unified response format") + url.set("https://casparser.in/docs") + + licenses { + license { + name.set("Apache-2.0") + } + } + + developers { + developer { + name.set("Cas Parser") + email.set("sameer@casparser.in") + } + } + + scm { + connection.set("scm:git:git://github.com/CASParser/cas-parser-java.git") + developerConnection.set("scm:git:git://github.com/CASParser/cas-parser-java.git") + url.set("https://github.com/CASParser/cas-parser-java") + } + + versionMapping { + allVariants { + fromResolutionResult() + } + } + } + } + } + repositories { + if (project.hasProperty("publishLocal")) { + maven { + name = "LocalFileSystem" + url = uri("${rootProject.layout.buildDirectory.get()}/local-maven-repo") + } + } + } +} + +signing { + val signingKeyId = System.getenv("GPG_SIGNING_KEY_ID")?.ifBlank { null } + val signingKey = System.getenv("GPG_SIGNING_KEY")?.ifBlank { null } + val signingPassword = System.getenv("GPG_SIGNING_PASSWORD")?.ifBlank { null } + if (signingKey != null && signingPassword != null) { + useInMemoryPgpKeys( + signingKeyId, + signingKey, + signingPassword, + ) + sign(publishing.publications["maven"]) + } +} + +tasks.named("publish") { + dependsOn(":closeAndReleaseSonatypeStagingRepository") +} diff --git a/cas-parser-java-client-okhttp/build.gradle.kts b/cas-parser-java-client-okhttp/build.gradle.kts new file mode 100644 index 0000000..0824fab --- /dev/null +++ b/cas-parser-java-client-okhttp/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("cas-parser.kotlin") + id("cas-parser.publish") +} + +dependencies { + api(project(":cas-parser-java-core")) + + implementation("com.squareup.okhttp3:okhttp:4.12.0") + + testImplementation(kotlin("test")) + testImplementation("org.assertj:assertj-core:3.27.7") + 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 new file mode 100644 index 0000000..ce3933f --- /dev/null +++ b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClient.kt @@ -0,0 +1,404 @@ +// File generated from our OpenAPI spec by Stainless. + +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.LogLevel +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 +import com.cas_parser.api.core.http.ProxyAuthenticator +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.databind.json.JsonMapper +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 +import kotlin.jvm.optionals.getOrNull + +/** + * A class that allows building an instance of [CasParserClient] with [OkHttpClient] as the + * underlying [HttpClient]. + */ +class CasParserOkHttpClient private constructor() { + + companion object { + + /** Returns a mutable builder for constructing an instance of [CasParserClient]. */ + @JvmStatic fun builder() = Builder() + + /** + * Returns a client configured using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + @JvmStatic fun fromEnv(): CasParserClient = builder().fromEnv().build() + } + + /** A builder for [CasParserOkHttpClient]. */ + class Builder internal constructor() { + + private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var dispatcherExecutorService: ExecutorService? = null + private var proxy: Proxy? = null + private var proxyAuthenticator: ProxyAuthenticator? = null + private var maxIdleConnections: Int? = null + private var keepAliveDuration: Duration? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null + + /** + * 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)`. */ + fun proxy(proxy: Optional) = proxy(proxy.getOrNull()) + + /** + * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication + * Required`. + */ + fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply { + this.proxyAuthenticator = proxyAuthenticator + } + + /** + * Alias for calling [Builder.proxyAuthenticator] with `proxyAuthenticator.orElse(null)`. + */ + fun proxyAuthenticator(proxyAuthenticator: Optional) = + proxyAuthenticator(proxyAuthenticator.getOrNull()) + + /** + * The maximum number of idle connections kept by the underlying OkHttp connection pool. + * + * If this is set, then [keepAliveDuration] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun maxIdleConnections(maxIdleConnections: Int?) = apply { + this.maxIdleConnections = maxIdleConnections + } + + /** + * Alias for [Builder.maxIdleConnections]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxIdleConnections(maxIdleConnections: Int) = + maxIdleConnections(maxIdleConnections as Int?) + + /** + * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`. + */ + fun maxIdleConnections(maxIdleConnections: Optional) = + maxIdleConnections(maxIdleConnections.getOrNull()) + + /** + * The keep-alive duration for idle connections in the underlying OkHttp connection pool. + * + * If this is set, then [maxIdleConnections] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun keepAliveDuration(keepAliveDuration: Duration?) = apply { + this.keepAliveDuration = keepAliveDuration + } + + /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */ + fun keepAliveDuration(keepAliveDuration: Optional) = + keepAliveDuration(keepAliveDuration.getOrNull()) + + /** + * The socket factory used to secure HTTPS connections. + * + * If this is set, then [trustManager] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + /** Alias for calling [Builder.sslSocketFactory] with `sslSocketFactory.orElse(null)`. */ + fun sslSocketFactory(sslSocketFactory: Optional) = + sslSocketFactory(sslSocketFactory.getOrNull()) + + /** + * The trust manager used to secure HTTPS connections. + * + * If this is set, then [sslSocketFactory] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + /** Alias for calling [Builder.trustManager] with `trustManager.orElse(null)`. */ + fun trustManager(trustManager: Optional) = + trustManager(trustManager.getOrNull()) + + /** + * The verifier used to confirm that response certificates apply to requested hostnames for + * HTTPS connections. + * + * If unset, then a default hostname verifier is used. + */ + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + /** Alias for calling [Builder.hostnameVerifier] with `hostnameVerifier.orElse(null)`. */ + fun hostnameVerifier(hostnameVerifier: Optional) = + hostnameVerifier(hostnameVerifier.getOrNull()) + + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee + * that the SDK will work correctly when using an incompatible Jackson version. + */ + fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { + clientOptions.checkJacksonVersionCompatibility(checkJacksonVersionCompatibility) + } + + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.cas_parser.api.core.jsonMapper]. The default is usually sufficient and + * rarely needs to be overridden. + */ + 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. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.casparser.in`. + */ + fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) } + + /** Alias for calling [Builder.baseUrl] with `baseUrl.orElse(null)`. */ + fun baseUrl(baseUrl: Optional) = baseUrl(baseUrl.getOrNull()) + + /** + * Whether to call `validate` on every response before returning it. + * + * Setting this to `true` is _not_ forwards compatible with new types from the API for + * existing fields. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) } + + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) } + + /** Your API key for authentication. Use `sandbox-with-json-responses` as Sandbox key. */ + fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } + + fun headers(headers: Headers) = apply { clientOptions.headers(headers) } + + fun headers(headers: Map>) = apply { + clientOptions.headers(headers) + } + + fun putHeader(name: String, value: String) = apply { clientOptions.putHeader(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { + clientOptions.putHeaders(name, values) + } + + fun putAllHeaders(headers: Headers) = apply { clientOptions.putAllHeaders(headers) } + + fun putAllHeaders(headers: Map>) = apply { + clientOptions.putAllHeaders(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { + clientOptions.replaceHeaders(name, value) + } + + fun replaceHeaders(name: String, values: Iterable) = apply { + clientOptions.replaceHeaders(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { clientOptions.replaceAllHeaders(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + clientOptions.replaceAllHeaders(headers) + } + + fun removeHeaders(name: String) = apply { clientOptions.removeHeaders(name) } + + fun removeAllHeaders(names: Set) = apply { clientOptions.removeAllHeaders(names) } + + fun queryParams(queryParams: QueryParams) = apply { clientOptions.queryParams(queryParams) } + + fun queryParams(queryParams: Map>) = apply { + clientOptions.queryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { + clientOptions.putQueryParam(key, value) + } + + fun putQueryParams(key: String, values: Iterable) = apply { + clientOptions.putQueryParams(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + clientOptions.replaceQueryParams(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + clientOptions.replaceQueryParams(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun removeQueryParams(key: String) = apply { clientOptions.removeQueryParams(key) } + + fun removeAllQueryParams(keys: Set) = apply { + clientOptions.removeAllQueryParams(keys) + } + + /** + * Updates configuration using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + fun fromEnv() = apply { clientOptions.fromEnv() } + + /** + * Returns an immutable instance of [CasParserClient]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CasParserClient = + CasParserClientImpl( + clientOptions + .httpClient( + OkHttpClient.builder() + .timeout(clientOptions.timeout()) + .proxy(proxy) + .proxyAuthenticator(proxyAuthenticator) + .maxIdleConnections(maxIdleConnections) + .keepAliveDuration(keepAliveDuration) + .dispatcherExecutorService(dispatcherExecutorService) + .sslSocketFactory(sslSocketFactory) + .trustManager(trustManager) + .hostnameVerifier(hostnameVerifier) + .build() + ) + .build() + ) + } +} 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 new file mode 100644 index 0000000..53c82ef --- /dev/null +++ b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/CasParserOkHttpClientAsync.kt @@ -0,0 +1,404 @@ +// File generated from our OpenAPI spec by Stainless. + +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.LogLevel +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 +import com.cas_parser.api.core.http.ProxyAuthenticator +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.databind.json.JsonMapper +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 +import kotlin.jvm.optionals.getOrNull + +/** + * A class that allows building an instance of [CasParserClientAsync] with [OkHttpClient] as the + * underlying [HttpClient]. + */ +class CasParserOkHttpClientAsync private constructor() { + + companion object { + + /** Returns a mutable builder for constructing an instance of [CasParserClientAsync]. */ + @JvmStatic fun builder() = Builder() + + /** + * Returns a client configured using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + @JvmStatic fun fromEnv(): CasParserClientAsync = builder().fromEnv().build() + } + + /** A builder for [CasParserOkHttpClientAsync]. */ + class Builder internal constructor() { + + private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var dispatcherExecutorService: ExecutorService? = null + private var proxy: Proxy? = null + private var proxyAuthenticator: ProxyAuthenticator? = null + private var maxIdleConnections: Int? = null + private var keepAliveDuration: Duration? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null + + /** + * 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)`. */ + fun proxy(proxy: Optional) = proxy(proxy.getOrNull()) + + /** + * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication + * Required`. + */ + fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply { + this.proxyAuthenticator = proxyAuthenticator + } + + /** + * Alias for calling [Builder.proxyAuthenticator] with `proxyAuthenticator.orElse(null)`. + */ + fun proxyAuthenticator(proxyAuthenticator: Optional) = + proxyAuthenticator(proxyAuthenticator.getOrNull()) + + /** + * The maximum number of idle connections kept by the underlying OkHttp connection pool. + * + * If this is set, then [keepAliveDuration] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun maxIdleConnections(maxIdleConnections: Int?) = apply { + this.maxIdleConnections = maxIdleConnections + } + + /** + * Alias for [Builder.maxIdleConnections]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun maxIdleConnections(maxIdleConnections: Int) = + maxIdleConnections(maxIdleConnections as Int?) + + /** + * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`. + */ + fun maxIdleConnections(maxIdleConnections: Optional) = + maxIdleConnections(maxIdleConnections.getOrNull()) + + /** + * The keep-alive duration for idle connections in the underlying OkHttp connection pool. + * + * If this is set, then [maxIdleConnections] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun keepAliveDuration(keepAliveDuration: Duration?) = apply { + this.keepAliveDuration = keepAliveDuration + } + + /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */ + fun keepAliveDuration(keepAliveDuration: Optional) = + keepAliveDuration(keepAliveDuration.getOrNull()) + + /** + * The socket factory used to secure HTTPS connections. + * + * If this is set, then [trustManager] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + /** Alias for calling [Builder.sslSocketFactory] with `sslSocketFactory.orElse(null)`. */ + fun sslSocketFactory(sslSocketFactory: Optional) = + sslSocketFactory(sslSocketFactory.getOrNull()) + + /** + * The trust manager used to secure HTTPS connections. + * + * If this is set, then [sslSocketFactory] must also be set. + * + * If unset, then the system default is used. Most applications should not call this method, + * and instead use the system default. The default include special optimizations that can be + * lost if the implementation is modified. + */ + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + /** Alias for calling [Builder.trustManager] with `trustManager.orElse(null)`. */ + fun trustManager(trustManager: Optional) = + trustManager(trustManager.getOrNull()) + + /** + * The verifier used to confirm that response certificates apply to requested hostnames for + * HTTPS connections. + * + * If unset, then a default hostname verifier is used. + */ + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + /** Alias for calling [Builder.hostnameVerifier] with `hostnameVerifier.orElse(null)`. */ + fun hostnameVerifier(hostnameVerifier: Optional) = + hostnameVerifier(hostnameVerifier.getOrNull()) + + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee + * that the SDK will work correctly when using an incompatible Jackson version. + */ + fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { + clientOptions.checkJacksonVersionCompatibility(checkJacksonVersionCompatibility) + } + + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.cas_parser.api.core.jsonMapper]. The default is usually sufficient and + * rarely needs to be overridden. + */ + 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. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.casparser.in`. + */ + fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) } + + /** Alias for calling [Builder.baseUrl] with `baseUrl.orElse(null)`. */ + fun baseUrl(baseUrl: Optional) = baseUrl(baseUrl.getOrNull()) + + /** + * Whether to call `validate` on every response before returning it. + * + * Setting this to `true` is _not_ forwards compatible with new types from the API for + * existing fields. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) } + + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + fun logLevel(logLevel: LogLevel) = apply { clientOptions.logLevel(logLevel) } + + /** Your API key for authentication. Use `sandbox-with-json-responses` as Sandbox key. */ + fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } + + fun headers(headers: Headers) = apply { clientOptions.headers(headers) } + + fun headers(headers: Map>) = apply { + clientOptions.headers(headers) + } + + fun putHeader(name: String, value: String) = apply { clientOptions.putHeader(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { + clientOptions.putHeaders(name, values) + } + + fun putAllHeaders(headers: Headers) = apply { clientOptions.putAllHeaders(headers) } + + fun putAllHeaders(headers: Map>) = apply { + clientOptions.putAllHeaders(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { + clientOptions.replaceHeaders(name, value) + } + + fun replaceHeaders(name: String, values: Iterable) = apply { + clientOptions.replaceHeaders(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { clientOptions.replaceAllHeaders(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + clientOptions.replaceAllHeaders(headers) + } + + fun removeHeaders(name: String) = apply { clientOptions.removeHeaders(name) } + + fun removeAllHeaders(names: Set) = apply { clientOptions.removeAllHeaders(names) } + + fun queryParams(queryParams: QueryParams) = apply { clientOptions.queryParams(queryParams) } + + fun queryParams(queryParams: Map>) = apply { + clientOptions.queryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { + clientOptions.putQueryParam(key, value) + } + + fun putQueryParams(key: String, values: Iterable) = apply { + clientOptions.putQueryParams(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + clientOptions.putAllQueryParams(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + clientOptions.replaceQueryParams(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + clientOptions.replaceQueryParams(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + clientOptions.replaceAllQueryParams(queryParams) + } + + fun removeQueryParams(key: String) = apply { clientOptions.removeQueryParams(key) } + + fun removeAllQueryParams(keys: Set) = apply { + clientOptions.removeAllQueryParams(keys) + } + + /** + * Updates configuration using system properties and environment variables. + * + * @see ClientOptions.Builder.fromEnv + */ + fun fromEnv() = apply { clientOptions.fromEnv() } + + /** + * Returns an immutable instance of [CasParserClientAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CasParserClientAsync = + CasParserClientAsyncImpl( + clientOptions + .httpClient( + OkHttpClient.builder() + .timeout(clientOptions.timeout()) + .proxy(proxy) + .proxyAuthenticator(proxyAuthenticator) + .maxIdleConnections(maxIdleConnections) + .keepAliveDuration(keepAliveDuration) + .dispatcherExecutorService(dispatcherExecutorService) + .sslSocketFactory(sslSocketFactory) + .trustManager(trustManager) + .hostnameVerifier(hostnameVerifier) + .build() + ) + .build() + ) + } +} 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 new file mode 100644 index 0000000..5494e32 --- /dev/null +++ b/cas-parser-java-client-okhttp/src/main/kotlin/com/cas_parser/api/client/okhttp/OkHttpClient.kt @@ -0,0 +1,356 @@ +package com.cas_parser.api.client.okhttp + +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.Timeout +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.HttpClient +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpRequestBody +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.ProxyAuthenticator +import com.cas_parser.api.errors.CasParserIoException +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.net.Proxy +import java.time.Duration +import java.util.concurrent.CancellationException +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ExecutorService +import java.util.concurrent.TimeUnit +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import kotlin.jvm.optionals.getOrNull +import okhttp3.Call +import okhttp3.Callback +import okhttp3.ConnectionPool +import okhttp3.Dispatcher +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.Request +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import okio.BufferedSink +import okio.buffer +import okio.sink + +class OkHttpClient +internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient { + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { + val call = newCall(request, requestOptions) + + return try { + call.execute().toHttpResponse() + } catch (e: IOException) { + throw CasParserIoException("Request failed", e) + } finally { + request.body?.close() + } + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val future = CompletableFuture() + + val call = newCall(request, requestOptions) + call.enqueue( + object : Callback { + override fun onResponse(call: Call, response: Response) { + future.complete(response.toHttpResponse()) + } + + 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 + } + + override fun close() { + okHttpClient.dispatcher.executorService.shutdown() + okHttpClient.connectionPool.evictAll() + okHttpClient.cache?.close() + } + + private fun newCall(request: HttpRequest, requestOptions: RequestOptions): Call { + val clientBuilder = okHttpClient.newBuilder() + + requestOptions.timeout?.let { + clientBuilder + .connectTimeout(it.connect()) + .readTimeout(it.read()) + .writeTimeout(it.write()) + .callTimeout(it.request()) + } + + val client = clientBuilder.build() + return client.newCall(request.toRequest(client)) + } + + companion object { + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + + private var timeout: Timeout = Timeout.default() + private var proxy: Proxy? = null + private var proxyAuthenticator: ProxyAuthenticator? = null + private var maxIdleConnections: Int? = null + private var keepAliveDuration: Duration? = null + private var dispatcherExecutorService: ExecutorService? = null + private var sslSocketFactory: SSLSocketFactory? = null + private var trustManager: X509TrustManager? = null + private var hostnameVerifier: HostnameVerifier? = null + + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + + fun proxy(proxy: Proxy?) = apply { this.proxy = proxy } + + fun proxyAuthenticator(proxyAuthenticator: ProxyAuthenticator?) = apply { + this.proxyAuthenticator = proxyAuthenticator + } + + /** + * Sets the maximum number of idle connections kept by the underlying [ConnectionPool]. + * + * If this is set, then [keepAliveDuration] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun maxIdleConnections(maxIdleConnections: Int?) = apply { + this.maxIdleConnections = maxIdleConnections + } + + /** + * Sets the keep-alive duration for idle connections in the underlying [ConnectionPool]. + * + * If this is set, then [maxIdleConnections] must also be set. + * + * If unset, then OkHttp's default is used. + */ + fun keepAliveDuration(keepAliveDuration: Duration?) = apply { + this.keepAliveDuration = keepAliveDuration + } + + fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply { + this.dispatcherExecutorService = dispatcherExecutorService + } + + fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply { + this.sslSocketFactory = sslSocketFactory + } + + fun trustManager(trustManager: X509TrustManager?) = apply { + this.trustManager = trustManager + } + + fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply { + this.hostnameVerifier = hostnameVerifier + } + + 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 { + proxyAuthenticator?.let { auth -> + proxyAuthenticator { route, response -> + auth + .authenticate( + route?.proxy ?: Proxy.NO_PROXY, + response.request.toHttpRequest(), + response.toHttpResponse(), + ) + .getOrNull() + ?.toRequest(client = null) + } + } + + dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) } + + val maxIdleConnections = maxIdleConnections + val keepAliveDuration = keepAliveDuration + if (maxIdleConnections != null && keepAliveDuration != null) { + connectionPool( + ConnectionPool( + maxIdleConnections, + keepAliveDuration.toNanos(), + TimeUnit.NANOSECONDS, + ) + ) + } else { + check((maxIdleConnections != null) == (keepAliveDuration != null)) { + "Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set" + } + } + + val sslSocketFactory = sslSocketFactory + val trustManager = trustManager + if (sslSocketFactory != null && trustManager != null) { + sslSocketFactory(sslSocketFactory, trustManager) + } else { + check((sslSocketFactory != null) == (trustManager != null)) { + "Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set" + } + } + + hostnameVerifier?.let(::hostnameVerifier) + } + .build() + .apply { + // We usually make all our requests to the same host so it makes sense to + // raise the per-host limit to the overall limit. + dispatcher.maxRequestsPerHost = dispatcher.maxRequests + } + ) + } +} + +private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient?): Request { + var body: RequestBody? = body?.toRequestBody() + if (body == null && requiresBody(method)) { + body = "".toRequestBody() + } + + val builder = Request.Builder().url(toUrl()).method(method.name, body) + headers.names().forEach { name -> headers.values(name).forEach { builder.addHeader(name, it) } } + + if (client != null) { + if ( + !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0 + ) { + builder.addHeader( + "X-Stainless-Read-Timeout", + Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(), + ) + } + if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) { + builder.addHeader( + "X-Stainless-Timeout", + Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(), + ) + } + } + + return builder.build() +} + +/** `OkHttpClient` always requires a request body for some methods. */ +private fun requiresBody(method: HttpMethod): Boolean = + when (method) { + HttpMethod.POST, + HttpMethod.PUT, + HttpMethod.PATCH -> true + else -> false + } + +private fun HttpRequest.toUrl(): String { + val builder = baseUrl.toHttpUrl().newBuilder() + pathSegments.forEach(builder::addPathSegment) + queryParams.keys().forEach { key -> + queryParams.values(key).forEach { builder.addQueryParameter(key, it) } + } + + return builder.toString() +} + +private fun HttpRequestBody.toRequestBody(): RequestBody { + val mediaType = contentType()?.toMediaType() + val length = contentLength() + + return object : RequestBody() { + override fun contentType(): MediaType? = mediaType + + override fun contentLength(): Long = length + + override fun isOneShot(): Boolean = !repeatable() + + override fun writeTo(sink: BufferedSink) = writeTo(sink.outputStream()) + } +} + +private fun Request.toHttpRequest(): HttpRequest { + val builder = HttpRequest.builder().method(HttpMethod.valueOf(method)).baseUrl(url.toBaseUrl()) + url.pathSegments.forEach(builder::addPathSegment) + url.queryParameterNames.forEach { name -> + url.queryParameterValues(name).filterNotNull().forEach { builder.putQueryParam(name, it) } + } + headers.forEach { (name, value) -> builder.putHeader(name, value) } + body?.let { builder.body(it.toHttpRequestBody()) } + return builder.build() +} + +private fun HttpUrl.toBaseUrl(): String = buildString { + append(scheme).append("://").append(host) + if (port != HttpUrl.defaultPort(scheme)) { + append(":").append(port) + } +} + +private fun RequestBody.toHttpRequestBody(): HttpRequestBody { + val mediaType = contentType()?.toString() + val length = contentLength() + val isOneShot = isOneShot() + val source = this + return object : HttpRequestBody { + override fun contentType(): String? = mediaType + + override fun contentLength(): Long = length + + override fun repeatable(): Boolean = !isOneShot + + override fun writeTo(outputStream: OutputStream) { + val sink = outputStream.sink().buffer() + source.writeTo(sink) + sink.flush() + } + + override fun close() {} + } +} + +private fun Response.toHttpResponse(): HttpResponse { + val headers = headers.toHeaders() + + return object : HttpResponse { + override fun statusCode(): Int = code + + override fun headers(): Headers = headers + + override fun body(): InputStream = body!!.byteStream() + + override fun close() = body!!.close() + } +} + +private fun okhttp3.Headers.toHeaders(): Headers { + val headersBuilder = Headers.builder() + forEach { (name, value) -> headersBuilder.put(name, value) } + return headersBuilder.build() +} diff --git a/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 new file mode 100644 index 0000000..ccacdad --- /dev/null +++ b/cas-parser-java-core/build.gradle.kts @@ -0,0 +1,41 @@ +plugins { + id("cas-parser.kotlin") + id("cas-parser.publish") +} + +configurations.all { + resolutionStrategy { + // 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") + } +} + +dependencies { + api("com.fasterxml.jackson.core:jackson-core:2.18.2") + api("com.fasterxml.jackson.core:jackson-databind:2.18.2") + api("com.google.errorprone:error_prone_annotations:2.33.0") + + implementation("com.fasterxml.jackson.core:jackson-annotations:2.18.2") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2") + + testImplementation(kotlin("test")) + testImplementation(project(":cas-parser-java-client-okhttp")) + testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2") + testImplementation("org.assertj:assertj-core:3.27.7") + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.3") + testImplementation("org.junit-pioneer:junit-pioneer:1.9.1") + testImplementation("org.mockito:mockito-core:5.14.2") + testImplementation("org.mockito:mockito-junit-jupiter:5.14.2") + testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt new file mode 100644 index 0000000..6c17b45 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt @@ -0,0 +1,265 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.client + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.services.blocking.AccessTokenService +import com.cas_parser.api.services.blocking.CamsKfintechService +import com.cas_parser.api.services.blocking.CdslService +import com.cas_parser.api.services.blocking.ContractNoteService +import com.cas_parser.api.services.blocking.CreditService +import com.cas_parser.api.services.blocking.InboundEmailService +import com.cas_parser.api.services.blocking.InboxService +import com.cas_parser.api.services.blocking.KfintechService +import com.cas_parser.api.services.blocking.LogService +import com.cas_parser.api.services.blocking.NsdlService +import com.cas_parser.api.services.blocking.SmartService +import com.cas_parser.api.services.blocking.VerifyTokenService +import java.util.function.Consumer + +/** + * A client for interacting with the Cas Parser REST API synchronously. You can also switch to + * asynchronous execution via the [async] method. + * + * This client performs best when you create a single instance and reuse it for all interactions + * with the REST API. This is because each client holds its own connection pool and thread pools. + * Reusing connections and threads reduces latency and saves memory. The client also handles rate + * limiting per client. This means that creating and using multiple instances at the same time will + * not respect rate limits. + * + * The threads and connections that are held will be released automatically if they remain idle. But + * if you are writing an application that needs to aggressively release unused resources, then you + * may call [close]. + */ +interface CasParserClient { + + /** + * Returns a version of this client that uses asynchronous execution. + * + * The returned client shares its resources, like its connection pool and thread pools, with + * this client. + */ + fun async(): CasParserClientAsync + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CasParserClient + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + fun credits(): CreditService + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + fun logs(): LogService + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + fun accessToken(): AccessTokenService + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + fun verifyToken(): VerifyTokenService + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun camsKfintech(): CamsKfintechService + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun cdsl(): CdslService + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ + fun contractNote(): ContractNoteService + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + fun inbox(): InboxService + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + fun kfintech(): KfintechService + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun nsdl(): NsdlService + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun smart(): SmartService + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or + * file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + fun inboundEmail(): InboundEmailService + + /** + * Closes this client, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because the client is long-lived and + * usually should not be synchronously closed via try-with-resources. + * + * It's also usually not necessary to call this method at all. the default HTTP client + * automatically releases threads and connections if they remain idle, but if you are writing an + * application that needs to aggressively release unused resources, then you may call this + * method. + */ + fun close() + + /** A view of [CasParserClient] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CasParserClient.WithRawResponse + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + fun credits(): CreditService.WithRawResponse + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + fun logs(): LogService.WithRawResponse + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + fun accessToken(): AccessTokenService.WithRawResponse + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + fun verifyToken(): VerifyTokenService.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun camsKfintech(): CamsKfintechService.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun cdsl(): CdslService.WithRawResponse + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, + * Groww, Upstox, ICICI etc. + */ + fun contractNote(): ContractNoteService.WithRawResponse + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + fun inbox(): InboxService.WithRawResponse + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + fun kfintech(): KfintechService.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun nsdl(): NsdlService.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun smart(): SmartService.WithRawResponse + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth + * or file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + fun inboundEmail(): InboundEmailService.WithRawResponse + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt new file mode 100644 index 0000000..575d503 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt @@ -0,0 +1,269 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.client + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.services.async.AccessTokenServiceAsync +import com.cas_parser.api.services.async.CamsKfintechServiceAsync +import com.cas_parser.api.services.async.CdslServiceAsync +import com.cas_parser.api.services.async.ContractNoteServiceAsync +import com.cas_parser.api.services.async.CreditServiceAsync +import com.cas_parser.api.services.async.InboundEmailServiceAsync +import com.cas_parser.api.services.async.InboxServiceAsync +import com.cas_parser.api.services.async.KfintechServiceAsync +import com.cas_parser.api.services.async.LogServiceAsync +import com.cas_parser.api.services.async.NsdlServiceAsync +import com.cas_parser.api.services.async.SmartServiceAsync +import com.cas_parser.api.services.async.VerifyTokenServiceAsync +import java.util.function.Consumer + +/** + * A client for interacting with the Cas Parser REST API asynchronously. You can also switch to + * synchronous execution via the [sync] method. + * + * This client performs best when you create a single instance and reuse it for all interactions + * with the REST API. This is because each client holds its own connection pool and thread pools. + * Reusing connections and threads reduces latency and saves memory. The client also handles rate + * limiting per client. This means that creating and using multiple instances at the same time will + * not respect rate limits. + * + * The threads and connections that are held will be released automatically if they remain idle. But + * if you are writing an application that needs to aggressively release unused resources, then you + * may call [close]. + */ +interface CasParserClientAsync { + + /** + * Returns a version of this client that uses synchronous execution. + * + * The returned client shares its resources, like its connection pool and thread pools, with + * this client. + */ + fun sync(): CasParserClient + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CasParserClientAsync + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + fun credits(): CreditServiceAsync + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + fun logs(): LogServiceAsync + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + fun accessToken(): AccessTokenServiceAsync + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + fun verifyToken(): VerifyTokenServiceAsync + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun camsKfintech(): CamsKfintechServiceAsync + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun cdsl(): CdslServiceAsync + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ + fun contractNote(): ContractNoteServiceAsync + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + fun inbox(): InboxServiceAsync + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + fun kfintech(): KfintechServiceAsync + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun nsdl(): NsdlServiceAsync + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun smart(): SmartServiceAsync + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or + * file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + fun inboundEmail(): InboundEmailServiceAsync + + /** + * Closes this client, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because the client is long-lived and + * usually should not be synchronously closed via try-with-resources. + * + * It's also usually not necessary to call this method at all. the default HTTP client + * automatically releases threads and connections if they remain idle, but if you are writing an + * application that needs to aggressively release unused resources, then you may call this + * method. + */ + fun close() + + /** + * A view of [CasParserClientAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): CasParserClientAsync.WithRawResponse + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + fun credits(): CreditServiceAsync.WithRawResponse + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + fun logs(): LogServiceAsync.WithRawResponse + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + fun accessToken(): AccessTokenServiceAsync.WithRawResponse + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + fun verifyToken(): VerifyTokenServiceAsync.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun camsKfintech(): CamsKfintechServiceAsync.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun cdsl(): CdslServiceAsync.WithRawResponse + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, + * Groww, Upstox, ICICI etc. + */ + fun contractNote(): ContractNoteServiceAsync.WithRawResponse + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + fun inbox(): InboxServiceAsync.WithRawResponse + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + fun kfintech(): KfintechServiceAsync.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun nsdl(): NsdlServiceAsync.WithRawResponse + + /** Endpoints for parsing CAS PDF files from different sources. */ + fun smart(): SmartServiceAsync.WithRawResponse + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth + * or file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + fun inboundEmail(): InboundEmailServiceAsync.WithRawResponse + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt new file mode 100644 index 0000000..eed1327 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt @@ -0,0 +1,345 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.client + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.getPackageVersion +import com.cas_parser.api.services.async.AccessTokenServiceAsync +import com.cas_parser.api.services.async.AccessTokenServiceAsyncImpl +import com.cas_parser.api.services.async.CamsKfintechServiceAsync +import com.cas_parser.api.services.async.CamsKfintechServiceAsyncImpl +import com.cas_parser.api.services.async.CdslServiceAsync +import com.cas_parser.api.services.async.CdslServiceAsyncImpl +import com.cas_parser.api.services.async.ContractNoteServiceAsync +import com.cas_parser.api.services.async.ContractNoteServiceAsyncImpl +import com.cas_parser.api.services.async.CreditServiceAsync +import com.cas_parser.api.services.async.CreditServiceAsyncImpl +import com.cas_parser.api.services.async.InboundEmailServiceAsync +import com.cas_parser.api.services.async.InboundEmailServiceAsyncImpl +import com.cas_parser.api.services.async.InboxServiceAsync +import com.cas_parser.api.services.async.InboxServiceAsyncImpl +import com.cas_parser.api.services.async.KfintechServiceAsync +import com.cas_parser.api.services.async.KfintechServiceAsyncImpl +import com.cas_parser.api.services.async.LogServiceAsync +import com.cas_parser.api.services.async.LogServiceAsyncImpl +import com.cas_parser.api.services.async.NsdlServiceAsync +import com.cas_parser.api.services.async.NsdlServiceAsyncImpl +import com.cas_parser.api.services.async.SmartServiceAsync +import com.cas_parser.api.services.async.SmartServiceAsyncImpl +import com.cas_parser.api.services.async.VerifyTokenServiceAsync +import com.cas_parser.api.services.async.VerifyTokenServiceAsyncImpl +import java.util.function.Consumer + +class CasParserClientAsyncImpl(private val clientOptions: ClientOptions) : CasParserClientAsync { + + private val clientOptionsWithUserAgent = + if (clientOptions.headers.names().contains("User-Agent")) clientOptions + else + clientOptions + .toBuilder() + .putHeader("User-Agent", "${javaClass.simpleName}/Java ${getPackageVersion()}") + .build() + + // Pass the original clientOptions so that this client sets its own User-Agent. + private val sync: CasParserClient by lazy { CasParserClientImpl(clientOptions) } + + private val withRawResponse: CasParserClientAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val credits: CreditServiceAsync by lazy { + CreditServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val logs: LogServiceAsync by lazy { LogServiceAsyncImpl(clientOptionsWithUserAgent) } + + private val accessToken: AccessTokenServiceAsync by lazy { + AccessTokenServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val verifyToken: VerifyTokenServiceAsync by lazy { + VerifyTokenServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val camsKfintech: CamsKfintechServiceAsync by lazy { + CamsKfintechServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val cdsl: CdslServiceAsync by lazy { CdslServiceAsyncImpl(clientOptionsWithUserAgent) } + + private val contractNote: ContractNoteServiceAsync by lazy { + ContractNoteServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val inbox: InboxServiceAsync by lazy { + InboxServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val kfintech: KfintechServiceAsync by lazy { + KfintechServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val nsdl: NsdlServiceAsync by lazy { NsdlServiceAsyncImpl(clientOptionsWithUserAgent) } + + private val smart: SmartServiceAsync by lazy { + SmartServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val inboundEmail: InboundEmailServiceAsync by lazy { + InboundEmailServiceAsyncImpl(clientOptionsWithUserAgent) + } + + override fun sync(): CasParserClient = sync + + override fun withRawResponse(): CasParserClientAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CasParserClientAsync = + CasParserClientAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + override fun credits(): CreditServiceAsync = credits + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + override fun logs(): LogServiceAsync = logs + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + override fun accessToken(): AccessTokenServiceAsync = accessToken + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + override fun verifyToken(): VerifyTokenServiceAsync = verifyToken + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun camsKfintech(): CamsKfintechServiceAsync = camsKfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun cdsl(): CdslServiceAsync = cdsl + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ + override fun contractNote(): ContractNoteServiceAsync = contractNote + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + override fun inbox(): InboxServiceAsync = inbox + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + override fun kfintech(): KfintechServiceAsync = kfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun nsdl(): NsdlServiceAsync = nsdl + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun smart(): SmartServiceAsync = smart + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or + * file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + override fun inboundEmail(): InboundEmailServiceAsync = inboundEmail + + override fun close() = clientOptions.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CasParserClientAsync.WithRawResponse { + + private val credits: CreditServiceAsync.WithRawResponse by lazy { + CreditServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val logs: LogServiceAsync.WithRawResponse by lazy { + LogServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val accessToken: AccessTokenServiceAsync.WithRawResponse by lazy { + AccessTokenServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val verifyToken: VerifyTokenServiceAsync.WithRawResponse by lazy { + VerifyTokenServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val camsKfintech: CamsKfintechServiceAsync.WithRawResponse by lazy { + CamsKfintechServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val cdsl: CdslServiceAsync.WithRawResponse by lazy { + CdslServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val contractNote: ContractNoteServiceAsync.WithRawResponse by lazy { + ContractNoteServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val inbox: InboxServiceAsync.WithRawResponse by lazy { + InboxServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val kfintech: KfintechServiceAsync.WithRawResponse by lazy { + KfintechServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val nsdl: NsdlServiceAsync.WithRawResponse by lazy { + NsdlServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val smart: SmartServiceAsync.WithRawResponse by lazy { + SmartServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val inboundEmail: InboundEmailServiceAsync.WithRawResponse by lazy { + InboundEmailServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: Consumer + ): CasParserClientAsync.WithRawResponse = + CasParserClientAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + override fun credits(): CreditServiceAsync.WithRawResponse = credits + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + override fun logs(): LogServiceAsync.WithRawResponse = logs + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + override fun accessToken(): AccessTokenServiceAsync.WithRawResponse = accessToken + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + override fun verifyToken(): VerifyTokenServiceAsync.WithRawResponse = verifyToken + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun camsKfintech(): CamsKfintechServiceAsync.WithRawResponse = camsKfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun cdsl(): CdslServiceAsync.WithRawResponse = cdsl + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, + * Groww, Upstox, ICICI etc. + */ + override fun contractNote(): ContractNoteServiceAsync.WithRawResponse = contractNote + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + override fun inbox(): InboxServiceAsync.WithRawResponse = inbox + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + override fun kfintech(): KfintechServiceAsync.WithRawResponse = kfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun nsdl(): NsdlServiceAsync.WithRawResponse = nsdl + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun smart(): SmartServiceAsync.WithRawResponse = smart + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth + * or file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + override fun inboundEmail(): InboundEmailServiceAsync.WithRawResponse = inboundEmail + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt new file mode 100644 index 0000000..75ee210 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt @@ -0,0 +1,339 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.client + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.getPackageVersion +import com.cas_parser.api.services.blocking.AccessTokenService +import com.cas_parser.api.services.blocking.AccessTokenServiceImpl +import com.cas_parser.api.services.blocking.CamsKfintechService +import com.cas_parser.api.services.blocking.CamsKfintechServiceImpl +import com.cas_parser.api.services.blocking.CdslService +import com.cas_parser.api.services.blocking.CdslServiceImpl +import com.cas_parser.api.services.blocking.ContractNoteService +import com.cas_parser.api.services.blocking.ContractNoteServiceImpl +import com.cas_parser.api.services.blocking.CreditService +import com.cas_parser.api.services.blocking.CreditServiceImpl +import com.cas_parser.api.services.blocking.InboundEmailService +import com.cas_parser.api.services.blocking.InboundEmailServiceImpl +import com.cas_parser.api.services.blocking.InboxService +import com.cas_parser.api.services.blocking.InboxServiceImpl +import com.cas_parser.api.services.blocking.KfintechService +import com.cas_parser.api.services.blocking.KfintechServiceImpl +import com.cas_parser.api.services.blocking.LogService +import com.cas_parser.api.services.blocking.LogServiceImpl +import com.cas_parser.api.services.blocking.NsdlService +import com.cas_parser.api.services.blocking.NsdlServiceImpl +import com.cas_parser.api.services.blocking.SmartService +import com.cas_parser.api.services.blocking.SmartServiceImpl +import com.cas_parser.api.services.blocking.VerifyTokenService +import com.cas_parser.api.services.blocking.VerifyTokenServiceImpl +import java.util.function.Consumer + +class CasParserClientImpl(private val clientOptions: ClientOptions) : CasParserClient { + + private val clientOptionsWithUserAgent = + if (clientOptions.headers.names().contains("User-Agent")) clientOptions + else + clientOptions + .toBuilder() + .putHeader("User-Agent", "${javaClass.simpleName}/Java ${getPackageVersion()}") + .build() + + // Pass the original clientOptions so that this client sets its own User-Agent. + private val async: CasParserClientAsync by lazy { CasParserClientAsyncImpl(clientOptions) } + + private val withRawResponse: CasParserClient.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val credits: CreditService by lazy { CreditServiceImpl(clientOptionsWithUserAgent) } + + private val logs: LogService by lazy { LogServiceImpl(clientOptionsWithUserAgent) } + + private val accessToken: AccessTokenService by lazy { + AccessTokenServiceImpl(clientOptionsWithUserAgent) + } + + private val verifyToken: VerifyTokenService by lazy { + VerifyTokenServiceImpl(clientOptionsWithUserAgent) + } + + private val camsKfintech: CamsKfintechService by lazy { + CamsKfintechServiceImpl(clientOptionsWithUserAgent) + } + + private val cdsl: CdslService by lazy { CdslServiceImpl(clientOptionsWithUserAgent) } + + private val contractNote: ContractNoteService by lazy { + ContractNoteServiceImpl(clientOptionsWithUserAgent) + } + + private val inbox: InboxService by lazy { InboxServiceImpl(clientOptionsWithUserAgent) } + + private val kfintech: KfintechService by lazy { + KfintechServiceImpl(clientOptionsWithUserAgent) + } + + private val nsdl: NsdlService by lazy { NsdlServiceImpl(clientOptionsWithUserAgent) } + + private val smart: SmartService by lazy { SmartServiceImpl(clientOptionsWithUserAgent) } + + private val inboundEmail: InboundEmailService by lazy { + InboundEmailServiceImpl(clientOptionsWithUserAgent) + } + + override fun async(): CasParserClientAsync = async + + override fun withRawResponse(): CasParserClient.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CasParserClient = + CasParserClientImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + override fun credits(): CreditService = credits + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ + override fun logs(): LogService = logs + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + override fun accessToken(): AccessTokenService = accessToken + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ + override fun verifyToken(): VerifyTokenService = verifyToken + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun camsKfintech(): CamsKfintechService = camsKfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun cdsl(): CdslService = cdsl + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ + override fun contractNote(): ContractNoteService = contractNote + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + override fun inbox(): InboxService = inbox + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + override fun kfintech(): KfintechService = kfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun nsdl(): NsdlService = nsdl + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun smart(): SmartService = smart + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or + * file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + override fun inboundEmail(): InboundEmailService = inboundEmail + + override fun close() = clientOptions.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CasParserClient.WithRawResponse { + + private val credits: CreditService.WithRawResponse by lazy { + CreditServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val logs: LogService.WithRawResponse by lazy { + LogServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val accessToken: AccessTokenService.WithRawResponse by lazy { + AccessTokenServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val verifyToken: VerifyTokenService.WithRawResponse by lazy { + VerifyTokenServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val camsKfintech: CamsKfintechService.WithRawResponse by lazy { + CamsKfintechServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val cdsl: CdslService.WithRawResponse by lazy { + CdslServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val contractNote: ContractNoteService.WithRawResponse by lazy { + ContractNoteServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val inbox: InboxService.WithRawResponse by lazy { + InboxServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val kfintech: KfintechService.WithRawResponse by lazy { + KfintechServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val nsdl: NsdlService.WithRawResponse by lazy { + NsdlServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val smart: SmartService.WithRawResponse by lazy { + SmartServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val inboundEmail: InboundEmailService.WithRawResponse by lazy { + InboundEmailServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: Consumer + ): CasParserClient.WithRawResponse = + CasParserClientImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + override fun credits(): CreditService.WithRawResponse = credits + + /** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your + * API usage and remaining quota. + */ + override fun logs(): LogService.WithRawResponse = logs + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + override fun accessToken(): AccessTokenService.WithRawResponse = accessToken + + /** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. + * Access tokens can be used in place of API keys on all v4 endpoints. + */ + override fun verifyToken(): VerifyTokenService.WithRawResponse = verifyToken + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun camsKfintech(): CamsKfintechService.WithRawResponse = camsKfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun cdsl(): CdslService.WithRawResponse = cdsl + + /** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, + * Groww, Upstox, ICICI etc. + */ + override fun contractNote(): ContractNoteService.WithRawResponse = contractNote + + /** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ + override fun inbox(): InboxService.WithRawResponse = inbox + + /** Endpoints for generating new CAS documents via email mailback (KFintech). */ + override fun kfintech(): KfintechService.WithRawResponse = kfintech + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun nsdl(): NsdlService.WithRawResponse = nsdl + + /** Endpoints for parsing CAS PDF files from different sources. */ + override fun smart(): SmartService.WithRawResponse = smart + + /** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth + * or file upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to + * ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your + * webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ + override fun inboundEmail(): InboundEmailService.WithRawResponse = inboundEmail + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/BaseDeserializer.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/BaseDeserializer.kt new file mode 100644 index 0000000..a6dcabc --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/BaseDeserializer.kt @@ -0,0 +1,44 @@ +package com.cas_parser.api.core + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.BeanProperty +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JavaType +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.deser.ContextualDeserializer +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import kotlin.reflect.KClass + +abstract class BaseDeserializer(type: KClass) : + StdDeserializer(type.java), ContextualDeserializer { + + override fun createContextual( + context: DeserializationContext, + property: BeanProperty?, + ): JsonDeserializer { + return this + } + + override fun deserialize(parser: JsonParser, context: DeserializationContext): T { + return parser.codec.deserialize(parser.readValueAsTree()) + } + + protected abstract fun ObjectCodec.deserialize(node: JsonNode): T + + protected fun ObjectCodec.tryDeserialize(node: JsonNode, type: TypeReference): T? = + try { + readValue(treeAsTokens(node), type) + } catch (e: Exception) { + null + } + + protected fun ObjectCodec.tryDeserialize(node: JsonNode, type: JavaType): T? = + try { + readValue(treeAsTokens(node), type) + } catch (e: Exception) { + null + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/BaseSerializer.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/BaseSerializer.kt new file mode 100644 index 0000000..5ea87e0 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/BaseSerializer.kt @@ -0,0 +1,6 @@ +package com.cas_parser.api.core + +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import kotlin.reflect.KClass + +abstract class BaseSerializer(type: KClass) : StdSerializer(type.java) diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Check.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Check.kt new file mode 100644 index 0000000..c0b5e80 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Check.kt @@ -0,0 +1,96 @@ +@file:JvmName("Check") + +package com.cas_parser.api.core + +import com.fasterxml.jackson.core.Version +import com.fasterxml.jackson.core.util.VersionUtil + +fun checkRequired(name: String, condition: Boolean) = + check(condition) { "`$name` is required, but was not set" } + +fun checkRequired(name: String, value: T?): T = + checkNotNull(value) { "`$name` is required, but was not set" } + +@JvmSynthetic +internal fun checkKnown(name: String, value: JsonField): T = + value.asKnown().orElseThrow { + IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}") + } + +@JvmSynthetic +internal fun checkKnown(name: String, value: MultipartField): T = + value.value.asKnown().orElseThrow { + IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}") + } + +@JvmSynthetic +internal fun checkLength(name: String, value: String, length: Int): String = + value.also { + check(it.length == length) { "`$name` must have length $length, but was ${it.length}" } + } + +@JvmSynthetic +internal fun checkMinLength(name: String, value: String, minLength: Int): String = + value.also { + check(it.length >= minLength) { + if (minLength == 1) "`$name` must be non-empty, but was empty" + else "`$name` must have at least length $minLength, but was ${it.length}" + } + } + +@JvmSynthetic +internal fun checkMaxLength(name: String, value: String, maxLength: Int): String = + value.also { + check(it.length <= maxLength) { + "`$name` must have at most length $maxLength, but was ${it.length}" + } + } + +@JvmSynthetic +internal fun checkJacksonVersionCompatibility() { + val incompatibleJacksonVersions = + RUNTIME_JACKSON_VERSIONS.mapNotNull { + val badVersionReason = BAD_JACKSON_VERSIONS[it.toString()] + when { + it.majorVersion != MINIMUM_JACKSON_VERSION.majorVersion -> + it to "incompatible major version" + it.minorVersion < MINIMUM_JACKSON_VERSION.minorVersion -> + it to "minor version too low" + it.minorVersion == MINIMUM_JACKSON_VERSION.minorVersion && + it.patchLevel < MINIMUM_JACKSON_VERSION.patchLevel -> + it to "patch version too low" + badVersionReason != null -> it to badVersionReason + else -> null + } + } + check(incompatibleJacksonVersions.isEmpty()) { + """ +This SDK requires a minimum Jackson version of $MINIMUM_JACKSON_VERSION, but the following incompatible Jackson versions were detected at runtime: + +${incompatibleJacksonVersions.asSequence().map { (version, incompatibilityReason) -> + "- `${version.toFullString().replace("/", ":")}` ($incompatibilityReason)" +}.joinToString("\n")} + +This can happen if you are either: +1. Directly depending on different Jackson versions +2. Depending on some library that depends on different Jackson versions, potentially transitively + +Double-check that you are depending on compatible Jackson versions. + +See https://www.github.com/CASParser/cas-parser-java#jackson for more information. + """ + .trimIndent() + } +} + +private val MINIMUM_JACKSON_VERSION: Version = VersionUtil.parseVersion("2.13.4", null, null) +private val BAD_JACKSON_VERSIONS: Map = + mapOf("2.18.1" to "due to https://github.com/FasterXML/jackson-databind/issues/4639") +private val RUNTIME_JACKSON_VERSIONS: List = + listOf( + com.fasterxml.jackson.core.json.PackageVersion.VERSION, + com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION, + com.fasterxml.jackson.datatype.jdk8.PackageVersion.VERSION, + com.fasterxml.jackson.datatype.jsr310.PackageVersion.VERSION, + com.fasterxml.jackson.module.kotlin.PackageVersion.VERSION, + ) 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 new file mode 100644 index 0000000..00f28b4 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ClientOptions.kt @@ -0,0 +1,496 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core + +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.HttpClient +import com.cas_parser.api.core.http.LoggingHttpClient +import com.cas_parser.api.core.http.PhantomReachableClosingHttpClient +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.http.RetryingHttpClient +import com.fasterxml.jackson.databind.json.JsonMapper +import java.time.Clock +import java.time.Duration +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** A class representing the SDK client configuration. */ +class ClientOptions +private constructor( + private val originalHttpClient: HttpClient, + /** + * The HTTP client to use in the SDK. + * + * Use the one published in `cas-parser-java-client-okhttp` or implement your own. + * + * This class takes ownership of the client and closes it when closed. + */ + @get:JvmName("httpClient") val httpClient: HttpClient, + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee that + * the SDK will work correctly when using an incompatible Jackson version. + */ + @get:JvmName("checkJacksonVersionCompatibility") val checkJacksonVersionCompatibility: Boolean, + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.cas_parser.api.core.jsonMapper]. The default is usually sufficient and + * 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. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + @get:JvmName("clock") val clock: Clock, + private val baseUrl: String?, + /** Headers to send with the request. */ + @get:JvmName("headers") val headers: Headers, + /** Query params to send with the request. */ + @get:JvmName("queryParams") val queryParams: QueryParams, + /** + * Whether to call `validate` on every response before returning it. + * + * Setting this to `true` is _not_ forwards compatible with new types from the API for existing + * fields. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + @get:JvmName("responseValidation") val responseValidation: Boolean, + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + @get:JvmName("timeout") val timeout: Timeout, + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + @get:JvmName("maxRetries") val maxRetries: Int, + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + @get:JvmName("logLevel") val logLevel: LogLevel, + /** Your API key for authentication. Use `sandbox-with-json-responses` as Sandbox key. */ + @get:JvmName("apiKey") val apiKey: String, +) { + + init { + if (checkJacksonVersionCompatibility) { + checkJacksonVersionCompatibility() + } + } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.casparser.in`. + */ + fun baseUrl(): String = baseUrl ?: PRODUCTION_URL + + fun toBuilder() = Builder().from(this) + + companion object { + + const val PRODUCTION_URL = "https://api.casparser.in" + + /** + * Returns a mutable builder for constructing an instance of [ClientOptions]. + * + * The following fields are required: + * ```java + * .httpClient() + * .apiKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + + /** + * Returns options configured using system properties and environment variables. + * + * @see Builder.fromEnv + */ + @JvmStatic fun fromEnv(): ClientOptions = builder().fromEnv().build() + } + + /** A builder for [ClientOptions]. */ + class Builder internal 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() + private var queryParams: QueryParams.Builder = QueryParams.builder() + private var responseValidation: Boolean = false + private var timeout: Timeout = Timeout.default() + private var maxRetries: Int = 2 + private var logLevel: LogLevel = LogLevel.fromEnv() + private var apiKey: String? = null + + @JvmSynthetic + internal fun from(clientOptions: ClientOptions) = apply { + httpClient = clientOptions.originalHttpClient + checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility + jsonMapper = clientOptions.jsonMapper + sleeper = clientOptions.sleeper + clock = clientOptions.clock + baseUrl = clientOptions.baseUrl + headers = clientOptions.headers.toBuilder() + queryParams = clientOptions.queryParams.toBuilder() + responseValidation = clientOptions.responseValidation + timeout = clientOptions.timeout + maxRetries = clientOptions.maxRetries + logLevel = clientOptions.logLevel + apiKey = clientOptions.apiKey + } + + /** + * The HTTP client to use in the SDK. + * + * Use the one published in `cas-parser-java-client-okhttp` or implement your own. + * + * This class takes ownership of the client and closes it when closed. + */ + fun httpClient(httpClient: HttpClient) = apply { + this.httpClient = PhantomReachableClosingHttpClient(httpClient) + } + + /** + * Whether to throw an exception if any of the Jackson versions detected at runtime are + * incompatible with the SDK's minimum supported Jackson version (2.13.4). + * + * Defaults to true. Use extreme caution when disabling this option. There is no guarantee + * that the SDK will work correctly when using an incompatible Jackson version. + */ + fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { + this.checkJacksonVersionCompatibility = checkJacksonVersionCompatibility + } + + /** + * The Jackson JSON mapper to use for serializing and deserializing JSON. + * + * Defaults to [com.cas_parser.api.core.jsonMapper]. The default is usually sufficient and + * rarely needs to be overridden. + */ + 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. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { this.clock = clock } + + /** + * The base URL to use for every request. + * + * Defaults to the production environment: `https://api.casparser.in`. + */ + fun baseUrl(baseUrl: String?) = apply { this.baseUrl = baseUrl } + + /** Alias for calling [Builder.baseUrl] with `baseUrl.orElse(null)`. */ + fun baseUrl(baseUrl: Optional) = baseUrl(baseUrl.getOrNull()) + + /** + * Whether to call `validate` on every response before returning it. + * + * Setting this to `true` is _not_ forwards compatible with new types from the API for + * existing fields. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + fun responseValidation(responseValidation: Boolean) = apply { + this.responseValidation = responseValidation + } + + /** + * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding + * retries. + * + * Defaults to [Timeout.default]. + */ + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + /** + * Sets the maximum time allowed for a complete HTTP call, not including retries. + * + * See [Timeout.request] for more details. + * + * For fine-grained control, pass a [Timeout] object. + */ + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + + /** + * The maximum number of times to retry failed requests, with a short exponential backoff + * between requests. + * + * Only the following error types are retried: + * - Connection errors (for example, due to a network connectivity problem) + * - 408 Request Timeout + * - 409 Conflict + * - 429 Rate Limit + * - 5xx Internal + * + * The API may also explicitly instruct the SDK to retry or not retry a request. + * + * Defaults to 2. + */ + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } + + /** + * The level at which to log request and response information. + * + * [fromEnv] will set the level from environment variables. See [LogLevel.fromEnv]. + * + * Defaults to [LogLevel.fromEnv]. + */ + fun logLevel(logLevel: LogLevel) = apply { this.logLevel = logLevel } + + /** Your API key for authentication. Use `sandbox-with-json-responses` as Sandbox key. */ + fun apiKey(apiKey: String) = apply { this.apiKey = apiKey } + + fun headers(headers: Headers) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun headers(headers: Map>) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun putHeader(name: String, value: String) = apply { headers.put(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { headers.put(name, values) } + + fun putAllHeaders(headers: Headers) = apply { this.headers.putAll(headers) } + + fun putAllHeaders(headers: Map>) = apply { + this.headers.putAll(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { headers.replace(name, value) } + + fun replaceHeaders(name: String, values: Iterable) = apply { + headers.replace(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { this.headers.replaceAll(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + this.headers.replaceAll(headers) + } + + fun removeHeaders(name: String) = apply { headers.remove(name) } + + fun removeAllHeaders(names: Set) = apply { headers.removeAll(names) } + + fun queryParams(queryParams: QueryParams) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun queryParams(queryParams: Map>) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { queryParams.put(key, value) } + + fun putQueryParams(key: String, values: Iterable) = apply { + queryParams.put(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.putAll(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + this.queryParams.putAll(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + queryParams.replace(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + queryParams.replace(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun removeQueryParams(key: String) = apply { queryParams.remove(key) } + + fun removeAllQueryParams(keys: Set) = apply { queryParams.removeAll(keys) } + + fun timeout(): Timeout = timeout + + /** + * Updates configuration using system properties and environment variables. + * + * See this table for the available options: + * + * |Setter |System property |Environment variable |Required|Default value | + * |---------|-------------------|---------------------|--------|----------------------------| + * |`apiKey` |`casparser.apiKey` |`CAS_PARSER_API_KEY` |true |- | + * |`baseUrl`|`casparser.baseUrl`|`CAS_PARSER_BASE_URL`|true |`"https://api.casparser.in"`| + * + * System properties take precedence over environment variables. + */ + fun fromEnv() = apply { + logLevel(LogLevel.fromEnv()) + (System.getProperty("casparser.baseUrl") ?: System.getenv("CAS_PARSER_BASE_URL"))?.let { + baseUrl(it) + } + (System.getProperty("casparser.apiKey") ?: System.getenv("CAS_PARSER_API_KEY"))?.let { + apiKey(it) + } + System.getenv("CAS_PARSER_CUSTOM_HEADERS")?.let { customHeadersEnv -> + for (line in customHeadersEnv.split("\n")) { + val colon = line.indexOf(':') + if (colon >= 0) { + putHeader(line.substring(0, colon).trim(), line.substring(colon + 1).trim()) + } + } + } + } + + /** + * Returns an immutable instance of [ClientOptions]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .httpClient() + * .apiKey() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ClientOptions { + val httpClient = checkRequired("httpClient", httpClient) + val sleeper = sleeper ?: PhantomReachableSleeper(DefaultSleeper()) + val apiKey = checkRequired("apiKey", apiKey) + + val headers = Headers.builder() + val queryParams = QueryParams.builder() + headers.put("X-Stainless-Lang", "java") + headers.put("X-Stainless-Arch", getOsArch()) + headers.put("X-Stainless-OS", getOsName()) + headers.put("X-Stainless-OS-Version", getOsVersion()) + headers.put("X-Stainless-Package-Version", getPackageVersion()) + headers.put("X-Stainless-Runtime", "JRE") + headers.put("X-Stainless-Runtime-Version", getJavaVersion()) + headers.put("X-Stainless-Kotlin-Version", KotlinVersion.CURRENT.toString()) + // We replace after all the default headers to allow end-users to overwrite them. + headers.replaceAll(this.headers.build()) + queryParams.replaceAll(this.queryParams.build()) + apiKey.let { + if (!it.isEmpty()) { + headers.replace("x-api-key", it) + } + } + + return ClientOptions( + httpClient, + RetryingHttpClient.builder() + .httpClient( + LoggingHttpClient.builder() + .httpClient(httpClient) + .clock(clock) + .level(logLevel) + .build() + ) + .sleeper(sleeper) + .clock(clock) + .maxRetries(maxRetries) + .build(), + checkJacksonVersionCompatibility, + jsonMapper, + sleeper, + clock, + baseUrl, + headers.build(), + queryParams.build(), + responseValidation, + timeout, + maxRetries, + logLevel, + apiKey, + ) + } + } + + /** + * Closes these client options, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because the client options are + * long-lived and usually should not be synchronously closed via try-with-resources. + * + * It's also usually not necessary to call this method at all. the default client automatically + * releases threads and connections if they remain idle, but if you are writing an application + * that needs to aggressively release unused resources, then you may call this method. + */ + 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/LogLevel.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/LogLevel.kt new file mode 100644 index 0000000..4e66497 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/LogLevel.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core + +/** The level at which to log request and response information. */ +enum class LogLevel { + /** No logging. */ + OFF, + /** Minimal request and response summary logs. No headers or bodies are logged. */ + INFO, + /** [INFO] logs plus details about request failures. */ + ERROR, + /** + * Full request and response logs. Sensitive headers are redacted, but sensitive data in request + * and response bodies may still be visible. + */ + DEBUG; + + /** Returns whether this level is at or higher than the given [level]. */ + fun shouldLog(level: LogLevel): Boolean = ordinal >= level.ordinal + + companion object { + + /** Returns a [LogLevel] based on the `CAS_PARSER_LOG` environment variable. */ + fun fromEnv() = + when (System.getenv("CAS_PARSER_LOG")?.lowercase()) { + "info" -> INFO + "error" -> ERROR + "debug" -> DEBUG + else -> OFF + } + } +} 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 new file mode 100644 index 0000000..0c4aadd --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/ObjectMappers.kt @@ -0,0 +1,180 @@ +@file:JvmName("ObjectMappers") + +package com.cas_parser.api.core + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParseException +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.MapperFeature +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.cfg.CoercionAction +import com.fasterxml.jackson.databind.cfg.CoercionInputShape +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.type.LogicalType +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.kotlinModule +import java.io.InputStream +import java.time.DateTimeException +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.OffsetDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField + +fun jsonMapper(): JsonMapper = JSON_MAPPER + +private val JSON_MAPPER: JsonMapper = + JsonMapper.builder() + .addModule(kotlinModule()) + .addModule(Jdk8Module()) + .addModule(JavaTimeModule()) + .addModule( + SimpleModule() + .addSerializer(InputStreamSerializer) + .addDeserializer(OffsetDateTime::class.java, LenientOffsetDateTimeDeserializer()) + ) + .withCoercionConfig(LogicalType.Boolean) { + it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .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) + } + .withCoercionConfig(LogicalType.Float) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Textual) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .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) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Collection) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.Map) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Object, CoercionAction.Fail) + } + .withCoercionConfig(LogicalType.POJO) { + it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Float, CoercionAction.Fail) + .setCoercion(CoercionInputShape.String, CoercionAction.Fail) + .setCoercion(CoercionInputShape.Array, CoercionAction.Fail) + } + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE) + .disable(SerializationFeature.FLUSH_AFTER_WRITE_VALUE) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS) + .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS) + .disable(MapperFeature.AUTO_DETECT_CREATORS) + .disable(MapperFeature.AUTO_DETECT_FIELDS) + .disable(MapperFeature.AUTO_DETECT_GETTERS) + .disable(MapperFeature.AUTO_DETECT_IS_GETTERS) + .disable(MapperFeature.AUTO_DETECT_SETTERS) + .build() + +/** A serializer that serializes [InputStream] to bytes. */ +private object InputStreamSerializer : BaseSerializer(InputStream::class) { + + private fun readResolve(): Any = InputStreamSerializer + + override fun serialize( + value: InputStream?, + gen: JsonGenerator?, + serializers: SerializerProvider?, + ) { + if (value == null) { + gen?.writeNull() + } else { + value.use { gen?.writeBinary(it.readBytes()) } + } + } +} + +/** + * A deserializer that can deserialize [OffsetDateTime] from datetimes, dates, and zoned datetimes. + */ +private class LenientOffsetDateTimeDeserializer : + StdDeserializer(OffsetDateTime::class.java) { + + companion object { + + private val DATE_TIME_FORMATTERS = + listOf( + DateTimeFormatter.ISO_LOCAL_DATE_TIME, + DateTimeFormatter.ISO_LOCAL_DATE, + DateTimeFormatter.ISO_ZONED_DATE_TIME, + ) + } + + override fun logicalType(): LogicalType = LogicalType.DateTime + + override fun deserialize(p: JsonParser, context: DeserializationContext): OffsetDateTime { + val exceptions = mutableListOf() + + for (formatter in DATE_TIME_FORMATTERS) { + try { + val temporal = formatter.parse(p.text) + + return when { + !temporal.isSupported(ChronoField.HOUR_OF_DAY) -> + LocalDate.from(temporal) + .atStartOfDay() + .atZone(ZoneId.of("UTC")) + .toOffsetDateTime() + !temporal.isSupported(ChronoField.OFFSET_SECONDS) -> + LocalDateTime.from(temporal).atZone(ZoneId.of("UTC")).toOffsetDateTime() + else -> OffsetDateTime.from(temporal) + } + } catch (e: DateTimeException) { + exceptions.add(e) + } + } + + 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/Params.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Params.kt new file mode 100644 index 0000000..5e3891e --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Params.kt @@ -0,0 +1,16 @@ +package com.cas_parser.api.core + +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams + +/** An interface representing parameters passed to a service method. */ +interface Params { + /** The full set of headers in the parameters, including both fixed and additional headers. */ + fun _headers(): Headers + + /** + * The full set of query params in the parameters, including both fixed and additional query + * params. + */ + fun _queryParams(): QueryParams +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachable.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachable.kt new file mode 100644 index 0000000..a08ef9b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachable.kt @@ -0,0 +1,56 @@ +@file:JvmName("PhantomReachable") + +package com.cas_parser.api.core + +import com.cas_parser.api.errors.CasParserException +import java.lang.reflect.InvocationTargetException + +/** + * Closes [closeable] when [observed] becomes only phantom reachable. + * + * This is a wrapper around a Java 9+ [java.lang.ref.Cleaner], or a no-op in older Java versions. + */ +@JvmSynthetic +internal fun closeWhenPhantomReachable(observed: Any, closeable: AutoCloseable) { + check(observed !== closeable) { + "`observed` cannot be the same object as `closeable` because it would never become phantom reachable" + } + closeWhenPhantomReachable(observed, closeable::close) +} + +/** + * Calls [close] when [observed] becomes only phantom reachable. + * + * This is a wrapper around a Java 9+ [java.lang.ref.Cleaner], or a no-op in older Java versions. + */ +@JvmSynthetic +internal fun closeWhenPhantomReachable(observed: Any, close: () -> Unit) { + closeWhenPhantomReachable?.let { it(observed, close) } +} + +private val closeWhenPhantomReachable: ((Any, () -> Unit) -> Unit)? by lazy { + try { + val cleanerClass = Class.forName("java.lang.ref.Cleaner") + val cleanerCreate = cleanerClass.getMethod("create") + val cleanerRegister = + cleanerClass.getMethod("register", Any::class.java, Runnable::class.java) + val cleanerObject = cleanerCreate.invoke(null); + + { observed, close -> + try { + cleanerRegister.invoke(cleanerObject, observed, Runnable { close() }) + } catch (e: ReflectiveOperationException) { + if (e is InvocationTargetException) { + when (val cause = e.cause) { + is RuntimeException, + is Error -> throw cause + } + } + throw CasParserException("Unexpected reflective invocation failure", e) + } + } + } catch (e: ReflectiveOperationException) { + // We're running Java 8, which has no Cleaner. + null + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachableExecutorService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachableExecutorService.kt new file mode 100644 index 0000000..d26c7ba --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PhantomReachableExecutorService.kt @@ -0,0 +1,58 @@ +package com.cas_parser.api.core + +import java.util.concurrent.Callable +import java.util.concurrent.ExecutorService +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit + +/** + * A delegating wrapper around an [ExecutorService] that shuts it down once it's only phantom + * reachable. + * + * This class ensures the [ExecutorService] is shut down even if the user forgets to do it. + */ +internal class PhantomReachableExecutorService(private val executorService: ExecutorService) : + ExecutorService { + init { + closeWhenPhantomReachable(this) { executorService.shutdown() } + } + + override fun execute(command: Runnable) = executorService.execute(command) + + override fun shutdown() = executorService.shutdown() + + override fun shutdownNow(): MutableList = executorService.shutdownNow() + + override fun isShutdown(): Boolean = executorService.isShutdown + + override fun isTerminated(): Boolean = executorService.isTerminated + + override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean = + executorService.awaitTermination(timeout, unit) + + override fun submit(task: Callable): Future = executorService.submit(task) + + override fun submit(task: Runnable, result: T): Future = + executorService.submit(task, result) + + override fun submit(task: Runnable): Future<*> = executorService.submit(task) + + override fun invokeAll( + tasks: MutableCollection> + ): MutableList> = executorService.invokeAll(tasks) + + override fun invokeAll( + tasks: MutableCollection>, + timeout: Long, + unit: TimeUnit, + ): MutableList> = executorService.invokeAll(tasks, timeout, unit) + + override fun invokeAny(tasks: MutableCollection>): T = + executorService.invokeAny(tasks) + + override fun invokeAny( + tasks: MutableCollection>, + timeout: Long, + unit: TimeUnit, + ): T = executorService.invokeAny(tasks, timeout, unit) +} 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/PrepareRequest.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PrepareRequest.kt new file mode 100644 index 0000000..7d0287b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/PrepareRequest.kt @@ -0,0 +1,24 @@ +@file:JvmName("PrepareRequest") + +package com.cas_parser.api.core + +import com.cas_parser.api.core.http.HttpRequest +import java.util.concurrent.CompletableFuture + +@JvmSynthetic +internal fun HttpRequest.prepare(clientOptions: ClientOptions, params: Params): HttpRequest = + toBuilder() + .putAllQueryParams(clientOptions.queryParams) + .replaceAllQueryParams(params._queryParams()) + .putAllHeaders(clientOptions.headers) + .replaceAllHeaders(params._headers()) + .build() + +@JvmSynthetic +internal fun HttpRequest.prepareAsync( + clientOptions: ClientOptions, + params: Params, +): CompletableFuture = + // This async version exists to make it easier to add async specific preparation logic in the + // future. + CompletableFuture.completedFuture(prepare(clientOptions, params)) diff --git a/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 new file mode 100644 index 0000000..f946e80 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt @@ -0,0 +1,42 @@ +@file:JvmName("Properties") + +package com.cas_parser.api.core + +import com.cas_parser.api.client.CasParserClient + +fun getOsArch(): String { + val osArch = System.getProperty("os.arch") + + return when (osArch) { + null -> "unknown" + "i386", + "x32", + "x86" -> "x32" + "amd64", + "x86_64" -> "x64" + "arm" -> "arm" + "aarch64" -> "arm64" + else -> "other:$osArch" + } +} + +fun getOsName(): String { + val osName = System.getProperty("os.name") + val vendorUrl = System.getProperty("java.vendor.url") + + return when { + osName == null -> "Unknown" + osName.startsWith("Linux") && vendorUrl == "http://www.android.com/" -> "Android" + osName.startsWith("Linux") -> "Linux" + osName.startsWith("Mac OS") -> "MacOS" + osName.startsWith("Windows") -> "Windows" + else -> "Other:$osName" + } +} + +fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown" + +fun getPackageVersion(): String = + CasParserClient::class.java.`package`?.implementationVersion ?: "unknown" + +fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown" diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/RequestOptions.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/RequestOptions.kt new file mode 100644 index 0000000..b873e13 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/RequestOptions.kt @@ -0,0 +1,55 @@ +package com.cas_parser.api.core + +import java.time.Duration + +class RequestOptions private constructor(val responseValidation: Boolean?, val timeout: Timeout?) { + + companion object { + + private val NONE = builder().build() + + @JvmStatic fun none() = NONE + + @JvmSynthetic + internal fun from(clientOptions: ClientOptions): RequestOptions = + builder() + .responseValidation(clientOptions.responseValidation) + .timeout(clientOptions.timeout) + .build() + + @JvmStatic fun builder() = Builder() + } + + fun applyDefaults(options: RequestOptions): RequestOptions = + RequestOptions( + responseValidation = responseValidation ?: options.responseValidation, + timeout = + if (options.timeout != null && timeout != null) timeout.assign(options.timeout) + else timeout ?: options.timeout, + ) + + class Builder internal constructor() { + + private var responseValidation: Boolean? = null + private var timeout: Timeout? = null + + /** + * Whether to call `validate` on the response before returning it. + * + * Setting this to `true` is _not_ forwards compatible with new types from the API for + * existing fields. + * + * Defaults to false, which means the shape of the response will not be validated upfront. + * Instead, validation will only occur for the parts of the response that are accessed. + */ + fun responseValidation(responseValidation: Boolean) = apply { + this.responseValidation = responseValidation + } + + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build()) + + fun build(): RequestOptions = RequestOptions(responseValidation, timeout) + } +} 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/Timeout.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Timeout.kt new file mode 100644 index 0000000..8aaa968 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Timeout.kt @@ -0,0 +1,171 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core + +import java.time.Duration +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** A class containing timeouts for various processing phases of a request. */ +class Timeout +private constructor( + private val connect: Duration?, + private val read: Duration?, + private val write: Duration?, + private val request: Duration?, +) { + + /** + * The maximum time allowed to establish a connection with a host. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun connect(): Duration = connect ?: Duration.ofMinutes(1) + + /** + * The maximum time allowed between two data packets when waiting for the server’s response. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun read(): Duration = read ?: request() + + /** + * The maximum time allowed between two data packets when sending the request to the server. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun write(): Duration = write ?: request() + + /** + * The maximum time allowed for a complete HTTP call, not including retries. + * + * This includes resolving DNS, connecting, writing the request body, server processing, as well + * as reading the response body. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun request(): Duration = request ?: Duration.ofMinutes(1) + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun default() = builder().build() + + /** Returns a mutable builder for constructing an instance of [Timeout]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Timeout]. */ + class Builder internal constructor() { + + private var connect: Duration? = null + private var read: Duration? = null + private var write: Duration? = null + private var request: Duration? = null + + @JvmSynthetic + internal fun from(timeout: Timeout) = apply { + connect = timeout.connect + read = timeout.read + write = timeout.write + request = timeout.request + } + + /** + * The maximum time allowed to establish a connection with a host. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun connect(connect: Duration?) = apply { this.connect = connect } + + /** Alias for calling [Builder.connect] with `connect.orElse(null)`. */ + fun connect(connect: Optional) = connect(connect.getOrNull()) + + /** + * The maximum time allowed between two data packets when waiting for the server’s response. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun read(read: Duration?) = apply { this.read = read } + + /** Alias for calling [Builder.read] with `read.orElse(null)`. */ + fun read(read: Optional) = read(read.getOrNull()) + + /** + * The maximum time allowed between two data packets when sending the request to the server. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `request()`. + */ + fun write(write: Duration?) = apply { this.write = write } + + /** Alias for calling [Builder.write] with `write.orElse(null)`. */ + fun write(write: Optional) = write(write.getOrNull()) + + /** + * The maximum time allowed for a complete HTTP call, not including retries. + * + * This includes resolving DNS, connecting, writing the request body, server processing, as + * well as reading the response body. + * + * A value of [Duration.ZERO] means there's no timeout. + * + * Defaults to `Duration.ofMinutes(1)`. + */ + fun request(request: Duration?) = apply { this.request = request } + + /** Alias for calling [Builder.request] with `request.orElse(null)`. */ + fun request(request: Optional) = request(request.getOrNull()) + + /** + * Returns an immutable instance of [Timeout]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Timeout = Timeout(connect, read, write, request) + } + + @JvmSynthetic + internal fun assign(target: Timeout): Timeout = + target + .toBuilder() + .apply { + connect?.let(this::connect) + read?.let(this::read) + write?.let(this::write) + request?.let(this::request) + } + .build() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Timeout && + connect == other.connect && + read == other.read && + write == other.write && + request == other.request + } + + override fun hashCode(): Int = Objects.hash(connect, read, write, request) + + override fun toString() = + "Timeout{connect=$connect, read=$read, write=$write, request=$request}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Utils.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Utils.kt new file mode 100644 index 0000000..978563a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Utils.kt @@ -0,0 +1,121 @@ +@file:JvmName("Utils") + +package com.cas_parser.api.core + +import com.cas_parser.api.errors.CasParserInvalidDataException +import java.util.Collections +import java.util.SortedMap +import java.util.SortedSet +import java.util.concurrent.CompletableFuture +import java.util.concurrent.locks.Lock + +@JvmSynthetic +internal fun T?.getOrThrow(name: String): T = + this ?: throw CasParserInvalidDataException("`${name}` is not present") + +@JvmSynthetic +internal fun List.toImmutable(): List = + if (isEmpty()) Collections.emptyList() else Collections.unmodifiableList(toList()) + +@JvmSynthetic +internal fun > SortedSet.toImmutable(): SortedSet = + if (isEmpty()) Collections.emptySortedSet() + else Collections.unmodifiableSortedSet(toSortedSet(comparator() ?: Comparator.naturalOrder())) + +@JvmSynthetic +internal fun Map.toImmutable(): Map = + if (isEmpty()) immutableEmptyMap() else Collections.unmodifiableMap(toMap()) + +@JvmSynthetic internal fun immutableEmptyMap(): Map = Collections.emptyMap() + +@JvmSynthetic +internal fun , V> SortedMap.toImmutable(): SortedMap = + if (isEmpty()) Collections.emptySortedMap() + else Collections.unmodifiableSortedMap(toSortedMap(comparator())) + +/** + * Returns all elements that yield the largest value for the given function, or an empty list if + * there are zero elements. + * + * This is similar to [Sequence.maxByOrNull] except it returns _all_ elements that yield the largest + * value; not just the first one. + */ +@JvmSynthetic +internal fun > Sequence.allMaxBy(selector: (T) -> R): List { + var maxValue: R? = null + val maxElements = mutableListOf() + + val iterator = iterator() + while (iterator.hasNext()) { + val element = iterator.next() + val value = selector(element) + if (maxValue == null || value > maxValue) { + maxValue = value + maxElements.clear() + maxElements.add(element) + } else if (value == maxValue) { + maxElements.add(element) + } + } + + return maxElements +} + +/** + * Returns whether [this] is equal to [other]. + * + * This differs from [Object.equals] because it also deeply equates arrays based on their contents, + * even when there are arrays directly nested within other arrays. + */ +@JvmSynthetic +internal infix fun Any?.contentEquals(other: Any?): Boolean = + arrayOf(this).contentDeepEquals(arrayOf(other)) + +/** + * Returns a hash of the given sequence of [values]. + * + * This differs from [java.util.Objects.hash] because it also deeply hashes arrays based on their + * contents, even when there are arrays directly nested within other arrays. + */ +@JvmSynthetic internal fun contentHash(vararg values: Any?): Int = values.contentDeepHashCode() + +/** + * Returns a [String] representation of [this]. + * + * This differs from [Object.toString] because it also deeply stringifies arrays based on their + * contents, even when there are arrays directly nested within other arrays. + */ +@JvmSynthetic +internal fun Any?.contentToString(): String { + var string = arrayOf(this).contentDeepToString() + if (string.startsWith('[')) { + string = string.substring(1) + } + if (string.endsWith(']')) { + string = string.substring(0, string.length - 1) + } + return string +} + +internal interface Enum + +/** + * Executes the given [action] while holding the lock, returning a [CompletableFuture] with the + * result. + * + * @param action The asynchronous action to execute while holding the lock + * @return A [CompletableFuture] that completes with the result of the action + */ +@JvmSynthetic +internal fun Lock.withLockAsync(action: () -> CompletableFuture): CompletableFuture { + lock() + val future = + try { + action() + } catch (e: Throwable) { + unlock() + throw e + } + future.whenComplete { _, _ -> unlock() } + return future +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Values.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Values.kt new file mode 100644 index 0000000..08c2a3f --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Values.kt @@ -0,0 +1,723 @@ +package com.cas_parser.api.core + +import com.cas_parser.api.errors.CasParserInvalidDataException +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.BeanProperty +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JavaType +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.node.JsonNodeType.ARRAY +import com.fasterxml.jackson.databind.node.JsonNodeType.BINARY +import com.fasterxml.jackson.databind.node.JsonNodeType.BOOLEAN +import com.fasterxml.jackson.databind.node.JsonNodeType.MISSING +import com.fasterxml.jackson.databind.node.JsonNodeType.NULL +import com.fasterxml.jackson.databind.node.JsonNodeType.NUMBER +import com.fasterxml.jackson.databind.node.JsonNodeType.OBJECT +import com.fasterxml.jackson.databind.node.JsonNodeType.POJO +import com.fasterxml.jackson.databind.node.JsonNodeType.STRING +import com.fasterxml.jackson.databind.ser.std.NullSerializer +import java.io.InputStream +import java.util.Objects +import java.util.Optional + +/** + * A class representing a serializable JSON field. + * + * It can either be a [KnownValue] value of type [T], matching the type the SDK expects, or an + * arbitrary JSON value that bypasses the type system (via [JsonValue]). + */ +@JsonDeserialize(using = JsonField.Deserializer::class) +sealed class JsonField { + + /** + * Returns whether this field is missing, which means it will be omitted from the serialized + * JSON entirely. + */ + fun isMissing(): Boolean = this is JsonMissing + + /** Whether this field is explicitly set to `null`. */ + fun isNull(): Boolean = this is JsonNull + + /** + * Returns an [Optional] containing this field's "known" value, meaning it matches the type the + * SDK expects, or an empty [Optional] if this field contains an arbitrary [JsonValue]. + * + * This is the opposite of [asUnknown]. + */ + fun asKnown(): + Optional< + // Safe because `Optional` is effectively covariant, but Kotlin doesn't know that. + @UnsafeVariance + T + > = Optional.ofNullable((this as? KnownValue)?.value) + + /** + * Returns an [Optional] containing this field's arbitrary [JsonValue], meaning it mismatches + * the type the SDK expects, or an empty [Optional] if this field contains a "known" value. + * + * This is the opposite of [asKnown]. + */ + fun asUnknown(): Optional = Optional.ofNullable(this as? JsonValue) + + /** + * Returns an [Optional] containing this field's boolean value, or an empty [Optional] if it + * doesn't contain a boolean. + * + * This method checks for both a [KnownValue] containing a boolean and for [JsonBoolean]. + */ + fun asBoolean(): Optional = + when (this) { + is JsonBoolean -> Optional.of(value) + is KnownValue -> Optional.ofNullable(value as? Boolean) + else -> Optional.empty() + } + + /** + * Returns an [Optional] containing this field's numerical value, or an empty [Optional] if it + * doesn't contain a number. + * + * This method checks for both a [KnownValue] containing a number and for [JsonNumber]. + */ + fun asNumber(): Optional = + when (this) { + is JsonNumber -> Optional.of(value) + is KnownValue -> Optional.ofNullable(value as? Number) + else -> Optional.empty() + } + + /** + * Returns an [Optional] containing this field's string value, or an empty [Optional] if it + * doesn't contain a string. + * + * This method checks for both a [KnownValue] containing a string and for [JsonString]. + */ + fun asString(): Optional = + when (this) { + is JsonString -> Optional.of(value) + is KnownValue -> Optional.ofNullable(value as? String) + else -> Optional.empty() + } + + fun asStringOrThrow(): String = + asString().orElseThrow { CasParserInvalidDataException("Value is not a string") } + + /** + * Returns an [Optional] containing this field's list value, or an empty [Optional] if it + * doesn't contain a list. + * + * This method checks for both a [KnownValue] containing a list and for [JsonArray]. + */ + fun asArray(): Optional> = + when (this) { + is JsonArray -> Optional.of(values) + is KnownValue -> + Optional.ofNullable( + (value as? List<*>)?.map { + try { + JsonValue.from(it) + } catch (e: IllegalArgumentException) { + // The known value is a list, but not all values are convertible to + // `JsonValue`. + return Optional.empty() + } + } + ) + else -> Optional.empty() + } + + /** + * Returns an [Optional] containing this field's map value, or an empty [Optional] if it doesn't + * contain a map. + * + * This method checks for both a [KnownValue] containing a map and for [JsonObject]. + */ + fun asObject(): Optional> = + when (this) { + is JsonObject -> Optional.of(values) + is KnownValue -> + Optional.ofNullable( + (value as? Map<*, *>) + ?.map { (key, value) -> + if (key !is String) { + return Optional.empty() + } + + val jsonValue = + try { + JsonValue.from(value) + } catch (e: IllegalArgumentException) { + // The known value is a map, but not all items are convertible + // to `JsonValue`. + return Optional.empty() + } + + key to jsonValue + } + ?.toMap() + ) + else -> Optional.empty() + } + + @JvmSynthetic + internal fun getRequired(name: String): T = + when (this) { + is KnownValue -> value + is JsonMissing -> throw CasParserInvalidDataException("`$name` is not set") + is JsonNull -> throw CasParserInvalidDataException("`$name` is null") + else -> throw CasParserInvalidDataException("`$name` is invalid, received $this") + } + + @JvmSynthetic + internal fun getOptional( + name: String + ): Optional< + // Safe because `Optional` is effectively covariant, but Kotlin doesn't know that. + @UnsafeVariance + T + > = + when (this) { + is KnownValue -> Optional.of(value) + is JsonMissing, + is JsonNull -> Optional.empty() + else -> throw CasParserInvalidDataException("`$name` is invalid, received $this") + } + + @JvmSynthetic + internal fun map(transform: (T) -> R): JsonField = + when (this) { + is KnownValue -> KnownValue.of(transform(value)) + is JsonValue -> this + } + + @JvmSynthetic internal fun accept(consume: (T) -> Unit) = asKnown().ifPresent(consume) + + /** Returns the result of calling the [visitor] method corresponding to this field's state. */ + fun accept(visitor: Visitor): R = + when (this) { + is KnownValue -> visitor.visitKnown(value) + is JsonValue -> accept(visitor as JsonValue.Visitor) + } + + /** + * An interface that defines how to map each possible state of a `JsonField` to a value of + * type [R]. + */ + interface Visitor : JsonValue.Visitor { + + fun visitKnown(value: T): R = visitDefault() + } + + companion object { + + /** Returns a [JsonField] containing the given "known" [value]. */ + @JvmStatic fun of(value: T): JsonField = KnownValue.of(value) + + /** + * Returns a [JsonField] containing the given "known" [value], or [JsonNull] if [value] is + * null. + */ + @JvmStatic + fun ofNullable(value: T?): JsonField = + when (value) { + null -> JsonNull.of() + else -> KnownValue.of(value) + } + } + + /** + * This class is a Jackson filter that can be used to exclude missing properties from objects. + * This filter should not be used directly and should instead use the @ExcludeMissing + * annotation. + */ + class IsMissing { + + override fun equals(other: Any?): Boolean = other is JsonMissing + + override fun hashCode(): Int = Objects.hash() + } + + class Deserializer(private val type: JavaType? = null) : + BaseDeserializer>(JsonField::class) { + + override fun createContextual( + context: DeserializationContext, + property: BeanProperty?, + ): JsonDeserializer> = Deserializer(context.contextualType?.containedType(0)) + + override fun ObjectCodec.deserialize(node: JsonNode): JsonField<*> = + type?.let { tryDeserialize(node, type) }?.let { of(it) } + ?: JsonValue.fromJsonNode(node) + + override fun getNullValue(context: DeserializationContext): JsonField<*> = JsonNull.of() + } +} + +/** + * A class representing an arbitrary JSON value. + * + * It is immutable and assignable to any [JsonField], regardless of its expected type (i.e. its + * generic type argument). + */ +@JsonDeserialize(using = JsonValue.Deserializer::class) +sealed class JsonValue : JsonField() { + + fun convert(type: TypeReference): R? = JSON_MAPPER.convertValue(this, type) + + fun convert(type: Class): R? = JSON_MAPPER.convertValue(this, type) + + /** Returns the result of calling the [visitor] method corresponding to this value's variant. */ + fun accept(visitor: Visitor): R = + when (this) { + is JsonMissing -> visitor.visitMissing() + is JsonNull -> visitor.visitNull() + is JsonBoolean -> visitor.visitBoolean(value) + is JsonNumber -> visitor.visitNumber(value) + is JsonString -> visitor.visitString(value) + is JsonArray -> visitor.visitArray(values) + is JsonObject -> visitor.visitObject(values) + } + + /** + * An interface that defines how to map each variant state of a [JsonValue] to a value of type + * [R]. + */ + interface Visitor { + + fun visitNull(): R = visitDefault() + + fun visitMissing(): R = visitDefault() + + fun visitBoolean(value: Boolean): R = visitDefault() + + fun visitNumber(value: Number): R = visitDefault() + + fun visitString(value: String): R = visitDefault() + + fun visitArray(values: List): R = visitDefault() + + fun visitObject(values: Map): R = visitDefault() + + /** + * The default implementation for unimplemented visitor methods. + * + * @throws IllegalArgumentException in the default implementation. + */ + fun visitDefault(): R = throw IllegalArgumentException("Unexpected value") + } + + companion object { + + private val JSON_MAPPER = jsonMapper() + + /** + * Converts the given [value] to a [JsonValue]. + * + * This method works best on primitive types, [List] values, [Map] values, and nested + * combinations of these. For example: + * ```java + * // Create primitive JSON values + * JsonValue nullValue = JsonValue.from(null); + * JsonValue booleanValue = JsonValue.from(true); + * JsonValue numberValue = JsonValue.from(42); + * JsonValue stringValue = JsonValue.from("Hello World!"); + * + * // Create a JSON array value equivalent to `["Hello", "World"]` + * JsonValue arrayValue = JsonValue.from(List.of("Hello", "World")); + * + * // Create a JSON object value equivalent to `{ "a": 1, "b": 2 }` + * JsonValue objectValue = JsonValue.from(Map.of( + * "a", 1, + * "b", 2 + * )); + * + * // Create an arbitrarily nested JSON equivalent to: + * // { + * // "a": [1, 2], + * // "b": [3, 4] + * // } + * JsonValue complexValue = JsonValue.from(Map.of( + * "a", List.of(1, 2), + * "b", List.of(3, 4) + * )); + * ``` + * + * @throws IllegalArgumentException if [value] is not JSON serializable. + */ + @JvmStatic + fun from(value: Any?): JsonValue = + when (value) { + null -> JsonNull.of() + is JsonValue -> value + else -> JSON_MAPPER.convertValue(value, JsonValue::class.java) + } + + /** + * Returns a [JsonValue] converted from the given Jackson [JsonNode]. + * + * @throws IllegalStateException for unsupported node types. + */ + @JvmStatic + fun fromJsonNode(node: JsonNode): JsonValue = + when (node.nodeType) { + MISSING -> JsonMissing.of() + NULL -> JsonNull.of() + BOOLEAN -> JsonBoolean.of(node.booleanValue()) + NUMBER -> JsonNumber.of(node.numberValue()) + STRING -> JsonString.of(node.textValue()) + ARRAY -> + JsonArray.of(node.elements().asSequence().map { fromJsonNode(it) }.toList()) + OBJECT -> + JsonObject.of( + node.fields().asSequence().map { it.key to fromJsonNode(it.value) }.toMap() + ) + BINARY, + POJO, + null -> throw IllegalStateException("Unexpected JsonNode type: ${node.nodeType}") + } + } + + class Deserializer : BaseDeserializer(JsonValue::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): JsonValue = fromJsonNode(node) + + override fun getNullValue(context: DeserializationContext?): JsonValue = JsonNull.of() + } +} + +/** + * A class representing a "known" JSON serializable value of type [T], matching the type the SDK + * expects. + * + * It is assignable to `JsonField`. + */ +class KnownValue +private constructor( + @com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: T +) : JsonField() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is KnownValue<*> && value contentEquals other.value + } + + override fun hashCode() = contentHash(value) + + override fun toString() = value.contentToString() + + companion object { + + /** Returns a [KnownValue] containing the given [value]. */ + @JsonCreator @JvmStatic fun of(value: T) = KnownValue(value) + } +} + +/** + * A [JsonValue] representing an omitted JSON field. + * + * An instance of this class will cause a JSON field to be omitted from the serialized JSON + * entirely. + */ +@JsonSerialize(using = JsonMissing.Serializer::class) +class JsonMissing : JsonValue() { + + override fun toString() = "" + + companion object { + + private val INSTANCE: JsonMissing = JsonMissing() + + /** Returns the singleton instance of [JsonMissing]. */ + @JvmStatic fun of() = INSTANCE + } + + class Serializer : BaseSerializer(JsonMissing::class) { + + override fun serialize( + value: JsonMissing, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + throw IllegalStateException("JsonMissing cannot be serialized") + } + } +} + +/** A [JsonValue] representing a JSON `null` value. */ +@JsonSerialize(using = NullSerializer::class) +class JsonNull : JsonValue() { + + override fun toString() = "null" + + companion object { + + private val INSTANCE: JsonNull = JsonNull() + + /** Returns the singleton instance of [JsonMissing]. */ + @JsonCreator @JvmStatic fun of() = INSTANCE + } +} + +/** A [JsonValue] representing a JSON boolean value. */ +class JsonBoolean +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: Boolean +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonBoolean && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + companion object { + + /** Returns a [JsonBoolean] containing the given [value]. */ + @JsonCreator @JvmStatic fun of(value: Boolean) = JsonBoolean(value) + } +} + +/** A [JsonValue] representing a JSON number value. */ +class JsonNumber +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: Number +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonNumber && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + + companion object { + + /** Returns a [JsonNumber] containing the given [value]. */ + @JsonCreator @JvmStatic fun of(value: Number) = JsonNumber(value) + } +} + +/** A [JsonValue] representing a JSON string value. */ +class JsonString +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: String +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonString && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value + + companion object { + + /** Returns a [JsonString] containing the given [value]. */ + @JsonCreator @JvmStatic fun of(value: String) = JsonString(value) + } +} + +/** A [JsonValue] representing a JSON array value. */ +class JsonArray +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue + @get:JvmName("values") + val values: List +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonArray && values == other.values + } + + override fun hashCode() = values.hashCode() + + override fun toString() = values.toString() + + companion object { + + /** Returns a [JsonArray] containing the given [values]. */ + @JsonCreator @JvmStatic fun of(values: List) = JsonArray(values.toImmutable()) + } +} + +/** A [JsonValue] representing a JSON object value. */ +class JsonObject +private constructor( + @get:com.fasterxml.jackson.annotation.JsonValue + @get:JvmName("values") + val values: Map +) : JsonValue() { + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is JsonObject && values == other.values + } + + override fun hashCode() = values.hashCode() + + override fun toString() = values.toString() + + companion object { + + /** Returns a [JsonObject] containing the given [values]. */ + @JsonCreator + @JvmStatic + fun of(values: Map) = JsonObject(values.toImmutable()) + } +} + +/** A Jackson annotation for excluding fields set to [JsonMissing] from the serialized JSON. */ +@JacksonAnnotationsInside +@JsonInclude(JsonInclude.Include.CUSTOM, valueFilter = JsonField.IsMissing::class) +annotation class ExcludeMissing + +/** A class representing a field in a `multipart/form-data` request. */ +class MultipartField +private constructor( + /** A [JsonField] value, which will be serialized to zero or more parts. */ + @get:com.fasterxml.jackson.annotation.JsonValue @get:JvmName("value") val value: JsonField, + /** A content type for the serialized parts. */ + @get:JvmName("contentType") val contentType: String, + private val filename: String?, +) { + + companion object { + + /** + * Returns a [MultipartField] containing the given [value] as a [KnownValue]. + * + * [contentType] will be set to `application/octet-stream` if [value] is binary data, or + * `text/plain; charset=utf-8` otherwise. + */ + @JvmStatic fun of(value: T?) = builder().value(value).build() + + /** + * Returns a [MultipartField] containing the given [value]. + * + * [contentType] will be set to `application/octet-stream` if [value] is binary data, or + * `text/plain; charset=utf-8` otherwise. + */ + @JvmStatic fun of(value: JsonField) = builder().value(value).build() + + /** + * Returns a mutable builder for constructing an instance of [MultipartField]. + * + * The following fields are required: + * ```java + * .value() + * ``` + * + * If [contentType] is unset, then it will be set to `application/octet-stream` if [value] + * is binary data, or `text/plain; charset=utf-8` otherwise. + */ + @JvmStatic fun builder() = Builder() + } + + /** Returns the filename directive that will be included in the serialized field. */ + fun filename(): Optional = Optional.ofNullable(filename) + + @JvmSynthetic + internal fun map(transform: (T) -> R): MultipartField = + builder().value(value.map(transform)).contentType(contentType).filename(filename).build() + + /** A builder for [MultipartField]. */ + class Builder internal constructor() { + + private var value: JsonField? = null + private var contentType: String? = null + private var filename: String? = null + + fun value(value: JsonField) = apply { this.value = value } + + fun value(value: T?) = value(JsonField.ofNullable(value)) + + fun contentType(contentType: String) = apply { this.contentType = contentType } + + fun filename(filename: String?) = apply { this.filename = filename } + + /** Alias for calling [Builder.filename] with `filename.orElse(null)`. */ + fun filename(filename: Optional) = filename(filename.orElse(null)) + + /** + * Returns an immutable instance of [MultipartField]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .value() + * ``` + * + * If [contentType] is unset, then it will be set to `application/octet-stream` if [value] + * is binary data, or `text/plain; charset=utf-8` otherwise. + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MultipartField { + val value = checkRequired("value", value) + return MultipartField( + value, + contentType + ?: if ( + value is KnownValue && + (value.value is InputStream || value.value is ByteArray) + ) + "application/octet-stream" + else "text/plain; charset=utf-8", + filename, + ) + } + } + + private val hashCode: Int by lazy { contentHash(value, contentType, filename) } + + override fun hashCode(): Int = hashCode + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is MultipartField<*> && + value == other.value && + contentType == other.contentType && + filename == other.filename + } + + override fun toString(): String = + "MultipartField{value=$value, contentType=$contentType, filename=$filename}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/ErrorHandler.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/ErrorHandler.kt new file mode 100644 index 0000000..1209491 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/ErrorHandler.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:JvmName("ErrorHandler") + +package com.cas_parser.api.core.handlers + +import com.cas_parser.api.core.JsonMissing +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.errors.BadRequestException +import com.cas_parser.api.errors.InternalServerException +import com.cas_parser.api.errors.NotFoundException +import com.cas_parser.api.errors.PermissionDeniedException +import com.cas_parser.api.errors.RateLimitException +import com.cas_parser.api.errors.UnauthorizedException +import com.cas_parser.api.errors.UnexpectedStatusCodeException +import com.cas_parser.api.errors.UnprocessableEntityException +import com.fasterxml.jackson.databind.json.JsonMapper + +@JvmSynthetic +internal fun errorBodyHandler(jsonMapper: JsonMapper): Handler { + val handler = jsonHandler(jsonMapper) + + return object : Handler { + override fun handle(response: HttpResponse): JsonValue = + try { + handler.handle(response) + } catch (e: Exception) { + JsonMissing.of() + } + } +} + +@JvmSynthetic +internal fun errorHandler(errorBodyHandler: Handler): Handler = + object : Handler { + override fun handle(response: HttpResponse): HttpResponse = + when (val statusCode = response.statusCode()) { + in 200..299 -> response + 400 -> + throw BadRequestException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 401 -> + throw UnauthorizedException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 403 -> + throw PermissionDeniedException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 404 -> + throw NotFoundException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 422 -> + throw UnprocessableEntityException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + 429 -> + throw RateLimitException.builder() + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + in 500..599 -> + throw InternalServerException.builder() + .statusCode(statusCode) + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + else -> + throw UnexpectedStatusCodeException.builder() + .statusCode(statusCode) + .headers(response.headers()) + .body(errorBodyHandler.handle(response)) + .build() + } + } diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/JsonHandler.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/JsonHandler.kt new file mode 100644 index 0000000..94121e6 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/JsonHandler.kt @@ -0,0 +1,20 @@ +@file:JvmName("JsonHandler") + +package com.cas_parser.api.core.handlers + +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.errors.CasParserInvalidDataException +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef + +@JvmSynthetic +internal inline fun jsonHandler(jsonMapper: JsonMapper): Handler = + object : Handler { + override fun handle(response: HttpResponse): T = + try { + jsonMapper.readValue(response.body(), jacksonTypeRef()) + } catch (e: Exception) { + throw CasParserInvalidDataException("Error reading response", e) + } + } diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/StringHandler.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/StringHandler.kt new file mode 100644 index 0000000..908418d --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/handlers/StringHandler.kt @@ -0,0 +1,13 @@ +@file:JvmName("StringHandler") + +package com.cas_parser.api.core.handlers + +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler + +@JvmSynthetic internal fun stringHandler(): Handler = StringHandlerInternal + +private object StringHandlerInternal : Handler { + override fun handle(response: HttpResponse): String = + response.body().readBytes().toString(Charsets.UTF_8) +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/AsyncStreamResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/AsyncStreamResponse.kt new file mode 100644 index 0000000..561090a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/AsyncStreamResponse.kt @@ -0,0 +1,157 @@ +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.http.AsyncStreamResponse.Handler +import java.util.Optional +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executor +import java.util.concurrent.atomic.AtomicReference + +/** + * A class providing access to an API response as an asynchronous stream of chunks of type [T], + * where each chunk can be individually processed as soon as it arrives instead of waiting on the + * full response. + */ +interface AsyncStreamResponse { + + /** + * Registers [handler] to be called for events of this stream. + * + * [handler]'s methods will be called in the client's configured or default thread pool. + * + * @throws IllegalStateException if [subscribe] has already been called. + */ + fun subscribe(handler: Handler): AsyncStreamResponse + + /** + * Registers [handler] to be called for events of this stream. + * + * [handler]'s methods will be called in the given [executor]. + * + * @throws IllegalStateException if [subscribe] has already been called. + */ + fun subscribe(handler: Handler, executor: Executor): AsyncStreamResponse + + /** + * Returns a future that completes when a stream is fully consumed, errors, or gets closed + * early. + */ + fun onCompleteFuture(): CompletableFuture + + /** + * Closes this resource, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because this response should not be + * synchronously closed via try-with-resources. + */ + fun close() + + /** A class for handling streaming events. */ + fun interface Handler { + + /** Called whenever a chunk is received. */ + fun onNext(value: T) + + /** + * Called when a stream is fully consumed, errors, or gets closed early. + * + * [onNext] will not be called once this method is called. + * + * @param error Non-empty if the stream completed due to an error. + */ + fun onComplete(error: Optional) {} + } +} + +@JvmSynthetic +internal fun CompletableFuture>.toAsync(streamHandlerExecutor: Executor) = + PhantomReachableClosingAsyncStreamResponse( + object : AsyncStreamResponse { + + private val onCompleteFuture = CompletableFuture() + private val state = AtomicReference(State.NEW) + + init { + this@toAsync.whenComplete { _, error -> + // If an error occurs from the original future, then we should resolve the + // `onCompleteFuture` even if `subscribe` has not been called. + error?.let(onCompleteFuture::completeExceptionally) + } + } + + override fun subscribe(handler: Handler): AsyncStreamResponse = + subscribe(handler, streamHandlerExecutor) + + override fun subscribe( + handler: Handler, + executor: Executor, + ): AsyncStreamResponse = apply { + // TODO(JDK): Use `compareAndExchange` once targeting JDK 9. + check(state.compareAndSet(State.NEW, State.SUBSCRIBED)) { + if (state.get() == State.SUBSCRIBED) "Cannot subscribe more than once" + else "Cannot subscribe after the response is closed" + } + + this@toAsync.whenCompleteAsync( + { streamResponse, futureError -> + if (state.get() == State.CLOSED) { + // Avoid doing any work if `close` was called before the future + // completed. + return@whenCompleteAsync + } + + if (futureError != null) { + // An error occurred before we started passing chunks to the handler. + handler.onComplete(Optional.of(futureError)) + return@whenCompleteAsync + } + + var streamError: Throwable? = null + try { + streamResponse.stream().forEach(handler::onNext) + } catch (e: Throwable) { + streamError = e + } + + try { + handler.onComplete(Optional.ofNullable(streamError)) + } finally { + try { + // Notify completion via the `onCompleteFuture` as well. This is in + // a separate `try-finally` block so that we still complete the + // future if `handler.onComplete` throws. + if (streamError == null) { + onCompleteFuture.complete(null) + } else { + onCompleteFuture.completeExceptionally(streamError) + } + } finally { + close() + } + } + }, + executor, + ) + } + + override fun onCompleteFuture(): CompletableFuture = onCompleteFuture + + override fun close() { + val previousState = state.getAndSet(State.CLOSED) + if (previousState == State.CLOSED) { + return + } + + this@toAsync.whenComplete { streamResponse, error -> streamResponse?.close() } + // When the stream is closed, we should always consider it closed. If it closed due + // to an error, then we will have already completed the future earlier, and this + // will be a no-op. + onCompleteFuture.complete(null) + } + } + ) + +private enum class State { + NEW, + SUBSCRIBED, + CLOSED, +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/Headers.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/Headers.kt new file mode 100644 index 0000000..52ab0bd --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/Headers.kt @@ -0,0 +1,115 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.JsonArray +import com.cas_parser.api.core.JsonBoolean +import com.cas_parser.api.core.JsonMissing +import com.cas_parser.api.core.JsonNull +import com.cas_parser.api.core.JsonNumber +import com.cas_parser.api.core.JsonObject +import com.cas_parser.api.core.JsonString +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.toImmutable +import java.util.TreeMap + +class Headers +private constructor( + private val map: Map>, + @get:JvmName("size") val size: Int, +) { + + fun isEmpty(): Boolean = map.isEmpty() + + fun names(): Set = map.keys + + fun values(name: String): List = map[name].orEmpty() + + fun toBuilder(): Builder = Builder().putAll(map) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + + private val map: MutableMap> = + TreeMap(String.CASE_INSENSITIVE_ORDER) + private var size: Int = 0 + + fun put(name: String, value: JsonValue): Builder = apply { + when (value) { + is JsonMissing, + is JsonNull -> {} + is JsonBoolean -> put(name, value.value.toString()) + is JsonNumber -> put(name, value.value.toString()) + is JsonString -> put(name, value.value) + is JsonArray -> value.values.forEach { put(name, it) } + is JsonObject -> + value.values.forEach { (nestedName, value) -> put("$name.$nestedName", value) } + } + } + + fun put(name: String, value: String) = apply { + map.getOrPut(name) { mutableListOf() }.add(value) + size++ + } + + fun put(name: String, values: Iterable) = apply { values.forEach { put(name, it) } } + + fun putAll(headers: Map>) = apply { headers.forEach(::put) } + + fun putAll(headers: Headers) = apply { + headers.names().forEach { put(it, headers.values(it)) } + } + + fun replace(name: String, value: String) = apply { + remove(name) + put(name, value) + } + + fun replace(name: String, values: Iterable) = apply { + remove(name) + put(name, values) + } + + fun replaceAll(headers: Map>) = apply { + headers.forEach(::replace) + } + + fun replaceAll(headers: Headers) = apply { + headers.names().forEach { replace(it, headers.values(it)) } + } + + fun remove(name: String) = apply { size -= map.remove(name).orEmpty().size } + + fun removeAll(names: Set) = apply { names.forEach(::remove) } + + fun clear() = apply { + map.clear() + size = 0 + } + + fun build() = + Headers( + map.mapValuesTo(TreeMap(String.CASE_INSENSITIVE_ORDER)) { (_, values) -> + values.toImmutable() + } + .toImmutable(), + size, + ) + } + + override fun hashCode(): Int = map.hashCode() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Headers && map == other.map + } + + override fun toString(): String = "Headers{map=$map}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpClient.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpClient.kt new file mode 100644 index 0000000..0801f90 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpClient.kt @@ -0,0 +1,26 @@ +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.RequestOptions +import java.lang.AutoCloseable +import java.util.concurrent.CompletableFuture + +interface HttpClient : AutoCloseable { + + fun execute( + request: HttpRequest, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + fun execute(request: HttpRequest): HttpResponse = execute(request, RequestOptions.none()) + + fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + fun executeAsync(request: HttpRequest): CompletableFuture = + executeAsync(request, RequestOptions.none()) + + /** 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/HttpMethod.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpMethod.kt new file mode 100644 index 0000000..039990b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpMethod.kt @@ -0,0 +1,13 @@ +package com.cas_parser.api.core.http + +enum class HttpMethod { + GET, + HEAD, + POST, + PUT, + DELETE, + CONNECT, + OPTIONS, + TRACE, + PATCH, +} 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 new file mode 100644 index 0000000..9679caa --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequest.kt @@ -0,0 +1,176 @@ +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( + @get:JvmName("method") val method: HttpMethod, + @get:JvmName("baseUrl") val baseUrl: String, + @get:JvmName("pathSegments") val pathSegments: List, + @get:JvmName("headers") val headers: Headers, + @get:JvmName("queryParams") val queryParams: QueryParams, + @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 = + "HttpRequest{method=$method, baseUrl=$baseUrl, pathSegments=$pathSegments, headers=$headers, queryParams=$queryParams, body=$body}" + + companion object { + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + + private var method: HttpMethod? = null + private var baseUrl: String? = null + private var pathSegments: MutableList = mutableListOf() + private var headers: Headers.Builder = Headers.builder() + private var queryParams: QueryParams.Builder = QueryParams.builder() + private var body: HttpRequestBody? = null + + @JvmSynthetic + internal fun from(request: HttpRequest) = apply { + method = request.method + baseUrl = request.baseUrl + pathSegments = request.pathSegments.toMutableList() + headers = request.headers.toBuilder() + queryParams = request.queryParams.toBuilder() + body = request.body + } + + fun method(method: HttpMethod) = apply { this.method = method } + + fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl } + + fun addPathSegment(pathSegment: String) = apply { pathSegments.add(pathSegment) } + + fun addPathSegments(vararg pathSegments: String) = apply { + this.pathSegments.addAll(pathSegments) + } + + fun headers(headers: Headers) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun headers(headers: Map>) = apply { + this.headers.clear() + putAllHeaders(headers) + } + + fun putHeader(name: String, value: String) = apply { headers.put(name, value) } + + fun putHeaders(name: String, values: Iterable) = apply { headers.put(name, values) } + + fun putAllHeaders(headers: Headers) = apply { this.headers.putAll(headers) } + + fun putAllHeaders(headers: Map>) = apply { + this.headers.putAll(headers) + } + + fun replaceHeaders(name: String, value: String) = apply { headers.replace(name, value) } + + fun replaceHeaders(name: String, values: Iterable) = apply { + headers.replace(name, values) + } + + fun replaceAllHeaders(headers: Headers) = apply { this.headers.replaceAll(headers) } + + fun replaceAllHeaders(headers: Map>) = apply { + this.headers.replaceAll(headers) + } + + fun removeHeaders(name: String) = apply { headers.remove(name) } + + fun removeAllHeaders(names: Set) = apply { headers.removeAll(names) } + + fun queryParams(queryParams: QueryParams) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun queryParams(queryParams: Map>) = apply { + this.queryParams.clear() + putAllQueryParams(queryParams) + } + + fun putQueryParam(key: String, value: String) = apply { queryParams.put(key, value) } + + fun putQueryParams(key: String, values: Iterable) = apply { + queryParams.put(key, values) + } + + fun putAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.putAll(queryParams) + } + + fun putAllQueryParams(queryParams: Map>) = apply { + this.queryParams.putAll(queryParams) + } + + fun replaceQueryParams(key: String, value: String) = apply { + queryParams.replace(key, value) + } + + fun replaceQueryParams(key: String, values: Iterable) = apply { + queryParams.replace(key, values) + } + + fun replaceAllQueryParams(queryParams: QueryParams) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun replaceAllQueryParams(queryParams: Map>) = apply { + this.queryParams.replaceAll(queryParams) + } + + fun removeQueryParams(key: String) = apply { queryParams.remove(key) } + + fun removeAllQueryParams(keys: Set) = apply { queryParams.removeAll(keys) } + + fun body(body: HttpRequestBody) = apply { this.body = body } + + fun build(): HttpRequest = + HttpRequest( + checkRequired("method", method), + checkRequired("baseUrl", baseUrl), + pathSegments.toImmutable(), + headers.build(), + queryParams.build(), + body, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBodies.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBodies.kt new file mode 100644 index 0000000..6f7c370 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBodies.kt @@ -0,0 +1,267 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:JvmName("HttpRequestBodies") + +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.MultipartField +import com.cas_parser.api.core.toImmutable +import com.cas_parser.api.errors.CasParserInvalidDataException +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.databind.node.JsonNodeType +import java.io.ByteArrayInputStream +import java.io.InputStream +import java.io.OutputStream +import java.util.UUID +import kotlin.jvm.optionals.getOrNull + +@JvmSynthetic +internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody = + object : HttpRequestBody { + private val bytes: ByteArray by lazy { jsonMapper.writeValueAsBytes(value) } + + override fun writeTo(outputStream: OutputStream) = outputStream.write(bytes) + + override fun contentType(): String = "application/json" + + override fun contentLength(): Long = bytes.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} + } + +@JvmSynthetic +internal fun multipartFormData( + jsonMapper: JsonMapper, + fields: Map>, +): HttpRequestBody = + MultipartBody.Builder() + .apply { + fields.forEach { (name, field) -> + val knownValue = field.value.asKnown().getOrNull() + val parts = + if (knownValue is InputStream) { + // Read directly from the `InputStream` instead of reading it all + // into memory due to the `jsonMapper` serialization below. + sequenceOf(name to knownValue) + } else { + val node = jsonMapper.valueToTree(field.value) + serializePart(name, node) + } + + parts.forEach { (name, bytes) -> + val partBody = + if (bytes is ByteArrayInputStream) { + val byteArray = bytes.readBytes() + + object : HttpRequestBody { + + override fun writeTo(outputStream: OutputStream) { + outputStream.write(byteArray) + } + + override fun contentType(): String = field.contentType + + override fun contentLength(): Long = byteArray.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} + } + } else { + object : HttpRequestBody { + + override fun writeTo(outputStream: OutputStream) { + bytes.copyTo(outputStream) + } + + override fun contentType(): String = field.contentType + + override fun contentLength(): Long = -1L + + override fun repeatable(): Boolean = false + + override fun close() = bytes.close() + } + } + + addPart( + MultipartBody.Part.create( + name, + field.filename().getOrNull(), + field.contentType, + partBody, + ) + ) + } + } + } + .build() + +private fun serializePart(name: String, node: JsonNode): Sequence> = + when (node.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> emptySequence() + JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream()) + JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream()) + JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream()) + JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream()) + JsonNodeType.ARRAY -> + sequenceOf( + name to + node + .elements() + .asSequence() + .mapNotNull { element -> + when (element.nodeType) { + JsonNodeType.MISSING, + JsonNodeType.NULL -> null + JsonNodeType.STRING -> element.textValue() + JsonNodeType.BOOLEAN -> element.booleanValue().toString() + JsonNodeType.NUMBER -> element.numberValue().toString() + null, + JsonNodeType.BINARY, + JsonNodeType.ARRAY, + JsonNodeType.OBJECT, + JsonNodeType.POJO -> + throw CasParserInvalidDataException( + "Unexpected JsonNode type in array: ${element.nodeType}" + ) + } + } + .joinToString(",") + .byteInputStream() + ) + JsonNodeType.OBJECT -> + node.fields().asSequence().flatMap { (key, value) -> + serializePart("$name[$key]", value) + } + JsonNodeType.POJO, + null -> throw CasParserInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") + } + +private class MultipartBody +private constructor(private val boundary: String, private val parts: List) : HttpRequestBody { + private val boundaryBytes: ByteArray = boundary.toByteArray() + private val contentType = "multipart/form-data; boundary=$boundary" + + // This must remain in sync with `contentLength`. + override fun writeTo(outputStream: OutputStream) { + parts.forEach { part -> + outputStream.write(DASHDASH) + outputStream.write(boundaryBytes) + outputStream.write(CRLF) + + outputStream.write(CONTENT_DISPOSITION) + outputStream.write(part.contentDisposition.toByteArray()) + outputStream.write(CRLF) + + outputStream.write(CONTENT_TYPE) + outputStream.write(part.contentType.toByteArray()) + outputStream.write(CRLF) + + outputStream.write(CRLF) + part.body.writeTo(outputStream) + outputStream.write(CRLF) + } + + outputStream.write(DASHDASH) + outputStream.write(boundaryBytes) + outputStream.write(DASHDASH) + outputStream.write(CRLF) + } + + override fun contentType(): String = contentType + + // This must remain in sync with `writeTo`. + override fun contentLength(): Long { + var byteCount = 0L + + parts.forEach { part -> + val contentLength = part.body.contentLength() + if (contentLength == -1L) { + return -1L + } + + byteCount += + DASHDASH.size + + boundaryBytes.size + + CRLF.size + + CONTENT_DISPOSITION.size + + part.contentDisposition.toByteArray().size + + CRLF.size + + CONTENT_TYPE.size + + part.contentType.toByteArray().size + + CRLF.size + + CRLF.size + + contentLength + + CRLF.size + } + + byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size + return byteCount + } + + override fun repeatable(): Boolean = parts.all { it.body.repeatable() } + + override fun close() { + parts.forEach { it.body.close() } + } + + class Builder { + private val boundary = UUID.randomUUID().toString() + private val parts: MutableList = mutableListOf() + + fun addPart(part: Part) = apply { parts.add(part) } + + fun build() = MultipartBody(boundary, parts.toImmutable()) + } + + class Part + private constructor( + val contentDisposition: String, + val contentType: String, + val body: HttpRequestBody, + ) { + companion object { + fun create( + name: String, + filename: String?, + contentType: String, + body: HttpRequestBody, + ): Part { + val disposition = buildString { + append("form-data; name=") + appendQuotedString(name) + if (filename != null) { + append("; filename=") + appendQuotedString(filename) + } + } + return Part(disposition, contentType, body) + } + } + } + + companion object { + private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte()) + private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte()) + private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray() + private val CONTENT_TYPE = "Content-Type: ".toByteArray() + + private fun StringBuilder.appendQuotedString(key: String) { + append('"') + for (ch in key) { + when (ch) { + '\n' -> append("%0A") + '\r' -> append("%0D") + '"' -> append("%22") + else -> append(ch) + } + } + append('"') + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBody.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBody.kt new file mode 100644 index 0000000..28c90f7 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBody.kt @@ -0,0 +1,25 @@ +package com.cas_parser.api.core.http + +import java.io.OutputStream +import java.lang.AutoCloseable + +interface HttpRequestBody : AutoCloseable { + + fun writeTo(outputStream: OutputStream) + + fun contentType(): String? + + fun contentLength(): Long + + /** + * Determines if a request can be repeated in a meaningful way, for example before doing a + * retry. + * + * The most typical case when a request can't be retried is if the request body is being + * streamed. In this case the body data isn't available on subsequent attempts. + */ + fun repeatable(): Boolean + + /** 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/HttpResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpResponse.kt new file mode 100644 index 0000000..f19a687 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpResponse.kt @@ -0,0 +1,22 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core.http + +import java.io.InputStream + +interface HttpResponse : AutoCloseable { + + fun statusCode(): Int + + fun headers(): Headers + + fun body(): InputStream + + /** Overridden from [AutoCloseable] to not have a checked exception in its signature. */ + override fun close() + + interface Handler { + + fun handle(response: HttpResponse): T + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpResponseFor.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpResponseFor.kt new file mode 100644 index 0000000..2ad845f --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpResponseFor.kt @@ -0,0 +1,25 @@ +package com.cas_parser.api.core.http + +import java.io.InputStream + +interface HttpResponseFor : HttpResponse { + + fun parse(): T +} + +@JvmSynthetic +internal fun HttpResponse.parseable(parse: () -> T): HttpResponseFor = + object : HttpResponseFor { + + private val parsed: T by lazy { parse() } + + override fun parse(): T = parsed + + override fun statusCode(): Int = this@parseable.statusCode() + + override fun headers(): Headers = this@parseable.headers() + + override fun body(): InputStream = this@parseable.body() + + override fun close() = this@parseable.close() + } diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/LoggingHttpClient.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/LoggingHttpClient.kt new file mode 100644 index 0000000..a482a89 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/LoggingHttpClient.kt @@ -0,0 +1,628 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.LogLevel +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.toImmutable +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.OutputStream +import java.nio.ByteBuffer +import java.nio.charset.CharacterCodingException +import java.nio.charset.Charset +import java.nio.charset.CharsetDecoder +import java.nio.charset.CodingErrorAction +import java.nio.charset.StandardCharsets +import java.time.Clock +import java.time.Duration +import java.time.OffsetDateTime +import java.util.SortedSet +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletionException +import kotlin.time.toKotlinDuration + +/** A wrapper [HttpClient] around [httpClient] that logs request and response information. */ +class LoggingHttpClient +private constructor( + /** The underlying [HttpClient] for making requests. */ + @get:JvmName("httpClient") val httpClient: HttpClient, + /** + * Sensitive headers to redact from logs. + * + * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie")`. + */ + @get:JvmName("redactedHeaders") val redactedHeaders: SortedSet, + /** + * The clock to use for measuring request and response durations. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + @get:JvmName("clock") val clock: Clock, + /** + * The log level to use. + * + * Pass [LogLevel.fromEnv] to read from environment variables. + */ + @get:JvmName("level") val level: LogLevel, +) : HttpClient { + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { + val loggingRequest = logRequest(request) + + val before = OffsetDateTime.now(clock) + val response = + try { + httpClient.execute(loggingRequest, requestOptions) + } catch (e: Throwable) { + logFailure(e, Duration.between(before, OffsetDateTime.now(clock))) + throw e + } + + val took = Duration.between(before, OffsetDateTime.now(clock)) + return logResponse(response, took) + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val loggingRequest = logRequest(request) + + val before = OffsetDateTime.now(clock) + val future = + try { + httpClient.executeAsync(loggingRequest, requestOptions) + } catch (e: Throwable) { + logFailure(e, Duration.between(before, OffsetDateTime.now(clock))) + throw e + } + return future.handle { response, error -> + val took = Duration.between(before, OffsetDateTime.now(clock)) + if (error != null) { + logFailure(unwrapCompletionException(error), took) + throw error + } + logResponse(response, took) + } + } + + private fun logRequest(request: HttpRequest): HttpRequest { + if (!level.shouldLog(LogLevel.INFO)) { + return request + } + + System.err.println( + buildString { + append("--> ${request.method} ${request.url()}") + request.body?.let { + val length = it.contentLength() + append(if (length >= 0) " ($length-byte body)" else " (unknown-length body)") + } + } + ) + + if (!level.shouldLog(LogLevel.DEBUG)) { + return request + } + + logHeaders(request.headers) + + if (request.body == null) { + System.err.println("--> END ${request.method}") + System.err.println() + return request + } + + return request + .toBuilder() + .body(LoggingHttpRequestBody(request.method, request.body)) + .build() + } + + private fun logResponse(response: HttpResponse, took: Duration): HttpResponse { + if (!level.shouldLog(LogLevel.INFO)) { + return response + } + + val contentLength = response.headers().values("Content-Length").firstOrNull()?.toIntOrNull() + System.err.println( + "<-- ${response.statusCode()} (${ + buildString { + append(took.format()) + contentLength?.let { append(", $contentLength-byte body") } + } + })" + ) + + if (!level.shouldLog(LogLevel.DEBUG)) { + return response + } + + logHeaders(response.headers()) + return LoggingHttpResponse(response) + } + + private fun logFailure(error: Throwable, took: Duration) { + if (!level.shouldLog(LogLevel.ERROR)) { + return + } + + System.err.println( + buildString { + append("<-- !! ${error.javaClass.simpleName}") + error.message?.let { append(": $it") } + append(" (${took.format()})") + } + ) + } + + private fun unwrapCompletionException(error: Throwable): Throwable = + if (error is CompletionException && error.cause != null) error.cause!! else error + + private fun logHeaders(headers: Headers) = + headers.names().forEach { name -> + headers.values(name).forEach { value -> + System.err.println("$name: ${if (redactedHeaders.contains(name)) "██" else value}") + } + } + + override fun close() = httpClient.close() + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [LoggingHttpClient]. + * + * The following fields are required: + * ```java + * .httpClient() + * .level() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LoggingHttpClient]. */ + class Builder internal constructor() { + + private var httpClient: HttpClient? = null + private var redactedHeaders: Set = + setOf("authorization", "api-key", "x-api-key", "cookie", "set-cookie") + private var clock: Clock = Clock.systemUTC() + private var level: LogLevel? = null + + @JvmSynthetic + internal fun from(loggingHttpClient: LoggingHttpClient) = apply { + httpClient = loggingHttpClient.httpClient + redactedHeaders = loggingHttpClient.redactedHeaders + clock = loggingHttpClient.clock + level = loggingHttpClient.level + } + + /** The underlying [HttpClient] for making requests. */ + fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient } + + /** + * Sensitive headers to redact from logs. + * + * Defaults to `Set.of("authorization", "api-key", "x-api-key", "cookie", "set-cookie")`. + */ + fun redactedHeaders(redactedHeaders: Set) = apply { + this.redactedHeaders = redactedHeaders + } + + /** + * The clock to use for measuring request and response durations. + * + * This is primarily useful for using a fake clock in tests. + * + * Defaults to [Clock.systemUTC]. + */ + fun clock(clock: Clock) = apply { this.clock = clock } + + /** + * The log level to use. + * + * Pass [LogLevel.fromEnv] to read from environment variables. + */ + fun level(level: LogLevel) = apply { this.level = level } + + /** + * Returns an immutable instance of [LoggingHttpClient]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .httpClient() + * .level() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): LoggingHttpClient = + LoggingHttpClient( + checkRequired("httpClient", httpClient), + redactedHeaders.toSortedSet(String.CASE_INSENSITIVE_ORDER).toImmutable(), + clock, + checkRequired("level", level), + ) + } +} + +/** + * An [HttpRequestBody] wrapper that delegates to [body] while also logging line by line as it's + * written. + * + * The logging occurs in a streaming manner with minimal buffering. + */ +private class LoggingHttpRequestBody( + private val method: HttpMethod, + private val body: HttpRequestBody, +) : HttpRequestBody { + + private val charset by lazy { parseCharset(body.contentType()) } + + override fun writeTo(outputStream: OutputStream) { + val loggingOutputStream = LoggingOutputStream(outputStream, charset) + body.writeTo(loggingOutputStream) + + loggingOutputStream.flush() + System.err.println("--> END $method (${loggingOutputStream.writeCount()}-byte body)") + System.err.println() + } + + override fun contentType(): String? = body.contentType() + + override fun contentLength(): Long = body.contentLength() + + override fun repeatable(): Boolean = body.repeatable() + + override fun close() = body.close() +} + +/** + * An [OutputStream] wrapper that delegates to [outputStream] while also logging bytes line by line + * as it's written to. + * + * The written content is assumed to be in the given [charset] and the logging occurs in a streaming + * manner with minimal buffering. + */ +private class LoggingOutputStream(private val outputStream: OutputStream, charset: Charset?) : + OutputStream() { + + private val buffer = LoggingBuffer(charset) + + fun writeCount() = buffer.writeCount() + + override fun write(b: Int) { + outputStream.write(b) + buffer.write(b) + } + + override fun write(b: ByteArray, off: Int, len: Int) { + outputStream.write(b, off, len) + for (i in off until off + len) { + buffer.write(b[i].toInt() and 0xFF) + } + } + + /** Prints any currently buffered content. */ + override fun flush() { + buffer.flush() + outputStream.flush() + } + + override fun close() = outputStream.close() +} + +/** + * An [HttpResponse] wrapper that delegates to [response] while also logging line-by-line as it's + * read. + * + * The logging occurs in a streaming manner with minimal buffering. + */ +private class LoggingHttpResponse(private val response: HttpResponse) : HttpResponse { + + private val loggingBody: Lazy = lazy { + LoggingInputStream( + response.body(), + parseCharset(response.headers().values("Content-Type").firstOrNull()), + ) + } + + override fun statusCode(): Int = response.statusCode() + + override fun headers(): Headers = response.headers() + + override fun body(): InputStream = loggingBody.value + + override fun close() { + if (loggingBody.isInitialized()) { + loggingBody.value.close() + } + response.close() + } +} + +/** + * An [InputStream] wrapper that delegates to [inputStream] while also logging bytes line by line as + * it's read. + * + * The contents of [inputStream] are assumed to be in the given [charset] and the logging occurs in + * a streaming manner with minimal buffering. + */ +private class LoggingInputStream(private val inputStream: InputStream, charset: Charset?) : + InputStream() { + + private var isDone = false + private val buffer = LoggingBuffer(charset) + + override fun read(): Int { + if (isDone) { + return -1 + } + + val b = inputStream.read() + + if (b == -1) { + markDone() + return b + } + + buffer.write(b) + return b + } + + override fun read(b: ByteArray, off: Int, len: Int): Int { + if (isDone) { + return -1 + } + + val bytesRead = inputStream.read(b, off, len) + + if (bytesRead == -1) { + markDone() + return bytesRead + } + + for (i in off until off + bytesRead) { + buffer.write(b[i].toInt() and 0xFF) + } + return bytesRead + } + + override fun close() { + if (!isDone) { + markDone(closedEarly = true) + } + inputStream.close() + } + + private fun markDone(closedEarly: Boolean = false) { + isDone = true + buffer.flush() + val suffix = if (closedEarly) ", closed early" else "" + System.err.println("<-- END HTTP (${buffer.writeCount()}-byte body$suffix)") + System.err.println() + } +} + +/** + * A byte buffer that prints line by line, using the given [charset], as bytes are written to it. + * + * When [charset] is `null`, the buffer performs an upfront check to detect binary content. If + * non-whitespace ISO control characters are found in the first [PROBABLY_UTF8_CODE_POINT_LIMIT] + * code points, body logging is suppressed entirely. + */ +private class LoggingBuffer(charset: Charset?) { + + private val charset = charset ?: StandardCharsets.UTF_8 + + private val decoder: CharsetDecoder = + this.charset + .newDecoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT) + private var writeCount = 0 + private val buffer = ByteArrayOutputStream(128) + + /** + * Whether logging has been suppressed because the content doesn't appear to be readable text. + * + * This is only set when [charset] is `null` and the content fails the [isProbablyUtf8] check. + */ + private var suppressed = false + + /** + * Bytes accumulated for the [isProbablyUtf8] check before any lines are printed. + * + * Once the check passes (or [charset] is non-null), this is set to `null` and bytes flow + * directly to [buffer]. + */ + private var prefetchBuffer: ByteArrayOutputStream? = + if (charset != null) null else ByteArrayOutputStream(128) + + fun writeCount() = writeCount + + fun write(b: Int) { + if (writeCount == 0) { + // Print a newline before we start printing anything to separate the printed content + // from previous content. + System.err.println() + } + + writeCount++ + + if (suppressed) { + return + } + + val prefetch = prefetchBuffer + if (prefetch != null) { + prefetch.write(b) + // Continue accumulating until we have enough bytes to decide. + if (prefetch.size() < PROBABLY_UTF8_BYTE_LIMIT && b != '\n'.code) { + return + } + // We have enough bytes. Check if the content is probably UTF-8. + prefetchBuffer = null + val bytes = prefetch.toByteArray() + if (!isProbablyUtf8(bytes)) { + suppressed = true + System.err.println("(binary body omitted)") + return + } + // Content looks like UTF-8. Feed the accumulated bytes into the normal buffer. + for (byte in bytes) { + writeToBuffer(byte.toInt() and 0xFF) + } + return + } + + writeToBuffer(b) + } + + private fun writeToBuffer(b: Int) { + if (b == '\n'.code) { + flush() + return + } + + buffer.write(b) + } + + /** Prints any currently buffered content. */ + fun flush() { + if (suppressed) { + return + } + + // If we still have a prefetch buffer when flush is called (body was shorter than the + // limit), run the check now. + val prefetch = prefetchBuffer + if (prefetch != null) { + prefetchBuffer = null + val bytes = prefetch.toByteArray() + if (bytes.isEmpty()) { + return + } + if (!isProbablyUtf8(bytes)) { + suppressed = true + System.err.println("(binary body omitted)") + return + } + for (byte in bytes) { + writeToBuffer(byte.toInt() and 0xFF) + } + } + + if (buffer.size() == 0) { + return + } + + val line = + try { + decoder.decode(ByteBuffer.wrap(buffer.toByteArray())) + } catch (e: CharacterCodingException) { + "(omitted line is not valid $charset)" + } + buffer.reset() + System.err.println(line) + } +} + +/** The maximum number of code points to sample when checking if content is probably UTF-8. */ +private const val PROBABLY_UTF8_CODE_POINT_LIMIT = 64 + +/** + * The maximum number of bytes to accumulate before running the [isProbablyUtf8] check. UTF-8 code + * points are at most 4 bytes, so this accommodates [PROBABLY_UTF8_CODE_POINT_LIMIT] code points. + */ +private const val PROBABLY_UTF8_BYTE_LIMIT = PROBABLY_UTF8_CODE_POINT_LIMIT * 4 + +/** + * Returns `true` if the given [bytes] probably contain human-readable UTF-8 text. + * + * Decodes up to [PROBABLY_UTF8_CODE_POINT_LIMIT] code points and returns `false` if any + * non-whitespace ISO control characters are found, or if the bytes are not valid UTF-8. + */ +private fun isProbablyUtf8(bytes: ByteArray): Boolean { + try { + val decoder = + StandardCharsets.UTF_8.newDecoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT) + val charBuffer = decoder.decode(ByteBuffer.wrap(bytes)) + var codePointCount = 0 + var i = 0 + while (i < charBuffer.length && codePointCount < PROBABLY_UTF8_CODE_POINT_LIMIT) { + val codePoint = Character.codePointAt(charBuffer, i) + if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { + return false + } + i += Character.charCount(codePoint) + codePointCount++ + } + return true + } catch (e: CharacterCodingException) { + return false + } +} + +/** Returns the [Charset] in the given [contentType] string, or `null` if unspecified. */ +private fun parseCharset(contentType: String?): Charset? = + contentType + ?.split(";") + ?.drop(1) + ?.map { it.trim() } + ?.firstOrNull { it.startsWith("charset=", ignoreCase = true) } + ?.substringAfter("=") + ?.trim() + ?.removeSurrounding("\"") + ?.let { runCatching { charset(it) }.getOrNull() } + +/** Formats the [Duration] into a string like "1m 40s 467ms". */ +private fun Duration.format(): String = + toKotlinDuration().toComponents { days, hours, minutes, seconds, nanoseconds -> + buildString { + val milliseconds = nanoseconds / 1_000_000 + if (days > 0) { + append("${days}d") + } + if (hours > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${hours}h") + } + if (minutes > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${minutes}m") + } + if (seconds > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${seconds}s") + } + if (milliseconds > 0) { + if (isNotEmpty()) { + append(" ") + } + append("${milliseconds}ms") + } + + if (isEmpty()) { + append("0s") + } + } + } diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingAsyncStreamResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingAsyncStreamResponse.kt new file mode 100644 index 0000000..493c17a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingAsyncStreamResponse.kt @@ -0,0 +1,56 @@ +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.closeWhenPhantomReachable +import com.cas_parser.api.core.http.AsyncStreamResponse.Handler +import java.util.Optional +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executor + +/** + * A delegating wrapper around an `AsyncStreamResponse` that closes it once it's only phantom + * reachable. + * + * This class ensures the `AsyncStreamResponse` is closed even if the user forgets to close it. + */ +internal class PhantomReachableClosingAsyncStreamResponse( + private val asyncStreamResponse: AsyncStreamResponse +) : AsyncStreamResponse { + + /** + * An object used for keeping `asyncStreamResponse` open while the object is still reachable. + */ + private val reachabilityTracker = Object() + + init { + closeWhenPhantomReachable(reachabilityTracker, asyncStreamResponse::close) + } + + override fun subscribe(handler: Handler): AsyncStreamResponse = apply { + asyncStreamResponse.subscribe(TrackedHandler(handler, reachabilityTracker)) + } + + override fun subscribe(handler: Handler, executor: Executor): AsyncStreamResponse = + apply { + asyncStreamResponse.subscribe(TrackedHandler(handler, reachabilityTracker), executor) + } + + override fun onCompleteFuture(): CompletableFuture = + asyncStreamResponse.onCompleteFuture() + + override fun close() = asyncStreamResponse.close() +} + +/** + * A wrapper around a `Handler` that also references a `reachabilityTracker` object. + * + * Referencing the `reachabilityTracker` object prevents it from getting reclaimed while the handler + * is still reachable. + */ +private class TrackedHandler( + private val handler: Handler, + private val reachabilityTracker: Any, +) : Handler { + override fun onNext(value: T) = handler.onNext(value) + + override fun onComplete(error: Optional) = handler.onComplete(error) +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingHttpClient.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingHttpClient.kt new file mode 100644 index 0000000..cb87081 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingHttpClient.kt @@ -0,0 +1,26 @@ +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.closeWhenPhantomReachable +import java.util.concurrent.CompletableFuture + +/** + * A delegating wrapper around an `HttpClient` that closes it once it's only phantom reachable. + * + * This class ensures the `HttpClient` is closed even if the user forgets to close it. + */ +internal class PhantomReachableClosingHttpClient(private val httpClient: HttpClient) : HttpClient { + init { + closeWhenPhantomReachable(this, httpClient) + } + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse = + httpClient.execute(request, requestOptions) + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = httpClient.executeAsync(request, requestOptions) + + override fun close() = httpClient.close() +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingStreamResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingStreamResponse.kt new file mode 100644 index 0000000..1972f32 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/PhantomReachableClosingStreamResponse.kt @@ -0,0 +1,21 @@ +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.closeWhenPhantomReachable +import java.util.stream.Stream + +/** + * A delegating wrapper around a `StreamResponse` that closes it once it's only phantom reachable. + * + * This class ensures the `StreamResponse` is closed even if the user forgets to close it. + */ +internal class PhantomReachableClosingStreamResponse( + private val streamResponse: StreamResponse +) : StreamResponse { + init { + closeWhenPhantomReachable(this, streamResponse) + } + + override fun stream(): Stream = streamResponse.stream() + + override fun close() = streamResponse.close() +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/ProxyAuthenticator.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/ProxyAuthenticator.kt new file mode 100644 index 0000000..a131d34 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/ProxyAuthenticator.kt @@ -0,0 +1,59 @@ +package com.cas_parser.api.core.http + +import java.net.Proxy +import java.nio.charset.Charset +import java.nio.charset.StandardCharsets +import java.util.Base64 +import java.util.Optional + +/** + * Provides credentials when an HTTP proxy responds with `407 Proxy Authentication Required`. + * + * Implementations inspect the 407 [response] (typically its `Proxy-Authenticate` header) and return + * the request to retry with a `Proxy-Authorization` header set, or [Optional.empty] to abandon + * authentication and surface the 407 to the caller. + * + * Implementations must be thread-safe; they may be invoked concurrently from multiple HTTP calls. + */ +fun interface ProxyAuthenticator { + + /** + * @param proxy the proxy that produced the challenge, or [Proxy.NO_PROXY] if the route is not + * yet established + * @param request the request that produced [response] + * @param response the 407 challenge response + * @return the retry request to send (typically [request] with a `Proxy-Authorization` header + * added), or [Optional.empty] to abandon authentication + */ + fun authenticate( + proxy: Proxy, + request: HttpRequest, + response: HttpResponse, + ): Optional + + companion object { + + /** + * A [ProxyAuthenticator] that uses RFC 7617 Basic authentication with the ISO-8859-1 + * charset. + */ + @JvmStatic + fun basic(username: String, password: String): ProxyAuthenticator = + basic(username, password, StandardCharsets.ISO_8859_1) + + /** + * A [ProxyAuthenticator] that uses RFC 7617 Basic authentication with the given [charset]. + */ + @JvmStatic + fun basic(username: String, password: String, charset: Charset): ProxyAuthenticator { + val token = + Base64.getEncoder().encodeToString("$username:$password".toByteArray(charset)) + val headerValue = "Basic $token" + return ProxyAuthenticator { _, request, _ -> + Optional.of( + request.toBuilder().putHeader("Proxy-Authorization", headerValue).build() + ) + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/QueryParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/QueryParams.kt new file mode 100644 index 0000000..8ae0e4b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/QueryParams.kt @@ -0,0 +1,129 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.JsonArray +import com.cas_parser.api.core.JsonBoolean +import com.cas_parser.api.core.JsonMissing +import com.cas_parser.api.core.JsonNull +import com.cas_parser.api.core.JsonNumber +import com.cas_parser.api.core.JsonObject +import com.cas_parser.api.core.JsonString +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.toImmutable + +class QueryParams +private constructor( + private val map: Map>, + @get:JvmName("size") val size: Int, +) { + + fun isEmpty(): Boolean = map.isEmpty() + + fun keys(): Set = map.keys + + fun values(key: String): List = map[key].orEmpty() + + fun toBuilder(): Builder = Builder().putAll(map) + + companion object { + + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + + private val map: MutableMap> = mutableMapOf() + private var size: Int = 0 + + fun put(key: String, value: JsonValue): Builder = apply { + when (value) { + is JsonMissing, + is JsonNull -> {} + is JsonBoolean -> put(key, value.value.toString()) + is JsonNumber -> put(key, value.value.toString()) + is JsonString -> put(key, value.value) + is JsonArray -> + put( + key, + value.values + .asSequence() + .mapNotNull { + when (it) { + is JsonMissing, + is JsonNull -> null + is JsonBoolean -> it.value.toString() + is JsonNumber -> it.value.toString() + is JsonString -> it.value + is JsonArray, + is JsonObject -> + throw IllegalArgumentException( + "Cannot comma separate non-primitives in query params" + ) + } + } + .joinToString(","), + ) + is JsonObject -> + value.values.forEach { (nestedKey, value) -> put("$key[$nestedKey]", value) } + } + } + + fun put(key: String, value: String) = apply { + map.getOrPut(key) { mutableListOf() }.add(value) + size++ + } + + fun put(key: String, values: Iterable) = apply { values.forEach { put(key, it) } } + + fun putAll(queryParams: Map>) = apply { + queryParams.forEach(::put) + } + + fun putAll(queryParams: QueryParams) = apply { + queryParams.keys().forEach { put(it, queryParams.values(it)) } + } + + fun replace(key: String, value: String) = apply { + remove(key) + put(key, value) + } + + fun replace(key: String, values: Iterable) = apply { + remove(key) + put(key, values) + } + + fun replaceAll(queryParams: Map>) = apply { + queryParams.forEach(::replace) + } + + fun replaceAll(queryParams: QueryParams) = apply { + queryParams.keys().forEach { replace(it, queryParams.values(it)) } + } + + fun remove(key: String) = apply { size -= map.remove(key).orEmpty().size } + + fun removeAll(keys: Set) = apply { keys.forEach(::remove) } + + fun clear() = apply { + map.clear() + size = 0 + } + + fun build() = + QueryParams(map.mapValues { (_, values) -> values.toImmutable() }.toImmutable(), size) + } + + override fun hashCode(): Int = map.hashCode() + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is QueryParams && map == other.map + } + + override fun toString(): String = "QueryParams{map=$map}" +} 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 new file mode 100644 index 0000000..5487fce --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt @@ -0,0 +1,262 @@ +// File generated from our OpenAPI spec by Stainless. + +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 +import java.io.IOException +import java.time.Clock +import java.time.Duration +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeParseException +import java.time.temporal.ChronoUnit +import java.util.UUID +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ThreadLocalRandom +import java.util.concurrent.TimeUnit +import java.util.function.Function +import kotlin.math.min +import kotlin.math.pow + +class RetryingHttpClient +private constructor( + private val httpClient: HttpClient, + private val sleeper: Sleeper, + private val clock: Clock, + private val maxRetries: Int, + private val idempotencyHeader: String?, +) : HttpClient { + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { + var modifiedRequest = maybeAddIdempotencyHeader(request) + + // Don't send the current retry count in the headers if the caller set their own value. + val shouldSendRetryCount = + !modifiedRequest.headers.names().contains("X-Stainless-Retry-Count") + + var retries = 0 + + while (true) { + if (shouldSendRetryCount) { + modifiedRequest = setRetryCountHeader(modifiedRequest, retries) + } + + if (!isRetryable(modifiedRequest)) { + return httpClient.execute(modifiedRequest, requestOptions) + } + + val response = + try { + val response = httpClient.execute(modifiedRequest, requestOptions) + if (++retries > maxRetries || !shouldRetry(response)) { + return response + } + + response + } catch (throwable: Throwable) { + if (++retries > maxRetries || !shouldRetry(throwable)) { + throw throwable + } + + null + } + + val backoffDuration = getRetryBackoffDuration(retries, response) + // All responses must be closed, so close the failed one before retrying. + response?.close() + sleeper.sleep(backoffDuration) + } + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val modifiedRequest = maybeAddIdempotencyHeader(request) + + // Don't send the current retry count in the headers if the caller set their own value. + val shouldSendRetryCount = + !modifiedRequest.headers.names().contains("X-Stainless-Retry-Count") + + var retries = 0 + + fun executeWithRetries( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val requestWithRetryCount = + if (shouldSendRetryCount) setRetryCountHeader(request, retries) else request + + val responseFuture = httpClient.executeAsync(requestWithRetryCount, requestOptions) + if (!isRetryable(requestWithRetryCount)) { + return responseFuture + } + + return responseFuture + .handleAsync( + fun( + response: HttpResponse?, + throwable: Throwable?, + ): CompletableFuture { + if (response != null) { + if (++retries > maxRetries || !shouldRetry(response)) { + return CompletableFuture.completedFuture(response) + } + } else { + if (++retries > maxRetries || !shouldRetry(throwable!!)) { + val failedFuture = CompletableFuture() + failedFuture.completeExceptionally(throwable) + return failedFuture + } + } + + val backoffDuration = getRetryBackoffDuration(retries, response) + // All responses must be closed, so close the failed one before retrying. + response?.close() + return sleeper.sleepAsync(backoffDuration).thenCompose { + executeWithRetries(requestWithRetryCount, requestOptions) + } + } + ) { + // Run in the same thread. + it.run() + } + .thenCompose(Function.identity()) + } + + return executeWithRetries(modifiedRequest, requestOptions) + } + + 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 + // the body data aren't available on subsequent attempts. + request.body?.repeatable() ?: true + + private fun setRetryCountHeader(request: HttpRequest, retries: Int): HttpRequest = + request.toBuilder().replaceHeaders("X-Stainless-Retry-Count", retries.toString()).build() + + private fun idempotencyKey(): String = "stainless-java-retry-${UUID.randomUUID()}" + + private fun maybeAddIdempotencyHeader(request: HttpRequest): HttpRequest { + if (idempotencyHeader == null || request.headers.names().contains(idempotencyHeader)) { + return request + } + + return request + .toBuilder() + // Set a header to uniquely identify the request when retried. + .putHeader(idempotencyHeader, idempotencyKey()) + .build() + } + + private fun shouldRetry(response: HttpResponse): Boolean { + // Note: this is not a standard header + val shouldRetryHeader = response.headers().values("X-Should-Retry").getOrNull(0) + val statusCode = response.statusCode() + + return when { + // If the server explicitly says whether to retry, obey + shouldRetryHeader == "true" -> true + shouldRetryHeader == "false" -> false + + // Retry on request timeouts + statusCode == 408 -> true + // Retry on lock timeouts + statusCode == 409 -> true + // Retry on rate limits + statusCode == 429 -> true + // Retry internal errors + statusCode >= 500 -> true + else -> false + } + } + + private fun shouldRetry(throwable: Throwable): Boolean = + // Only retry known retryable exceptions, other exceptions are not intended to be retried. + throwable is IOException || + throwable is CasParserIoException || + throwable is CasParserRetryableException + + private fun getRetryBackoffDuration(retries: Int, response: HttpResponse?): Duration { + // About the Retry-After header: + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After + response + ?.headers() + ?.let { headers -> + headers + .values("Retry-After-Ms") + .getOrNull(0) + ?.toFloatOrNull() + ?.times(TimeUnit.MILLISECONDS.toNanos(1)) + ?: headers.values("Retry-After").getOrNull(0)?.let { retryAfter -> + retryAfter.toFloatOrNull()?.times(TimeUnit.SECONDS.toNanos(1)) + ?: try { + ChronoUnit.NANOS.between( + OffsetDateTime.now(clock), + OffsetDateTime.parse( + retryAfter, + DateTimeFormatter.RFC_1123_DATE_TIME, + ), + ) + } catch (e: DateTimeParseException) { + null + } + } + } + ?.let { retryAfterNanos -> + // If the API asks us to wait a certain amount of time, do what it says. + return Duration.ofNanos(retryAfterNanos.toLong()) + } + + // Apply exponential backoff, but not more than the max. + val backoffSeconds = min(0.5 * 2.0.pow(retries - 1), 8.0) + + // Apply some jitter + val jitter = 1.0 - 0.25 * ThreadLocalRandom.current().nextDouble() + + return Duration.ofNanos((TimeUnit.SECONDS.toNanos(1) * backoffSeconds * jitter).toLong()) + } + + companion object { + + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + + private var httpClient: HttpClient? = null + 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 } + + fun sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper } + + fun clock(clock: Clock) = apply { this.clock = clock } + + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } + + fun idempotencyHeader(header: String) = apply { this.idempotencyHeader = header } + + fun build(): HttpClient = + RetryingHttpClient( + checkRequired("httpClient", httpClient), + sleeper ?: DefaultSleeper(), + clock, + maxRetries, + idempotencyHeader, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/StreamResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/StreamResponse.kt new file mode 100644 index 0000000..6e4c1f2 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/StreamResponse.kt @@ -0,0 +1,19 @@ +package com.cas_parser.api.core.http + +import java.util.stream.Stream + +interface StreamResponse : AutoCloseable { + + fun stream(): Stream + + /** Overridden from [AutoCloseable] to not have a checked exception in its signature. */ + override fun close() +} + +@JvmSynthetic +internal fun StreamResponse.map(transform: (T) -> R): StreamResponse = + object : StreamResponse { + override fun stream(): Stream = this@map.stream().map(transform) + + override fun close() = this@map.close() + } diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/BadRequestException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/BadRequestException.kt new file mode 100644 index 0000000..074ca61 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/BadRequestException.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class BadRequestException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + CasParserServiceException( + "400: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = 400 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BadRequestException]. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BadRequestException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(badRequestException: BadRequestException) = apply { + headers = badRequestException.headers + body = badRequestException.body + cause = badRequestException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [BadRequestException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BadRequestException = + BadRequestException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserException.kt new file mode 100644 index 0000000..c1edd1c --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserException.kt @@ -0,0 +1,5 @@ +package com.cas_parser.api.errors + +open class CasParserException +@JvmOverloads +constructor(message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserInvalidDataException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserInvalidDataException.kt new file mode 100644 index 0000000..bee598d --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserInvalidDataException.kt @@ -0,0 +1,5 @@ +package com.cas_parser.api.errors + +class CasParserInvalidDataException +@JvmOverloads +constructor(message: String? = null, cause: Throwable? = null) : CasParserException(message, cause) diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserIoException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserIoException.kt new file mode 100644 index 0000000..1a1b36b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserIoException.kt @@ -0,0 +1,5 @@ +package com.cas_parser.api.errors + +class CasParserIoException +@JvmOverloads +constructor(message: String? = null, cause: Throwable? = null) : CasParserException(message, cause) diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserRetryableException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserRetryableException.kt new file mode 100644 index 0000000..dc5ccb2 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserRetryableException.kt @@ -0,0 +1,14 @@ +package com.cas_parser.api.errors + +/** + * Exception that indicates a transient error that can be retried. + * + * When this exception is thrown during an HTTP request, the SDK will automatically retry the + * request up to the maximum number of retries. + * + * @param message A descriptive error message + * @param cause The underlying cause of this exception, if any + */ +class CasParserRetryableException +@JvmOverloads +constructor(message: String? = null, cause: Throwable? = null) : CasParserException(message, cause) diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserServiceException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserServiceException.kt new file mode 100644 index 0000000..6f5136e --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/CasParserServiceException.kt @@ -0,0 +1,17 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.http.Headers + +abstract class CasParserServiceException +protected constructor(message: String, cause: Throwable? = null) : + CasParserException(message, cause) { + + abstract fun statusCode(): Int + + abstract fun headers(): Headers + + abstract fun body(): JsonValue +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/InternalServerException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/InternalServerException.kt new file mode 100644 index 0000000..75987e6 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/InternalServerException.kt @@ -0,0 +1,96 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class InternalServerException +private constructor( + private val statusCode: Int, + private val headers: Headers, + private val body: JsonValue, + cause: Throwable?, +) : + CasParserServiceException( + "$statusCode: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = statusCode + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [InternalServerException]. + * + * The following fields are required: + * ```java + * .statusCode() + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InternalServerException]. */ + class Builder internal constructor() { + + private var statusCode: Int? = null + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(internalServerException: InternalServerException) = apply { + statusCode = internalServerException.statusCode + headers = internalServerException.headers + body = internalServerException.body + cause = internalServerException.cause + } + + fun statusCode(statusCode: Int) = apply { this.statusCode = statusCode } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [InternalServerException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .statusCode() + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InternalServerException = + InternalServerException( + checkRequired("statusCode", statusCode), + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/NotFoundException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/NotFoundException.kt new file mode 100644 index 0000000..6856cd6 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/NotFoundException.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class NotFoundException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + CasParserServiceException( + "404: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = 404 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [NotFoundException]. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [NotFoundException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(notFoundException: NotFoundException) = apply { + headers = notFoundException.headers + body = notFoundException.body + cause = notFoundException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [NotFoundException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): NotFoundException = + NotFoundException(checkRequired("headers", headers), checkRequired("body", body), cause) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/PermissionDeniedException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/PermissionDeniedException.kt new file mode 100644 index 0000000..bb97c2e --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/PermissionDeniedException.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class PermissionDeniedException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + CasParserServiceException( + "403: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = 403 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [PermissionDeniedException]. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [PermissionDeniedException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(permissionDeniedException: PermissionDeniedException) = apply { + headers = permissionDeniedException.headers + body = permissionDeniedException.body + cause = permissionDeniedException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [PermissionDeniedException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): PermissionDeniedException = + PermissionDeniedException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/RateLimitException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/RateLimitException.kt new file mode 100644 index 0000000..c14384b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/RateLimitException.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class RateLimitException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + CasParserServiceException( + "429: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = 429 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [RateLimitException]. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [RateLimitException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(rateLimitException: RateLimitException) = apply { + headers = rateLimitException.headers + body = rateLimitException.body + cause = rateLimitException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [RateLimitException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): RateLimitException = + RateLimitException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnauthorizedException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnauthorizedException.kt new file mode 100644 index 0000000..3d9e25a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnauthorizedException.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class UnauthorizedException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + CasParserServiceException( + "401: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = 401 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UnauthorizedException]. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnauthorizedException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(unauthorizedException: UnauthorizedException) = apply { + headers = unauthorizedException.headers + body = unauthorizedException.body + cause = unauthorizedException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [UnauthorizedException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnauthorizedException = + UnauthorizedException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnexpectedStatusCodeException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnexpectedStatusCodeException.kt new file mode 100644 index 0000000..2ea9523 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnexpectedStatusCodeException.kt @@ -0,0 +1,97 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class UnexpectedStatusCodeException +private constructor( + private val statusCode: Int, + private val headers: Headers, + private val body: JsonValue, + cause: Throwable?, +) : + CasParserServiceException( + "$statusCode: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = statusCode + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [UnexpectedStatusCodeException]. + * + * The following fields are required: + * ```java + * .statusCode() + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnexpectedStatusCodeException]. */ + class Builder internal constructor() { + + private var statusCode: Int? = null + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(unexpectedStatusCodeException: UnexpectedStatusCodeException) = apply { + statusCode = unexpectedStatusCodeException.statusCode + headers = unexpectedStatusCodeException.headers + body = unexpectedStatusCodeException.body + cause = unexpectedStatusCodeException.cause + } + + fun statusCode(statusCode: Int) = apply { this.statusCode = statusCode } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [UnexpectedStatusCodeException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .statusCode() + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnexpectedStatusCodeException = + UnexpectedStatusCodeException( + checkRequired("statusCode", statusCode), + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnprocessableEntityException.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnprocessableEntityException.kt new file mode 100644 index 0000000..a6a9f9a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/errors/UnprocessableEntityException.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.errors + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class UnprocessableEntityException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + CasParserServiceException( + "422: ${if (body.isMissing()) "Unknown" else jsonMapper().writeValueAsString(body)}", + cause, + ) { + + override fun statusCode(): Int = 422 + + override fun headers(): Headers = headers + + override fun body(): JsonValue = body + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [UnprocessableEntityException]. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnprocessableEntityException]. */ + class Builder internal constructor() { + + private var headers: Headers? = null + private var body: JsonValue? = null + private var cause: Throwable? = null + + @JvmSynthetic + internal fun from(unprocessableEntityException: UnprocessableEntityException) = apply { + headers = unprocessableEntityException.headers + body = unprocessableEntityException.body + cause = unprocessableEntityException.cause + } + + fun headers(headers: Headers) = apply { this.headers = headers } + + fun body(body: JsonValue) = apply { this.body = body } + + fun cause(cause: Throwable?) = apply { this.cause = cause } + + /** Alias for calling [Builder.cause] with `cause.orElse(null)`. */ + fun cause(cause: Optional) = cause(cause.getOrNull()) + + /** + * Returns an immutable instance of [UnprocessableEntityException]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .headers() + * .body() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnprocessableEntityException = + UnprocessableEntityException( + checkRequired("headers", headers), + checkRequired("body", body), + cause, + ) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateParams.kt new file mode 100644 index 0000000..d8e5a20 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateParams.kt @@ -0,0 +1,421 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.accesstoken + +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.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 + +/** + * Generate a short-lived access token from your API key. + * + * **Use this endpoint from your backend** to create tokens that can be safely passed to + * frontend/SDK. + * + * **Legacy path:** `/v1/access-token` (still supported) + * + * Access tokens: + * - Are prefixed with `at_` for easy identification + * - Valid for up to 60 minutes + * - Can be used in place of API keys on all v4 endpoints + * - Cannot be used to generate other access tokens + */ +class AccessTokenCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Token validity in minutes (max 60) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiryMinutes(): Optional = body.expiryMinutes() + + /** + * Returns the raw JSON value of [expiryMinutes]. + * + * Unlike [expiryMinutes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _expiryMinutes(): JsonField = body._expiryMinutes() + + 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 { + + @JvmStatic fun none(): AccessTokenCreateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [AccessTokenCreateParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AccessTokenCreateParams]. */ + 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(accessTokenCreateParams: AccessTokenCreateParams) = apply { + body = accessTokenCreateParams.body.toBuilder() + additionalHeaders = accessTokenCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = accessTokenCreateParams.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: + * - [expiryMinutes] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Token validity in minutes (max 60) */ + fun expiryMinutes(expiryMinutes: Long) = apply { body.expiryMinutes(expiryMinutes) } + + /** + * Sets [Builder.expiryMinutes] to an arbitrary JSON value. + * + * You should usually call [Builder.expiryMinutes] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun expiryMinutes(expiryMinutes: JsonField) = apply { + body.expiryMinutes(expiryMinutes) + } + + 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 [AccessTokenCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AccessTokenCreateParams = + AccessTokenCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val expiryMinutes: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("expiry_minutes") + @ExcludeMissing + expiryMinutes: JsonField = JsonMissing.of() + ) : this(expiryMinutes, mutableMapOf()) + + /** + * Token validity in minutes (max 60) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun expiryMinutes(): Optional = expiryMinutes.getOptional("expiry_minutes") + + /** + * Returns the raw JSON value of [expiryMinutes]. + * + * Unlike [expiryMinutes], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("expiry_minutes") + @ExcludeMissing + fun _expiryMinutes(): JsonField = expiryMinutes + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var expiryMinutes: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + expiryMinutes = body.expiryMinutes + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Token validity in minutes (max 60) */ + fun expiryMinutes(expiryMinutes: Long) = expiryMinutes(JsonField.of(expiryMinutes)) + + /** + * Sets [Builder.expiryMinutes] to an arbitrary JSON value. + * + * You should usually call [Builder.expiryMinutes] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiryMinutes(expiryMinutes: JsonField) = apply { + this.expiryMinutes = expiryMinutes + } + + 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. + */ + fun build(): Body = Body(expiryMinutes, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + expiryMinutes() + 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 (expiryMinutes.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + expiryMinutes == other.expiryMinutes && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(expiryMinutes, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{expiryMinutes=$expiryMinutes, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccessTokenCreateParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "AccessTokenCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateResponse.kt new file mode 100644 index 0000000..00a9e51 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateResponse.kt @@ -0,0 +1,247 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.accesstoken + +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 AccessTokenCreateResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val accessToken: JsonField, + private val expiresIn: JsonField, + private val tokenType: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("access_token") + @ExcludeMissing + accessToken: JsonField = JsonMissing.of(), + @JsonProperty("expires_in") @ExcludeMissing expiresIn: JsonField = JsonMissing.of(), + @JsonProperty("token_type") @ExcludeMissing tokenType: JsonField = JsonMissing.of(), + ) : this(accessToken, expiresIn, tokenType, mutableMapOf()) + + /** + * The at_ prefixed access token + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun accessToken(): Optional = accessToken.getOptional("access_token") + + /** + * Token validity in seconds + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresIn(): Optional = expiresIn.getOptional("expires_in") + + /** + * Always "api_key" - token is a drop-in replacement for x-api-key header + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tokenType(): Optional = tokenType.getOptional("token_type") + + /** + * Returns the raw JSON value of [accessToken]. + * + * Unlike [accessToken], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("access_token") + @ExcludeMissing + fun _accessToken(): JsonField = accessToken + + /** + * Returns the raw JSON value of [expiresIn]. + * + * Unlike [expiresIn], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expires_in") @ExcludeMissing fun _expiresIn(): JsonField = expiresIn + + /** + * Returns the raw JSON value of [tokenType]. + * + * Unlike [tokenType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("token_type") @ExcludeMissing fun _tokenType(): JsonField = tokenType + + @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 [AccessTokenCreateResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AccessTokenCreateResponse]. */ + class Builder internal constructor() { + + private var accessToken: JsonField = JsonMissing.of() + private var expiresIn: JsonField = JsonMissing.of() + private var tokenType: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(accessTokenCreateResponse: AccessTokenCreateResponse) = apply { + accessToken = accessTokenCreateResponse.accessToken + expiresIn = accessTokenCreateResponse.expiresIn + tokenType = accessTokenCreateResponse.tokenType + additionalProperties = accessTokenCreateResponse.additionalProperties.toMutableMap() + } + + /** The at_ prefixed access token */ + fun accessToken(accessToken: String) = accessToken(JsonField.of(accessToken)) + + /** + * Sets [Builder.accessToken] to an arbitrary JSON value. + * + * You should usually call [Builder.accessToken] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun accessToken(accessToken: JsonField) = apply { this.accessToken = accessToken } + + /** Token validity in seconds */ + fun expiresIn(expiresIn: Long) = expiresIn(JsonField.of(expiresIn)) + + /** + * Sets [Builder.expiresIn] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresIn] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun expiresIn(expiresIn: JsonField) = apply { this.expiresIn = expiresIn } + + /** Always "api_key" - token is a drop-in replacement for x-api-key header */ + fun tokenType(tokenType: String) = tokenType(JsonField.of(tokenType)) + + /** + * Sets [Builder.tokenType] to an arbitrary JSON value. + * + * You should usually call [Builder.tokenType] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun tokenType(tokenType: JsonField) = apply { this.tokenType = tokenType } + + 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 [AccessTokenCreateResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AccessTokenCreateResponse = + AccessTokenCreateResponse( + accessToken, + expiresIn, + tokenType, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): AccessTokenCreateResponse = apply { + if (validated) { + return@apply + } + + accessToken() + expiresIn() + tokenType() + 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 (accessToken.asKnown().isPresent) 1 else 0) + + (if (expiresIn.asKnown().isPresent) 1 else 0) + + (if (tokenType.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AccessTokenCreateResponse && + accessToken == other.accessToken && + expiresIn == other.expiresIn && + tokenType == other.tokenType && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accessToken, expiresIn, tokenType, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AccessTokenCreateResponse{accessToken=$accessToken, expiresIn=$expiresIn, tokenType=$tokenType, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/CamsKfintechParseParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/CamsKfintechParseParams.kt new file mode 100644 index 0000000..20c4db0 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/CamsKfintechParseParams.kt @@ -0,0 +1,512 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +import com.cas_parser.api.core.ExcludeMissing +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.MultipartField +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +import com.cas_parser.api.errors.CasParserInvalidDataException +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonProperty +import java.util.Collections +import java.util.Objects +import java.util.Optional + +/** + * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF files + * and returns data in a unified format. Use this endpoint when you know the PDF is from CAMS or + * KFintech. + */ +class CamsKfintechParseParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun password(): Optional = body.password() + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfFile(): Optional = body.pdfFile() + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfUrl(): Optional = body.pdfUrl() + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _password(): MultipartField = body._password() + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfFile(): MultipartField = body._pdfFile() + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfUrl(): MultipartField = body._pdfUrl() + + 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 { + + @JvmStatic fun none(): CamsKfintechParseParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [CamsKfintechParseParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CamsKfintechParseParams]. */ + 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(camsKfintechParseParams: CamsKfintechParseParams) = apply { + body = camsKfintechParseParams.body.toBuilder() + additionalHeaders = camsKfintechParseParams.additionalHeaders.toBuilder() + additionalQueryParams = camsKfintechParseParams.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: + * - [password] + * - [pdfFile] + * - [pdfUrl] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Password for the PDF file (if required) */ + fun password(password: String) = apply { body.password(password) } + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { body.password(password) } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = apply { body.pdfFile(pdfFile) } + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { body.pdfFile(pdfFile) } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = apply { body.pdfUrl(pdfUrl) } + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { body.pdfUrl(pdfUrl) } + + 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 [CamsKfintechParseParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CamsKfintechParseParams = + CamsKfintechParseParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Map> = + (mapOf("password" to _password(), "pdf_file" to _pdfFile(), "pdf_url" to _pdfUrl()) + + _additionalBodyProperties().mapValues { (_, value) -> MultipartField.of(value) }) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** Provide either `pdf_file` OR `pdf_url` (one is required) */ + class Body + private constructor( + private val password: MultipartField, + private val pdfFile: MultipartField, + private val pdfUrl: MultipartField, + private val additionalProperties: MutableMap, + ) { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun password(): Optional = password.value.getOptional("password") + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfFile(): Optional = pdfFile.value.getOptional("pdf_file") + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfUrl(): Optional = pdfUrl.value.getOptional("pdf_url") + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("password") @ExcludeMissing fun _password(): MultipartField = password + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("pdf_file") @ExcludeMissing fun _pdfFile(): MultipartField = pdfFile + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + @JsonProperty("pdf_url") @ExcludeMissing fun _pdfUrl(): MultipartField = pdfUrl + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var password: MultipartField = MultipartField.of(null) + private var pdfFile: MultipartField = MultipartField.of(null) + private var pdfUrl: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + password = body.password + pdfFile = body.pdfFile + pdfUrl = body.pdfUrl + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Password for the PDF file (if required) */ + fun password(password: String) = password(MultipartField.of(password)) + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { this.password = password } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = pdfFile(MultipartField.of(pdfFile)) + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { this.pdfFile = pdfFile } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = pdfUrl(MultipartField.of(pdfUrl)) + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { this.pdfUrl = pdfUrl } + + 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. + */ + fun build(): Body = Body(password, pdfFile, pdfUrl, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + password() + pdfFile() + pdfUrl() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CasParserInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + password == other.password && + pdfFile == other.pdfFile && + pdfUrl == other.pdfUrl && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(password, pdfFile, pdfUrl, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{password=$password, pdfFile=$pdfFile, pdfUrl=$pdfUrl, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CamsKfintechParseParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "CamsKfintechParseParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/LinkedHolder.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/LinkedHolder.kt new file mode 100644 index 0000000..4ccd215 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/LinkedHolder.kt @@ -0,0 +1,197 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +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 LinkedHolder +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val name: JsonField, + private val pan: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("pan") @ExcludeMissing pan: JsonField = JsonMissing.of(), + ) : this(name, pan, mutableMapOf()) + + /** + * Name of the account holder + * + * @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") + + /** + * PAN of the account holder + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pan(): Optional = pan.getOptional("pan") + + /** + * 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 + + /** + * Returns the raw JSON value of [pan]. + * + * Unlike [pan], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pan") @ExcludeMissing fun _pan(): JsonField = pan + + @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 [LinkedHolder]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LinkedHolder]. */ + class Builder internal constructor() { + + private var name: JsonField = JsonMissing.of() + private var pan: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(linkedHolder: LinkedHolder) = apply { + name = linkedHolder.name + pan = linkedHolder.pan + additionalProperties = linkedHolder.additionalProperties.toMutableMap() + } + + /** Name of the account holder */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** PAN of the account holder */ + fun pan(pan: String) = pan(JsonField.of(pan)) + + /** + * Sets [Builder.pan] to an arbitrary JSON value. + * + * You should usually call [Builder.pan] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pan(pan: JsonField) = apply { this.pan = pan } + + 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 [LinkedHolder]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): LinkedHolder = LinkedHolder(name, pan, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): LinkedHolder = apply { + if (validated) { + return@apply + } + + name() + pan() + 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 (name.asKnown().isPresent) 1 else 0) + (if (pan.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LinkedHolder && + name == other.name && + pan == other.pan && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(name, pan, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "LinkedHolder{name=$name, pan=$pan, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/Transaction.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/Transaction.kt new file mode 100644 index 0000000..94bec94 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/Transaction.kt @@ -0,0 +1,1155 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +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.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.time.LocalDate +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** 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(), + ) + + /** + * Additional transaction-specific fields that vary by source + * + * @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") + + /** + * 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") + + /** + * Balance units after transaction + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun balance(): Optional = balance.getOptional("balance") + + /** + * Transaction date (YYYY-MM-DD) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun date(): Optional = date.getOptional("date") + + /** + * Transaction description/particulars + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun description(): Optional = description.getOptional("description") + + /** + * Dividend rate (for DIVIDEND_PAYOUT transactions) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun dividendRate(): Optional = dividendRate.getOptional("dividend_rate") + + /** + * NAV/price per unit on transaction date + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun nav(): Optional = nav.getOptional("nav") + + /** + * Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION, SWITCH_IN, + * SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT, DIVIDEND_REINVEST, + * SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC, REVERSAL, UNKNOWN. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Number of units involved in transaction + * + * @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") + + /** + * 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 [amount]. + * + * Unlike [amount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amount") @ExcludeMissing fun _amount(): JsonField = amount + + /** + * Returns the raw JSON value of [balance]. + * + * Unlike [balance], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("balance") @ExcludeMissing fun _balance(): JsonField = balance + + /** + * Returns the raw JSON value of [date]. + * + * Unlike [date], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("date") @ExcludeMissing fun _date(): JsonField = date + + /** + * Returns the raw JSON value of [description]. + * + * Unlike [description], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("description") @ExcludeMissing fun _description(): JsonField = description + + /** + * Returns the raw JSON value of [dividendRate]. + * + * Unlike [dividendRate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dividend_rate") + @ExcludeMissing + fun _dividendRate(): JsonField = dividendRate + + /** + * Returns the raw JSON value of [nav]. + * + * Unlike [nav], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nav") @ExcludeMissing fun _nav(): JsonField = nav + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + /** + * 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 + + @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 [Transaction]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Transaction]. */ + class Builder internal constructor() { + + private var additionalInfo: JsonField = JsonMissing.of() + private var amount: JsonField = JsonMissing.of() + private var balance: JsonField = JsonMissing.of() + private var date: JsonField = JsonMissing.of() + private var description: JsonField = JsonMissing.of() + private var dividendRate: JsonField = JsonMissing.of() + private var nav: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var units: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(transaction: Transaction) = apply { + additionalInfo = transaction.additionalInfo + amount = transaction.amount + balance = transaction.balance + date = transaction.date + description = transaction.description + dividendRate = transaction.dividendRate + nav = transaction.nav + type = transaction.type + units = transaction.units + additionalProperties = transaction.additionalProperties.toMutableMap() + } + + /** Additional transaction-specific fields that vary by source */ + 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 + } + + /** Transaction amount in currency (computed from units × price/NAV) */ + fun amount(amount: Float?) = amount(JsonField.ofNullable(amount)) + + /** + * Alias for [Builder.amount]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun amount(amount: Float) = amount(amount as Float?) + + /** Alias for calling [Builder.amount] with `amount.orElse(null)`. */ + fun amount(amount: Optional) = amount(amount.getOrNull()) + + /** + * Sets [Builder.amount] to an arbitrary JSON value. + * + * You should usually call [Builder.amount] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun amount(amount: JsonField) = apply { this.amount = amount } + + /** Balance units after transaction */ + fun balance(balance: Float) = balance(JsonField.of(balance)) + + /** + * Sets [Builder.balance] to an arbitrary JSON value. + * + * You should usually call [Builder.balance] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun balance(balance: JsonField) = apply { this.balance = balance } + + /** Transaction date (YYYY-MM-DD) */ + fun date(date: LocalDate) = date(JsonField.of(date)) + + /** + * Sets [Builder.date] to an arbitrary JSON value. + * + * You should usually call [Builder.date] with a well-typed [LocalDate] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun date(date: JsonField) = apply { this.date = date } + + /** Transaction description/particulars */ + fun description(description: String) = description(JsonField.of(description)) + + /** + * Sets [Builder.description] to an arbitrary JSON value. + * + * You should usually call [Builder.description] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun description(description: JsonField) = apply { this.description = description } + + /** Dividend rate (for DIVIDEND_PAYOUT transactions) */ + fun dividendRate(dividendRate: Float?) = dividendRate(JsonField.ofNullable(dividendRate)) + + /** + * Alias for [Builder.dividendRate]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun dividendRate(dividendRate: Float) = dividendRate(dividendRate as Float?) + + /** Alias for calling [Builder.dividendRate] with `dividendRate.orElse(null)`. */ + fun dividendRate(dividendRate: Optional) = dividendRate(dividendRate.getOrNull()) + + /** + * Sets [Builder.dividendRate] to an arbitrary JSON value. + * + * You should usually call [Builder.dividendRate] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun dividendRate(dividendRate: JsonField) = apply { + this.dividendRate = dividendRate + } + + /** NAV/price per unit on transaction date */ + fun nav(nav: Float?) = nav(JsonField.ofNullable(nav)) + + /** + * Alias for [Builder.nav]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun nav(nav: Float) = nav(nav as Float?) + + /** Alias for calling [Builder.nav] with `nav.orElse(null)`. */ + fun nav(nav: Optional) = nav(nav.getOrNull()) + + /** + * Sets [Builder.nav] to an arbitrary JSON value. + * + * You should usually call [Builder.nav] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun nav(nav: JsonField) = apply { this.nav = nav } + + /** + * Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION, SWITCH_IN, + * SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT, DIVIDEND_REINVEST, + * SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC, REVERSAL, UNKNOWN. + */ + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun type(type: JsonField) = apply { this.type = type } + + /** Number of units involved in transaction */ + fun units(units: Float) = units(JsonField.of(units)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + 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 [Transaction]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Transaction = + Transaction( + additionalInfo, + amount, + balance, + date, + description, + dividendRate, + nav, + type, + units, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Transaction = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + amount() + balance() + date() + description() + dividendRate() + nav() + type().ifPresent { it.validate() } + units() + 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 = + (additionalInfo.asKnown().getOrNull()?.validity() ?: 0) + + (if (amount.asKnown().isPresent) 1 else 0) + + (if (balance.asKnown().isPresent) 1 else 0) + + (if (date.asKnown().isPresent) 1 else 0) + + (if (description.asKnown().isPresent) 1 else 0) + + (if (dividendRate.asKnown().isPresent) 1 else 0) + + (if (nav.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (units.asKnown().isPresent) 1 else 0) + + /** Additional transaction-specific fields that vary by source */ + class AdditionalInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val capitalWithdrawal: JsonField, + private val credit: JsonField, + private val debit: JsonField, + private val incomeDistribution: JsonField, + private val orderNo: JsonField, + private val price: JsonField, + private val stampDuty: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("capital_withdrawal") + @ExcludeMissing + capitalWithdrawal: JsonField = JsonMissing.of(), + @JsonProperty("credit") @ExcludeMissing credit: JsonField = JsonMissing.of(), + @JsonProperty("debit") @ExcludeMissing debit: JsonField = JsonMissing.of(), + @JsonProperty("income_distribution") + @ExcludeMissing + incomeDistribution: JsonField = JsonMissing.of(), + @JsonProperty("order_no") @ExcludeMissing orderNo: JsonField = JsonMissing.of(), + @JsonProperty("price") @ExcludeMissing price: JsonField = JsonMissing.of(), + @JsonProperty("stamp_duty") + @ExcludeMissing + stampDuty: JsonField = JsonMissing.of(), + ) : this( + capitalWithdrawal, + credit, + debit, + incomeDistribution, + orderNo, + price, + stampDuty, + mutableMapOf(), + ) + + /** + * Capital withdrawal amount (CDSL MF transactions) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun capitalWithdrawal(): Optional = + capitalWithdrawal.getOptional("capital_withdrawal") + + /** + * Units credited (demat transactions) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun credit(): Optional = credit.getOptional("credit") + + /** + * Units debited (demat transactions) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun debit(): Optional = debit.getOptional("debit") + + /** + * Income distribution amount (CDSL MF transactions) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun incomeDistribution(): Optional = + incomeDistribution.getOptional("income_distribution") + + /** + * Order/transaction reference number (demat transactions) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun orderNo(): Optional = orderNo.getOptional("order_no") + + /** + * Price per unit (NSDL/CDSL MF transactions) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun price(): Optional = price.getOptional("price") + + /** + * Stamp duty charged + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun stampDuty(): Optional = stampDuty.getOptional("stamp_duty") + + /** + * Returns the raw JSON value of [capitalWithdrawal]. + * + * Unlike [capitalWithdrawal], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("capital_withdrawal") + @ExcludeMissing + fun _capitalWithdrawal(): JsonField = capitalWithdrawal + + /** + * Returns the raw JSON value of [credit]. + * + * Unlike [credit], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("credit") @ExcludeMissing fun _credit(): JsonField = credit + + /** + * Returns the raw JSON value of [debit]. + * + * Unlike [debit], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("debit") @ExcludeMissing fun _debit(): JsonField = debit + + /** + * Returns the raw JSON value of [incomeDistribution]. + * + * Unlike [incomeDistribution], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("income_distribution") + @ExcludeMissing + fun _incomeDistribution(): JsonField = incomeDistribution + + /** + * Returns the raw JSON value of [orderNo]. + * + * Unlike [orderNo], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("order_no") @ExcludeMissing fun _orderNo(): JsonField = orderNo + + /** + * Returns the raw JSON value of [price]. + * + * Unlike [price], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("price") @ExcludeMissing fun _price(): JsonField = price + + /** + * Returns the raw JSON value of [stampDuty]. + * + * Unlike [stampDuty], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("stamp_duty") @ExcludeMissing fun _stampDuty(): JsonField = stampDuty + + @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 [AdditionalInfo]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var capitalWithdrawal: JsonField = JsonMissing.of() + private var credit: JsonField = JsonMissing.of() + private var debit: JsonField = JsonMissing.of() + private var incomeDistribution: JsonField = JsonMissing.of() + private var orderNo: JsonField = JsonMissing.of() + private var price: JsonField = JsonMissing.of() + private var stampDuty: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + capitalWithdrawal = additionalInfo.capitalWithdrawal + credit = additionalInfo.credit + debit = additionalInfo.debit + incomeDistribution = additionalInfo.incomeDistribution + orderNo = additionalInfo.orderNo + price = additionalInfo.price + stampDuty = additionalInfo.stampDuty + additionalProperties = additionalInfo.additionalProperties.toMutableMap() + } + + /** Capital withdrawal amount (CDSL MF transactions) */ + fun capitalWithdrawal(capitalWithdrawal: Float) = + capitalWithdrawal(JsonField.of(capitalWithdrawal)) + + /** + * Sets [Builder.capitalWithdrawal] to an arbitrary JSON value. + * + * You should usually call [Builder.capitalWithdrawal] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun capitalWithdrawal(capitalWithdrawal: JsonField) = apply { + this.capitalWithdrawal = capitalWithdrawal + } + + /** Units credited (demat transactions) */ + fun credit(credit: Float) = credit(JsonField.of(credit)) + + /** + * Sets [Builder.credit] to an arbitrary JSON value. + * + * You should usually call [Builder.credit] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun credit(credit: JsonField) = apply { this.credit = credit } + + /** Units debited (demat transactions) */ + fun debit(debit: Float) = debit(JsonField.of(debit)) + + /** + * Sets [Builder.debit] to an arbitrary JSON value. + * + * You should usually call [Builder.debit] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun debit(debit: JsonField) = apply { this.debit = debit } + + /** Income distribution amount (CDSL MF transactions) */ + fun incomeDistribution(incomeDistribution: Float) = + incomeDistribution(JsonField.of(incomeDistribution)) + + /** + * Sets [Builder.incomeDistribution] to an arbitrary JSON value. + * + * You should usually call [Builder.incomeDistribution] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun incomeDistribution(incomeDistribution: JsonField) = apply { + this.incomeDistribution = incomeDistribution + } + + /** Order/transaction reference number (demat transactions) */ + fun orderNo(orderNo: String) = orderNo(JsonField.of(orderNo)) + + /** + * Sets [Builder.orderNo] to an arbitrary JSON value. + * + * You should usually call [Builder.orderNo] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun orderNo(orderNo: JsonField) = apply { this.orderNo = orderNo } + + /** Price per unit (NSDL/CDSL MF transactions) */ + fun price(price: Float) = price(JsonField.of(price)) + + /** + * Sets [Builder.price] to an arbitrary JSON value. + * + * You should usually call [Builder.price] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun price(price: JsonField) = apply { this.price = price } + + /** Stamp duty charged */ + fun stampDuty(stampDuty: Float) = stampDuty(JsonField.of(stampDuty)) + + /** + * Sets [Builder.stampDuty] to an arbitrary JSON value. + * + * You should usually call [Builder.stampDuty] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun stampDuty(stampDuty: JsonField) = apply { this.stampDuty = stampDuty } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + capitalWithdrawal, + credit, + debit, + incomeDistribution, + orderNo, + price, + stampDuty, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + capitalWithdrawal() + credit() + debit() + incomeDistribution() + orderNo() + price() + stampDuty() + 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 (capitalWithdrawal.asKnown().isPresent) 1 else 0) + + (if (credit.asKnown().isPresent) 1 else 0) + + (if (debit.asKnown().isPresent) 1 else 0) + + (if (incomeDistribution.asKnown().isPresent) 1 else 0) + + (if (orderNo.asKnown().isPresent) 1 else 0) + + (if (price.asKnown().isPresent) 1 else 0) + + (if (stampDuty.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AdditionalInfo && + capitalWithdrawal == other.capitalWithdrawal && + credit == other.credit && + debit == other.debit && + incomeDistribution == other.incomeDistribution && + orderNo == other.orderNo && + price == other.price && + stampDuty == other.stampDuty && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + capitalWithdrawal, + credit, + debit, + incomeDistribution, + orderNo, + price, + stampDuty, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AdditionalInfo{capitalWithdrawal=$capitalWithdrawal, credit=$credit, debit=$debit, incomeDistribution=$incomeDistribution, orderNo=$orderNo, price=$price, stampDuty=$stampDuty, additionalProperties=$additionalProperties}" + } + + /** + * Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION, SWITCH_IN, + * SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT, DIVIDEND_REINVEST, + * SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC, REVERSAL, UNKNOWN. + */ + class Type @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 PURCHASE = of("PURCHASE") + + @JvmField val PURCHASE_SIP = of("PURCHASE_SIP") + + @JvmField val REDEMPTION = of("REDEMPTION") + + @JvmField val SWITCH_IN = of("SWITCH_IN") + + @JvmField val SWITCH_IN_MERGER = of("SWITCH_IN_MERGER") + + @JvmField val SWITCH_OUT = of("SWITCH_OUT") + + @JvmField val SWITCH_OUT_MERGER = of("SWITCH_OUT_MERGER") + + @JvmField val DIVIDEND_PAYOUT = of("DIVIDEND_PAYOUT") + + @JvmField val DIVIDEND_REINVEST = of("DIVIDEND_REINVEST") + + @JvmField val SEGREGATION = of("SEGREGATION") + + @JvmField val STAMP_DUTY_TAX = of("STAMP_DUTY_TAX") + + @JvmField val TDS_TAX = of("TDS_TAX") + + @JvmField val STT_TAX = of("STT_TAX") + + @JvmField val MISC = of("MISC") + + @JvmField val REVERSAL = of("REVERSAL") + + @JvmField val UNKNOWN = of("UNKNOWN") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + PURCHASE, + PURCHASE_SIP, + REDEMPTION, + SWITCH_IN, + SWITCH_IN_MERGER, + SWITCH_OUT, + SWITCH_OUT_MERGER, + DIVIDEND_PAYOUT, + DIVIDEND_REINVEST, + SEGREGATION, + STAMP_DUTY_TAX, + TDS_TAX, + STT_TAX, + MISC, + REVERSAL, + UNKNOWN, + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] 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 { + PURCHASE, + PURCHASE_SIP, + REDEMPTION, + SWITCH_IN, + SWITCH_IN_MERGER, + SWITCH_OUT, + SWITCH_OUT_MERGER, + DIVIDEND_PAYOUT, + DIVIDEND_REINVEST, + SEGREGATION, + STAMP_DUTY_TAX, + TDS_TAX, + STT_TAX, + MISC, + REVERSAL, + UNKNOWN, + /** An enum member indicating that [Type] 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) { + PURCHASE -> Value.PURCHASE + PURCHASE_SIP -> Value.PURCHASE_SIP + REDEMPTION -> Value.REDEMPTION + SWITCH_IN -> Value.SWITCH_IN + SWITCH_IN_MERGER -> Value.SWITCH_IN_MERGER + SWITCH_OUT -> Value.SWITCH_OUT + SWITCH_OUT_MERGER -> Value.SWITCH_OUT_MERGER + DIVIDEND_PAYOUT -> Value.DIVIDEND_PAYOUT + DIVIDEND_REINVEST -> Value.DIVIDEND_REINVEST + SEGREGATION -> Value.SEGREGATION + STAMP_DUTY_TAX -> Value.STAMP_DUTY_TAX + TDS_TAX -> Value.TDS_TAX + STT_TAX -> Value.STT_TAX + MISC -> Value.MISC + REVERSAL -> Value.REVERSAL + UNKNOWN -> Value.UNKNOWN + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and don't + * want to throw for the unknown case. + * + * @throws CasParserInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + PURCHASE -> Known.PURCHASE + PURCHASE_SIP -> Known.PURCHASE_SIP + REDEMPTION -> Known.REDEMPTION + SWITCH_IN -> Known.SWITCH_IN + SWITCH_IN_MERGER -> Known.SWITCH_IN_MERGER + SWITCH_OUT -> Known.SWITCH_OUT + SWITCH_OUT_MERGER -> Known.SWITCH_OUT_MERGER + DIVIDEND_PAYOUT -> Known.DIVIDEND_PAYOUT + DIVIDEND_REINVEST -> Known.DIVIDEND_REINVEST + SEGREGATION -> Known.SEGREGATION + STAMP_DUTY_TAX -> Known.STAMP_DUTY_TAX + TDS_TAX -> Known.TDS_TAX + STT_TAX -> Known.STT_TAX + MISC -> Known.MISC + REVERSAL -> Known.REVERSAL + UNKNOWN -> Known.UNKNOWN + else -> throw CasParserInvalidDataException("Unknown Type: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Type = 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 Type && 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 Transaction && + additionalInfo == other.additionalInfo && + amount == other.amount && + balance == other.balance && + date == other.date && + description == other.description && + dividendRate == other.dividendRate && + nav == other.nav && + type == other.type && + units == other.units && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + amount, + balance, + date, + description, + dividendRate, + nav, + type, + units, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Transaction{additionalInfo=$additionalInfo, amount=$amount, balance=$balance, date=$date, description=$description, dividendRate=$dividendRate, nav=$nav, type=$type, units=$units, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/UnifiedResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/UnifiedResponse.kt new file mode 100644 index 0000000..cceadd0 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/camskfintech/UnifiedResponse.kt @@ -0,0 +1,10986 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.LocalDate +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +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, +) { + + @JsonCreator + private constructor( + @JsonProperty("demat_accounts") + @ExcludeMissing + dematAccounts: JsonField> = JsonMissing.of(), + @JsonProperty("insurance") + @ExcludeMissing + insurance: JsonField = JsonMissing.of(), + @JsonProperty("investor") @ExcludeMissing investor: JsonField = JsonMissing.of(), + @JsonProperty("meta") @ExcludeMissing meta: JsonField = JsonMissing.of(), + @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, nps, summary, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun dematAccounts(): Optional> = dematAccounts.getOptional("demat_accounts") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun insurance(): Optional = insurance.getOptional("insurance") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun investor(): Optional = investor.getOptional("investor") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun meta(): Optional = meta.getOptional("meta") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + 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). + */ + fun summary(): Optional = summary.getOptional("summary") + + /** + * Returns the raw JSON value of [dematAccounts]. + * + * Unlike [dematAccounts], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("demat_accounts") + @ExcludeMissing + fun _dematAccounts(): JsonField> = dematAccounts + + /** + * Returns the raw JSON value of [insurance]. + * + * Unlike [insurance], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("insurance") @ExcludeMissing fun _insurance(): JsonField = insurance + + /** + * Returns the raw JSON value of [investor]. + * + * Unlike [investor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("investor") @ExcludeMissing fun _investor(): JsonField = investor + + /** + * Returns the raw JSON value of [meta]. + * + * Unlike [meta], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("meta") @ExcludeMissing fun _meta(): JsonField = meta + + /** + * Returns the raw JSON value of [mutualFunds]. + * + * Unlike [mutualFunds], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("mutual_funds") + @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]. + * + * Unlike [summary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("summary") @ExcludeMissing fun _summary(): JsonField = summary + + @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 [UnifiedResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnifiedResponse]. */ + class Builder internal constructor() { + + private var dematAccounts: JsonField>? = null + private var insurance: JsonField = JsonMissing.of() + 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() + + @JvmSynthetic + internal fun from(unifiedResponse: UnifiedResponse) = apply { + dematAccounts = unifiedResponse.dematAccounts.map { it.toMutableList() } + insurance = unifiedResponse.insurance + 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() + } + + fun dematAccounts(dematAccounts: List) = + dematAccounts(JsonField.of(dematAccounts)) + + /** + * Sets [Builder.dematAccounts] to an arbitrary JSON value. + * + * You should usually call [Builder.dematAccounts] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun dematAccounts(dematAccounts: JsonField>) = apply { + this.dematAccounts = dematAccounts.map { it.toMutableList() } + } + + /** + * Adds a single [DematAccount] to [dematAccounts]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addDematAccount(dematAccount: DematAccount) = apply { + dematAccounts = + (dematAccounts ?: JsonField.of(mutableListOf())).also { + checkKnown("dematAccounts", it).add(dematAccount) + } + } + + fun insurance(insurance: Insurance) = insurance(JsonField.of(insurance)) + + /** + * Sets [Builder.insurance] to an arbitrary JSON value. + * + * You should usually call [Builder.insurance] with a well-typed [Insurance] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun insurance(insurance: JsonField) = apply { this.insurance = insurance } + + fun investor(investor: Investor) = investor(JsonField.of(investor)) + + /** + * Sets [Builder.investor] to an arbitrary JSON value. + * + * You should usually call [Builder.investor] with a well-typed [Investor] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun investor(investor: JsonField) = apply { this.investor = investor } + + fun meta(meta: Meta) = meta(JsonField.of(meta)) + + /** + * Sets [Builder.meta] to an arbitrary JSON value. + * + * You should usually call [Builder.meta] with a well-typed [Meta] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun meta(meta: JsonField) = apply { this.meta = meta } + + fun mutualFunds(mutualFunds: List) = mutualFunds(JsonField.of(mutualFunds)) + + /** + * Sets [Builder.mutualFunds] to an arbitrary JSON value. + * + * You should usually call [Builder.mutualFunds] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun mutualFunds(mutualFunds: JsonField>) = apply { + this.mutualFunds = mutualFunds.map { it.toMutableList() } + } + + /** + * Adds a single [MutualFund] to [mutualFunds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addMutualFund(mutualFund: MutualFund) = apply { + mutualFunds = + (mutualFunds ?: JsonField.of(mutableListOf())).also { + checkKnown("mutualFunds", it).add(mutualFund) + } + } + + /** 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)) + + /** + * Sets [Builder.summary] to an arbitrary JSON value. + * + * You should usually call [Builder.summary] with a well-typed [Summary] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun summary(summary: JsonField) = apply { this.summary = summary } + + 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 [UnifiedResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UnifiedResponse = + UnifiedResponse( + (dematAccounts ?: JsonMissing.of()).map { it.toImmutable() }, + insurance, + investor, + meta, + (mutualFunds ?: JsonMissing.of()).map { it.toImmutable() }, + (nps ?: JsonMissing.of()).map { it.toImmutable() }, + summary, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): UnifiedResponse = apply { + if (validated) { + return@apply + } + + dematAccounts().ifPresent { it.forEach { it.validate() } } + insurance().ifPresent { it.validate() } + 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 + } + + 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 = + (dematAccounts.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (insurance.asKnown().getOrNull()?.validity() ?: 0) + + (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, + private val clientId: JsonField, + private val dematType: JsonField, + private val dpId: JsonField, + private val dpName: JsonField, + private val holdings: JsonField, + private val linkedHolders: JsonField>, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + additionalInfo: JsonField = JsonMissing.of(), + @JsonProperty("bo_id") @ExcludeMissing boId: JsonField = JsonMissing.of(), + @JsonProperty("client_id") + @ExcludeMissing + clientId: JsonField = JsonMissing.of(), + @JsonProperty("demat_type") + @ExcludeMissing + dematType: JsonField = JsonMissing.of(), + @JsonProperty("dp_id") @ExcludeMissing dpId: JsonField = JsonMissing.of(), + @JsonProperty("dp_name") @ExcludeMissing dpName: JsonField = JsonMissing.of(), + @JsonProperty("holdings") + @ExcludeMissing + holdings: JsonField = JsonMissing.of(), + @JsonProperty("linked_holders") + @ExcludeMissing + linkedHolders: JsonField> = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this( + additionalInfo, + boId, + clientId, + dematType, + dpId, + dpName, + holdings, + linkedHolders, + value, + mutableMapOf(), + ) + + /** + * Additional information specific to the demat account type + * + * @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") + + /** + * Beneficiary Owner ID (primarily for CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun boId(): Optional = boId.getOptional("bo_id") + + /** + * Client ID + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun clientId(): Optional = clientId.getOptional("client_id") + + /** + * Type of demat account + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun dematType(): Optional = dematType.getOptional("demat_type") + + /** + * Depository Participant ID + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun dpId(): Optional = dpId.getOptional("dp_id") + + /** + * Depository Participant name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun dpName(): Optional = dpName.getOptional("dp_name") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + 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 + * + * @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") + + /** + * 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 [boId]. + * + * Unlike [boId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bo_id") @ExcludeMissing fun _boId(): JsonField = boId + + /** + * Returns the raw JSON value of [clientId]. + * + * Unlike [clientId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("client_id") @ExcludeMissing fun _clientId(): JsonField = clientId + + /** + * Returns the raw JSON value of [dematType]. + * + * Unlike [dematType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("demat_type") + @ExcludeMissing + fun _dematType(): JsonField = dematType + + /** + * Returns the raw JSON value of [dpId]. + * + * Unlike [dpId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dp_id") @ExcludeMissing fun _dpId(): JsonField = dpId + + /** + * Returns the raw JSON value of [dpName]. + * + * Unlike [dpName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dp_name") @ExcludeMissing fun _dpName(): JsonField = dpName + + /** + * Returns the raw JSON value of [holdings]. + * + * Unlike [holdings], this method doesn't throw if the JSON field has an unexpected type. + */ + @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]. + * + * Unlike [value], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value + + @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 [DematAccount]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DematAccount]. */ + class Builder internal constructor() { + + private var additionalInfo: JsonField = JsonMissing.of() + private var boId: JsonField = JsonMissing.of() + private var clientId: JsonField = JsonMissing.of() + private var dematType: JsonField = JsonMissing.of() + 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() + + @JvmSynthetic + internal fun from(dematAccount: DematAccount) = apply { + additionalInfo = dematAccount.additionalInfo + boId = dematAccount.boId + clientId = dematAccount.clientId + dematType = dematAccount.dematType + dpId = dematAccount.dpId + dpName = dematAccount.dpName + holdings = dematAccount.holdings + linkedHolders = dematAccount.linkedHolders.map { it.toMutableList() } + value = dematAccount.value + additionalProperties = dematAccount.additionalProperties.toMutableMap() + } + + /** Additional information specific to the demat account type */ + 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 + } + + /** Beneficiary Owner ID (primarily for CDSL) */ + fun boId(boId: String) = boId(JsonField.of(boId)) + + /** + * Sets [Builder.boId] to an arbitrary JSON value. + * + * You should usually call [Builder.boId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun boId(boId: JsonField) = apply { this.boId = boId } + + /** Client ID */ + fun clientId(clientId: String) = clientId(JsonField.of(clientId)) + + /** + * Sets [Builder.clientId] to an arbitrary JSON value. + * + * You should usually call [Builder.clientId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun clientId(clientId: JsonField) = apply { this.clientId = clientId } + + /** Type of demat account */ + fun dematType(dematType: DematType) = dematType(JsonField.of(dematType)) + + /** + * Sets [Builder.dematType] to an arbitrary JSON value. + * + * You should usually call [Builder.dematType] with a well-typed [DematType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dematType(dematType: JsonField) = apply { this.dematType = dematType } + + /** Depository Participant ID */ + fun dpId(dpId: String) = dpId(JsonField.of(dpId)) + + /** + * Sets [Builder.dpId] to an arbitrary JSON value. + * + * You should usually call [Builder.dpId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun dpId(dpId: JsonField) = apply { this.dpId = dpId } + + /** Depository Participant name */ + fun dpName(dpName: String) = dpName(JsonField.of(dpName)) + + /** + * Sets [Builder.dpName] to an arbitrary JSON value. + * + * You should usually call [Builder.dpName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dpName(dpName: JsonField) = apply { this.dpName = dpName } + + fun holdings(holdings: Holdings) = holdings(JsonField.of(holdings)) + + /** + * Sets [Builder.holdings] to an arbitrary JSON value. + * + * You should usually call [Builder.holdings] with a well-typed [Holdings] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + 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)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [DematAccount]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): DematAccount = + DematAccount( + additionalInfo, + boId, + clientId, + dematType, + dpId, + dpName, + holdings, + (linkedHolders ?: JsonMissing.of()).map { it.toImmutable() }, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): DematAccount = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + boId() + clientId() + dematType().ifPresent { it.validate() } + dpId() + dpName() + holdings().ifPresent { it.validate() } + linkedHolders().ifPresent { it.forEach { it.validate() } } + value() + 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 = + (additionalInfo.asKnown().getOrNull()?.validity() ?: 0) + + (if (boId.asKnown().isPresent) 1 else 0) + + (if (clientId.asKnown().isPresent) 1 else 0) + + (dematType.asKnown().getOrNull()?.validity() ?: 0) + + (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, + private val boType: JsonField, + private val bsda: JsonField, + private val email: JsonField, + private val linkedPans: JsonField>, + private val nominee: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("bo_status") + @ExcludeMissing + boStatus: JsonField = JsonMissing.of(), + @JsonProperty("bo_sub_status") + @ExcludeMissing + boSubStatus: JsonField = JsonMissing.of(), + @JsonProperty("bo_type") + @ExcludeMissing + boType: JsonField = JsonMissing.of(), + @JsonProperty("bsda") @ExcludeMissing bsda: JsonField = JsonMissing.of(), + @JsonProperty("email") @ExcludeMissing email: JsonField = JsonMissing.of(), + @JsonProperty("linked_pans") + @ExcludeMissing + linkedPans: JsonField> = JsonMissing.of(), + @JsonProperty("nominee") + @ExcludeMissing + nominee: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this( + boStatus, + boSubStatus, + boType, + bsda, + email, + linkedPans, + nominee, + status, + mutableMapOf(), + ) + + /** + * Beneficiary Owner status (CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun boStatus(): Optional = boStatus.getOptional("bo_status") + + /** + * Beneficiary Owner sub-status (CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun boSubStatus(): Optional = boSubStatus.getOptional("bo_sub_status") + + /** + * Beneficiary Owner type (CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun boType(): Optional = boType.getOptional("bo_type") + + /** + * Basic Services Demat Account status (CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun bsda(): Optional = bsda.getOptional("bsda") + + /** + * Email associated with the demat account (CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun email(): Optional = email.getOptional("email") + + /** + * List of linked PAN numbers (NSDL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun linkedPans(): Optional> = linkedPans.getOptional("linked_pans") + + /** + * Nominee details (CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun nominee(): Optional = nominee.getOptional("nominee") + + /** + * Account status (CDSL) + * + * @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 [boStatus]. + * + * Unlike [boStatus], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("bo_status") @ExcludeMissing fun _boStatus(): JsonField = boStatus + + /** + * Returns the raw JSON value of [boSubStatus]. + * + * Unlike [boSubStatus], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("bo_sub_status") + @ExcludeMissing + fun _boSubStatus(): JsonField = boSubStatus + + /** + * Returns the raw JSON value of [boType]. + * + * Unlike [boType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bo_type") @ExcludeMissing fun _boType(): JsonField = boType + + /** + * Returns the raw JSON value of [bsda]. + * + * Unlike [bsda], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bsda") @ExcludeMissing fun _bsda(): JsonField = bsda + + /** + * 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 [linkedPans]. + * + * Unlike [linkedPans], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("linked_pans") + @ExcludeMissing + fun _linkedPans(): JsonField> = linkedPans + + /** + * Returns the raw JSON value of [nominee]. + * + * Unlike [nominee], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nominee") @ExcludeMissing fun _nominee(): JsonField = nominee + + /** + * 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 [AdditionalInfo]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var boStatus: JsonField = JsonMissing.of() + private var boSubStatus: JsonField = JsonMissing.of() + private var boType: JsonField = JsonMissing.of() + private var bsda: JsonField = JsonMissing.of() + private var email: JsonField = JsonMissing.of() + private var linkedPans: JsonField>? = null + private var nominee: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + boStatus = additionalInfo.boStatus + boSubStatus = additionalInfo.boSubStatus + boType = additionalInfo.boType + bsda = additionalInfo.bsda + email = additionalInfo.email + linkedPans = additionalInfo.linkedPans.map { it.toMutableList() } + nominee = additionalInfo.nominee + status = additionalInfo.status + additionalProperties = additionalInfo.additionalProperties.toMutableMap() + } + + /** Beneficiary Owner status (CDSL) */ + fun boStatus(boStatus: String) = boStatus(JsonField.of(boStatus)) + + /** + * Sets [Builder.boStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.boStatus] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun boStatus(boStatus: JsonField) = apply { this.boStatus = boStatus } + + /** Beneficiary Owner sub-status (CDSL) */ + fun boSubStatus(boSubStatus: String) = boSubStatus(JsonField.of(boSubStatus)) + + /** + * Sets [Builder.boSubStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.boSubStatus] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun boSubStatus(boSubStatus: JsonField) = apply { + this.boSubStatus = boSubStatus + } + + /** Beneficiary Owner type (CDSL) */ + fun boType(boType: String) = boType(JsonField.of(boType)) + + /** + * Sets [Builder.boType] to an arbitrary JSON value. + * + * You should usually call [Builder.boType] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun boType(boType: JsonField) = apply { this.boType = boType } + + /** Basic Services Demat Account status (CDSL) */ + fun bsda(bsda: String) = bsda(JsonField.of(bsda)) + + /** + * Sets [Builder.bsda] to an arbitrary JSON value. + * + * You should usually call [Builder.bsda] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bsda(bsda: JsonField) = apply { this.bsda = bsda } + + /** Email associated with the demat account (CDSL) */ + 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 } + + /** List of linked PAN numbers (NSDL) */ + fun linkedPans(linkedPans: List) = linkedPans(JsonField.of(linkedPans)) + + /** + * Sets [Builder.linkedPans] to an arbitrary JSON value. + * + * You should usually call [Builder.linkedPans] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun linkedPans(linkedPans: JsonField>) = apply { + this.linkedPans = linkedPans.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [linkedPans]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addLinkedPan(linkedPan: String) = apply { + linkedPans = + (linkedPans ?: JsonField.of(mutableListOf())).also { + checkKnown("linkedPans", it).add(linkedPan) + } + } + + /** Nominee details (CDSL) */ + fun nominee(nominee: String) = nominee(JsonField.of(nominee)) + + /** + * Sets [Builder.nominee] to an arbitrary JSON value. + * + * You should usually call [Builder.nominee] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nominee(nominee: JsonField) = apply { this.nominee = nominee } + + /** Account status (CDSL) */ + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + boStatus, + boSubStatus, + boType, + bsda, + email, + (linkedPans ?: JsonMissing.of()).map { it.toImmutable() }, + nominee, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + boStatus() + boSubStatus() + boType() + bsda() + email() + linkedPans() + nominee() + 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 (boStatus.asKnown().isPresent) 1 else 0) + + (if (boSubStatus.asKnown().isPresent) 1 else 0) + + (if (boType.asKnown().isPresent) 1 else 0) + + (if (bsda.asKnown().isPresent) 1 else 0) + + (if (email.asKnown().isPresent) 1 else 0) + + (linkedPans.asKnown().getOrNull()?.size ?: 0) + + (if (nominee.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 AdditionalInfo && + boStatus == other.boStatus && + boSubStatus == other.boSubStatus && + boType == other.boType && + bsda == other.bsda && + email == other.email && + linkedPans == other.linkedPans && + nominee == other.nominee && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + boStatus, + boSubStatus, + boType, + bsda, + email, + linkedPans, + nominee, + status, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AdditionalInfo{boStatus=$boStatus, boSubStatus=$boSubStatus, boType=$boType, bsda=$bsda, email=$email, linkedPans=$linkedPans, nominee=$nominee, status=$status, additionalProperties=$additionalProperties}" + } + + /** Type of demat account */ + class DematType @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 NSDL = of("NSDL") + + @JvmField val CDSL = of("CDSL") + + @JvmStatic fun of(value: String) = DematType(JsonField.of(value)) + } + + /** An enum containing [DematType]'s known values. */ + enum class Known { + NSDL, + CDSL, + } + + /** + * An enum containing [DematType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [DematType] 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 { + NSDL, + CDSL, + /** + * An enum member indicating that [DematType] 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) { + NSDL -> Value.NSDL + CDSL -> Value.CDSL + 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) { + NSDL -> Known.NSDL + CDSL -> Known.CDSL + else -> throw CasParserInvalidDataException("Unknown DematType: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): DematType = 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 DematType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Holdings + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val aifs: JsonField>, + private val corporateBonds: JsonField>, + private val dematMutualFunds: JsonField>, + private val equities: JsonField>, + private val governmentSecurities: JsonField>, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("aifs") @ExcludeMissing aifs: JsonField> = JsonMissing.of(), + @JsonProperty("corporate_bonds") + @ExcludeMissing + corporateBonds: JsonField> = JsonMissing.of(), + @JsonProperty("demat_mutual_funds") + @ExcludeMissing + dematMutualFunds: JsonField> = JsonMissing.of(), + @JsonProperty("equities") + @ExcludeMissing + equities: JsonField> = JsonMissing.of(), + @JsonProperty("government_securities") + @ExcludeMissing + governmentSecurities: JsonField> = JsonMissing.of(), + ) : this( + aifs, + corporateBonds, + dematMutualFunds, + equities, + governmentSecurities, + mutableMapOf(), + ) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun aifs(): Optional> = aifs.getOptional("aifs") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun corporateBonds(): Optional> = + corporateBonds.getOptional("corporate_bonds") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun dematMutualFunds(): Optional> = + dematMutualFunds.getOptional("demat_mutual_funds") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun equities(): Optional> = equities.getOptional("equities") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun governmentSecurities(): Optional> = + governmentSecurities.getOptional("government_securities") + + /** + * Returns the raw JSON value of [aifs]. + * + * Unlike [aifs], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("aifs") @ExcludeMissing fun _aifs(): JsonField> = aifs + + /** + * Returns the raw JSON value of [corporateBonds]. + * + * Unlike [corporateBonds], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("corporate_bonds") + @ExcludeMissing + fun _corporateBonds(): JsonField> = corporateBonds + + /** + * Returns the raw JSON value of [dematMutualFunds]. + * + * Unlike [dematMutualFunds], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("demat_mutual_funds") + @ExcludeMissing + fun _dematMutualFunds(): JsonField> = dematMutualFunds + + /** + * Returns the raw JSON value of [equities]. + * + * Unlike [equities], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("equities") + @ExcludeMissing + fun _equities(): JsonField> = equities + + /** + * Returns the raw JSON value of [governmentSecurities]. + * + * Unlike [governmentSecurities], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("government_securities") + @ExcludeMissing + fun _governmentSecurities(): JsonField> = governmentSecurities + + @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 [Holdings]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Holdings]. */ + class Builder internal constructor() { + + private var aifs: JsonField>? = null + private var corporateBonds: JsonField>? = null + private var dematMutualFunds: JsonField>? = null + private var equities: JsonField>? = null + private var governmentSecurities: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(holdings: Holdings) = apply { + aifs = holdings.aifs.map { it.toMutableList() } + corporateBonds = holdings.corporateBonds.map { it.toMutableList() } + dematMutualFunds = holdings.dematMutualFunds.map { it.toMutableList() } + equities = holdings.equities.map { it.toMutableList() } + governmentSecurities = holdings.governmentSecurities.map { it.toMutableList() } + additionalProperties = holdings.additionalProperties.toMutableMap() + } + + fun aifs(aifs: List) = aifs(JsonField.of(aifs)) + + /** + * Sets [Builder.aifs] to an arbitrary JSON value. + * + * You should usually call [Builder.aifs] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun aifs(aifs: JsonField>) = apply { + this.aifs = aifs.map { it.toMutableList() } + } + + /** + * Adds a single [Aif] to [aifs]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAif(aif: Aif) = apply { + aifs = + (aifs ?: JsonField.of(mutableListOf())).also { + checkKnown("aifs", it).add(aif) + } + } + + fun corporateBonds(corporateBonds: List) = + corporateBonds(JsonField.of(corporateBonds)) + + /** + * Sets [Builder.corporateBonds] to an arbitrary JSON value. + * + * You should usually call [Builder.corporateBonds] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun corporateBonds(corporateBonds: JsonField>) = apply { + this.corporateBonds = corporateBonds.map { it.toMutableList() } + } + + /** + * Adds a single [CorporateBond] to [corporateBonds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addCorporateBond(corporateBond: CorporateBond) = apply { + corporateBonds = + (corporateBonds ?: JsonField.of(mutableListOf())).also { + checkKnown("corporateBonds", it).add(corporateBond) + } + } + + fun dematMutualFunds(dematMutualFunds: List) = + dematMutualFunds(JsonField.of(dematMutualFunds)) + + /** + * Sets [Builder.dematMutualFunds] to an arbitrary JSON value. + * + * You should usually call [Builder.dematMutualFunds] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun dematMutualFunds(dematMutualFunds: JsonField>) = apply { + this.dematMutualFunds = dematMutualFunds.map { it.toMutableList() } + } + + /** + * Adds a single [DematMutualFund] to [dematMutualFunds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addDematMutualFund(dematMutualFund: DematMutualFund) = apply { + dematMutualFunds = + (dematMutualFunds ?: JsonField.of(mutableListOf())).also { + checkKnown("dematMutualFunds", it).add(dematMutualFund) + } + } + + fun equities(equities: List) = equities(JsonField.of(equities)) + + /** + * Sets [Builder.equities] to an arbitrary JSON value. + * + * You should usually call [Builder.equities] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun equities(equities: JsonField>) = apply { + this.equities = equities.map { it.toMutableList() } + } + + /** + * Adds a single [Equity] to [equities]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addEquity(equity: Equity) = apply { + equities = + (equities ?: JsonField.of(mutableListOf())).also { + checkKnown("equities", it).add(equity) + } + } + + fun governmentSecurities(governmentSecurities: List) = + governmentSecurities(JsonField.of(governmentSecurities)) + + /** + * Sets [Builder.governmentSecurities] to an arbitrary JSON value. + * + * You should usually call [Builder.governmentSecurities] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun governmentSecurities( + governmentSecurities: JsonField> + ) = apply { + this.governmentSecurities = governmentSecurities.map { it.toMutableList() } + } + + /** + * Adds a single [GovernmentSecurity] to [governmentSecurities]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addGovernmentSecurity(governmentSecurity: GovernmentSecurity) = apply { + governmentSecurities = + (governmentSecurities ?: JsonField.of(mutableListOf())).also { + checkKnown("governmentSecurities", it).add(governmentSecurity) + } + } + + 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 [Holdings]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Holdings = + Holdings( + (aifs ?: JsonMissing.of()).map { it.toImmutable() }, + (corporateBonds ?: JsonMissing.of()).map { it.toImmutable() }, + (dematMutualFunds ?: JsonMissing.of()).map { it.toImmutable() }, + (equities ?: JsonMissing.of()).map { it.toImmutable() }, + (governmentSecurities ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Holdings = apply { + if (validated) { + return@apply + } + + aifs().ifPresent { it.forEach { it.validate() } } + corporateBonds().ifPresent { it.forEach { it.validate() } } + dematMutualFunds().ifPresent { it.forEach { it.validate() } } + equities().ifPresent { it.forEach { it.validate() } } + governmentSecurities().ifPresent { it.forEach { it.validate() } } + 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 = + (aifs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (corporateBonds.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (dematMutualFunds.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (equities.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (governmentSecurities.asKnown().getOrNull()?.sumOf { it.validity().toInt() } + ?: 0) + + class Aif + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + 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, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + 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, transactions, units, value, mutableMapOf()) + + /** + * 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 + * + * @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") + + /** + * Name of the AIF + * + * @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") + + /** + * 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 + * + * @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") + + /** + * 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") + + /** + * 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]. + * + * Unlike [isin], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("isin") @ExcludeMissing fun _isin(): JsonField = isin + + /** + * 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 + + /** + * 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]. + * + * Unlike [units], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("units") @ExcludeMissing fun _units(): JsonField = units + + /** + * 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 + + @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 [Aif]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Aif]. */ + class Builder internal constructor() { + + 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() + + @JvmSynthetic + internal fun from(aif: Aif) = apply { + 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: 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 + } + + /** ISIN code of the AIF */ + fun isin(isin: String) = isin(JsonField.of(isin)) + + /** + * Sets [Builder.isin] to an arbitrary JSON value. + * + * 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. + */ + fun isin(isin: JsonField) = apply { this.isin = isin } + + /** Name of the AIF */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + 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)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + /** Current market value of the holding */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [Aif]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Aif = + Aif( + additionalInfo, + isin, + name, + (transactions ?: JsonMissing.of()).map { it.toImmutable() }, + units, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Aif = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + isin() + name() + transactions().ifPresent { it.forEach { it.validate() } } + units() + value() + 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 = + (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) + + /** 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()) + + /** + * 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") + + /** + * 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") + + /** + * 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 + + /** + * 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 + + @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 + * [AdditionalInfo]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var closeUnits: JsonField = JsonMissing.of() + private var openUnits: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + closeUnits = additionalInfo.closeUnits + openUnits = additionalInfo.openUnits + additionalProperties = + additionalInfo.additionalProperties.toMutableMap() + } + + /** 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 + } + + /** 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 + } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + closeUnits, + openUnits, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + closeUnits() + openUnits() + 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 (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}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Aif && + additionalInfo == other.additionalInfo && + isin == other.isin && + name == other.name && + transactions == other.transactions && + units == other.units && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + isin, + name, + transactions, + units, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Aif{additionalInfo=$additionalInfo, isin=$isin, name=$name, transactions=$transactions, units=$units, value=$value, additionalProperties=$additionalProperties}" + } + + class CorporateBond + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + 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, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + 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, transactions, units, value, mutableMapOf()) + + /** + * Additional information specific to the corporate bond + * + * @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 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") + + /** + * 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") + + /** + * 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 + * + * @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") + + /** + * 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") + + /** + * 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]. + * + * Unlike [isin], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("isin") @ExcludeMissing fun _isin(): JsonField = isin + + /** + * 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 + + /** + * 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]. + * + * Unlike [units], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("units") @ExcludeMissing fun _units(): JsonField = units + + /** + * 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 + + @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 [CorporateBond]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CorporateBond]. */ + class Builder internal constructor() { + + 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() + + @JvmSynthetic + internal fun from(corporateBond: CorporateBond) = apply { + additionalInfo = corporateBond.additionalInfo + isin = corporateBond.isin + name = corporateBond.name + transactions = corporateBond.transactions.map { it.toMutableList() } + units = corporateBond.units + value = corporateBond.value + additionalProperties = corporateBond.additionalProperties.toMutableMap() + } + + /** Additional information specific to the corporate bond */ + 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 + } + + /** ISIN code of the corporate bond */ + fun isin(isin: String) = isin(JsonField.of(isin)) + + /** + * Sets [Builder.isin] to an arbitrary JSON value. + * + * 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. + */ + fun isin(isin: JsonField) = apply { this.isin = isin } + + /** Name of the corporate bond */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + 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)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + /** Current market value of the holding */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [CorporateBond]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CorporateBond = + CorporateBond( + additionalInfo, + isin, + name, + (transactions ?: JsonMissing.of()).map { it.toImmutable() }, + units, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): CorporateBond = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + isin() + name() + transactions().ifPresent { it.forEach { it.validate() } } + units() + value() + 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 = + (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) + + /** Additional information specific to the corporate bond */ + 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()) + + /** + * 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") + + /** + * 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") + + /** + * 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 + + /** + * 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 + + @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 + * [AdditionalInfo]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var closeUnits: JsonField = JsonMissing.of() + private var openUnits: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + closeUnits = additionalInfo.closeUnits + openUnits = additionalInfo.openUnits + additionalProperties = + additionalInfo.additionalProperties.toMutableMap() + } + + /** 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 + } + + /** 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 + } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + closeUnits, + openUnits, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + closeUnits() + openUnits() + 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 (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}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CorporateBond && + additionalInfo == other.additionalInfo && + isin == other.isin && + name == other.name && + transactions == other.transactions && + units == other.units && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + isin, + name, + transactions, + units, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CorporateBond{additionalInfo=$additionalInfo, isin=$isin, name=$name, transactions=$transactions, units=$units, value=$value, additionalProperties=$additionalProperties}" + } + + class DematMutualFund + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + 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, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + 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, transactions, units, value, mutableMapOf()) + + /** + * Additional information specific to the mutual fund + * + * @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 mutual fund + * + * @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") + + /** + * Name of the mutual fund + * + * @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") + + /** + * 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 + * + * @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") + + /** + * 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") + + /** + * 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]. + * + * Unlike [isin], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("isin") @ExcludeMissing fun _isin(): JsonField = isin + + /** + * 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 + + /** + * 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]. + * + * Unlike [units], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("units") @ExcludeMissing fun _units(): JsonField = units + + /** + * 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 + + @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 [DematMutualFund]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DematMutualFund]. */ + class Builder internal constructor() { + + 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() + + @JvmSynthetic + internal fun from(dematMutualFund: DematMutualFund) = apply { + additionalInfo = dematMutualFund.additionalInfo + isin = dematMutualFund.isin + name = dematMutualFund.name + transactions = dematMutualFund.transactions.map { it.toMutableList() } + units = dematMutualFund.units + value = dematMutualFund.value + additionalProperties = dematMutualFund.additionalProperties.toMutableMap() + } + + /** Additional information specific to the mutual fund */ + 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 + } + + /** ISIN code of the mutual fund */ + fun isin(isin: String) = isin(JsonField.of(isin)) + + /** + * Sets [Builder.isin] to an arbitrary JSON value. + * + * 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. + */ + fun isin(isin: JsonField) = apply { this.isin = isin } + + /** Name of the mutual fund */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + 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)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + /** Current market value of the holding */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [DematMutualFund]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): DematMutualFund = + DematMutualFund( + additionalInfo, + isin, + name, + (transactions ?: JsonMissing.of()).map { it.toImmutable() }, + units, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): DematMutualFund = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + isin() + name() + transactions().ifPresent { it.forEach { it.validate() } } + units() + value() + 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 = + (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) + + /** Additional information specific to the mutual fund */ + 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()) + + /** + * 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") + + /** + * 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") + + /** + * 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 + + /** + * 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 + + @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 + * [AdditionalInfo]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var closeUnits: JsonField = JsonMissing.of() + private var openUnits: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + closeUnits = additionalInfo.closeUnits + openUnits = additionalInfo.openUnits + additionalProperties = + additionalInfo.additionalProperties.toMutableMap() + } + + /** 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 + } + + /** 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 + } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + closeUnits, + openUnits, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + closeUnits() + openUnits() + 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 (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}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DematMutualFund && + additionalInfo == other.additionalInfo && + isin == other.isin && + name == other.name && + transactions == other.transactions && + units == other.units && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + isin, + name, + transactions, + units, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DematMutualFund{additionalInfo=$additionalInfo, isin=$isin, name=$name, transactions=$transactions, units=$units, value=$value, additionalProperties=$additionalProperties}" + } + + class Equity + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + 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, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + 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, transactions, units, value, mutableMapOf()) + + /** + * Additional information specific to the equity + * + * @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 equity + * + * @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") + + /** + * Name of the equity + * + * @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") + + /** + * 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 + * + * @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") + + /** + * 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") + + /** + * 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]. + * + * Unlike [isin], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("isin") @ExcludeMissing fun _isin(): JsonField = isin + + /** + * 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 + + /** + * 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]. + * + * Unlike [units], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("units") @ExcludeMissing fun _units(): JsonField = units + + /** + * 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 + + @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 [Equity]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Equity]. */ + class Builder internal constructor() { + + 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() + + @JvmSynthetic + internal fun from(equity: Equity) = apply { + additionalInfo = equity.additionalInfo + isin = equity.isin + name = equity.name + transactions = equity.transactions.map { it.toMutableList() } + units = equity.units + value = equity.value + additionalProperties = equity.additionalProperties.toMutableMap() + } + + /** Additional information specific to the equity */ + 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 + } + + /** ISIN code of the equity */ + fun isin(isin: String) = isin(JsonField.of(isin)) + + /** + * Sets [Builder.isin] to an arbitrary JSON value. + * + * 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. + */ + fun isin(isin: JsonField) = apply { this.isin = isin } + + /** Name of the equity */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + 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)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + /** Current market value of the holding */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [Equity]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Equity = + Equity( + additionalInfo, + isin, + name, + (transactions ?: JsonMissing.of()).map { it.toImmutable() }, + units, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Equity = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + isin() + name() + transactions().ifPresent { it.forEach { it.validate() } } + units() + value() + 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 = + (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) + + /** Additional information specific to the equity */ + 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()) + + /** + * 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") + + /** + * 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") + + /** + * 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 + + /** + * 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 + + @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 + * [AdditionalInfo]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var closeUnits: JsonField = JsonMissing.of() + private var openUnits: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + closeUnits = additionalInfo.closeUnits + openUnits = additionalInfo.openUnits + additionalProperties = + additionalInfo.additionalProperties.toMutableMap() + } + + /** 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 + } + + /** 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 + } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + closeUnits, + openUnits, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + closeUnits() + openUnits() + 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 (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}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Equity && + additionalInfo == other.additionalInfo && + isin == other.isin && + name == other.name && + transactions == other.transactions && + units == other.units && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + isin, + name, + transactions, + units, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Equity{additionalInfo=$additionalInfo, isin=$isin, name=$name, transactions=$transactions, units=$units, value=$value, additionalProperties=$additionalProperties}" + } + + class GovernmentSecurity + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + 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, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + 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, transactions, units, value, mutableMapOf()) + + /** + * Additional information specific to the government security + * + * @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 government security + * + * @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") + + /** + * Name of the government security + * + * @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") + + /** + * 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 + * + * @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") + + /** + * 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") + + /** + * 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]. + * + * Unlike [isin], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("isin") @ExcludeMissing fun _isin(): JsonField = isin + + /** + * 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 + + /** + * 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]. + * + * Unlike [units], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("units") @ExcludeMissing fun _units(): JsonField = units + + /** + * 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 + + @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 + * [GovernmentSecurity]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [GovernmentSecurity]. */ + class Builder internal constructor() { + + 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() + + @JvmSynthetic + internal fun from(governmentSecurity: GovernmentSecurity) = apply { + additionalInfo = governmentSecurity.additionalInfo + isin = governmentSecurity.isin + name = governmentSecurity.name + transactions = governmentSecurity.transactions.map { it.toMutableList() } + units = governmentSecurity.units + value = governmentSecurity.value + additionalProperties = + governmentSecurity.additionalProperties.toMutableMap() + } + + /** Additional information specific to the government security */ + 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 + } + + /** ISIN code of the government security */ + fun isin(isin: String) = isin(JsonField.of(isin)) + + /** + * Sets [Builder.isin] to an arbitrary JSON value. + * + * 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. + */ + fun isin(isin: JsonField) = apply { this.isin = isin } + + /** Name of the government security */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + 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)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + /** Current market value of the holding */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [GovernmentSecurity]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): GovernmentSecurity = + GovernmentSecurity( + additionalInfo, + isin, + name, + (transactions ?: JsonMissing.of()).map { it.toImmutable() }, + units, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): GovernmentSecurity = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + isin() + name() + transactions().ifPresent { it.forEach { it.validate() } } + units() + value() + 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 = + (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) + + /** Additional information specific to the government security */ + 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()) + + /** + * 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") + + /** + * 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") + + /** + * 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 + + /** + * 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 + + @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 + * [AdditionalInfo]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var closeUnits: JsonField = JsonMissing.of() + private var openUnits: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = + mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + closeUnits = additionalInfo.closeUnits + openUnits = additionalInfo.openUnits + additionalProperties = + additionalInfo.additionalProperties.toMutableMap() + } + + /** 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 + } + + /** 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 + } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + closeUnits, + openUnits, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + closeUnits() + openUnits() + 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 (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}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is GovernmentSecurity && + additionalInfo == other.additionalInfo && + isin == other.isin && + name == other.name && + transactions == other.transactions && + units == other.units && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + isin, + name, + transactions, + units, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "GovernmentSecurity{additionalInfo=$additionalInfo, isin=$isin, name=$name, transactions=$transactions, units=$units, value=$value, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Holdings && + aifs == other.aifs && + corporateBonds == other.corporateBonds && + dematMutualFunds == other.dematMutualFunds && + equities == other.equities && + governmentSecurities == other.governmentSecurities && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + aifs, + corporateBonds, + dematMutualFunds, + equities, + governmentSecurities, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Holdings{aifs=$aifs, corporateBonds=$corporateBonds, dematMutualFunds=$dematMutualFunds, equities=$equities, governmentSecurities=$governmentSecurities, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DematAccount && + additionalInfo == other.additionalInfo && + boId == other.boId && + clientId == other.clientId && + dematType == other.dematType && + dpId == other.dpId && + dpName == other.dpName && + holdings == other.holdings && + linkedHolders == other.linkedHolders && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + boId, + clientId, + dematType, + dpId, + dpName, + holdings, + linkedHolders, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DematAccount{additionalInfo=$additionalInfo, boId=$boId, clientId=$clientId, dematType=$dematType, dpId=$dpId, dpName=$dpName, holdings=$holdings, linkedHolders=$linkedHolders, value=$value, additionalProperties=$additionalProperties}" + } + + class Insurance + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val lifeInsurancePolicies: JsonField>, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("life_insurance_policies") + @ExcludeMissing + lifeInsurancePolicies: JsonField> = JsonMissing.of() + ) : this(lifeInsurancePolicies, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun lifeInsurancePolicies(): Optional> = + lifeInsurancePolicies.getOptional("life_insurance_policies") + + /** + * Returns the raw JSON value of [lifeInsurancePolicies]. + * + * Unlike [lifeInsurancePolicies], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("life_insurance_policies") + @ExcludeMissing + fun _lifeInsurancePolicies(): JsonField> = lifeInsurancePolicies + + @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 [Insurance]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Insurance]. */ + class Builder internal constructor() { + + private var lifeInsurancePolicies: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(insurance: Insurance) = apply { + lifeInsurancePolicies = insurance.lifeInsurancePolicies.map { it.toMutableList() } + additionalProperties = insurance.additionalProperties.toMutableMap() + } + + fun lifeInsurancePolicies(lifeInsurancePolicies: List) = + lifeInsurancePolicies(JsonField.of(lifeInsurancePolicies)) + + /** + * Sets [Builder.lifeInsurancePolicies] to an arbitrary JSON value. + * + * You should usually call [Builder.lifeInsurancePolicies] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun lifeInsurancePolicies(lifeInsurancePolicies: JsonField>) = + apply { + this.lifeInsurancePolicies = lifeInsurancePolicies.map { it.toMutableList() } + } + + /** + * Adds a single [LifeInsurancePolicy] to [lifeInsurancePolicies]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addLifeInsurancePolicy(lifeInsurancePolicy: LifeInsurancePolicy) = apply { + lifeInsurancePolicies = + (lifeInsurancePolicies ?: JsonField.of(mutableListOf())).also { + checkKnown("lifeInsurancePolicies", it).add(lifeInsurancePolicy) + } + } + + 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 [Insurance]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Insurance = + Insurance( + (lifeInsurancePolicies ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Insurance = apply { + if (validated) { + return@apply + } + + lifeInsurancePolicies().ifPresent { it.forEach { it.validate() } } + 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 = + (lifeInsurancePolicies.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + class LifeInsurancePolicy + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val additionalInfo: JsonValue, + private val lifeAssured: JsonField, + private val policyName: JsonField, + private val policyNumber: JsonField, + private val premiumAmount: JsonField, + private val premiumFrequency: JsonField, + private val provider: JsonField, + private val status: JsonField, + private val sumAssured: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + additionalInfo: JsonValue = JsonMissing.of(), + @JsonProperty("life_assured") + @ExcludeMissing + lifeAssured: JsonField = JsonMissing.of(), + @JsonProperty("policy_name") + @ExcludeMissing + policyName: JsonField = JsonMissing.of(), + @JsonProperty("policy_number") + @ExcludeMissing + policyNumber: JsonField = JsonMissing.of(), + @JsonProperty("premium_amount") + @ExcludeMissing + premiumAmount: JsonField = JsonMissing.of(), + @JsonProperty("premium_frequency") + @ExcludeMissing + premiumFrequency: JsonField = JsonMissing.of(), + @JsonProperty("provider") + @ExcludeMissing + provider: JsonField = JsonMissing.of(), + @JsonProperty("status") + @ExcludeMissing + status: JsonField = JsonMissing.of(), + @JsonProperty("sum_assured") + @ExcludeMissing + sumAssured: JsonField = JsonMissing.of(), + ) : this( + additionalInfo, + lifeAssured, + policyName, + policyNumber, + premiumAmount, + premiumFrequency, + provider, + status, + sumAssured, + mutableMapOf(), + ) + + /** + * Additional information specific to the policy + * + * This arbitrary value can be deserialized into a custom type using the `convert` + * method: + * ```java + * MyClass myObject = lifeInsurancePolicy.additionalInfo().convert(MyClass.class); + * ``` + */ + @JsonProperty("additional_info") + @ExcludeMissing + fun _additionalInfo(): JsonValue = additionalInfo + + /** + * Name of the life assured + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun lifeAssured(): Optional = lifeAssured.getOptional("life_assured") + + /** + * Name of the insurance policy + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun policyName(): Optional = policyName.getOptional("policy_name") + + /** + * Insurance policy number + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun policyNumber(): Optional = policyNumber.getOptional("policy_number") + + /** + * Premium amount + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun premiumAmount(): Optional = premiumAmount.getOptional("premium_amount") + + /** + * Frequency of premium payment (e.g., Annual, Monthly) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun premiumFrequency(): Optional = + premiumFrequency.getOptional("premium_frequency") + + /** + * Insurance company name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") + + /** + * Status of the policy (e.g., Active, Lapsed) + * + * @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") + + /** + * Sum assured amount + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun sumAssured(): Optional = sumAssured.getOptional("sum_assured") + + /** + * Returns the raw JSON value of [lifeAssured]. + * + * Unlike [lifeAssured], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("life_assured") + @ExcludeMissing + fun _lifeAssured(): JsonField = lifeAssured + + /** + * Returns the raw JSON value of [policyName]. + * + * Unlike [policyName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("policy_name") + @ExcludeMissing + fun _policyName(): JsonField = policyName + + /** + * Returns the raw JSON value of [policyNumber]. + * + * Unlike [policyNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("policy_number") + @ExcludeMissing + fun _policyNumber(): JsonField = policyNumber + + /** + * Returns the raw JSON value of [premiumAmount]. + * + * Unlike [premiumAmount], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("premium_amount") + @ExcludeMissing + fun _premiumAmount(): JsonField = premiumAmount + + /** + * Returns the raw JSON value of [premiumFrequency]. + * + * Unlike [premiumFrequency], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("premium_frequency") + @ExcludeMissing + fun _premiumFrequency(): JsonField = premiumFrequency + + /** + * Returns the raw JSON value of [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider + + /** + * 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 + + /** + * Returns the raw JSON value of [sumAssured]. + * + * Unlike [sumAssured], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("sum_assured") + @ExcludeMissing + fun _sumAssured(): JsonField = sumAssured + + @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 [LifeInsurancePolicy]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LifeInsurancePolicy]. */ + class Builder internal constructor() { + + private var additionalInfo: JsonValue = JsonMissing.of() + private var lifeAssured: JsonField = JsonMissing.of() + private var policyName: JsonField = JsonMissing.of() + private var policyNumber: JsonField = JsonMissing.of() + private var premiumAmount: JsonField = JsonMissing.of() + private var premiumFrequency: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var sumAssured: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(lifeInsurancePolicy: LifeInsurancePolicy) = apply { + additionalInfo = lifeInsurancePolicy.additionalInfo + lifeAssured = lifeInsurancePolicy.lifeAssured + policyName = lifeInsurancePolicy.policyName + policyNumber = lifeInsurancePolicy.policyNumber + premiumAmount = lifeInsurancePolicy.premiumAmount + premiumFrequency = lifeInsurancePolicy.premiumFrequency + provider = lifeInsurancePolicy.provider + status = lifeInsurancePolicy.status + sumAssured = lifeInsurancePolicy.sumAssured + additionalProperties = lifeInsurancePolicy.additionalProperties.toMutableMap() + } + + /** Additional information specific to the policy */ + fun additionalInfo(additionalInfo: JsonValue) = apply { + this.additionalInfo = additionalInfo + } + + /** Name of the life assured */ + fun lifeAssured(lifeAssured: String) = lifeAssured(JsonField.of(lifeAssured)) + + /** + * Sets [Builder.lifeAssured] to an arbitrary JSON value. + * + * You should usually call [Builder.lifeAssured] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun lifeAssured(lifeAssured: JsonField) = apply { + this.lifeAssured = lifeAssured + } + + /** Name of the insurance policy */ + fun policyName(policyName: String) = policyName(JsonField.of(policyName)) + + /** + * Sets [Builder.policyName] to an arbitrary JSON value. + * + * You should usually call [Builder.policyName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun policyName(policyName: JsonField) = apply { + this.policyName = policyName + } + + /** Insurance policy number */ + fun policyNumber(policyNumber: String) = policyNumber(JsonField.of(policyNumber)) + + /** + * Sets [Builder.policyNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.policyNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun policyNumber(policyNumber: JsonField) = apply { + this.policyNumber = policyNumber + } + + /** Premium amount */ + fun premiumAmount(premiumAmount: Float) = premiumAmount(JsonField.of(premiumAmount)) + + /** + * Sets [Builder.premiumAmount] to an arbitrary JSON value. + * + * You should usually call [Builder.premiumAmount] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun premiumAmount(premiumAmount: JsonField) = apply { + this.premiumAmount = premiumAmount + } + + /** Frequency of premium payment (e.g., Annual, Monthly) */ + fun premiumFrequency(premiumFrequency: String) = + premiumFrequency(JsonField.of(premiumFrequency)) + + /** + * Sets [Builder.premiumFrequency] to an arbitrary JSON value. + * + * You should usually call [Builder.premiumFrequency] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun premiumFrequency(premiumFrequency: JsonField) = apply { + this.premiumFrequency = premiumFrequency + } + + /** Insurance company name */ + fun provider(provider: String) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + /** Status of the policy (e.g., Active, Lapsed) */ + 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 } + + /** Sum assured amount */ + fun sumAssured(sumAssured: Float) = sumAssured(JsonField.of(sumAssured)) + + /** + * Sets [Builder.sumAssured] to an arbitrary JSON value. + * + * You should usually call [Builder.sumAssured] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun sumAssured(sumAssured: JsonField) = apply { + this.sumAssured = sumAssured + } + + 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 [LifeInsurancePolicy]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): LifeInsurancePolicy = + LifeInsurancePolicy( + additionalInfo, + lifeAssured, + policyName, + policyNumber, + premiumAmount, + premiumFrequency, + provider, + status, + sumAssured, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): LifeInsurancePolicy = apply { + if (validated) { + return@apply + } + + lifeAssured() + policyName() + policyNumber() + premiumAmount() + premiumFrequency() + provider() + status() + sumAssured() + 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 (lifeAssured.asKnown().isPresent) 1 else 0) + + (if (policyName.asKnown().isPresent) 1 else 0) + + (if (policyNumber.asKnown().isPresent) 1 else 0) + + (if (premiumAmount.asKnown().isPresent) 1 else 0) + + (if (premiumFrequency.asKnown().isPresent) 1 else 0) + + (if (provider.asKnown().isPresent) 1 else 0) + + (if (status.asKnown().isPresent) 1 else 0) + + (if (sumAssured.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LifeInsurancePolicy && + additionalInfo == other.additionalInfo && + lifeAssured == other.lifeAssured && + policyName == other.policyName && + policyNumber == other.policyNumber && + premiumAmount == other.premiumAmount && + premiumFrequency == other.premiumFrequency && + provider == other.provider && + status == other.status && + sumAssured == other.sumAssured && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + lifeAssured, + policyName, + policyNumber, + premiumAmount, + premiumFrequency, + provider, + status, + sumAssured, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "LifeInsurancePolicy{additionalInfo=$additionalInfo, lifeAssured=$lifeAssured, policyName=$policyName, policyNumber=$policyNumber, premiumAmount=$premiumAmount, premiumFrequency=$premiumFrequency, provider=$provider, status=$status, sumAssured=$sumAssured, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Insurance && + lifeInsurancePolicies == other.lifeInsurancePolicies && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(lifeInsurancePolicies, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Insurance{lifeInsurancePolicies=$lifeInsurancePolicies, additionalProperties=$additionalProperties}" + } + + class Investor + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val address: JsonField, + private val casId: JsonField, + private val email: JsonField, + private val mobile: JsonField, + private val name: JsonField, + private val pan: JsonField, + private val pincode: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("address") @ExcludeMissing address: JsonField = JsonMissing.of(), + @JsonProperty("cas_id") @ExcludeMissing casId: JsonField = JsonMissing.of(), + @JsonProperty("email") @ExcludeMissing email: JsonField = JsonMissing.of(), + @JsonProperty("mobile") @ExcludeMissing mobile: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("pan") @ExcludeMissing pan: JsonField = JsonMissing.of(), + @JsonProperty("pincode") @ExcludeMissing pincode: JsonField = JsonMissing.of(), + ) : this(address, casId, email, mobile, name, pan, pincode, mutableMapOf()) + + /** + * Address of the investor + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun address(): Optional = address.getOptional("address") + + /** + * CAS ID of the investor (only for NSDL and CDSL) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun casId(): Optional = casId.getOptional("cas_id") + + /** + * Email address of the investor + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun email(): Optional = email.getOptional("email") + + /** + * Mobile number of the investor + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun mobile(): Optional = mobile.getOptional("mobile") + + /** + * Name of the investor + * + * @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") + + /** + * PAN (Permanent Account Number) of the investor + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pan(): Optional = pan.getOptional("pan") + + /** + * Postal code of the investor's address + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pincode(): Optional = pincode.getOptional("pincode") + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + /** + * Returns the raw JSON value of [casId]. + * + * Unlike [casId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cas_id") @ExcludeMissing fun _casId(): JsonField = casId + + /** + * 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 [mobile]. + * + * Unlike [mobile], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("mobile") @ExcludeMissing fun _mobile(): JsonField = mobile + + /** + * 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 + + /** + * Returns the raw JSON value of [pan]. + * + * Unlike [pan], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pan") @ExcludeMissing fun _pan(): JsonField = pan + + /** + * Returns the raw JSON value of [pincode]. + * + * Unlike [pincode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pincode") @ExcludeMissing fun _pincode(): JsonField = pincode + + @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 [Investor]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Investor]. */ + class Builder internal constructor() { + + private var address: JsonField = JsonMissing.of() + private var casId: JsonField = JsonMissing.of() + private var email: JsonField = JsonMissing.of() + private var mobile: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var pan: JsonField = JsonMissing.of() + private var pincode: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(investor: Investor) = apply { + address = investor.address + casId = investor.casId + email = investor.email + mobile = investor.mobile + name = investor.name + pan = investor.pan + pincode = investor.pincode + additionalProperties = investor.additionalProperties.toMutableMap() + } + + /** Address of the investor */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + /** CAS ID of the investor (only for NSDL and CDSL) */ + fun casId(casId: String) = casId(JsonField.of(casId)) + + /** + * Sets [Builder.casId] to an arbitrary JSON value. + * + * You should usually call [Builder.casId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun casId(casId: JsonField) = apply { this.casId = casId } + + /** Email address of the investor */ + 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 } + + /** Mobile number of the investor */ + fun mobile(mobile: String) = mobile(JsonField.of(mobile)) + + /** + * Sets [Builder.mobile] to an arbitrary JSON value. + * + * You should usually call [Builder.mobile] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun mobile(mobile: JsonField) = apply { this.mobile = mobile } + + /** Name of the investor */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** PAN (Permanent Account Number) of the investor */ + fun pan(pan: String) = pan(JsonField.of(pan)) + + /** + * Sets [Builder.pan] to an arbitrary JSON value. + * + * You should usually call [Builder.pan] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun pan(pan: JsonField) = apply { this.pan = pan } + + /** Postal code of the investor's address */ + fun pincode(pincode: String) = pincode(JsonField.of(pincode)) + + /** + * Sets [Builder.pincode] to an arbitrary JSON value. + * + * You should usually call [Builder.pincode] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pincode(pincode: JsonField) = apply { this.pincode = pincode } + + 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 [Investor]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Investor = + Investor( + address, + casId, + email, + mobile, + name, + pan, + pincode, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Investor = apply { + if (validated) { + return@apply + } + + address() + casId() + email() + mobile() + name() + pan() + pincode() + 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 (address.asKnown().isPresent) 1 else 0) + + (if (casId.asKnown().isPresent) 1 else 0) + + (if (email.asKnown().isPresent) 1 else 0) + + (if (mobile.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (pan.asKnown().isPresent) 1 else 0) + + (if (pincode.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Investor && + address == other.address && + casId == other.casId && + email == other.email && + mobile == other.mobile && + name == other.name && + pan == other.pan && + pincode == other.pincode && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(address, casId, email, mobile, name, pan, pincode, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Investor{address=$address, casId=$casId, email=$email, mobile=$mobile, name=$name, pan=$pan, pincode=$pincode, additionalProperties=$additionalProperties}" + } + + class Meta + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val casType: JsonField, + private val generatedAt: JsonField, + private val statementPeriod: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("cas_type") + @ExcludeMissing + casType: JsonField = JsonMissing.of(), + @JsonProperty("generated_at") + @ExcludeMissing + generatedAt: JsonField = JsonMissing.of(), + @JsonProperty("statement_period") + @ExcludeMissing + statementPeriod: JsonField = JsonMissing.of(), + ) : this(casType, generatedAt, statementPeriod, mutableMapOf()) + + /** + * Type of CAS detected and processed + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun casType(): Optional = casType.getOptional("cas_type") + + /** + * Timestamp when the response was generated + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun generatedAt(): Optional = generatedAt.getOptional("generated_at") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun statementPeriod(): Optional = + statementPeriod.getOptional("statement_period") + + /** + * Returns the raw JSON value of [casType]. + * + * Unlike [casType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cas_type") @ExcludeMissing fun _casType(): JsonField = casType + + /** + * Returns the raw JSON value of [generatedAt]. + * + * Unlike [generatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("generated_at") + @ExcludeMissing + fun _generatedAt(): JsonField = generatedAt + + /** + * Returns the raw JSON value of [statementPeriod]. + * + * Unlike [statementPeriod], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("statement_period") + @ExcludeMissing + fun _statementPeriod(): JsonField = statementPeriod + + @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 [Meta]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Meta]. */ + class Builder internal constructor() { + + private var casType: JsonField = JsonMissing.of() + private var generatedAt: JsonField = JsonMissing.of() + private var statementPeriod: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(meta: Meta) = apply { + casType = meta.casType + generatedAt = meta.generatedAt + statementPeriod = meta.statementPeriod + additionalProperties = meta.additionalProperties.toMutableMap() + } + + /** Type of CAS detected and processed */ + fun casType(casType: CasType) = casType(JsonField.of(casType)) + + /** + * Sets [Builder.casType] to an arbitrary JSON value. + * + * You should usually call [Builder.casType] with a well-typed [CasType] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun casType(casType: JsonField) = apply { this.casType = casType } + + /** Timestamp when the response was generated */ + fun generatedAt(generatedAt: OffsetDateTime) = generatedAt(JsonField.of(generatedAt)) + + /** + * Sets [Builder.generatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.generatedAt] with a well-typed [OffsetDateTime] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun generatedAt(generatedAt: JsonField) = apply { + this.generatedAt = generatedAt + } + + fun statementPeriod(statementPeriod: StatementPeriod) = + statementPeriod(JsonField.of(statementPeriod)) + + /** + * Sets [Builder.statementPeriod] to an arbitrary JSON value. + * + * You should usually call [Builder.statementPeriod] with a well-typed [StatementPeriod] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun statementPeriod(statementPeriod: JsonField) = apply { + this.statementPeriod = statementPeriod + } + + 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 [Meta]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Meta = + Meta(casType, generatedAt, statementPeriod, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Meta = apply { + if (validated) { + return@apply + } + + casType().ifPresent { it.validate() } + generatedAt() + statementPeriod().ifPresent { it.validate() } + 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 = + (casType.asKnown().getOrNull()?.validity() ?: 0) + + (if (generatedAt.asKnown().isPresent) 1 else 0) + + (statementPeriod.asKnown().getOrNull()?.validity() ?: 0) + + /** Type of CAS detected and processed */ + class CasType @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 NSDL = of("NSDL") + + @JvmField val CDSL = of("CDSL") + + @JvmField val CAMS_KFINTECH = of("CAMS_KFINTECH") + + @JvmStatic fun of(value: String) = CasType(JsonField.of(value)) + } + + /** An enum containing [CasType]'s known values. */ + enum class Known { + NSDL, + CDSL, + CAMS_KFINTECH, + } + + /** + * An enum containing [CasType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CasType] 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 { + NSDL, + CDSL, + CAMS_KFINTECH, + /** + * An enum member indicating that [CasType] 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) { + NSDL -> Value.NSDL + CDSL -> Value.CDSL + CAMS_KFINTECH -> Value.CAMS_KFINTECH + 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) { + NSDL -> Known.NSDL + CDSL -> Known.CDSL + CAMS_KFINTECH -> Known.CAMS_KFINTECH + else -> throw CasParserInvalidDataException("Unknown CasType: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): CasType = 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 CasType && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class StatementPeriod + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val from: JsonField, + private val to: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("from") @ExcludeMissing from: JsonField = JsonMissing.of(), + @JsonProperty("to") @ExcludeMissing to: JsonField = JsonMissing.of(), + ) : this(from, to, mutableMapOf()) + + /** + * Start date of the statement period + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun from(): Optional = from.getOptional("from") + + /** + * End date of the statement period + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun to(): Optional = to.getOptional("to") + + /** + * Returns the raw JSON value of [from]. + * + * Unlike [from], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("from") @ExcludeMissing fun _from(): JsonField = from + + /** + * Returns the raw JSON value of [to]. + * + * Unlike [to], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("to") @ExcludeMissing fun _to(): JsonField = to + + @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 [StatementPeriod]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [StatementPeriod]. */ + class Builder internal constructor() { + + private var from: JsonField = JsonMissing.of() + private var to: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(statementPeriod: StatementPeriod) = apply { + from = statementPeriod.from + to = statementPeriod.to + additionalProperties = statementPeriod.additionalProperties.toMutableMap() + } + + /** Start date of the statement period */ + fun from(from: LocalDate) = from(JsonField.of(from)) + + /** + * Sets [Builder.from] to an arbitrary JSON value. + * + * You should usually call [Builder.from] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun from(from: JsonField) = apply { this.from = from } + + /** End date of the statement period */ + fun to(to: LocalDate) = to(JsonField.of(to)) + + /** + * Sets [Builder.to] to an arbitrary JSON value. + * + * You should usually call [Builder.to] with a well-typed [LocalDate] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun to(to: JsonField) = apply { this.to = to } + + 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 [StatementPeriod]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): StatementPeriod = + StatementPeriod(from, to, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): StatementPeriod = apply { + if (validated) { + return@apply + } + + from() + to() + 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 (from.asKnown().isPresent) 1 else 0) + (if (to.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is StatementPeriod && + from == other.from && + to == other.to && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(from, to, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "StatementPeriod{from=$from, to=$to, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Meta && + casType == other.casType && + generatedAt == other.generatedAt && + statementPeriod == other.statementPeriod && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(casType, generatedAt, statementPeriod, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Meta{casType=$casType, generatedAt=$generatedAt, statementPeriod=$statementPeriod, additionalProperties=$additionalProperties}" + } + + class MutualFund + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val additionalInfo: JsonField, + private val amc: JsonField, + private val folioNumber: JsonField, + private val linkedHolders: JsonField>, + private val registrar: JsonField, + private val schemes: JsonField>, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + additionalInfo: JsonField = JsonMissing.of(), + @JsonProperty("amc") @ExcludeMissing amc: JsonField = JsonMissing.of(), + @JsonProperty("folio_number") + @ExcludeMissing + folioNumber: JsonField = JsonMissing.of(), + @JsonProperty("linked_holders") + @ExcludeMissing + linkedHolders: JsonField> = JsonMissing.of(), + @JsonProperty("registrar") + @ExcludeMissing + registrar: JsonField = JsonMissing.of(), + @JsonProperty("schemes") + @ExcludeMissing + schemes: JsonField> = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this( + additionalInfo, + amc, + folioNumber, + linkedHolders, + registrar, + schemes, + value, + mutableMapOf(), + ) + + /** + * Additional folio information + * + * @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") + + /** + * Asset Management Company name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun amc(): Optional = amc.getOptional("amc") + + /** + * Folio number + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun folioNumber(): Optional = folioNumber.getOptional("folio_number") + + /** + * List of account holders linked to this mutual fund folio + * + * @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") + + /** + * Registrar and Transfer Agent name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun registrar(): Optional = registrar.getOptional("registrar") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun schemes(): Optional> = schemes.getOptional("schemes") + + /** + * Total value of the folio + * + * @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") + + /** + * 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 [amc]. + * + * Unlike [amc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("amc") @ExcludeMissing fun _amc(): JsonField = amc + + /** + * Returns the raw JSON value of [folioNumber]. + * + * Unlike [folioNumber], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("folio_number") + @ExcludeMissing + fun _folioNumber(): JsonField = folioNumber + + /** + * 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 [registrar]. + * + * Unlike [registrar], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("registrar") @ExcludeMissing fun _registrar(): JsonField = registrar + + /** + * Returns the raw JSON value of [schemes]. + * + * Unlike [schemes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("schemes") @ExcludeMissing fun _schemes(): JsonField> = schemes + + /** + * 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 + + @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 [MutualFund]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MutualFund]. */ + class Builder internal constructor() { + + private var additionalInfo: JsonField = JsonMissing.of() + private var amc: JsonField = JsonMissing.of() + private var folioNumber: JsonField = JsonMissing.of() + private var linkedHolders: JsonField>? = null + private var registrar: JsonField = JsonMissing.of() + private var schemes: JsonField>? = null + private var value: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(mutualFund: MutualFund) = apply { + additionalInfo = mutualFund.additionalInfo + amc = mutualFund.amc + folioNumber = mutualFund.folioNumber + linkedHolders = mutualFund.linkedHolders.map { it.toMutableList() } + registrar = mutualFund.registrar + schemes = mutualFund.schemes.map { it.toMutableList() } + value = mutualFund.value + additionalProperties = mutualFund.additionalProperties.toMutableMap() + } + + /** Additional folio information */ + 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 + } + + /** Asset Management Company name */ + fun amc(amc: String) = amc(JsonField.of(amc)) + + /** + * Sets [Builder.amc] to an arbitrary JSON value. + * + * You should usually call [Builder.amc] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun amc(amc: JsonField) = apply { this.amc = amc } + + /** Folio number */ + fun folioNumber(folioNumber: String) = folioNumber(JsonField.of(folioNumber)) + + /** + * Sets [Builder.folioNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.folioNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun folioNumber(folioNumber: JsonField) = apply { + this.folioNumber = folioNumber + } + + /** List of account holders linked to this mutual fund folio */ + 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) + } + } + + /** Registrar and Transfer Agent name */ + fun registrar(registrar: String) = registrar(JsonField.of(registrar)) + + /** + * Sets [Builder.registrar] to an arbitrary JSON value. + * + * You should usually call [Builder.registrar] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun registrar(registrar: JsonField) = apply { this.registrar = registrar } + + fun schemes(schemes: List) = schemes(JsonField.of(schemes)) + + /** + * Sets [Builder.schemes] to an arbitrary JSON value. + * + * You should usually call [Builder.schemes] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun schemes(schemes: JsonField>) = apply { + this.schemes = schemes.map { it.toMutableList() } + } + + /** + * Adds a single [Scheme] to [schemes]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addScheme(scheme: Scheme) = apply { + schemes = + (schemes ?: JsonField.of(mutableListOf())).also { + checkKnown("schemes", it).add(scheme) + } + } + + /** Total value of the folio */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [MutualFund]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): MutualFund = + MutualFund( + additionalInfo, + amc, + folioNumber, + (linkedHolders ?: JsonMissing.of()).map { it.toImmutable() }, + registrar, + (schemes ?: JsonMissing.of()).map { it.toImmutable() }, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): MutualFund = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + amc() + folioNumber() + linkedHolders().ifPresent { it.forEach { it.validate() } } + registrar() + schemes().ifPresent { it.forEach { it.validate() } } + value() + 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 = + (additionalInfo.asKnown().getOrNull()?.validity() ?: 0) + + (if (amc.asKnown().isPresent) 1 else 0) + + (if (folioNumber.asKnown().isPresent) 1 else 0) + + (linkedHolders.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (registrar.asKnown().isPresent) 1 else 0) + + (schemes.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (value.asKnown().isPresent) 1 else 0) + + /** Additional folio information */ + class AdditionalInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val kyc: JsonField, + private val pan: JsonField, + private val pankyc: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("kyc") @ExcludeMissing kyc: JsonField = JsonMissing.of(), + @JsonProperty("pan") @ExcludeMissing pan: JsonField = JsonMissing.of(), + @JsonProperty("pankyc") @ExcludeMissing pankyc: JsonField = JsonMissing.of(), + ) : this(kyc, pan, pankyc, mutableMapOf()) + + /** + * KYC status of the folio + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun kyc(): Optional = kyc.getOptional("kyc") + + /** + * PAN associated with the folio + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun pan(): Optional = pan.getOptional("pan") + + /** + * PAN KYC status + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun pankyc(): Optional = pankyc.getOptional("pankyc") + + /** + * Returns the raw JSON value of [kyc]. + * + * Unlike [kyc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("kyc") @ExcludeMissing fun _kyc(): JsonField = kyc + + /** + * Returns the raw JSON value of [pan]. + * + * Unlike [pan], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pan") @ExcludeMissing fun _pan(): JsonField = pan + + /** + * Returns the raw JSON value of [pankyc]. + * + * Unlike [pankyc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pankyc") @ExcludeMissing fun _pankyc(): JsonField = pankyc + + @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 [AdditionalInfo]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var kyc: JsonField = JsonMissing.of() + private var pan: JsonField = JsonMissing.of() + private var pankyc: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + kyc = additionalInfo.kyc + pan = additionalInfo.pan + pankyc = additionalInfo.pankyc + additionalProperties = additionalInfo.additionalProperties.toMutableMap() + } + + /** KYC status of the folio */ + fun kyc(kyc: String) = kyc(JsonField.of(kyc)) + + /** + * Sets [Builder.kyc] to an arbitrary JSON value. + * + * You should usually call [Builder.kyc] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun kyc(kyc: JsonField) = apply { this.kyc = kyc } + + /** PAN associated with the folio */ + fun pan(pan: String) = pan(JsonField.of(pan)) + + /** + * Sets [Builder.pan] to an arbitrary JSON value. + * + * You should usually call [Builder.pan] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pan(pan: JsonField) = apply { this.pan = pan } + + /** PAN KYC status */ + fun pankyc(pankyc: String) = pankyc(JsonField.of(pankyc)) + + /** + * Sets [Builder.pankyc] to an arbitrary JSON value. + * + * You should usually call [Builder.pankyc] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun pankyc(pankyc: JsonField) = apply { this.pankyc = pankyc } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo(kyc, pan, pankyc, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + kyc() + pan() + pankyc() + 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 (kyc.asKnown().isPresent) 1 else 0) + + (if (pan.asKnown().isPresent) 1 else 0) + + (if (pankyc.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AdditionalInfo && + kyc == other.kyc && + pan == other.pan && + pankyc == other.pankyc && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(kyc, pan, pankyc, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AdditionalInfo{kyc=$kyc, pan=$pan, pankyc=$pankyc, additionalProperties=$additionalProperties}" + } + + class Scheme + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val additionalInfo: JsonField, + private val cost: JsonField, + private val gain: JsonField, + private val isin: JsonField, + private val name: JsonField, + private val nav: JsonField, + private val nominees: JsonField>, + private val transactions: JsonField>, + private val type: JsonField, + private val units: JsonField, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + additionalInfo: JsonField = JsonMissing.of(), + @JsonProperty("cost") @ExcludeMissing cost: JsonField = JsonMissing.of(), + @JsonProperty("gain") @ExcludeMissing gain: JsonField = JsonMissing.of(), + @JsonProperty("isin") @ExcludeMissing isin: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("nav") @ExcludeMissing nav: JsonField = JsonMissing.of(), + @JsonProperty("nominees") + @ExcludeMissing + nominees: JsonField> = JsonMissing.of(), + @JsonProperty("transactions") + @ExcludeMissing + transactions: JsonField> = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("units") @ExcludeMissing units: JsonField = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this( + additionalInfo, + cost, + gain, + isin, + name, + nav, + nominees, + transactions, + type, + units, + value, + mutableMapOf(), + ) + + /** + * Additional information specific to the scheme + * + * @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") + + /** + * Cost of investment + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun cost(): Optional = cost.getOptional("cost") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun gain(): Optional = gain.getOptional("gain") + + /** + * ISIN code of the scheme + * + * @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") + + /** + * Scheme name + * + * @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") + + /** + * Net Asset Value per unit + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun nav(): Optional = nav.getOptional("nav") + + /** + * List of nominees + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun nominees(): Optional> = nominees.getOptional("nominees") + + /** + * @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") + + /** + * Type of mutual fund scheme + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * 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") + + /** + * 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") + + /** + * 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 [cost]. + * + * Unlike [cost], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cost") @ExcludeMissing fun _cost(): JsonField = cost + + /** + * Returns the raw JSON value of [gain]. + * + * Unlike [gain], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("gain") @ExcludeMissing fun _gain(): JsonField = gain + + /** + * 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 + + /** + * 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 + + /** + * Returns the raw JSON value of [nav]. + * + * Unlike [nav], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nav") @ExcludeMissing fun _nav(): JsonField = nav + + /** + * Returns the raw JSON value of [nominees]. + * + * Unlike [nominees], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("nominees") + @ExcludeMissing + fun _nominees(): JsonField> = nominees + + /** + * 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 [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + /** + * 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 + + /** + * 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 + + @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 [Scheme]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Scheme]. */ + class Builder internal constructor() { + + private var additionalInfo: JsonField = JsonMissing.of() + private var cost: JsonField = JsonMissing.of() + private var gain: JsonField = JsonMissing.of() + private var isin: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var nav: JsonField = JsonMissing.of() + private var nominees: JsonField>? = null + private var transactions: JsonField>? = null + private var type: JsonField = JsonMissing.of() + private var units: JsonField = JsonMissing.of() + private var value: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(scheme: Scheme) = apply { + additionalInfo = scheme.additionalInfo + cost = scheme.cost + gain = scheme.gain + isin = scheme.isin + name = scheme.name + nav = scheme.nav + nominees = scheme.nominees.map { it.toMutableList() } + transactions = scheme.transactions.map { it.toMutableList() } + type = scheme.type + units = scheme.units + value = scheme.value + additionalProperties = scheme.additionalProperties.toMutableMap() + } + + /** Additional information specific to the scheme */ + 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 + } + + /** Cost of investment */ + fun cost(cost: Float) = cost(JsonField.of(cost)) + + /** + * Sets [Builder.cost] to an arbitrary JSON value. + * + * You should usually call [Builder.cost] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cost(cost: JsonField) = apply { this.cost = cost } + + fun gain(gain: Gain) = gain(JsonField.of(gain)) + + /** + * Sets [Builder.gain] to an arbitrary JSON value. + * + * You should usually call [Builder.gain] with a well-typed [Gain] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun gain(gain: JsonField) = apply { this.gain = gain } + + /** ISIN code of the scheme */ + fun isin(isin: String) = isin(JsonField.of(isin)) + + /** + * Sets [Builder.isin] to an arbitrary JSON value. + * + * 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. + */ + fun isin(isin: JsonField) = apply { this.isin = isin } + + /** Scheme name */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** Net Asset Value per unit */ + fun nav(nav: Float) = nav(JsonField.of(nav)) + + /** + * Sets [Builder.nav] to an arbitrary JSON value. + * + * You should usually call [Builder.nav] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nav(nav: JsonField) = apply { this.nav = nav } + + /** List of nominees */ + fun nominees(nominees: List) = nominees(JsonField.of(nominees)) + + /** + * Sets [Builder.nominees] to an arbitrary JSON value. + * + * You should usually call [Builder.nominees] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun nominees(nominees: JsonField>) = apply { + this.nominees = nominees.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [nominees]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addNominee(nominee: String) = apply { + nominees = + (nominees ?: JsonField.of(mutableListOf())).also { + checkKnown("nominees", it).add(nominee) + } + } + + 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) + } + } + + /** Type of mutual fund scheme */ + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun type(type: JsonField) = apply { this.type = type } + + /** Number of units held */ + fun units(units: Float) = units(JsonField.of(units)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + /** Current market value of the holding */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [Scheme]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Scheme = + Scheme( + additionalInfo, + cost, + gain, + isin, + name, + nav, + (nominees ?: JsonMissing.of()).map { it.toImmutable() }, + (transactions ?: JsonMissing.of()).map { it.toImmutable() }, + type, + units, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Scheme = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + cost() + gain().ifPresent { it.validate() } + isin() + name() + nav() + nominees() + transactions().ifPresent { it.forEach { it.validate() } } + type().ifPresent { it.validate() } + units() + value() + 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 = + (additionalInfo.asKnown().getOrNull()?.validity() ?: 0) + + (if (cost.asKnown().isPresent) 1 else 0) + + (gain.asKnown().getOrNull()?.validity() ?: 0) + + (if (isin.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (nav.asKnown().isPresent) 1 else 0) + + (nominees.asKnown().getOrNull()?.size ?: 0) + + (transactions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (if (units.asKnown().isPresent) 1 else 0) + + (if (value.asKnown().isPresent) 1 else 0) + + /** Additional information specific to the scheme */ + class AdditionalInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val advisor: JsonField, + private val amfi: JsonField, + private val closeUnits: JsonField, + private val openUnits: JsonField, + private val rtaCode: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("advisor") + @ExcludeMissing + advisor: JsonField = JsonMissing.of(), + @JsonProperty("amfi") + @ExcludeMissing + amfi: JsonField = JsonMissing.of(), + @JsonProperty("close_units") + @ExcludeMissing + closeUnits: JsonField = JsonMissing.of(), + @JsonProperty("open_units") + @ExcludeMissing + openUnits: JsonField = JsonMissing.of(), + @JsonProperty("rta_code") + @ExcludeMissing + rtaCode: JsonField = JsonMissing.of(), + ) : this(advisor, amfi, closeUnits, openUnits, rtaCode, mutableMapOf()) + + /** + * Financial advisor name (CAMS/KFintech) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun advisor(): Optional = advisor.getOptional("advisor") + + /** + * AMFI code for the scheme (CAMS/KFintech) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun amfi(): Optional = amfi.getOptional("amfi") + + /** + * Closing balance units for the statement period + * + * @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") + + /** + * Opening balance units for the statement period + * + * @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") + + /** + * RTA code for the scheme (CAMS/KFintech) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun rtaCode(): Optional = rtaCode.getOptional("rta_code") + + /** + * Returns the raw JSON value of [advisor]. + * + * Unlike [advisor], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("advisor") @ExcludeMissing fun _advisor(): JsonField = advisor + + /** + * Returns the raw JSON value of [amfi]. + * + * Unlike [amfi], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("amfi") @ExcludeMissing fun _amfi(): JsonField = amfi + + /** + * 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 + + /** + * 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 + + /** + * Returns the raw JSON value of [rtaCode]. + * + * Unlike [rtaCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("rta_code") + @ExcludeMissing + fun _rtaCode(): JsonField = rtaCode + + @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 [AdditionalInfo]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var advisor: JsonField = JsonMissing.of() + private var amfi: JsonField = JsonMissing.of() + private var closeUnits: JsonField = JsonMissing.of() + private var openUnits: JsonField = JsonMissing.of() + private var rtaCode: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + advisor = additionalInfo.advisor + amfi = additionalInfo.amfi + closeUnits = additionalInfo.closeUnits + openUnits = additionalInfo.openUnits + rtaCode = additionalInfo.rtaCode + additionalProperties = additionalInfo.additionalProperties.toMutableMap() + } + + /** Financial advisor name (CAMS/KFintech) */ + fun advisor(advisor: String) = advisor(JsonField.of(advisor)) + + /** + * Sets [Builder.advisor] to an arbitrary JSON value. + * + * You should usually call [Builder.advisor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun advisor(advisor: JsonField) = apply { this.advisor = advisor } + + /** AMFI code for the scheme (CAMS/KFintech) */ + fun amfi(amfi: String) = amfi(JsonField.of(amfi)) + + /** + * Sets [Builder.amfi] to an arbitrary JSON value. + * + * You should usually call [Builder.amfi] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun amfi(amfi: JsonField) = apply { this.amfi = amfi } + + /** Closing balance units for the statement period */ + 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 + } + + /** Opening balance units for the statement period */ + 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 + } + + /** RTA code for the scheme (CAMS/KFintech) */ + fun rtaCode(rtaCode: String) = rtaCode(JsonField.of(rtaCode)) + + /** + * Sets [Builder.rtaCode] to an arbitrary JSON value. + * + * You should usually call [Builder.rtaCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun rtaCode(rtaCode: JsonField) = apply { this.rtaCode = rtaCode } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo( + advisor, + amfi, + closeUnits, + openUnits, + rtaCode, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + advisor() + amfi() + closeUnits() + openUnits() + rtaCode() + 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 (advisor.asKnown().isPresent) 1 else 0) + + (if (amfi.asKnown().isPresent) 1 else 0) + + (if (closeUnits.asKnown().isPresent) 1 else 0) + + (if (openUnits.asKnown().isPresent) 1 else 0) + + (if (rtaCode.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is AdditionalInfo && + advisor == other.advisor && + amfi == other.amfi && + closeUnits == other.closeUnits && + openUnits == other.openUnits && + rtaCode == other.rtaCode && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + advisor, + amfi, + closeUnits, + openUnits, + rtaCode, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AdditionalInfo{advisor=$advisor, amfi=$amfi, closeUnits=$closeUnits, openUnits=$openUnits, rtaCode=$rtaCode, additionalProperties=$additionalProperties}" + } + + class Gain + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val absolute: JsonField, + private val percentage: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("absolute") + @ExcludeMissing + absolute: JsonField = JsonMissing.of(), + @JsonProperty("percentage") + @ExcludeMissing + percentage: JsonField = JsonMissing.of(), + ) : this(absolute, percentage, mutableMapOf()) + + /** + * Absolute gain or loss + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun absolute(): Optional = absolute.getOptional("absolute") + + /** + * Percentage gain or loss + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun percentage(): Optional = percentage.getOptional("percentage") + + /** + * Returns the raw JSON value of [absolute]. + * + * Unlike [absolute], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("absolute") + @ExcludeMissing + fun _absolute(): JsonField = absolute + + /** + * Returns the raw JSON value of [percentage]. + * + * Unlike [percentage], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("percentage") + @ExcludeMissing + fun _percentage(): JsonField = percentage + + @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 [Gain]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Gain]. */ + class Builder internal constructor() { + + private var absolute: JsonField = JsonMissing.of() + private var percentage: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(gain: Gain) = apply { + absolute = gain.absolute + percentage = gain.percentage + additionalProperties = gain.additionalProperties.toMutableMap() + } + + /** Absolute gain or loss */ + fun absolute(absolute: Float) = absolute(JsonField.of(absolute)) + + /** + * Sets [Builder.absolute] to an arbitrary JSON value. + * + * You should usually call [Builder.absolute] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun absolute(absolute: JsonField) = apply { this.absolute = absolute } + + /** Percentage gain or loss */ + fun percentage(percentage: Float) = percentage(JsonField.of(percentage)) + + /** + * Sets [Builder.percentage] to an arbitrary JSON value. + * + * You should usually call [Builder.percentage] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun percentage(percentage: JsonField) = apply { + this.percentage = percentage + } + + 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 [Gain]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Gain = + Gain(absolute, percentage, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Gain = apply { + if (validated) { + return@apply + } + + absolute() + percentage() + 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 (absolute.asKnown().isPresent) 1 else 0) + + (if (percentage.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Gain && + absolute == other.absolute && + percentage == other.percentage && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(absolute, percentage, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Gain{absolute=$absolute, percentage=$percentage, additionalProperties=$additionalProperties}" + } + + /** Type of mutual fund scheme */ + class Type @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 EQUITY = of("Equity") + + @JvmField val DEBT = of("Debt") + + @JvmField val HYBRID = of("Hybrid") + + @JvmField val OTHER = of("Other") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + EQUITY, + DEBT, + HYBRID, + OTHER, + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] 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 { + EQUITY, + DEBT, + HYBRID, + OTHER, + /** + * An enum member indicating that [Type] 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) { + EQUITY -> Value.EQUITY + DEBT -> Value.DEBT + HYBRID -> Value.HYBRID + OTHER -> Value.OTHER + 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) { + EQUITY -> Known.EQUITY + DEBT -> Known.DEBT + HYBRID -> Known.HYBRID + OTHER -> Known.OTHER + else -> throw CasParserInvalidDataException("Unknown Type: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Type = 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 Type && 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 Scheme && + additionalInfo == other.additionalInfo && + cost == other.cost && + gain == other.gain && + isin == other.isin && + name == other.name && + nav == other.nav && + nominees == other.nominees && + transactions == other.transactions && + type == other.type && + units == other.units && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + cost, + gain, + isin, + name, + nav, + nominees, + transactions, + type, + units, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Scheme{additionalInfo=$additionalInfo, cost=$cost, gain=$gain, isin=$isin, name=$name, nav=$nav, nominees=$nominees, transactions=$transactions, type=$type, units=$units, value=$value, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is MutualFund && + additionalInfo == other.additionalInfo && + amc == other.amc && + folioNumber == other.folioNumber && + linkedHolders == other.linkedHolders && + registrar == other.registrar && + schemes == other.schemes && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + amc, + folioNumber, + linkedHolders, + registrar, + schemes, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MutualFund{additionalInfo=$additionalInfo, amc=$amc, folioNumber=$folioNumber, linkedHolders=$linkedHolders, registrar=$registrar, schemes=$schemes, value=$value, additionalProperties=$additionalProperties}" + } + + class Np + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val additionalInfo: JsonValue, + private val cra: JsonField, + private val funds: JsonField>, + private val linkedHolders: JsonField>, + private val pran: JsonField, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + additionalInfo: JsonValue = JsonMissing.of(), + @JsonProperty("cra") @ExcludeMissing cra: JsonField = JsonMissing.of(), + @JsonProperty("funds") @ExcludeMissing funds: JsonField> = JsonMissing.of(), + @JsonProperty("linked_holders") + @ExcludeMissing + linkedHolders: JsonField> = JsonMissing.of(), + @JsonProperty("pran") @ExcludeMissing pran: JsonField = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this(additionalInfo, cra, funds, linkedHolders, pran, value, mutableMapOf()) + + /** + * Additional information specific to the NPS account + * + * This arbitrary value can be deserialized into a custom type using the `convert` method: + * ```java + * MyClass myObject = np.additionalInfo().convert(MyClass.class); + * ``` + */ + @JsonProperty("additional_info") + @ExcludeMissing + fun _additionalInfo(): JsonValue = additionalInfo + + /** + * Central Record Keeping Agency name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun cra(): Optional = cra.getOptional("cra") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun funds(): Optional> = funds.getOptional("funds") + + /** + * List of account holders linked to this NPS 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") + + /** + * Permanent Retirement Account Number (PRAN) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pran(): Optional = pran.getOptional("pran") + + /** + * Total value of the NPS account + * + * @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") + + /** + * Returns the raw JSON value of [cra]. + * + * Unlike [cra], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cra") @ExcludeMissing fun _cra(): JsonField = cra + + /** + * Returns the raw JSON value of [funds]. + * + * Unlike [funds], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("funds") @ExcludeMissing fun _funds(): JsonField> = funds + + /** + * 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 [pran]. + * + * Unlike [pran], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pran") @ExcludeMissing fun _pran(): JsonField = pran + + /** + * 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 + + @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 [Np]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Np]. */ + class Builder internal constructor() { + + private var additionalInfo: JsonValue = JsonMissing.of() + private var cra: JsonField = JsonMissing.of() + private var funds: JsonField>? = null + private var linkedHolders: JsonField>? = null + private var pran: JsonField = JsonMissing.of() + private var value: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(np: Np) = apply { + additionalInfo = np.additionalInfo + cra = np.cra + funds = np.funds.map { it.toMutableList() } + linkedHolders = np.linkedHolders.map { it.toMutableList() } + pran = np.pran + value = np.value + additionalProperties = np.additionalProperties.toMutableMap() + } + + /** Additional information specific to the NPS account */ + fun additionalInfo(additionalInfo: JsonValue) = apply { + this.additionalInfo = additionalInfo + } + + /** Central Record Keeping Agency name */ + fun cra(cra: String) = cra(JsonField.of(cra)) + + /** + * Sets [Builder.cra] to an arbitrary JSON value. + * + * You should usually call [Builder.cra] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun cra(cra: JsonField) = apply { this.cra = cra } + + fun funds(funds: List) = funds(JsonField.of(funds)) + + /** + * Sets [Builder.funds] to an arbitrary JSON value. + * + * You should usually call [Builder.funds] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun funds(funds: JsonField>) = apply { + this.funds = funds.map { it.toMutableList() } + } + + /** + * Adds a single [Fund] to [funds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addFund(fund: Fund) = apply { + funds = + (funds ?: JsonField.of(mutableListOf())).also { + checkKnown("funds", it).add(fund) + } + } + + /** List of account holders linked to this NPS 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) + } + } + + /** Permanent Retirement Account Number (PRAN) */ + fun pran(pran: String) = pran(JsonField.of(pran)) + + /** + * Sets [Builder.pran] to an arbitrary JSON value. + * + * You should usually call [Builder.pran] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun pran(pran: JsonField) = apply { this.pran = pran } + + /** Total value of the NPS account */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [Np]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Np = + Np( + additionalInfo, + cra, + (funds ?: JsonMissing.of()).map { it.toImmutable() }, + (linkedHolders ?: JsonMissing.of()).map { it.toImmutable() }, + pran, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Np = apply { + if (validated) { + return@apply + } + + cra() + funds().ifPresent { it.forEach { it.validate() } } + linkedHolders().ifPresent { it.forEach { it.validate() } } + pran() + value() + 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 (cra.asKnown().isPresent) 1 else 0) + + (funds.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (linkedHolders.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (pran.asKnown().isPresent) 1 else 0) + + (if (value.asKnown().isPresent) 1 else 0) + + class Fund + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val additionalInfo: JsonField, + private val cost: JsonField, + private val name: JsonField, + private val nav: JsonField, + private val units: JsonField, + private val value: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("additional_info") + @ExcludeMissing + additionalInfo: JsonField = JsonMissing.of(), + @JsonProperty("cost") @ExcludeMissing cost: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("nav") @ExcludeMissing nav: JsonField = JsonMissing.of(), + @JsonProperty("units") @ExcludeMissing units: JsonField = JsonMissing.of(), + @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(), + ) : this(additionalInfo, cost, name, nav, units, value, mutableMapOf()) + + /** + * Additional information specific to the NPS fund + * + * @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") + + /** + * Cost of investment + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun cost(): Optional = cost.getOptional("cost") + + /** + * Name of the NPS fund + * + * @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") + + /** + * Net Asset Value per unit + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun nav(): Optional = nav.getOptional("nav") + + /** + * 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") + + /** + * 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") + + /** + * 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 [cost]. + * + * Unlike [cost], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cost") @ExcludeMissing fun _cost(): JsonField = cost + + /** + * 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 + + /** + * Returns the raw JSON value of [nav]. + * + * Unlike [nav], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("nav") @ExcludeMissing fun _nav(): JsonField = nav + + /** + * 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 + + /** + * 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 + + @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 [Fund]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Fund]. */ + class Builder internal constructor() { + + private var additionalInfo: JsonField = JsonMissing.of() + private var cost: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var nav: JsonField = JsonMissing.of() + private var units: JsonField = JsonMissing.of() + private var value: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(fund: Fund) = apply { + additionalInfo = fund.additionalInfo + cost = fund.cost + name = fund.name + nav = fund.nav + units = fund.units + value = fund.value + additionalProperties = fund.additionalProperties.toMutableMap() + } + + /** Additional information specific to the NPS fund */ + 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 + } + + /** Cost of investment */ + fun cost(cost: Float) = cost(JsonField.of(cost)) + + /** + * Sets [Builder.cost] to an arbitrary JSON value. + * + * You should usually call [Builder.cost] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cost(cost: JsonField) = apply { this.cost = cost } + + /** Name of the NPS fund */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** Net Asset Value per unit */ + fun nav(nav: Float) = nav(JsonField.of(nav)) + + /** + * Sets [Builder.nav] to an arbitrary JSON value. + * + * You should usually call [Builder.nav] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun nav(nav: JsonField) = apply { this.nav = nav } + + /** Number of units held */ + fun units(units: Float) = units(JsonField.of(units)) + + /** + * Sets [Builder.units] to an arbitrary JSON value. + * + * You should usually call [Builder.units] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun units(units: JsonField) = apply { this.units = units } + + /** Current market value of the holding */ + fun value(value: Float) = value(JsonField.of(value)) + + /** + * Sets [Builder.value] to an arbitrary JSON value. + * + * You should usually call [Builder.value] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun value(value: JsonField) = apply { this.value = value } + + 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 [Fund]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Fund = + Fund( + additionalInfo, + cost, + name, + nav, + units, + value, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Fund = apply { + if (validated) { + return@apply + } + + additionalInfo().ifPresent { it.validate() } + cost() + name() + nav() + units() + value() + 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 = + (additionalInfo.asKnown().getOrNull()?.validity() ?: 0) + + (if (cost.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (nav.asKnown().isPresent) 1 else 0) + + (if (units.asKnown().isPresent) 1 else 0) + + (if (value.asKnown().isPresent) 1 else 0) + + /** Additional information specific to the NPS fund */ + class AdditionalInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val manager: JsonField, + private val tier: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("manager") + @ExcludeMissing + manager: JsonField = JsonMissing.of(), + @JsonProperty("tier") @ExcludeMissing tier: JsonField = JsonMissing.of(), + ) : this(manager, tier, mutableMapOf()) + + /** + * Fund manager name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun manager(): Optional = manager.getOptional("manager") + + /** + * NPS tier (Tier I or Tier II) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun tier(): Optional = tier.getOptional("tier") + + /** + * Returns the raw JSON value of [manager]. + * + * Unlike [manager], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("manager") @ExcludeMissing fun _manager(): JsonField = manager + + /** + * Returns the raw JSON value of [tier]. + * + * Unlike [tier], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("tier") @ExcludeMissing fun _tier(): JsonField = tier + + @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 [AdditionalInfo]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AdditionalInfo]. */ + class Builder internal constructor() { + + private var manager: JsonField = JsonMissing.of() + private var tier: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(additionalInfo: AdditionalInfo) = apply { + manager = additionalInfo.manager + tier = additionalInfo.tier + additionalProperties = additionalInfo.additionalProperties.toMutableMap() + } + + /** Fund manager name */ + fun manager(manager: String) = manager(JsonField.of(manager)) + + /** + * Sets [Builder.manager] to an arbitrary JSON value. + * + * You should usually call [Builder.manager] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun manager(manager: JsonField) = apply { this.manager = manager } + + /** NPS tier (Tier I or Tier II) */ + fun tier(tier: Tier?) = tier(JsonField.ofNullable(tier)) + + /** Alias for calling [Builder.tier] with `tier.orElse(null)`. */ + fun tier(tier: Optional) = tier(tier.getOrNull()) + + /** + * Sets [Builder.tier] to an arbitrary JSON value. + * + * You should usually call [Builder.tier] with a well-typed [Tier] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun tier(tier: JsonField) = apply { this.tier = tier } + + 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 [AdditionalInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AdditionalInfo = + AdditionalInfo(manager, tier, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): AdditionalInfo = apply { + if (validated) { + return@apply + } + + manager() + tier().ifPresent { it.validate() } + 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 (manager.asKnown().isPresent) 1 else 0) + + (tier.asKnown().getOrNull()?.validity() ?: 0) + + /** NPS tier (Tier I or Tier II) */ + class Tier @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 _1 = of(1.0) + + @JvmField val _2 = of(2.0) + + @JvmStatic fun of(value: Double) = Tier(JsonField.of(value)) + } + + /** An enum containing [Tier]'s known values. */ + enum class Known { + _1, + _2, + } + + /** + * An enum containing [Tier]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Tier] 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 { + _1, + _2, + /** + * An enum member indicating that [Tier] 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) { + _1 -> Value._1 + _2 -> Value._2 + 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) { + _1 -> Known._1 + _2 -> Known._2 + else -> throw CasParserInvalidDataException("Unknown Tier: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * @throws CasParserInvalidDataException if this class instance's value does not + * have the expected primitive type. + */ + fun asDouble(): Double = + _value().asNumber().getOrNull()?.toDouble() + ?: throw CasParserInvalidDataException("Value is not a Double") + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected + * types recursively. + * + * This method is _not_ forwards compatible with new types from the API for + * existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object + * doesn't match its expected type. + */ + fun validate(): Tier = 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 Tier && 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 AdditionalInfo && + manager == other.manager && + tier == other.tier && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(manager, tier, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AdditionalInfo{manager=$manager, tier=$tier, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Fund && + additionalInfo == other.additionalInfo && + cost == other.cost && + name == other.name && + nav == other.nav && + units == other.units && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(additionalInfo, cost, name, nav, units, value, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Fund{additionalInfo=$additionalInfo, cost=$cost, name=$name, nav=$nav, units=$units, value=$value, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Np && + additionalInfo == other.additionalInfo && + cra == other.cra && + funds == other.funds && + linkedHolders == other.linkedHolders && + pran == other.pran && + value == other.value && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + additionalInfo, + cra, + funds, + linkedHolders, + pran, + value, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Np{additionalInfo=$additionalInfo, cra=$cra, funds=$funds, linkedHolders=$linkedHolders, pran=$pran, value=$value, additionalProperties=$additionalProperties}" + } + + class Summary + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val accounts: JsonField, + private val totalValue: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("accounts") + @ExcludeMissing + accounts: JsonField = JsonMissing.of(), + @JsonProperty("total_value") + @ExcludeMissing + totalValue: JsonField = JsonMissing.of(), + ) : this(accounts, totalValue, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun accounts(): Optional = accounts.getOptional("accounts") + + /** + * Total portfolio value across all accounts + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun totalValue(): Optional = totalValue.getOptional("total_value") + + /** + * Returns the raw JSON value of [accounts]. + * + * Unlike [accounts], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("accounts") @ExcludeMissing fun _accounts(): JsonField = accounts + + /** + * Returns the raw JSON value of [totalValue]. + * + * Unlike [totalValue], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("total_value") + @ExcludeMissing + fun _totalValue(): JsonField = totalValue + + @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 [Summary]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Summary]. */ + class Builder internal constructor() { + + private var accounts: JsonField = JsonMissing.of() + private var totalValue: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(summary: Summary) = apply { + accounts = summary.accounts + totalValue = summary.totalValue + additionalProperties = summary.additionalProperties.toMutableMap() + } + + fun accounts(accounts: Accounts) = accounts(JsonField.of(accounts)) + + /** + * Sets [Builder.accounts] to an arbitrary JSON value. + * + * You should usually call [Builder.accounts] with a well-typed [Accounts] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun accounts(accounts: JsonField) = apply { this.accounts = accounts } + + /** Total portfolio value across all accounts */ + fun totalValue(totalValue: Float) = totalValue(JsonField.of(totalValue)) + + /** + * Sets [Builder.totalValue] to an arbitrary JSON value. + * + * You should usually call [Builder.totalValue] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun totalValue(totalValue: JsonField) = apply { this.totalValue = totalValue } + + 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 [Summary]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Summary = + Summary(accounts, totalValue, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Summary = apply { + if (validated) { + return@apply + } + + accounts().ifPresent { it.validate() } + totalValue() + 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 = + (accounts.asKnown().getOrNull()?.validity() ?: 0) + + (if (totalValue.asKnown().isPresent) 1 else 0) + + class Accounts + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val demat: JsonField, + private val insurance: JsonField, + private val mutualFunds: JsonField, + private val nps: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("demat") @ExcludeMissing demat: JsonField = JsonMissing.of(), + @JsonProperty("insurance") + @ExcludeMissing + insurance: JsonField = JsonMissing.of(), + @JsonProperty("mutual_funds") + @ExcludeMissing + mutualFunds: JsonField = JsonMissing.of(), + @JsonProperty("nps") @ExcludeMissing nps: JsonField = JsonMissing.of(), + ) : this(demat, insurance, mutualFunds, nps, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun demat(): Optional = demat.getOptional("demat") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun insurance(): Optional = insurance.getOptional("insurance") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun mutualFunds(): Optional = mutualFunds.getOptional("mutual_funds") + + /** + * @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") + + /** + * Returns the raw JSON value of [demat]. + * + * Unlike [demat], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("demat") @ExcludeMissing fun _demat(): JsonField = demat + + /** + * Returns the raw JSON value of [insurance]. + * + * Unlike [insurance], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("insurance") + @ExcludeMissing + fun _insurance(): JsonField = insurance + + /** + * Returns the raw JSON value of [mutualFunds]. + * + * Unlike [mutualFunds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("mutual_funds") + @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 + + @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 [Accounts]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Accounts]. */ + class Builder internal constructor() { + + private var demat: JsonField = JsonMissing.of() + private var insurance: JsonField = JsonMissing.of() + private var mutualFunds: JsonField = JsonMissing.of() + private var nps: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(accounts: Accounts) = apply { + demat = accounts.demat + insurance = accounts.insurance + mutualFunds = accounts.mutualFunds + nps = accounts.nps + additionalProperties = accounts.additionalProperties.toMutableMap() + } + + fun demat(demat: Demat) = demat(JsonField.of(demat)) + + /** + * Sets [Builder.demat] to an arbitrary JSON value. + * + * You should usually call [Builder.demat] with a well-typed [Demat] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun demat(demat: JsonField) = apply { this.demat = demat } + + fun insurance(insurance: Insurance) = insurance(JsonField.of(insurance)) + + /** + * Sets [Builder.insurance] to an arbitrary JSON value. + * + * You should usually call [Builder.insurance] with a well-typed [Insurance] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun insurance(insurance: JsonField) = apply { + this.insurance = insurance + } + + fun mutualFunds(mutualFunds: MutualFunds) = mutualFunds(JsonField.of(mutualFunds)) + + /** + * Sets [Builder.mutualFunds] to an arbitrary JSON value. + * + * You should usually call [Builder.mutualFunds] with a well-typed [MutualFunds] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun mutualFunds(mutualFunds: JsonField) = apply { + this.mutualFunds = mutualFunds + } + + fun nps(nps: Nps) = nps(JsonField.of(nps)) + + /** + * Sets [Builder.nps] to an arbitrary JSON value. + * + * You should usually call [Builder.nps] with a well-typed [Nps] 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 } + + 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 [Accounts]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Accounts = + Accounts( + demat, + insurance, + mutualFunds, + nps, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Accounts = apply { + if (validated) { + return@apply + } + + demat().ifPresent { it.validate() } + insurance().ifPresent { it.validate() } + mutualFunds().ifPresent { it.validate() } + nps().ifPresent { it.validate() } + 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 = + (demat.asKnown().getOrNull()?.validity() ?: 0) + + (insurance.asKnown().getOrNull()?.validity() ?: 0) + + (mutualFunds.asKnown().getOrNull()?.validity() ?: 0) + + (nps.asKnown().getOrNull()?.validity() ?: 0) + + class Demat + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val count: JsonField, + private val totalValue: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("count") + @ExcludeMissing + count: JsonField = JsonMissing.of(), + @JsonProperty("total_value") + @ExcludeMissing + totalValue: JsonField = JsonMissing.of(), + ) : this(count, totalValue, mutableMapOf()) + + /** + * Number of demat accounts + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun count(): Optional = count.getOptional("count") + + /** + * Total value of demat accounts + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun totalValue(): Optional = totalValue.getOptional("total_value") + + /** + * Returns the raw JSON value of [count]. + * + * Unlike [count], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("count") @ExcludeMissing fun _count(): JsonField = count + + /** + * Returns the raw JSON value of [totalValue]. + * + * Unlike [totalValue], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("total_value") + @ExcludeMissing + fun _totalValue(): JsonField = totalValue + + @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 [Demat]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Demat]. */ + class Builder internal constructor() { + + private var count: JsonField = JsonMissing.of() + private var totalValue: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(demat: Demat) = apply { + count = demat.count + totalValue = demat.totalValue + additionalProperties = demat.additionalProperties.toMutableMap() + } + + /** Number of demat accounts */ + fun count(count: Long) = count(JsonField.of(count)) + + /** + * Sets [Builder.count] to an arbitrary JSON value. + * + * You should usually call [Builder.count] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun count(count: JsonField) = apply { this.count = count } + + /** Total value of demat accounts */ + fun totalValue(totalValue: Float) = totalValue(JsonField.of(totalValue)) + + /** + * Sets [Builder.totalValue] to an arbitrary JSON value. + * + * You should usually call [Builder.totalValue] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun totalValue(totalValue: JsonField) = apply { + this.totalValue = totalValue + } + + 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 [Demat]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Demat = + Demat(count, totalValue, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Demat = apply { + if (validated) { + return@apply + } + + count() + totalValue() + 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 (count.asKnown().isPresent) 1 else 0) + + (if (totalValue.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Demat && + count == other.count && + totalValue == other.totalValue && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(count, totalValue, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Demat{count=$count, totalValue=$totalValue, additionalProperties=$additionalProperties}" + } + + class Insurance + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val count: JsonField, + private val totalValue: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("count") + @ExcludeMissing + count: JsonField = JsonMissing.of(), + @JsonProperty("total_value") + @ExcludeMissing + totalValue: JsonField = JsonMissing.of(), + ) : this(count, totalValue, mutableMapOf()) + + /** + * Number of insurance policies + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun count(): Optional = count.getOptional("count") + + /** + * Total value of insurance policies + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun totalValue(): Optional = totalValue.getOptional("total_value") + + /** + * Returns the raw JSON value of [count]. + * + * Unlike [count], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("count") @ExcludeMissing fun _count(): JsonField = count + + /** + * Returns the raw JSON value of [totalValue]. + * + * Unlike [totalValue], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("total_value") + @ExcludeMissing + fun _totalValue(): JsonField = totalValue + + @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 [Insurance]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Insurance]. */ + class Builder internal constructor() { + + private var count: JsonField = JsonMissing.of() + private var totalValue: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(insurance: Insurance) = apply { + count = insurance.count + totalValue = insurance.totalValue + additionalProperties = insurance.additionalProperties.toMutableMap() + } + + /** Number of insurance policies */ + fun count(count: Long) = count(JsonField.of(count)) + + /** + * Sets [Builder.count] to an arbitrary JSON value. + * + * You should usually call [Builder.count] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun count(count: JsonField) = apply { this.count = count } + + /** Total value of insurance policies */ + fun totalValue(totalValue: Float) = totalValue(JsonField.of(totalValue)) + + /** + * Sets [Builder.totalValue] to an arbitrary JSON value. + * + * You should usually call [Builder.totalValue] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun totalValue(totalValue: JsonField) = apply { + this.totalValue = totalValue + } + + 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 [Insurance]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Insurance = + Insurance(count, totalValue, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Insurance = apply { + if (validated) { + return@apply + } + + count() + totalValue() + 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 (count.asKnown().isPresent) 1 else 0) + + (if (totalValue.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Insurance && + count == other.count && + totalValue == other.totalValue && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(count, totalValue, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Insurance{count=$count, totalValue=$totalValue, additionalProperties=$additionalProperties}" + } + + class MutualFunds + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val count: JsonField, + private val totalValue: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("count") + @ExcludeMissing + count: JsonField = JsonMissing.of(), + @JsonProperty("total_value") + @ExcludeMissing + totalValue: JsonField = JsonMissing.of(), + ) : this(count, totalValue, mutableMapOf()) + + /** + * Number of mutual fund folios + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun count(): Optional = count.getOptional("count") + + /** + * Total value of mutual funds + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun totalValue(): Optional = totalValue.getOptional("total_value") + + /** + * Returns the raw JSON value of [count]. + * + * Unlike [count], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("count") @ExcludeMissing fun _count(): JsonField = count + + /** + * Returns the raw JSON value of [totalValue]. + * + * Unlike [totalValue], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("total_value") + @ExcludeMissing + fun _totalValue(): JsonField = totalValue + + @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 [MutualFunds]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MutualFunds]. */ + class Builder internal constructor() { + + private var count: JsonField = JsonMissing.of() + private var totalValue: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(mutualFunds: MutualFunds) = apply { + count = mutualFunds.count + totalValue = mutualFunds.totalValue + additionalProperties = mutualFunds.additionalProperties.toMutableMap() + } + + /** Number of mutual fund folios */ + fun count(count: Long) = count(JsonField.of(count)) + + /** + * Sets [Builder.count] to an arbitrary JSON value. + * + * You should usually call [Builder.count] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun count(count: JsonField) = apply { this.count = count } + + /** Total value of mutual funds */ + fun totalValue(totalValue: Float) = totalValue(JsonField.of(totalValue)) + + /** + * Sets [Builder.totalValue] to an arbitrary JSON value. + * + * You should usually call [Builder.totalValue] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun totalValue(totalValue: JsonField) = apply { + this.totalValue = totalValue + } + + 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 [MutualFunds]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): MutualFunds = + MutualFunds(count, totalValue, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): MutualFunds = apply { + if (validated) { + return@apply + } + + count() + totalValue() + 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 (count.asKnown().isPresent) 1 else 0) + + (if (totalValue.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is MutualFunds && + count == other.count && + totalValue == other.totalValue && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(count, totalValue, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MutualFunds{count=$count, totalValue=$totalValue, additionalProperties=$additionalProperties}" + } + + class Nps + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val count: JsonField, + private val totalValue: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("count") + @ExcludeMissing + count: JsonField = JsonMissing.of(), + @JsonProperty("total_value") + @ExcludeMissing + totalValue: JsonField = JsonMissing.of(), + ) : this(count, totalValue, mutableMapOf()) + + /** + * Number of NPS accounts + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun count(): Optional = count.getOptional("count") + + /** + * Total value of NPS accounts + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type + * (e.g. if the server responded with an unexpected value). + */ + fun totalValue(): Optional = totalValue.getOptional("total_value") + + /** + * Returns the raw JSON value of [count]. + * + * Unlike [count], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("count") @ExcludeMissing fun _count(): JsonField = count + + /** + * Returns the raw JSON value of [totalValue]. + * + * Unlike [totalValue], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("total_value") + @ExcludeMissing + fun _totalValue(): JsonField = totalValue + + @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 [Nps]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Nps]. */ + class Builder internal constructor() { + + private var count: JsonField = JsonMissing.of() + private var totalValue: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(nps: Nps) = apply { + count = nps.count + totalValue = nps.totalValue + additionalProperties = nps.additionalProperties.toMutableMap() + } + + /** Number of NPS accounts */ + fun count(count: Long) = count(JsonField.of(count)) + + /** + * Sets [Builder.count] to an arbitrary JSON value. + * + * You should usually call [Builder.count] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun count(count: JsonField) = apply { this.count = count } + + /** Total value of NPS accounts */ + fun totalValue(totalValue: Float) = totalValue(JsonField.of(totalValue)) + + /** + * Sets [Builder.totalValue] to an arbitrary JSON value. + * + * You should usually call [Builder.totalValue] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun totalValue(totalValue: JsonField) = apply { + this.totalValue = totalValue + } + + 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 [Nps]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Nps = Nps(count, totalValue, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): Nps = apply { + if (validated) { + return@apply + } + + count() + totalValue() + 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 (count.asKnown().isPresent) 1 else 0) + + (if (totalValue.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Nps && + count == other.count && + totalValue == other.totalValue && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(count, totalValue, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Nps{count=$count, totalValue=$totalValue, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Accounts && + demat == other.demat && + insurance == other.insurance && + mutualFunds == other.mutualFunds && + nps == other.nps && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(demat, insurance, mutualFunds, nps, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Accounts{demat=$demat, insurance=$insurance, mutualFunds=$mutualFunds, nps=$nps, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Summary && + accounts == other.accounts && + totalValue == other.totalValue && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(accounts, totalValue, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Summary{accounts=$accounts, totalValue=$totalValue, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is UnifiedResponse && + dematAccounts == other.dematAccounts && + insurance == other.insurance && + investor == other.investor && + meta == other.meta && + mutualFunds == other.mutualFunds && + nps == other.nps && + summary == other.summary && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + dematAccounts, + insurance, + investor, + meta, + mutualFunds, + nps, + summary, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnifiedResponse{dematAccounts=$dematAccounts, insurance=$insurance, investor=$investor, meta=$meta, mutualFunds=$mutualFunds, nps=$nps, summary=$summary, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/CdslParsePdfParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/CdslParsePdfParams.kt new file mode 100644 index 0000000..81df339 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/CdslParsePdfParams.kt @@ -0,0 +1,511 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl + +import com.cas_parser.api.core.ExcludeMissing +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.MultipartField +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +import com.cas_parser.api.errors.CasParserInvalidDataException +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonProperty +import java.util.Collections +import java.util.Objects +import java.util.Optional + +/** + * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and returns + * data in a unified format. Use this endpoint when you know the PDF is from CDSL. + */ +class CdslParsePdfParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun password(): Optional = body.password() + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfFile(): Optional = body.pdfFile() + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfUrl(): Optional = body.pdfUrl() + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _password(): MultipartField = body._password() + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfFile(): MultipartField = body._pdfFile() + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfUrl(): MultipartField = body._pdfUrl() + + 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 { + + @JvmStatic fun none(): CdslParsePdfParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [CdslParsePdfParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CdslParsePdfParams]. */ + 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(cdslParsePdfParams: CdslParsePdfParams) = apply { + body = cdslParsePdfParams.body.toBuilder() + additionalHeaders = cdslParsePdfParams.additionalHeaders.toBuilder() + additionalQueryParams = cdslParsePdfParams.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: + * - [password] + * - [pdfFile] + * - [pdfUrl] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Password for the PDF file (if required) */ + fun password(password: String) = apply { body.password(password) } + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { body.password(password) } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = apply { body.pdfFile(pdfFile) } + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { body.pdfFile(pdfFile) } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = apply { body.pdfUrl(pdfUrl) } + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { body.pdfUrl(pdfUrl) } + + 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 [CdslParsePdfParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CdslParsePdfParams = + CdslParsePdfParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Map> = + (mapOf("password" to _password(), "pdf_file" to _pdfFile(), "pdf_url" to _pdfUrl()) + + _additionalBodyProperties().mapValues { (_, value) -> MultipartField.of(value) }) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** Provide either `pdf_file` OR `pdf_url` (one is required) */ + class Body + private constructor( + private val password: MultipartField, + private val pdfFile: MultipartField, + private val pdfUrl: MultipartField, + private val additionalProperties: MutableMap, + ) { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun password(): Optional = password.value.getOptional("password") + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfFile(): Optional = pdfFile.value.getOptional("pdf_file") + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfUrl(): Optional = pdfUrl.value.getOptional("pdf_url") + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("password") @ExcludeMissing fun _password(): MultipartField = password + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("pdf_file") @ExcludeMissing fun _pdfFile(): MultipartField = pdfFile + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + @JsonProperty("pdf_url") @ExcludeMissing fun _pdfUrl(): MultipartField = pdfUrl + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var password: MultipartField = MultipartField.of(null) + private var pdfFile: MultipartField = MultipartField.of(null) + private var pdfUrl: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + password = body.password + pdfFile = body.pdfFile + pdfUrl = body.pdfUrl + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Password for the PDF file (if required) */ + fun password(password: String) = password(MultipartField.of(password)) + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { this.password = password } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = pdfFile(MultipartField.of(pdfFile)) + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { this.pdfFile = pdfFile } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = pdfUrl(MultipartField.of(pdfUrl)) + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { this.pdfUrl = pdfUrl } + + 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. + */ + fun build(): Body = Body(password, pdfFile, pdfUrl, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + password() + pdfFile() + pdfUrl() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CasParserInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + password == other.password && + pdfFile == other.pdfFile && + pdfUrl == other.pdfUrl && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(password, pdfFile, pdfUrl, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{password=$password, pdfFile=$pdfFile, pdfUrl=$pdfUrl, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CdslParsePdfParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "CdslParsePdfParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpParams.kt new file mode 100644 index 0000000..e1509fd --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpParams.kt @@ -0,0 +1,570 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +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 + +/** + * **Step 1 of 2**: Request OTP for CDSL CAS fetch. + * + * This endpoint: + * 1. Solves reCAPTCHA automatically (~15-20 seconds) + * 2. Submits login credentials to CDSL portal + * 3. Triggers OTP to user's registered mobile number + * + * After user receives OTP, call `/v4/cdsl/fetch/{session_id}/verify` to complete. + */ +class FetchRequestOtpParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * CDSL BO ID (16 digits) + * + * @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 boId(): String = body.boId() + + /** + * Date of birth (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 dob(): String = body.dob() + + /** + * PAN number + * + * @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 pan(): String = body.pan() + + /** + * Returns the raw JSON value of [boId]. + * + * Unlike [boId], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _boId(): JsonField = body._boId() + + /** + * Returns the raw JSON value of [dob]. + * + * Unlike [dob], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _dob(): JsonField = body._dob() + + /** + * Returns the raw JSON value of [pan]. + * + * Unlike [pan], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _pan(): JsonField = body._pan() + + 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 [FetchRequestOtpParams]. + * + * The following fields are required: + * ```java + * .boId() + * .dob() + * .pan() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [FetchRequestOtpParams]. */ + 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(fetchRequestOtpParams: FetchRequestOtpParams) = apply { + body = fetchRequestOtpParams.body.toBuilder() + additionalHeaders = fetchRequestOtpParams.additionalHeaders.toBuilder() + additionalQueryParams = fetchRequestOtpParams.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: + * - [boId] + * - [dob] + * - [pan] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** CDSL BO ID (16 digits) */ + fun boId(boId: String) = apply { body.boId(boId) } + + /** + * Sets [Builder.boId] to an arbitrary JSON value. + * + * You should usually call [Builder.boId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun boId(boId: JsonField) = apply { body.boId(boId) } + + /** Date of birth (YYYY-MM-DD) */ + fun dob(dob: String) = apply { body.dob(dob) } + + /** + * Sets [Builder.dob] to an arbitrary JSON value. + * + * You should usually call [Builder.dob] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun dob(dob: JsonField) = apply { body.dob(dob) } + + /** PAN number */ + fun pan(pan: String) = apply { body.pan(pan) } + + /** + * Sets [Builder.pan] to an arbitrary JSON value. + * + * You should usually call [Builder.pan] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pan(pan: JsonField) = apply { body.pan(pan) } + + 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 [FetchRequestOtpParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .boId() + * .dob() + * .pan() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): FetchRequestOtpParams = + FetchRequestOtpParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val boId: JsonField, + private val dob: JsonField, + private val pan: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("bo_id") @ExcludeMissing boId: JsonField = JsonMissing.of(), + @JsonProperty("dob") @ExcludeMissing dob: JsonField = JsonMissing.of(), + @JsonProperty("pan") @ExcludeMissing pan: JsonField = JsonMissing.of(), + ) : this(boId, dob, pan, mutableMapOf()) + + /** + * CDSL BO ID (16 digits) + * + * @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 boId(): String = boId.getRequired("bo_id") + + /** + * Date of birth (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 dob(): String = dob.getRequired("dob") + + /** + * PAN number + * + * @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 pan(): String = pan.getRequired("pan") + + /** + * Returns the raw JSON value of [boId]. + * + * Unlike [boId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bo_id") @ExcludeMissing fun _boId(): JsonField = boId + + /** + * Returns the raw JSON value of [dob]. + * + * Unlike [dob], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dob") @ExcludeMissing fun _dob(): JsonField = dob + + /** + * Returns the raw JSON value of [pan]. + * + * Unlike [pan], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pan") @ExcludeMissing fun _pan(): JsonField = pan + + @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 + * .boId() + * .dob() + * .pan() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var boId: JsonField? = null + private var dob: JsonField? = null + private var pan: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + boId = body.boId + dob = body.dob + pan = body.pan + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** CDSL BO ID (16 digits) */ + fun boId(boId: String) = boId(JsonField.of(boId)) + + /** + * Sets [Builder.boId] to an arbitrary JSON value. + * + * You should usually call [Builder.boId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun boId(boId: JsonField) = apply { this.boId = boId } + + /** Date of birth (YYYY-MM-DD) */ + fun dob(dob: String) = dob(JsonField.of(dob)) + + /** + * Sets [Builder.dob] to an arbitrary JSON value. + * + * You should usually call [Builder.dob] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun dob(dob: JsonField) = apply { this.dob = dob } + + /** PAN number */ + fun pan(pan: String) = pan(JsonField.of(pan)) + + /** + * Sets [Builder.pan] to an arbitrary JSON value. + * + * You should usually call [Builder.pan] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun pan(pan: JsonField) = apply { this.pan = pan } + + 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 + * .boId() + * .dob() + * .pan() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("boId", boId), + checkRequired("dob", dob), + checkRequired("pan", pan), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + boId() + dob() + pan() + 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 (boId.asKnown().isPresent) 1 else 0) + + (if (dob.asKnown().isPresent) 1 else 0) + + (if (pan.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + boId == other.boId && + dob == other.dob && + pan == other.pan && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(boId, dob, pan, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{boId=$boId, dob=$dob, pan=$pan, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FetchRequestOtpParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "FetchRequestOtpParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpResponse.kt new file mode 100644 index 0000000..12de1bb --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpResponse.kt @@ -0,0 +1,227 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +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 FetchRequestOtpResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val msg: JsonField, + private val sessionId: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("msg") @ExcludeMissing msg: JsonField = JsonMissing.of(), + @JsonProperty("session_id") @ExcludeMissing sessionId: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(msg, sessionId, 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") + + /** + * Session ID for verify step + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun sessionId(): Optional = sessionId.getOptional("session_id") + + /** + * @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 [sessionId]. + * + * Unlike [sessionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("session_id") @ExcludeMissing fun _sessionId(): JsonField = sessionId + + /** + * 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 [FetchRequestOtpResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [FetchRequestOtpResponse]. */ + class Builder internal constructor() { + + private var msg: JsonField = JsonMissing.of() + private var sessionId: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(fetchRequestOtpResponse: FetchRequestOtpResponse) = apply { + msg = fetchRequestOtpResponse.msg + sessionId = fetchRequestOtpResponse.sessionId + status = fetchRequestOtpResponse.status + additionalProperties = fetchRequestOtpResponse.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 } + + /** Session ID for verify step */ + fun sessionId(sessionId: String) = sessionId(JsonField.of(sessionId)) + + /** + * Sets [Builder.sessionId] to an arbitrary JSON value. + * + * You should usually call [Builder.sessionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun sessionId(sessionId: JsonField) = apply { this.sessionId = sessionId } + + 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 [FetchRequestOtpResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): FetchRequestOtpResponse = + FetchRequestOtpResponse(msg, sessionId, status, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): FetchRequestOtpResponse = apply { + if (validated) { + return@apply + } + + msg() + sessionId() + 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 (sessionId.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 FetchRequestOtpResponse && + msg == other.msg && + sessionId == other.sessionId && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(msg, sessionId, status, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "FetchRequestOtpResponse{msg=$msg, sessionId=$sessionId, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpParams.kt new file mode 100644 index 0000000..0a81238 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpParams.kt @@ -0,0 +1,514 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +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 + +/** + * **Step 2 of 2**: Verify OTP and retrieve CDSL CAS files. + * + * After successful verification, CAS PDFs are fetched from CDSL portal, uploaded to cloud storage, + * and returned as direct download URLs. + */ +class FetchVerifyOtpParams +private constructor( + private val sessionId: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun sessionId(): Optional = Optional.ofNullable(sessionId) + + /** + * OTP received on mobile + * + * @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 otp(): String = body.otp() + + /** + * Number of monthly statements to fetch (default 6) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun numPeriods(): Optional = body.numPeriods() + + /** + * Returns the raw JSON value of [otp]. + * + * Unlike [otp], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _otp(): JsonField = body._otp() + + /** + * Returns the raw JSON value of [numPeriods]. + * + * Unlike [numPeriods], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _numPeriods(): JsonField = body._numPeriods() + + 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 [FetchVerifyOtpParams]. + * + * The following fields are required: + * ```java + * .otp() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [FetchVerifyOtpParams]. */ + class Builder internal constructor() { + + private var sessionId: String? = null + 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(fetchVerifyOtpParams: FetchVerifyOtpParams) = apply { + sessionId = fetchVerifyOtpParams.sessionId + body = fetchVerifyOtpParams.body.toBuilder() + additionalHeaders = fetchVerifyOtpParams.additionalHeaders.toBuilder() + additionalQueryParams = fetchVerifyOtpParams.additionalQueryParams.toBuilder() + } + + fun sessionId(sessionId: String?) = apply { this.sessionId = sessionId } + + /** Alias for calling [Builder.sessionId] with `sessionId.orElse(null)`. */ + fun sessionId(sessionId: Optional) = sessionId(sessionId.getOrNull()) + + /** + * 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: + * - [otp] + * - [numPeriods] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** OTP received on mobile */ + fun otp(otp: String) = apply { body.otp(otp) } + + /** + * Sets [Builder.otp] to an arbitrary JSON value. + * + * You should usually call [Builder.otp] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun otp(otp: JsonField) = apply { body.otp(otp) } + + /** Number of monthly statements to fetch (default 6) */ + fun numPeriods(numPeriods: Long) = apply { body.numPeriods(numPeriods) } + + /** + * Sets [Builder.numPeriods] to an arbitrary JSON value. + * + * You should usually call [Builder.numPeriods] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun numPeriods(numPeriods: JsonField) = apply { body.numPeriods(numPeriods) } + + 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 [FetchVerifyOtpParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .otp() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): FetchVerifyOtpParams = + FetchVerifyOtpParams( + sessionId, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> sessionId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val otp: JsonField, + private val numPeriods: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("otp") @ExcludeMissing otp: JsonField = JsonMissing.of(), + @JsonProperty("num_periods") + @ExcludeMissing + numPeriods: JsonField = JsonMissing.of(), + ) : this(otp, numPeriods, mutableMapOf()) + + /** + * OTP received on mobile + * + * @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 otp(): String = otp.getRequired("otp") + + /** + * Number of monthly statements to fetch (default 6) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun numPeriods(): Optional = numPeriods.getOptional("num_periods") + + /** + * Returns the raw JSON value of [otp]. + * + * Unlike [otp], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("otp") @ExcludeMissing fun _otp(): JsonField = otp + + /** + * Returns the raw JSON value of [numPeriods]. + * + * Unlike [numPeriods], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("num_periods") @ExcludeMissing fun _numPeriods(): JsonField = numPeriods + + @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 + * .otp() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var otp: JsonField? = null + private var numPeriods: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + otp = body.otp + numPeriods = body.numPeriods + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** OTP received on mobile */ + fun otp(otp: String) = otp(JsonField.of(otp)) + + /** + * Sets [Builder.otp] to an arbitrary JSON value. + * + * You should usually call [Builder.otp] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun otp(otp: JsonField) = apply { this.otp = otp } + + /** Number of monthly statements to fetch (default 6) */ + fun numPeriods(numPeriods: Long) = numPeriods(JsonField.of(numPeriods)) + + /** + * Sets [Builder.numPeriods] to an arbitrary JSON value. + * + * You should usually call [Builder.numPeriods] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun numPeriods(numPeriods: JsonField) = apply { this.numPeriods = numPeriods } + + 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 + * .otp() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body(checkRequired("otp", otp), numPeriods, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + otp() + numPeriods() + 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 (otp.asKnown().isPresent) 1 else 0) + (if (numPeriods.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + otp == other.otp && + numPeriods == other.numPeriods && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(otp, numPeriods, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{otp=$otp, numPeriods=$numPeriods, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FetchVerifyOtpParams && + sessionId == other.sessionId && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(sessionId, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "FetchVerifyOtpParams{sessionId=$sessionId, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpResponse.kt new file mode 100644 index 0000000..49d6bdf --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpResponse.kt @@ -0,0 +1,428 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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 + +class FetchVerifyOtpResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val files: JsonField>, + private val msg: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("files") @ExcludeMissing files: JsonField> = JsonMissing.of(), + @JsonProperty("msg") @ExcludeMissing msg: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(files, msg, status, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun files(): Optional> = files.getOptional("files") + + /** + * @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 [files]. + * + * Unlike [files], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("files") @ExcludeMissing fun _files(): JsonField> = files + + /** + * 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 [FetchVerifyOtpResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [FetchVerifyOtpResponse]. */ + class Builder internal constructor() { + + private var files: JsonField>? = null + private var msg: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(fetchVerifyOtpResponse: FetchVerifyOtpResponse) = apply { + files = fetchVerifyOtpResponse.files.map { it.toMutableList() } + msg = fetchVerifyOtpResponse.msg + status = fetchVerifyOtpResponse.status + additionalProperties = fetchVerifyOtpResponse.additionalProperties.toMutableMap() + } + + fun files(files: List) = files(JsonField.of(files)) + + /** + * Sets [Builder.files] to an arbitrary JSON value. + * + * You should usually call [Builder.files] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun files(files: JsonField>) = apply { + this.files = files.map { it.toMutableList() } + } + + /** + * Adds a single [File] to [files]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addFile(file: File) = apply { + files = + (files ?: JsonField.of(mutableListOf())).also { checkKnown("files", it).add(file) } + } + + 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 [FetchVerifyOtpResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): FetchVerifyOtpResponse = + FetchVerifyOtpResponse( + (files ?: JsonMissing.of()).map { it.toImmutable() }, + msg, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): FetchVerifyOtpResponse = apply { + if (validated) { + return@apply + } + + files().ifPresent { it.forEach { it.validate() } } + 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 = + (files.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (msg.asKnown().isPresent) 1 else 0) + + (if (status.asKnown().isPresent) 1 else 0) + + class File + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val filename: JsonField, + private val url: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("filename") + @ExcludeMissing + filename: JsonField = JsonMissing.of(), + @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), + ) : this(filename, url, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun filename(): Optional = filename.getOptional("filename") + + /** + * Direct download URL (cloud storage) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun url(): Optional = url.getOptional("url") + + /** + * Returns the raw JSON value of [filename]. + * + * Unlike [filename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("filename") @ExcludeMissing fun _filename(): JsonField = filename + + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + + @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 [File]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [File]. */ + class Builder internal constructor() { + + private var filename: JsonField = JsonMissing.of() + private var url: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(file: File) = apply { + filename = file.filename + url = file.url + additionalProperties = file.additionalProperties.toMutableMap() + } + + fun filename(filename: String) = filename(JsonField.of(filename)) + + /** + * Sets [Builder.filename] to an arbitrary JSON value. + * + * You should usually call [Builder.filename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun filename(filename: JsonField) = apply { this.filename = filename } + + /** Direct download URL (cloud storage) */ + fun url(url: String) = url(JsonField.of(url)) + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun url(url: JsonField) = apply { this.url = url } + + 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 [File]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): File = File(filename, url, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): File = apply { + if (validated) { + return@apply + } + + filename() + url() + 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 (filename.asKnown().isPresent) 1 else 0) + (if (url.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is File && + filename == other.filename && + url == other.url && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(filename, url, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "File{filename=$filename, url=$url, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FetchVerifyOtpResponse && + files == other.files && + msg == other.msg && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(files, msg, status, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "FetchVerifyOtpResponse{files=$files, msg=$msg, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseParams.kt new file mode 100644 index 0000000..eee36e7 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseParams.kt @@ -0,0 +1,760 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.contractnote + +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.JsonValue +import com.cas_parser.api.core.MultipartField +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +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 + +/** + * This endpoint parses Contract Note PDF files from various brokers including Zerodha, Groww, + * Upstox, ICICI Securities, and others. + * + * **What is a Contract Note?** A contract note is a legal document that provides details of all + * trades executed by an investor. It includes: + * - Trade details with timestamps, quantities, and prices + * - Brokerage and charges breakdown + * - Settlement information + * - Regulatory compliance details + * + * **Supported Brokers:** + * - Zerodha Broking Limited + * - Groww Invest Tech Private Limited + * - Upstox (RKSV Securities) + * - ICICI Securities Limited + * - Auto-detection for unknown brokers + * + * **Key Features:** + * - **Auto-detection**: Automatically identifies broker type from PDF content + * - **Comprehensive parsing**: Extracts equity transactions, derivatives transactions, detailed + * trades, and charges + * - **Flexible input**: Accepts both file upload and URL-based PDF input + * - **Password protection**: Supports password-protected PDFs + * + * The API returns structured data including contract note information, client details, transaction + * summaries, and detailed trade-by-trade breakdowns. + */ +class ContractNoteParseParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Optional broker type override. If not provided, system will auto-detect. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun brokerType(): Optional = body.brokerType() + + /** + * Password for the PDF file (usually PAN number for Zerodha) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun password(): Optional = body.password() + + /** + * Base64 encoded contract note PDF file + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfFile(): Optional = body.pdfFile() + + /** + * URL to the contract note PDF file + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfUrl(): Optional = body.pdfUrl() + + /** + * Returns the raw multipart value of [brokerType]. + * + * Unlike [brokerType], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _brokerType(): MultipartField = body._brokerType() + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _password(): MultipartField = body._password() + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfFile(): MultipartField = body._pdfFile() + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfUrl(): MultipartField = body._pdfUrl() + + 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 { + + @JvmStatic fun none(): ContractNoteParseParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ContractNoteParseParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ContractNoteParseParams]. */ + 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(contractNoteParseParams: ContractNoteParseParams) = apply { + body = contractNoteParseParams.body.toBuilder() + additionalHeaders = contractNoteParseParams.additionalHeaders.toBuilder() + additionalQueryParams = contractNoteParseParams.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: + * - [brokerType] + * - [password] + * - [pdfFile] + * - [pdfUrl] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Optional broker type override. If not provided, system will auto-detect. */ + fun brokerType(brokerType: BrokerType) = apply { body.brokerType(brokerType) } + + /** + * Sets [Builder.brokerType] to an arbitrary multipart value. + * + * You should usually call [Builder.brokerType] with a well-typed [BrokerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun brokerType(brokerType: MultipartField) = apply { + body.brokerType(brokerType) + } + + /** Password for the PDF file (usually PAN number for Zerodha) */ + fun password(password: String) = apply { body.password(password) } + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { body.password(password) } + + /** Base64 encoded contract note PDF file */ + fun pdfFile(pdfFile: String) = apply { body.pdfFile(pdfFile) } + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { body.pdfFile(pdfFile) } + + /** URL to the contract note PDF file */ + fun pdfUrl(pdfUrl: String) = apply { body.pdfUrl(pdfUrl) } + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { body.pdfUrl(pdfUrl) } + + 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 [ContractNoteParseParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ContractNoteParseParams = + ContractNoteParseParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Map> = + (mapOf( + "broker_type" to _brokerType(), + "password" to _password(), + "pdf_file" to _pdfFile(), + "pdf_url" to _pdfUrl(), + ) + _additionalBodyProperties().mapValues { (_, value) -> MultipartField.of(value) }) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + private constructor( + private val brokerType: MultipartField, + private val password: MultipartField, + private val pdfFile: MultipartField, + private val pdfUrl: MultipartField, + private val additionalProperties: MutableMap, + ) { + + /** + * Optional broker type override. If not provided, system will auto-detect. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun brokerType(): Optional = brokerType.value.getOptional("broker_type") + + /** + * Password for the PDF file (usually PAN number for Zerodha) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun password(): Optional = password.value.getOptional("password") + + /** + * Base64 encoded contract note PDF file + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfFile(): Optional = pdfFile.value.getOptional("pdf_file") + + /** + * URL to the contract note PDF file + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfUrl(): Optional = pdfUrl.value.getOptional("pdf_url") + + /** + * Returns the raw multipart value of [brokerType]. + * + * Unlike [brokerType], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("broker_type") + @ExcludeMissing + fun _brokerType(): MultipartField = brokerType + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("password") @ExcludeMissing fun _password(): MultipartField = password + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("pdf_file") @ExcludeMissing fun _pdfFile(): MultipartField = pdfFile + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + @JsonProperty("pdf_url") @ExcludeMissing fun _pdfUrl(): MultipartField = pdfUrl + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var brokerType: MultipartField = MultipartField.of(null) + private var password: MultipartField = MultipartField.of(null) + private var pdfFile: MultipartField = MultipartField.of(null) + private var pdfUrl: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + brokerType = body.brokerType + password = body.password + pdfFile = body.pdfFile + pdfUrl = body.pdfUrl + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Optional broker type override. If not provided, system will auto-detect. */ + fun brokerType(brokerType: BrokerType) = brokerType(MultipartField.of(brokerType)) + + /** + * Sets [Builder.brokerType] to an arbitrary multipart value. + * + * You should usually call [Builder.brokerType] with a well-typed [BrokerType] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun brokerType(brokerType: MultipartField) = apply { + this.brokerType = brokerType + } + + /** Password for the PDF file (usually PAN number for Zerodha) */ + fun password(password: String) = password(MultipartField.of(password)) + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { this.password = password } + + /** Base64 encoded contract note PDF file */ + fun pdfFile(pdfFile: String) = pdfFile(MultipartField.of(pdfFile)) + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { this.pdfFile = pdfFile } + + /** URL to the contract note PDF file */ + fun pdfUrl(pdfUrl: String) = pdfUrl(MultipartField.of(pdfUrl)) + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { this.pdfUrl = pdfUrl } + + 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. + */ + fun build(): Body = + Body(brokerType, password, pdfFile, pdfUrl, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + brokerType().ifPresent { it.validate() } + password() + pdfFile() + pdfUrl() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CasParserInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + brokerType == other.brokerType && + password == other.password && + pdfFile == other.pdfFile && + pdfUrl == other.pdfUrl && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(brokerType, password, pdfFile, pdfUrl, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{brokerType=$brokerType, password=$password, pdfFile=$pdfFile, pdfUrl=$pdfUrl, additionalProperties=$additionalProperties}" + } + + /** Optional broker type override. If not provided, system will auto-detect. */ + class BrokerType @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 ZERODHA = of("zerodha") + + @JvmField val GROWW = of("groww") + + @JvmField val UPSTOX = of("upstox") + + @JvmField val ICICI = of("icici") + + @JvmStatic fun of(value: String) = BrokerType(JsonField.of(value)) + } + + /** An enum containing [BrokerType]'s known values. */ + enum class Known { + ZERODHA, + GROWW, + UPSTOX, + ICICI, + } + + /** + * An enum containing [BrokerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [BrokerType] 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 { + ZERODHA, + GROWW, + UPSTOX, + ICICI, + /** + * An enum member indicating that [BrokerType] 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) { + ZERODHA -> Value.ZERODHA + GROWW -> Value.GROWW + UPSTOX -> Value.UPSTOX + ICICI -> Value.ICICI + 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) { + ZERODHA -> Known.ZERODHA + GROWW -> Known.GROWW + UPSTOX -> Known.UPSTOX + ICICI -> Known.ICICI + else -> throw CasParserInvalidDataException("Unknown BrokerType: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): BrokerType = 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 BrokerType && 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 ContractNoteParseParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ContractNoteParseParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseResponse.kt new file mode 100644 index 0000000..9f81c80 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseResponse.kt @@ -0,0 +1,4031 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.contractnote + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.LocalDate +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class ContractNoteParseResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val data: JsonField, + private val msg: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("msg") @ExcludeMissing msg: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(data, msg, status, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun data(): Optional = data.getOptional("data") + + /** + * @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 [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data + + /** + * 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 [ContractNoteParseResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ContractNoteParseResponse]. */ + class Builder internal constructor() { + + private var data: JsonField = JsonMissing.of() + private var msg: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(contractNoteParseResponse: ContractNoteParseResponse) = apply { + data = contractNoteParseResponse.data + msg = contractNoteParseResponse.msg + status = contractNoteParseResponse.status + additionalProperties = contractNoteParseResponse.additionalProperties.toMutableMap() + } + + fun data(data: Data) = data(JsonField.of(data)) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed [Data] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun data(data: JsonField) = apply { this.data = data } + + 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 [ContractNoteParseResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ContractNoteParseResponse = + ContractNoteParseResponse(data, msg, status, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): ContractNoteParseResponse = apply { + if (validated) { + return@apply + } + + data().ifPresent { it.validate() } + 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 = + (data.asKnown().getOrNull()?.validity() ?: 0) + + (if (msg.asKnown().isPresent) 1 else 0) + + (if (status.asKnown().isPresent) 1 else 0) + + class Data + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val brokerInfo: JsonField, + private val chargesSummary: JsonField, + private val clientInfo: JsonField, + private val contractNoteInfo: JsonField, + private val derivativesTransactions: JsonField>, + private val detailedTrades: JsonField>, + private val equityTransactions: JsonField>, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("broker_info") + @ExcludeMissing + brokerInfo: JsonField = JsonMissing.of(), + @JsonProperty("charges_summary") + @ExcludeMissing + chargesSummary: JsonField = JsonMissing.of(), + @JsonProperty("client_info") + @ExcludeMissing + clientInfo: JsonField = JsonMissing.of(), + @JsonProperty("contract_note_info") + @ExcludeMissing + contractNoteInfo: JsonField = JsonMissing.of(), + @JsonProperty("derivatives_transactions") + @ExcludeMissing + derivativesTransactions: JsonField> = JsonMissing.of(), + @JsonProperty("detailed_trades") + @ExcludeMissing + detailedTrades: JsonField> = JsonMissing.of(), + @JsonProperty("equity_transactions") + @ExcludeMissing + equityTransactions: JsonField> = JsonMissing.of(), + ) : this( + brokerInfo, + chargesSummary, + clientInfo, + contractNoteInfo, + derivativesTransactions, + detailedTrades, + equityTransactions, + mutableMapOf(), + ) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun brokerInfo(): Optional = brokerInfo.getOptional("broker_info") + + /** + * Breakdown of various charges and fees + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun chargesSummary(): Optional = + chargesSummary.getOptional("charges_summary") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun clientInfo(): Optional = clientInfo.getOptional("client_info") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun contractNoteInfo(): Optional = + contractNoteInfo.getOptional("contract_note_info") + + /** + * Summary of derivatives transactions + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun derivativesTransactions(): Optional> = + derivativesTransactions.getOptional("derivatives_transactions") + + /** + * Detailed breakdown of all individual trades + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun detailedTrades(): Optional> = + detailedTrades.getOptional("detailed_trades") + + /** + * Summary of equity transactions grouped by security + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun equityTransactions(): Optional> = + equityTransactions.getOptional("equity_transactions") + + /** + * Returns the raw JSON value of [brokerInfo]. + * + * Unlike [brokerInfo], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("broker_info") + @ExcludeMissing + fun _brokerInfo(): JsonField = brokerInfo + + /** + * Returns the raw JSON value of [chargesSummary]. + * + * Unlike [chargesSummary], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("charges_summary") + @ExcludeMissing + fun _chargesSummary(): JsonField = chargesSummary + + /** + * Returns the raw JSON value of [clientInfo]. + * + * Unlike [clientInfo], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("client_info") + @ExcludeMissing + fun _clientInfo(): JsonField = clientInfo + + /** + * Returns the raw JSON value of [contractNoteInfo]. + * + * Unlike [contractNoteInfo], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("contract_note_info") + @ExcludeMissing + fun _contractNoteInfo(): JsonField = contractNoteInfo + + /** + * Returns the raw JSON value of [derivativesTransactions]. + * + * Unlike [derivativesTransactions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("derivatives_transactions") + @ExcludeMissing + fun _derivativesTransactions(): JsonField> = + derivativesTransactions + + /** + * Returns the raw JSON value of [detailedTrades]. + * + * Unlike [detailedTrades], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("detailed_trades") + @ExcludeMissing + fun _detailedTrades(): JsonField> = detailedTrades + + /** + * Returns the raw JSON value of [equityTransactions]. + * + * Unlike [equityTransactions], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("equity_transactions") + @ExcludeMissing + fun _equityTransactions(): JsonField> = equityTransactions + + @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 [Data]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var brokerInfo: JsonField = JsonMissing.of() + private var chargesSummary: JsonField = JsonMissing.of() + private var clientInfo: JsonField = JsonMissing.of() + private var contractNoteInfo: JsonField = JsonMissing.of() + private var derivativesTransactions: JsonField>? = + null + private var detailedTrades: JsonField>? = null + private var equityTransactions: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + brokerInfo = data.brokerInfo + chargesSummary = data.chargesSummary + clientInfo = data.clientInfo + contractNoteInfo = data.contractNoteInfo + derivativesTransactions = data.derivativesTransactions.map { it.toMutableList() } + detailedTrades = data.detailedTrades.map { it.toMutableList() } + equityTransactions = data.equityTransactions.map { it.toMutableList() } + additionalProperties = data.additionalProperties.toMutableMap() + } + + fun brokerInfo(brokerInfo: BrokerInfo) = brokerInfo(JsonField.of(brokerInfo)) + + /** + * Sets [Builder.brokerInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.brokerInfo] with a well-typed [BrokerInfo] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun brokerInfo(brokerInfo: JsonField) = apply { + this.brokerInfo = brokerInfo + } + + /** Breakdown of various charges and fees */ + fun chargesSummary(chargesSummary: ChargesSummary) = + chargesSummary(JsonField.of(chargesSummary)) + + /** + * Sets [Builder.chargesSummary] to an arbitrary JSON value. + * + * You should usually call [Builder.chargesSummary] with a well-typed [ChargesSummary] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun chargesSummary(chargesSummary: JsonField) = apply { + this.chargesSummary = chargesSummary + } + + fun clientInfo(clientInfo: ClientInfo) = clientInfo(JsonField.of(clientInfo)) + + /** + * Sets [Builder.clientInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.clientInfo] with a well-typed [ClientInfo] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun clientInfo(clientInfo: JsonField) = apply { + this.clientInfo = clientInfo + } + + fun contractNoteInfo(contractNoteInfo: ContractNoteInfo) = + contractNoteInfo(JsonField.of(contractNoteInfo)) + + /** + * Sets [Builder.contractNoteInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.contractNoteInfo] with a well-typed + * [ContractNoteInfo] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun contractNoteInfo(contractNoteInfo: JsonField) = apply { + this.contractNoteInfo = contractNoteInfo + } + + /** Summary of derivatives transactions */ + fun derivativesTransactions(derivativesTransactions: List) = + derivativesTransactions(JsonField.of(derivativesTransactions)) + + /** + * Sets [Builder.derivativesTransactions] to an arbitrary JSON value. + * + * You should usually call [Builder.derivativesTransactions] with a well-typed + * `List` value instead. This method is primarily for setting + * the field to an undocumented or not yet supported value. + */ + fun derivativesTransactions( + derivativesTransactions: JsonField> + ) = apply { + this.derivativesTransactions = derivativesTransactions.map { it.toMutableList() } + } + + /** + * Adds a single [DerivativesTransaction] to [derivativesTransactions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addDerivativesTransaction(derivativesTransaction: DerivativesTransaction) = apply { + derivativesTransactions = + (derivativesTransactions ?: JsonField.of(mutableListOf())).also { + checkKnown("derivativesTransactions", it).add(derivativesTransaction) + } + } + + /** Detailed breakdown of all individual trades */ + fun detailedTrades(detailedTrades: List) = + detailedTrades(JsonField.of(detailedTrades)) + + /** + * Sets [Builder.detailedTrades] to an arbitrary JSON value. + * + * You should usually call [Builder.detailedTrades] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun detailedTrades(detailedTrades: JsonField>) = apply { + this.detailedTrades = detailedTrades.map { it.toMutableList() } + } + + /** + * Adds a single [DetailedTrade] to [detailedTrades]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addDetailedTrade(detailedTrade: DetailedTrade) = apply { + detailedTrades = + (detailedTrades ?: JsonField.of(mutableListOf())).also { + checkKnown("detailedTrades", it).add(detailedTrade) + } + } + + /** Summary of equity transactions grouped by security */ + fun equityTransactions(equityTransactions: List) = + equityTransactions(JsonField.of(equityTransactions)) + + /** + * Sets [Builder.equityTransactions] to an arbitrary JSON value. + * + * You should usually call [Builder.equityTransactions] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun equityTransactions(equityTransactions: JsonField>) = apply { + this.equityTransactions = equityTransactions.map { it.toMutableList() } + } + + /** + * Adds a single [EquityTransaction] to [equityTransactions]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addEquityTransaction(equityTransaction: EquityTransaction) = apply { + equityTransactions = + (equityTransactions ?: JsonField.of(mutableListOf())).also { + checkKnown("equityTransactions", it).add(equityTransaction) + } + } + + 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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Data = + Data( + brokerInfo, + chargesSummary, + clientInfo, + contractNoteInfo, + (derivativesTransactions ?: JsonMissing.of()).map { it.toImmutable() }, + (detailedTrades ?: JsonMissing.of()).map { it.toImmutable() }, + (equityTransactions ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Data = apply { + if (validated) { + return@apply + } + + brokerInfo().ifPresent { it.validate() } + chargesSummary().ifPresent { it.validate() } + clientInfo().ifPresent { it.validate() } + contractNoteInfo().ifPresent { it.validate() } + derivativesTransactions().ifPresent { it.forEach { it.validate() } } + detailedTrades().ifPresent { it.forEach { it.validate() } } + equityTransactions().ifPresent { it.forEach { it.validate() } } + 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 = + (brokerInfo.asKnown().getOrNull()?.validity() ?: 0) + + (chargesSummary.asKnown().getOrNull()?.validity() ?: 0) + + (clientInfo.asKnown().getOrNull()?.validity() ?: 0) + + (contractNoteInfo.asKnown().getOrNull()?.validity() ?: 0) + + (derivativesTransactions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } + ?: 0) + + (detailedTrades.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (equityTransactions.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + class BrokerInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val brokerType: JsonField, + private val name: JsonField, + private val sebiRegistration: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("broker_type") + @ExcludeMissing + brokerType: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("sebi_registration") + @ExcludeMissing + sebiRegistration: JsonField = JsonMissing.of(), + ) : this(brokerType, name, sebiRegistration, mutableMapOf()) + + /** + * Auto-detected or specified broker type + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun brokerType(): Optional = brokerType.getOptional("broker_type") + + /** + * Broker company name + * + * @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") + + /** + * SEBI registration number of the broker + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun sebiRegistration(): Optional = + sebiRegistration.getOptional("sebi_registration") + + /** + * Returns the raw JSON value of [brokerType]. + * + * Unlike [brokerType], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("broker_type") + @ExcludeMissing + fun _brokerType(): JsonField = brokerType + + /** + * 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 + + /** + * Returns the raw JSON value of [sebiRegistration]. + * + * Unlike [sebiRegistration], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("sebi_registration") + @ExcludeMissing + fun _sebiRegistration(): JsonField = sebiRegistration + + @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 [BrokerInfo]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BrokerInfo]. */ + class Builder internal constructor() { + + private var brokerType: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var sebiRegistration: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(brokerInfo: BrokerInfo) = apply { + brokerType = brokerInfo.brokerType + name = brokerInfo.name + sebiRegistration = brokerInfo.sebiRegistration + additionalProperties = brokerInfo.additionalProperties.toMutableMap() + } + + /** Auto-detected or specified broker type */ + fun brokerType(brokerType: BrokerType) = brokerType(JsonField.of(brokerType)) + + /** + * Sets [Builder.brokerType] to an arbitrary JSON value. + * + * You should usually call [Builder.brokerType] with a well-typed [BrokerType] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun brokerType(brokerType: JsonField) = apply { + this.brokerType = brokerType + } + + /** Broker company name */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** SEBI registration number of the broker */ + fun sebiRegistration(sebiRegistration: String) = + sebiRegistration(JsonField.of(sebiRegistration)) + + /** + * Sets [Builder.sebiRegistration] to an arbitrary JSON value. + * + * You should usually call [Builder.sebiRegistration] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun sebiRegistration(sebiRegistration: JsonField) = apply { + this.sebiRegistration = sebiRegistration + } + + 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 [BrokerInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BrokerInfo = + BrokerInfo( + brokerType, + name, + sebiRegistration, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): BrokerInfo = apply { + if (validated) { + return@apply + } + + brokerType().ifPresent { it.validate() } + name() + sebiRegistration() + 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 = + (brokerType.asKnown().getOrNull()?.validity() ?: 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (sebiRegistration.asKnown().isPresent) 1 else 0) + + /** Auto-detected or specified broker type */ + class BrokerType + @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 ZERODHA = of("zerodha") + + @JvmField val GROWW = of("groww") + + @JvmField val UPSTOX = of("upstox") + + @JvmField val ICICI = of("icici") + + @JvmField val UNKNOWN = of("unknown") + + @JvmStatic fun of(value: String) = BrokerType(JsonField.of(value)) + } + + /** An enum containing [BrokerType]'s known values. */ + enum class Known { + ZERODHA, + GROWW, + UPSTOX, + ICICI, + UNKNOWN, + } + + /** + * An enum containing [BrokerType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [BrokerType] 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 { + ZERODHA, + GROWW, + UPSTOX, + ICICI, + UNKNOWN, + /** + * An enum member indicating that [BrokerType] 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) { + ZERODHA -> Value.ZERODHA + GROWW -> Value.GROWW + UPSTOX -> Value.UPSTOX + ICICI -> Value.ICICI + UNKNOWN -> Value.UNKNOWN + else -> Value._UNKNOWN + } + + /** + * Returns an enum member corresponding to this class instance's value. + * + * Use the [value] method instead if you're uncertain the value is always known and + * don't want to throw for the unknown case. + * + * @throws CasParserInvalidDataException if this class instance's value is a not a + * known member. + */ + fun known(): Known = + when (this) { + ZERODHA -> Known.ZERODHA + GROWW -> Known.GROWW + UPSTOX -> Known.UPSTOX + ICICI -> Known.ICICI + UNKNOWN -> Known.UNKNOWN + else -> throw CasParserInvalidDataException("Unknown BrokerType: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't + * match its expected type. + */ + fun validate(): BrokerType = 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 BrokerType && 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 BrokerInfo && + brokerType == other.brokerType && + name == other.name && + sebiRegistration == other.sebiRegistration && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(brokerType, name, sebiRegistration, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BrokerInfo{brokerType=$brokerType, name=$name, sebiRegistration=$sebiRegistration, additionalProperties=$additionalProperties}" + } + + /** Breakdown of various charges and fees */ + class ChargesSummary + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val cgst: JsonField, + private val exchangeTransactionCharges: JsonField, + private val igst: JsonField, + private val netAmountReceivablePayable: JsonField, + private val payInPayOutObligation: JsonField, + private val sebiTurnoverFees: JsonField, + private val securitiesTransactionTax: JsonField, + private val sgst: JsonField, + private val stampDuty: JsonField, + private val taxableValueBrokerage: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("cgst") @ExcludeMissing cgst: JsonField = JsonMissing.of(), + @JsonProperty("exchange_transaction_charges") + @ExcludeMissing + exchangeTransactionCharges: JsonField = JsonMissing.of(), + @JsonProperty("igst") @ExcludeMissing igst: JsonField = JsonMissing.of(), + @JsonProperty("net_amount_receivable_payable") + @ExcludeMissing + netAmountReceivablePayable: JsonField = JsonMissing.of(), + @JsonProperty("pay_in_pay_out_obligation") + @ExcludeMissing + payInPayOutObligation: JsonField = JsonMissing.of(), + @JsonProperty("sebi_turnover_fees") + @ExcludeMissing + sebiTurnoverFees: JsonField = JsonMissing.of(), + @JsonProperty("securities_transaction_tax") + @ExcludeMissing + securitiesTransactionTax: JsonField = JsonMissing.of(), + @JsonProperty("sgst") @ExcludeMissing sgst: JsonField = JsonMissing.of(), + @JsonProperty("stamp_duty") + @ExcludeMissing + stampDuty: JsonField = JsonMissing.of(), + @JsonProperty("taxable_value_brokerage") + @ExcludeMissing + taxableValueBrokerage: JsonField = JsonMissing.of(), + ) : this( + cgst, + exchangeTransactionCharges, + igst, + netAmountReceivablePayable, + payInPayOutObligation, + sebiTurnoverFees, + securitiesTransactionTax, + sgst, + stampDuty, + taxableValueBrokerage, + mutableMapOf(), + ) + + /** + * Central GST amount + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun cgst(): Optional = cgst.getOptional("cgst") + + /** + * Exchange transaction charges + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun exchangeTransactionCharges(): Optional = + exchangeTransactionCharges.getOptional("exchange_transaction_charges") + + /** + * Integrated GST amount + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun igst(): Optional = igst.getOptional("igst") + + /** + * Final net amount receivable or payable + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun netAmountReceivablePayable(): Optional = + netAmountReceivablePayable.getOptional("net_amount_receivable_payable") + + /** + * Net pay-in/pay-out obligation + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun payInPayOutObligation(): Optional = + payInPayOutObligation.getOptional("pay_in_pay_out_obligation") + + /** + * SEBI turnover fees + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun sebiTurnoverFees(): Optional = + sebiTurnoverFees.getOptional("sebi_turnover_fees") + + /** + * Securities Transaction Tax + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun securitiesTransactionTax(): Optional = + securitiesTransactionTax.getOptional("securities_transaction_tax") + + /** + * State GST amount + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun sgst(): Optional = sgst.getOptional("sgst") + + /** + * Stamp duty charges + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun stampDuty(): Optional = stampDuty.getOptional("stamp_duty") + + /** + * Taxable brokerage amount + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun taxableValueBrokerage(): Optional = + taxableValueBrokerage.getOptional("taxable_value_brokerage") + + /** + * Returns the raw JSON value of [cgst]. + * + * Unlike [cgst], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cgst") @ExcludeMissing fun _cgst(): JsonField = cgst + + /** + * Returns the raw JSON value of [exchangeTransactionCharges]. + * + * Unlike [exchangeTransactionCharges], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("exchange_transaction_charges") + @ExcludeMissing + fun _exchangeTransactionCharges(): JsonField = exchangeTransactionCharges + + /** + * Returns the raw JSON value of [igst]. + * + * Unlike [igst], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("igst") @ExcludeMissing fun _igst(): JsonField = igst + + /** + * Returns the raw JSON value of [netAmountReceivablePayable]. + * + * Unlike [netAmountReceivablePayable], this method doesn't throw if the JSON field has + * an unexpected type. + */ + @JsonProperty("net_amount_receivable_payable") + @ExcludeMissing + fun _netAmountReceivablePayable(): JsonField = netAmountReceivablePayable + + /** + * Returns the raw JSON value of [payInPayOutObligation]. + * + * Unlike [payInPayOutObligation], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("pay_in_pay_out_obligation") + @ExcludeMissing + fun _payInPayOutObligation(): JsonField = payInPayOutObligation + + /** + * Returns the raw JSON value of [sebiTurnoverFees]. + * + * Unlike [sebiTurnoverFees], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("sebi_turnover_fees") + @ExcludeMissing + fun _sebiTurnoverFees(): JsonField = sebiTurnoverFees + + /** + * Returns the raw JSON value of [securitiesTransactionTax]. + * + * Unlike [securitiesTransactionTax], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("securities_transaction_tax") + @ExcludeMissing + fun _securitiesTransactionTax(): JsonField = securitiesTransactionTax + + /** + * Returns the raw JSON value of [sgst]. + * + * Unlike [sgst], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sgst") @ExcludeMissing fun _sgst(): JsonField = sgst + + /** + * Returns the raw JSON value of [stampDuty]. + * + * Unlike [stampDuty], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("stamp_duty") + @ExcludeMissing + fun _stampDuty(): JsonField = stampDuty + + /** + * Returns the raw JSON value of [taxableValueBrokerage]. + * + * Unlike [taxableValueBrokerage], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("taxable_value_brokerage") + @ExcludeMissing + fun _taxableValueBrokerage(): JsonField = taxableValueBrokerage + + @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 [ChargesSummary]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ChargesSummary]. */ + class Builder internal constructor() { + + private var cgst: JsonField = JsonMissing.of() + private var exchangeTransactionCharges: JsonField = JsonMissing.of() + private var igst: JsonField = JsonMissing.of() + private var netAmountReceivablePayable: JsonField = JsonMissing.of() + private var payInPayOutObligation: JsonField = JsonMissing.of() + private var sebiTurnoverFees: JsonField = JsonMissing.of() + private var securitiesTransactionTax: JsonField = JsonMissing.of() + private var sgst: JsonField = JsonMissing.of() + private var stampDuty: JsonField = JsonMissing.of() + private var taxableValueBrokerage: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(chargesSummary: ChargesSummary) = apply { + cgst = chargesSummary.cgst + exchangeTransactionCharges = chargesSummary.exchangeTransactionCharges + igst = chargesSummary.igst + netAmountReceivablePayable = chargesSummary.netAmountReceivablePayable + payInPayOutObligation = chargesSummary.payInPayOutObligation + sebiTurnoverFees = chargesSummary.sebiTurnoverFees + securitiesTransactionTax = chargesSummary.securitiesTransactionTax + sgst = chargesSummary.sgst + stampDuty = chargesSummary.stampDuty + taxableValueBrokerage = chargesSummary.taxableValueBrokerage + additionalProperties = chargesSummary.additionalProperties.toMutableMap() + } + + /** Central GST amount */ + fun cgst(cgst: Float) = cgst(JsonField.of(cgst)) + + /** + * Sets [Builder.cgst] to an arbitrary JSON value. + * + * You should usually call [Builder.cgst] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cgst(cgst: JsonField) = apply { this.cgst = cgst } + + /** Exchange transaction charges */ + fun exchangeTransactionCharges(exchangeTransactionCharges: Float) = + exchangeTransactionCharges(JsonField.of(exchangeTransactionCharges)) + + /** + * Sets [Builder.exchangeTransactionCharges] to an arbitrary JSON value. + * + * You should usually call [Builder.exchangeTransactionCharges] with a well-typed + * [Float] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun exchangeTransactionCharges(exchangeTransactionCharges: JsonField) = + apply { + this.exchangeTransactionCharges = exchangeTransactionCharges + } + + /** Integrated GST amount */ + fun igst(igst: Float) = igst(JsonField.of(igst)) + + /** + * Sets [Builder.igst] to an arbitrary JSON value. + * + * You should usually call [Builder.igst] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun igst(igst: JsonField) = apply { this.igst = igst } + + /** Final net amount receivable or payable */ + fun netAmountReceivablePayable(netAmountReceivablePayable: Float) = + netAmountReceivablePayable(JsonField.of(netAmountReceivablePayable)) + + /** + * Sets [Builder.netAmountReceivablePayable] to an arbitrary JSON value. + * + * You should usually call [Builder.netAmountReceivablePayable] with a well-typed + * [Float] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun netAmountReceivablePayable(netAmountReceivablePayable: JsonField) = + apply { + this.netAmountReceivablePayable = netAmountReceivablePayable + } + + /** Net pay-in/pay-out obligation */ + fun payInPayOutObligation(payInPayOutObligation: Float) = + payInPayOutObligation(JsonField.of(payInPayOutObligation)) + + /** + * Sets [Builder.payInPayOutObligation] to an arbitrary JSON value. + * + * You should usually call [Builder.payInPayOutObligation] with a well-typed [Float] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun payInPayOutObligation(payInPayOutObligation: JsonField) = apply { + this.payInPayOutObligation = payInPayOutObligation + } + + /** SEBI turnover fees */ + fun sebiTurnoverFees(sebiTurnoverFees: Float) = + sebiTurnoverFees(JsonField.of(sebiTurnoverFees)) + + /** + * Sets [Builder.sebiTurnoverFees] to an arbitrary JSON value. + * + * You should usually call [Builder.sebiTurnoverFees] with a well-typed [Float] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun sebiTurnoverFees(sebiTurnoverFees: JsonField) = apply { + this.sebiTurnoverFees = sebiTurnoverFees + } + + /** Securities Transaction Tax */ + fun securitiesTransactionTax(securitiesTransactionTax: Float) = + securitiesTransactionTax(JsonField.of(securitiesTransactionTax)) + + /** + * Sets [Builder.securitiesTransactionTax] to an arbitrary JSON value. + * + * You should usually call [Builder.securitiesTransactionTax] with a well-typed + * [Float] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun securitiesTransactionTax(securitiesTransactionTax: JsonField) = apply { + this.securitiesTransactionTax = securitiesTransactionTax + } + + /** State GST amount */ + fun sgst(sgst: Float) = sgst(JsonField.of(sgst)) + + /** + * Sets [Builder.sgst] to an arbitrary JSON value. + * + * You should usually call [Builder.sgst] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun sgst(sgst: JsonField) = apply { this.sgst = sgst } + + /** Stamp duty charges */ + fun stampDuty(stampDuty: Float) = stampDuty(JsonField.of(stampDuty)) + + /** + * Sets [Builder.stampDuty] to an arbitrary JSON value. + * + * You should usually call [Builder.stampDuty] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun stampDuty(stampDuty: JsonField) = apply { this.stampDuty = stampDuty } + + /** Taxable brokerage amount */ + fun taxableValueBrokerage(taxableValueBrokerage: Float) = + taxableValueBrokerage(JsonField.of(taxableValueBrokerage)) + + /** + * Sets [Builder.taxableValueBrokerage] to an arbitrary JSON value. + * + * You should usually call [Builder.taxableValueBrokerage] with a well-typed [Float] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun taxableValueBrokerage(taxableValueBrokerage: JsonField) = apply { + this.taxableValueBrokerage = taxableValueBrokerage + } + + 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 [ChargesSummary]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ChargesSummary = + ChargesSummary( + cgst, + exchangeTransactionCharges, + igst, + netAmountReceivablePayable, + payInPayOutObligation, + sebiTurnoverFees, + securitiesTransactionTax, + sgst, + stampDuty, + taxableValueBrokerage, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): ChargesSummary = apply { + if (validated) { + return@apply + } + + cgst() + exchangeTransactionCharges() + igst() + netAmountReceivablePayable() + payInPayOutObligation() + sebiTurnoverFees() + securitiesTransactionTax() + sgst() + stampDuty() + taxableValueBrokerage() + 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 (cgst.asKnown().isPresent) 1 else 0) + + (if (exchangeTransactionCharges.asKnown().isPresent) 1 else 0) + + (if (igst.asKnown().isPresent) 1 else 0) + + (if (netAmountReceivablePayable.asKnown().isPresent) 1 else 0) + + (if (payInPayOutObligation.asKnown().isPresent) 1 else 0) + + (if (sebiTurnoverFees.asKnown().isPresent) 1 else 0) + + (if (securitiesTransactionTax.asKnown().isPresent) 1 else 0) + + (if (sgst.asKnown().isPresent) 1 else 0) + + (if (stampDuty.asKnown().isPresent) 1 else 0) + + (if (taxableValueBrokerage.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ChargesSummary && + cgst == other.cgst && + exchangeTransactionCharges == other.exchangeTransactionCharges && + igst == other.igst && + netAmountReceivablePayable == other.netAmountReceivablePayable && + payInPayOutObligation == other.payInPayOutObligation && + sebiTurnoverFees == other.sebiTurnoverFees && + securitiesTransactionTax == other.securitiesTransactionTax && + sgst == other.sgst && + stampDuty == other.stampDuty && + taxableValueBrokerage == other.taxableValueBrokerage && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + cgst, + exchangeTransactionCharges, + igst, + netAmountReceivablePayable, + payInPayOutObligation, + sebiTurnoverFees, + securitiesTransactionTax, + sgst, + stampDuty, + taxableValueBrokerage, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ChargesSummary{cgst=$cgst, exchangeTransactionCharges=$exchangeTransactionCharges, igst=$igst, netAmountReceivablePayable=$netAmountReceivablePayable, payInPayOutObligation=$payInPayOutObligation, sebiTurnoverFees=$sebiTurnoverFees, securitiesTransactionTax=$securitiesTransactionTax, sgst=$sgst, stampDuty=$stampDuty, taxableValueBrokerage=$taxableValueBrokerage, additionalProperties=$additionalProperties}" + } + + class ClientInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val address: JsonField, + private val gstStateCode: JsonField, + private val name: JsonField, + private val pan: JsonField, + private val placeOfSupply: JsonField, + private val ucc: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("address") + @ExcludeMissing + address: JsonField = JsonMissing.of(), + @JsonProperty("gst_state_code") + @ExcludeMissing + gstStateCode: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("pan") @ExcludeMissing pan: JsonField = JsonMissing.of(), + @JsonProperty("place_of_supply") + @ExcludeMissing + placeOfSupply: JsonField = JsonMissing.of(), + @JsonProperty("ucc") @ExcludeMissing ucc: JsonField = JsonMissing.of(), + ) : this(address, gstStateCode, name, pan, placeOfSupply, ucc, mutableMapOf()) + + /** + * Client address + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun address(): Optional = address.getOptional("address") + + /** + * GST state code + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun gstStateCode(): Optional = gstStateCode.getOptional("gst_state_code") + + /** + * Client name + * + * @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") + + /** + * Client PAN number + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun pan(): Optional = pan.getOptional("pan") + + /** + * GST place of supply + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun placeOfSupply(): Optional = placeOfSupply.getOptional("place_of_supply") + + /** + * Unique Client Code + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun ucc(): Optional = ucc.getOptional("ucc") + + /** + * Returns the raw JSON value of [address]. + * + * Unlike [address], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("address") @ExcludeMissing fun _address(): JsonField = address + + /** + * Returns the raw JSON value of [gstStateCode]. + * + * Unlike [gstStateCode], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("gst_state_code") + @ExcludeMissing + fun _gstStateCode(): JsonField = gstStateCode + + /** + * 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 + + /** + * Returns the raw JSON value of [pan]. + * + * Unlike [pan], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("pan") @ExcludeMissing fun _pan(): JsonField = pan + + /** + * Returns the raw JSON value of [placeOfSupply]. + * + * Unlike [placeOfSupply], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("place_of_supply") + @ExcludeMissing + fun _placeOfSupply(): JsonField = placeOfSupply + + /** + * Returns the raw JSON value of [ucc]. + * + * Unlike [ucc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ucc") @ExcludeMissing fun _ucc(): JsonField = ucc + + @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 [ClientInfo]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ClientInfo]. */ + class Builder internal constructor() { + + private var address: JsonField = JsonMissing.of() + private var gstStateCode: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var pan: JsonField = JsonMissing.of() + private var placeOfSupply: JsonField = JsonMissing.of() + private var ucc: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(clientInfo: ClientInfo) = apply { + address = clientInfo.address + gstStateCode = clientInfo.gstStateCode + name = clientInfo.name + pan = clientInfo.pan + placeOfSupply = clientInfo.placeOfSupply + ucc = clientInfo.ucc + additionalProperties = clientInfo.additionalProperties.toMutableMap() + } + + /** Client address */ + fun address(address: String) = address(JsonField.of(address)) + + /** + * Sets [Builder.address] to an arbitrary JSON value. + * + * You should usually call [Builder.address] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun address(address: JsonField) = apply { this.address = address } + + /** GST state code */ + fun gstStateCode(gstStateCode: String) = gstStateCode(JsonField.of(gstStateCode)) + + /** + * Sets [Builder.gstStateCode] to an arbitrary JSON value. + * + * You should usually call [Builder.gstStateCode] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun gstStateCode(gstStateCode: JsonField) = apply { + this.gstStateCode = gstStateCode + } + + /** Client name */ + fun name(name: String) = name(JsonField.of(name)) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * 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. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** Client PAN number */ + fun pan(pan: String) = pan(JsonField.of(pan)) + + /** + * Sets [Builder.pan] to an arbitrary JSON value. + * + * You should usually call [Builder.pan] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pan(pan: JsonField) = apply { this.pan = pan } + + /** GST place of supply */ + fun placeOfSupply(placeOfSupply: String) = + placeOfSupply(JsonField.of(placeOfSupply)) + + /** + * Sets [Builder.placeOfSupply] to an arbitrary JSON value. + * + * You should usually call [Builder.placeOfSupply] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun placeOfSupply(placeOfSupply: JsonField) = apply { + this.placeOfSupply = placeOfSupply + } + + /** Unique Client Code */ + fun ucc(ucc: String) = ucc(JsonField.of(ucc)) + + /** + * Sets [Builder.ucc] to an arbitrary JSON value. + * + * You should usually call [Builder.ucc] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun ucc(ucc: JsonField) = apply { this.ucc = ucc } + + 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 [ClientInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ClientInfo = + ClientInfo( + address, + gstStateCode, + name, + pan, + placeOfSupply, + ucc, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): ClientInfo = apply { + if (validated) { + return@apply + } + + address() + gstStateCode() + name() + pan() + placeOfSupply() + ucc() + 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 (address.asKnown().isPresent) 1 else 0) + + (if (gstStateCode.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (pan.asKnown().isPresent) 1 else 0) + + (if (placeOfSupply.asKnown().isPresent) 1 else 0) + + (if (ucc.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ClientInfo && + address == other.address && + gstStateCode == other.gstStateCode && + name == other.name && + pan == other.pan && + placeOfSupply == other.placeOfSupply && + ucc == other.ucc && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + address, + gstStateCode, + name, + pan, + placeOfSupply, + ucc, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ClientInfo{address=$address, gstStateCode=$gstStateCode, name=$name, pan=$pan, placeOfSupply=$placeOfSupply, ucc=$ucc, additionalProperties=$additionalProperties}" + } + + class ContractNoteInfo + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val contractNoteNumber: JsonField, + private val settlementDate: JsonField, + private val settlementNumber: JsonField, + private val tradeDate: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("contract_note_number") + @ExcludeMissing + contractNoteNumber: JsonField = JsonMissing.of(), + @JsonProperty("settlement_date") + @ExcludeMissing + settlementDate: JsonField = JsonMissing.of(), + @JsonProperty("settlement_number") + @ExcludeMissing + settlementNumber: JsonField = JsonMissing.of(), + @JsonProperty("trade_date") + @ExcludeMissing + tradeDate: JsonField = JsonMissing.of(), + ) : this( + contractNoteNumber, + settlementDate, + settlementNumber, + tradeDate, + mutableMapOf(), + ) + + /** + * Contract note reference number + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun contractNoteNumber(): Optional = + contractNoteNumber.getOptional("contract_note_number") + + /** + * Settlement date for the trades + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun settlementDate(): Optional = + settlementDate.getOptional("settlement_date") + + /** + * Settlement reference number + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun settlementNumber(): Optional = + settlementNumber.getOptional("settlement_number") + + /** + * Date when trades were executed + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun tradeDate(): Optional = tradeDate.getOptional("trade_date") + + /** + * Returns the raw JSON value of [contractNoteNumber]. + * + * Unlike [contractNoteNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("contract_note_number") + @ExcludeMissing + fun _contractNoteNumber(): JsonField = contractNoteNumber + + /** + * Returns the raw JSON value of [settlementDate]. + * + * Unlike [settlementDate], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("settlement_date") + @ExcludeMissing + fun _settlementDate(): JsonField = settlementDate + + /** + * Returns the raw JSON value of [settlementNumber]. + * + * Unlike [settlementNumber], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("settlement_number") + @ExcludeMissing + fun _settlementNumber(): JsonField = settlementNumber + + /** + * Returns the raw JSON value of [tradeDate]. + * + * Unlike [tradeDate], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("trade_date") + @ExcludeMissing + fun _tradeDate(): JsonField = tradeDate + + @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 [ContractNoteInfo]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ContractNoteInfo]. */ + class Builder internal constructor() { + + private var contractNoteNumber: JsonField = JsonMissing.of() + private var settlementDate: JsonField = JsonMissing.of() + private var settlementNumber: JsonField = JsonMissing.of() + private var tradeDate: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(contractNoteInfo: ContractNoteInfo) = apply { + contractNoteNumber = contractNoteInfo.contractNoteNumber + settlementDate = contractNoteInfo.settlementDate + settlementNumber = contractNoteInfo.settlementNumber + tradeDate = contractNoteInfo.tradeDate + additionalProperties = contractNoteInfo.additionalProperties.toMutableMap() + } + + /** Contract note reference number */ + fun contractNoteNumber(contractNoteNumber: String) = + contractNoteNumber(JsonField.of(contractNoteNumber)) + + /** + * Sets [Builder.contractNoteNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.contractNoteNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun contractNoteNumber(contractNoteNumber: JsonField) = apply { + this.contractNoteNumber = contractNoteNumber + } + + /** Settlement date for the trades */ + fun settlementDate(settlementDate: LocalDate) = + settlementDate(JsonField.of(settlementDate)) + + /** + * Sets [Builder.settlementDate] to an arbitrary JSON value. + * + * You should usually call [Builder.settlementDate] with a well-typed [LocalDate] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun settlementDate(settlementDate: JsonField) = apply { + this.settlementDate = settlementDate + } + + /** Settlement reference number */ + fun settlementNumber(settlementNumber: String) = + settlementNumber(JsonField.of(settlementNumber)) + + /** + * Sets [Builder.settlementNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.settlementNumber] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun settlementNumber(settlementNumber: JsonField) = apply { + this.settlementNumber = settlementNumber + } + + /** Date when trades were executed */ + fun tradeDate(tradeDate: LocalDate) = tradeDate(JsonField.of(tradeDate)) + + /** + * Sets [Builder.tradeDate] to an arbitrary JSON value. + * + * You should usually call [Builder.tradeDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun tradeDate(tradeDate: JsonField) = apply { + this.tradeDate = tradeDate + } + + 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 [ContractNoteInfo]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ContractNoteInfo = + ContractNoteInfo( + contractNoteNumber, + settlementDate, + settlementNumber, + tradeDate, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): ContractNoteInfo = apply { + if (validated) { + return@apply + } + + contractNoteNumber() + settlementDate() + settlementNumber() + tradeDate() + 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 (contractNoteNumber.asKnown().isPresent) 1 else 0) + + (if (settlementDate.asKnown().isPresent) 1 else 0) + + (if (settlementNumber.asKnown().isPresent) 1 else 0) + + (if (tradeDate.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ContractNoteInfo && + contractNoteNumber == other.contractNoteNumber && + settlementDate == other.settlementDate && + settlementNumber == other.settlementNumber && + tradeDate == other.tradeDate && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + contractNoteNumber, + settlementDate, + settlementNumber, + tradeDate, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ContractNoteInfo{contractNoteNumber=$contractNoteNumber, settlementDate=$settlementDate, settlementNumber=$settlementNumber, tradeDate=$tradeDate, additionalProperties=$additionalProperties}" + } + + class DerivativesTransaction + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val brokeragePerUnit: JsonField, + private val buySellBfCf: JsonField, + private val closingRatePerUnit: JsonField, + private val contractDescription: JsonField, + private val netTotal: JsonField, + private val quantity: JsonField, + private val wapPerUnit: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("brokerage_per_unit") + @ExcludeMissing + brokeragePerUnit: JsonField = JsonMissing.of(), + @JsonProperty("buy_sell_bf_cf") + @ExcludeMissing + buySellBfCf: JsonField = JsonMissing.of(), + @JsonProperty("closing_rate_per_unit") + @ExcludeMissing + closingRatePerUnit: JsonField = JsonMissing.of(), + @JsonProperty("contract_description") + @ExcludeMissing + contractDescription: JsonField = JsonMissing.of(), + @JsonProperty("net_total") + @ExcludeMissing + netTotal: JsonField = JsonMissing.of(), + @JsonProperty("quantity") + @ExcludeMissing + quantity: JsonField = JsonMissing.of(), + @JsonProperty("wap_per_unit") + @ExcludeMissing + wapPerUnit: JsonField = JsonMissing.of(), + ) : this( + brokeragePerUnit, + buySellBfCf, + closingRatePerUnit, + contractDescription, + netTotal, + quantity, + wapPerUnit, + mutableMapOf(), + ) + + /** + * Brokerage charged per unit + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun brokeragePerUnit(): Optional = + brokeragePerUnit.getOptional("brokerage_per_unit") + + /** + * Transaction type (Buy/Sell/Bring Forward/Carry Forward) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun buySellBfCf(): Optional = buySellBfCf.getOptional("buy_sell_bf_cf") + + /** + * Closing rate per unit + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun closingRatePerUnit(): Optional = + closingRatePerUnit.getOptional("closing_rate_per_unit") + + /** + * Derivatives contract description + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun contractDescription(): Optional = + contractDescription.getOptional("contract_description") + + /** + * Net total amount + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun netTotal(): Optional = netTotal.getOptional("net_total") + + /** + * Quantity traded + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun quantity(): Optional = quantity.getOptional("quantity") + + /** + * Weighted Average Price per unit + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun wapPerUnit(): Optional = wapPerUnit.getOptional("wap_per_unit") + + /** + * Returns the raw JSON value of [brokeragePerUnit]. + * + * Unlike [brokeragePerUnit], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("brokerage_per_unit") + @ExcludeMissing + fun _brokeragePerUnit(): JsonField = brokeragePerUnit + + /** + * Returns the raw JSON value of [buySellBfCf]. + * + * Unlike [buySellBfCf], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("buy_sell_bf_cf") + @ExcludeMissing + fun _buySellBfCf(): JsonField = buySellBfCf + + /** + * Returns the raw JSON value of [closingRatePerUnit]. + * + * Unlike [closingRatePerUnit], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("closing_rate_per_unit") + @ExcludeMissing + fun _closingRatePerUnit(): JsonField = closingRatePerUnit + + /** + * Returns the raw JSON value of [contractDescription]. + * + * Unlike [contractDescription], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("contract_description") + @ExcludeMissing + fun _contractDescription(): JsonField = contractDescription + + /** + * Returns the raw JSON value of [netTotal]. + * + * Unlike [netTotal], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("net_total") @ExcludeMissing fun _netTotal(): JsonField = netTotal + + /** + * Returns the raw JSON value of [quantity]. + * + * Unlike [quantity], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("quantity") @ExcludeMissing fun _quantity(): JsonField = quantity + + /** + * Returns the raw JSON value of [wapPerUnit]. + * + * Unlike [wapPerUnit], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("wap_per_unit") + @ExcludeMissing + fun _wapPerUnit(): JsonField = wapPerUnit + + @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 + * [DerivativesTransaction]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DerivativesTransaction]. */ + class Builder internal constructor() { + + private var brokeragePerUnit: JsonField = JsonMissing.of() + private var buySellBfCf: JsonField = JsonMissing.of() + private var closingRatePerUnit: JsonField = JsonMissing.of() + private var contractDescription: JsonField = JsonMissing.of() + private var netTotal: JsonField = JsonMissing.of() + private var quantity: JsonField = JsonMissing.of() + private var wapPerUnit: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(derivativesTransaction: DerivativesTransaction) = apply { + brokeragePerUnit = derivativesTransaction.brokeragePerUnit + buySellBfCf = derivativesTransaction.buySellBfCf + closingRatePerUnit = derivativesTransaction.closingRatePerUnit + contractDescription = derivativesTransaction.contractDescription + netTotal = derivativesTransaction.netTotal + quantity = derivativesTransaction.quantity + wapPerUnit = derivativesTransaction.wapPerUnit + additionalProperties = + derivativesTransaction.additionalProperties.toMutableMap() + } + + /** Brokerage charged per unit */ + fun brokeragePerUnit(brokeragePerUnit: Float) = + brokeragePerUnit(JsonField.of(brokeragePerUnit)) + + /** + * Sets [Builder.brokeragePerUnit] to an arbitrary JSON value. + * + * You should usually call [Builder.brokeragePerUnit] with a well-typed [Float] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun brokeragePerUnit(brokeragePerUnit: JsonField) = apply { + this.brokeragePerUnit = brokeragePerUnit + } + + /** Transaction type (Buy/Sell/Bring Forward/Carry Forward) */ + fun buySellBfCf(buySellBfCf: String) = buySellBfCf(JsonField.of(buySellBfCf)) + + /** + * Sets [Builder.buySellBfCf] to an arbitrary JSON value. + * + * You should usually call [Builder.buySellBfCf] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun buySellBfCf(buySellBfCf: JsonField) = apply { + this.buySellBfCf = buySellBfCf + } + + /** Closing rate per unit */ + fun closingRatePerUnit(closingRatePerUnit: Float) = + closingRatePerUnit(JsonField.of(closingRatePerUnit)) + + /** + * Sets [Builder.closingRatePerUnit] to an arbitrary JSON value. + * + * You should usually call [Builder.closingRatePerUnit] with a well-typed [Float] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun closingRatePerUnit(closingRatePerUnit: JsonField) = apply { + this.closingRatePerUnit = closingRatePerUnit + } + + /** Derivatives contract description */ + fun contractDescription(contractDescription: String) = + contractDescription(JsonField.of(contractDescription)) + + /** + * Sets [Builder.contractDescription] to an arbitrary JSON value. + * + * You should usually call [Builder.contractDescription] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun contractDescription(contractDescription: JsonField) = apply { + this.contractDescription = contractDescription + } + + /** Net total amount */ + fun netTotal(netTotal: Float) = netTotal(JsonField.of(netTotal)) + + /** + * Sets [Builder.netTotal] to an arbitrary JSON value. + * + * You should usually call [Builder.netTotal] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun netTotal(netTotal: JsonField) = apply { this.netTotal = netTotal } + + /** Quantity traded */ + fun quantity(quantity: Float) = quantity(JsonField.of(quantity)) + + /** + * Sets [Builder.quantity] to an arbitrary JSON value. + * + * You should usually call [Builder.quantity] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun quantity(quantity: JsonField) = apply { this.quantity = quantity } + + /** Weighted Average Price per unit */ + fun wapPerUnit(wapPerUnit: Float) = wapPerUnit(JsonField.of(wapPerUnit)) + + /** + * Sets [Builder.wapPerUnit] to an arbitrary JSON value. + * + * You should usually call [Builder.wapPerUnit] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun wapPerUnit(wapPerUnit: JsonField) = apply { + this.wapPerUnit = wapPerUnit + } + + 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 [DerivativesTransaction]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): DerivativesTransaction = + DerivativesTransaction( + brokeragePerUnit, + buySellBfCf, + closingRatePerUnit, + contractDescription, + netTotal, + quantity, + wapPerUnit, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): DerivativesTransaction = apply { + if (validated) { + return@apply + } + + brokeragePerUnit() + buySellBfCf() + closingRatePerUnit() + contractDescription() + netTotal() + quantity() + wapPerUnit() + 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 (brokeragePerUnit.asKnown().isPresent) 1 else 0) + + (if (buySellBfCf.asKnown().isPresent) 1 else 0) + + (if (closingRatePerUnit.asKnown().isPresent) 1 else 0) + + (if (contractDescription.asKnown().isPresent) 1 else 0) + + (if (netTotal.asKnown().isPresent) 1 else 0) + + (if (quantity.asKnown().isPresent) 1 else 0) + + (if (wapPerUnit.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DerivativesTransaction && + brokeragePerUnit == other.brokeragePerUnit && + buySellBfCf == other.buySellBfCf && + closingRatePerUnit == other.closingRatePerUnit && + contractDescription == other.contractDescription && + netTotal == other.netTotal && + quantity == other.quantity && + wapPerUnit == other.wapPerUnit && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + brokeragePerUnit, + buySellBfCf, + closingRatePerUnit, + contractDescription, + netTotal, + quantity, + wapPerUnit, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DerivativesTransaction{brokeragePerUnit=$brokeragePerUnit, buySellBfCf=$buySellBfCf, closingRatePerUnit=$closingRatePerUnit, contractDescription=$contractDescription, netTotal=$netTotal, quantity=$quantity, wapPerUnit=$wapPerUnit, additionalProperties=$additionalProperties}" + } + + class DetailedTrade + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val brokerage: JsonField, + private val buySell: JsonField, + private val closingRatePerUnit: JsonField, + private val exchange: JsonField, + private val netRatePerUnit: JsonField, + private val netTotal: JsonField, + private val orderNumber: JsonField, + private val orderTime: JsonField, + private val quantity: JsonField, + private val remarks: JsonField, + private val securityDescription: JsonField, + private val tradeNumber: JsonField, + private val tradeTime: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("brokerage") + @ExcludeMissing + brokerage: JsonField = JsonMissing.of(), + @JsonProperty("buy_sell") + @ExcludeMissing + buySell: JsonField = JsonMissing.of(), + @JsonProperty("closing_rate_per_unit") + @ExcludeMissing + closingRatePerUnit: JsonField = JsonMissing.of(), + @JsonProperty("exchange") + @ExcludeMissing + exchange: JsonField = JsonMissing.of(), + @JsonProperty("net_rate_per_unit") + @ExcludeMissing + netRatePerUnit: JsonField = JsonMissing.of(), + @JsonProperty("net_total") + @ExcludeMissing + netTotal: JsonField = JsonMissing.of(), + @JsonProperty("order_number") + @ExcludeMissing + orderNumber: JsonField = JsonMissing.of(), + @JsonProperty("order_time") + @ExcludeMissing + orderTime: JsonField = JsonMissing.of(), + @JsonProperty("quantity") + @ExcludeMissing + quantity: JsonField = JsonMissing.of(), + @JsonProperty("remarks") + @ExcludeMissing + remarks: JsonField = JsonMissing.of(), + @JsonProperty("security_description") + @ExcludeMissing + securityDescription: JsonField = JsonMissing.of(), + @JsonProperty("trade_number") + @ExcludeMissing + tradeNumber: JsonField = JsonMissing.of(), + @JsonProperty("trade_time") + @ExcludeMissing + tradeTime: JsonField = JsonMissing.of(), + ) : this( + brokerage, + buySell, + closingRatePerUnit, + exchange, + netRatePerUnit, + netTotal, + orderNumber, + orderTime, + quantity, + remarks, + securityDescription, + tradeNumber, + tradeTime, + mutableMapOf(), + ) + + /** + * Brokerage charged for this trade + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun brokerage(): Optional = brokerage.getOptional("brokerage") + + /** + * Transaction type (B for Buy, S for Sell) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun buySell(): Optional = buySell.getOptional("buy_sell") + + /** + * Closing rate per unit + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun closingRatePerUnit(): Optional = + closingRatePerUnit.getOptional("closing_rate_per_unit") + + /** + * Exchange name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun exchange(): Optional = exchange.getOptional("exchange") + + /** + * Net rate per unit + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun netRatePerUnit(): Optional = netRatePerUnit.getOptional("net_rate_per_unit") + + /** + * Net total for this trade + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun netTotal(): Optional = netTotal.getOptional("net_total") + + /** + * Order reference number + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun orderNumber(): Optional = orderNumber.getOptional("order_number") + + /** + * Time when order was placed + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun orderTime(): Optional = orderTime.getOptional("order_time") + + /** + * Quantity traded + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun quantity(): Optional = quantity.getOptional("quantity") + + /** + * Additional remarks or notes + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun remarks(): Optional = remarks.getOptional("remarks") + + /** + * Security name with exchange and ISIN + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun securityDescription(): Optional = + securityDescription.getOptional("security_description") + + /** + * Trade reference number + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun tradeNumber(): Optional = tradeNumber.getOptional("trade_number") + + /** + * Time when trade was executed + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun tradeTime(): Optional = tradeTime.getOptional("trade_time") + + /** + * Returns the raw JSON value of [brokerage]. + * + * Unlike [brokerage], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("brokerage") + @ExcludeMissing + fun _brokerage(): JsonField = brokerage + + /** + * Returns the raw JSON value of [buySell]. + * + * Unlike [buySell], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("buy_sell") @ExcludeMissing fun _buySell(): JsonField = buySell + + /** + * Returns the raw JSON value of [closingRatePerUnit]. + * + * Unlike [closingRatePerUnit], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("closing_rate_per_unit") + @ExcludeMissing + fun _closingRatePerUnit(): JsonField = closingRatePerUnit + + /** + * Returns the raw JSON value of [exchange]. + * + * Unlike [exchange], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("exchange") @ExcludeMissing fun _exchange(): JsonField = exchange + + /** + * Returns the raw JSON value of [netRatePerUnit]. + * + * Unlike [netRatePerUnit], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("net_rate_per_unit") + @ExcludeMissing + fun _netRatePerUnit(): JsonField = netRatePerUnit + + /** + * Returns the raw JSON value of [netTotal]. + * + * Unlike [netTotal], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("net_total") @ExcludeMissing fun _netTotal(): JsonField = netTotal + + /** + * Returns the raw JSON value of [orderNumber]. + * + * Unlike [orderNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("order_number") + @ExcludeMissing + fun _orderNumber(): JsonField = orderNumber + + /** + * Returns the raw JSON value of [orderTime]. + * + * Unlike [orderTime], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("order_time") + @ExcludeMissing + fun _orderTime(): JsonField = orderTime + + /** + * Returns the raw JSON value of [quantity]. + * + * Unlike [quantity], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("quantity") @ExcludeMissing fun _quantity(): JsonField = quantity + + /** + * Returns the raw JSON value of [remarks]. + * + * Unlike [remarks], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("remarks") @ExcludeMissing fun _remarks(): JsonField = remarks + + /** + * Returns the raw JSON value of [securityDescription]. + * + * Unlike [securityDescription], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("security_description") + @ExcludeMissing + fun _securityDescription(): JsonField = securityDescription + + /** + * Returns the raw JSON value of [tradeNumber]. + * + * Unlike [tradeNumber], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("trade_number") + @ExcludeMissing + fun _tradeNumber(): JsonField = tradeNumber + + /** + * Returns the raw JSON value of [tradeTime]. + * + * Unlike [tradeTime], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("trade_time") + @ExcludeMissing + fun _tradeTime(): JsonField = tradeTime + + @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 [DetailedTrade]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [DetailedTrade]. */ + class Builder internal constructor() { + + private var brokerage: JsonField = JsonMissing.of() + private var buySell: JsonField = JsonMissing.of() + private var closingRatePerUnit: JsonField = JsonMissing.of() + private var exchange: JsonField = JsonMissing.of() + private var netRatePerUnit: JsonField = JsonMissing.of() + private var netTotal: JsonField = JsonMissing.of() + private var orderNumber: JsonField = JsonMissing.of() + private var orderTime: JsonField = JsonMissing.of() + private var quantity: JsonField = JsonMissing.of() + private var remarks: JsonField = JsonMissing.of() + private var securityDescription: JsonField = JsonMissing.of() + private var tradeNumber: JsonField = JsonMissing.of() + private var tradeTime: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(detailedTrade: DetailedTrade) = apply { + brokerage = detailedTrade.brokerage + buySell = detailedTrade.buySell + closingRatePerUnit = detailedTrade.closingRatePerUnit + exchange = detailedTrade.exchange + netRatePerUnit = detailedTrade.netRatePerUnit + netTotal = detailedTrade.netTotal + orderNumber = detailedTrade.orderNumber + orderTime = detailedTrade.orderTime + quantity = detailedTrade.quantity + remarks = detailedTrade.remarks + securityDescription = detailedTrade.securityDescription + tradeNumber = detailedTrade.tradeNumber + tradeTime = detailedTrade.tradeTime + additionalProperties = detailedTrade.additionalProperties.toMutableMap() + } + + /** Brokerage charged for this trade */ + fun brokerage(brokerage: Float) = brokerage(JsonField.of(brokerage)) + + /** + * Sets [Builder.brokerage] to an arbitrary JSON value. + * + * You should usually call [Builder.brokerage] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun brokerage(brokerage: JsonField) = apply { this.brokerage = brokerage } + + /** Transaction type (B for Buy, S for Sell) */ + fun buySell(buySell: String) = buySell(JsonField.of(buySell)) + + /** + * Sets [Builder.buySell] to an arbitrary JSON value. + * + * You should usually call [Builder.buySell] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun buySell(buySell: JsonField) = apply { this.buySell = buySell } + + /** Closing rate per unit */ + fun closingRatePerUnit(closingRatePerUnit: Float) = + closingRatePerUnit(JsonField.of(closingRatePerUnit)) + + /** + * Sets [Builder.closingRatePerUnit] to an arbitrary JSON value. + * + * You should usually call [Builder.closingRatePerUnit] with a well-typed [Float] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun closingRatePerUnit(closingRatePerUnit: JsonField) = apply { + this.closingRatePerUnit = closingRatePerUnit + } + + /** Exchange name */ + fun exchange(exchange: String) = exchange(JsonField.of(exchange)) + + /** + * Sets [Builder.exchange] to an arbitrary JSON value. + * + * You should usually call [Builder.exchange] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun exchange(exchange: JsonField) = apply { this.exchange = exchange } + + /** Net rate per unit */ + fun netRatePerUnit(netRatePerUnit: Float) = + netRatePerUnit(JsonField.of(netRatePerUnit)) + + /** + * Sets [Builder.netRatePerUnit] to an arbitrary JSON value. + * + * You should usually call [Builder.netRatePerUnit] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun netRatePerUnit(netRatePerUnit: JsonField) = apply { + this.netRatePerUnit = netRatePerUnit + } + + /** Net total for this trade */ + fun netTotal(netTotal: Float) = netTotal(JsonField.of(netTotal)) + + /** + * Sets [Builder.netTotal] to an arbitrary JSON value. + * + * You should usually call [Builder.netTotal] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun netTotal(netTotal: JsonField) = apply { this.netTotal = netTotal } + + /** Order reference number */ + fun orderNumber(orderNumber: String) = orderNumber(JsonField.of(orderNumber)) + + /** + * Sets [Builder.orderNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.orderNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun orderNumber(orderNumber: JsonField) = apply { + this.orderNumber = orderNumber + } + + /** Time when order was placed */ + fun orderTime(orderTime: String) = orderTime(JsonField.of(orderTime)) + + /** + * Sets [Builder.orderTime] to an arbitrary JSON value. + * + * You should usually call [Builder.orderTime] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun orderTime(orderTime: JsonField) = apply { this.orderTime = orderTime } + + /** Quantity traded */ + fun quantity(quantity: Float) = quantity(JsonField.of(quantity)) + + /** + * Sets [Builder.quantity] to an arbitrary JSON value. + * + * You should usually call [Builder.quantity] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun quantity(quantity: JsonField) = apply { this.quantity = quantity } + + /** Additional remarks or notes */ + fun remarks(remarks: String) = remarks(JsonField.of(remarks)) + + /** + * Sets [Builder.remarks] to an arbitrary JSON value. + * + * You should usually call [Builder.remarks] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun remarks(remarks: JsonField) = apply { this.remarks = remarks } + + /** Security name with exchange and ISIN */ + fun securityDescription(securityDescription: String) = + securityDescription(JsonField.of(securityDescription)) + + /** + * Sets [Builder.securityDescription] to an arbitrary JSON value. + * + * You should usually call [Builder.securityDescription] with a well-typed [String] + * value instead. This method is primarily for setting the field to an undocumented + * or not yet supported value. + */ + fun securityDescription(securityDescription: JsonField) = apply { + this.securityDescription = securityDescription + } + + /** Trade reference number */ + fun tradeNumber(tradeNumber: String) = tradeNumber(JsonField.of(tradeNumber)) + + /** + * Sets [Builder.tradeNumber] to an arbitrary JSON value. + * + * You should usually call [Builder.tradeNumber] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun tradeNumber(tradeNumber: JsonField) = apply { + this.tradeNumber = tradeNumber + } + + /** Time when trade was executed */ + fun tradeTime(tradeTime: String) = tradeTime(JsonField.of(tradeTime)) + + /** + * Sets [Builder.tradeTime] to an arbitrary JSON value. + * + * You should usually call [Builder.tradeTime] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun tradeTime(tradeTime: JsonField) = apply { this.tradeTime = tradeTime } + + 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 [DetailedTrade]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): DetailedTrade = + DetailedTrade( + brokerage, + buySell, + closingRatePerUnit, + exchange, + netRatePerUnit, + netTotal, + orderNumber, + orderTime, + quantity, + remarks, + securityDescription, + tradeNumber, + tradeTime, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): DetailedTrade = apply { + if (validated) { + return@apply + } + + brokerage() + buySell() + closingRatePerUnit() + exchange() + netRatePerUnit() + netTotal() + orderNumber() + orderTime() + quantity() + remarks() + securityDescription() + tradeNumber() + tradeTime() + 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 (brokerage.asKnown().isPresent) 1 else 0) + + (if (buySell.asKnown().isPresent) 1 else 0) + + (if (closingRatePerUnit.asKnown().isPresent) 1 else 0) + + (if (exchange.asKnown().isPresent) 1 else 0) + + (if (netRatePerUnit.asKnown().isPresent) 1 else 0) + + (if (netTotal.asKnown().isPresent) 1 else 0) + + (if (orderNumber.asKnown().isPresent) 1 else 0) + + (if (orderTime.asKnown().isPresent) 1 else 0) + + (if (quantity.asKnown().isPresent) 1 else 0) + + (if (remarks.asKnown().isPresent) 1 else 0) + + (if (securityDescription.asKnown().isPresent) 1 else 0) + + (if (tradeNumber.asKnown().isPresent) 1 else 0) + + (if (tradeTime.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is DetailedTrade && + brokerage == other.brokerage && + buySell == other.buySell && + closingRatePerUnit == other.closingRatePerUnit && + exchange == other.exchange && + netRatePerUnit == other.netRatePerUnit && + netTotal == other.netTotal && + orderNumber == other.orderNumber && + orderTime == other.orderTime && + quantity == other.quantity && + remarks == other.remarks && + securityDescription == other.securityDescription && + tradeNumber == other.tradeNumber && + tradeTime == other.tradeTime && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + brokerage, + buySell, + closingRatePerUnit, + exchange, + netRatePerUnit, + netTotal, + orderNumber, + orderTime, + quantity, + remarks, + securityDescription, + tradeNumber, + tradeTime, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "DetailedTrade{brokerage=$brokerage, buySell=$buySell, closingRatePerUnit=$closingRatePerUnit, exchange=$exchange, netRatePerUnit=$netRatePerUnit, netTotal=$netTotal, orderNumber=$orderNumber, orderTime=$orderTime, quantity=$quantity, remarks=$remarks, securityDescription=$securityDescription, tradeNumber=$tradeNumber, tradeTime=$tradeTime, additionalProperties=$additionalProperties}" + } + + class EquityTransaction + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val buyQuantity: JsonField, + private val buyTotalValue: JsonField, + private val buyWap: JsonField, + private val isin: JsonField, + private val netObligation: JsonField, + private val securityName: JsonField, + private val securitySymbol: JsonField, + private val sellQuantity: JsonField, + private val sellTotalValue: JsonField, + private val sellWap: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("buy_quantity") + @ExcludeMissing + buyQuantity: JsonField = JsonMissing.of(), + @JsonProperty("buy_total_value") + @ExcludeMissing + buyTotalValue: JsonField = JsonMissing.of(), + @JsonProperty("buy_wap") + @ExcludeMissing + buyWap: JsonField = JsonMissing.of(), + @JsonProperty("isin") @ExcludeMissing isin: JsonField = JsonMissing.of(), + @JsonProperty("net_obligation") + @ExcludeMissing + netObligation: JsonField = JsonMissing.of(), + @JsonProperty("security_name") + @ExcludeMissing + securityName: JsonField = JsonMissing.of(), + @JsonProperty("security_symbol") + @ExcludeMissing + securitySymbol: JsonField = JsonMissing.of(), + @JsonProperty("sell_quantity") + @ExcludeMissing + sellQuantity: JsonField = JsonMissing.of(), + @JsonProperty("sell_total_value") + @ExcludeMissing + sellTotalValue: JsonField = JsonMissing.of(), + @JsonProperty("sell_wap") + @ExcludeMissing + sellWap: JsonField = JsonMissing.of(), + ) : this( + buyQuantity, + buyTotalValue, + buyWap, + isin, + netObligation, + securityName, + securitySymbol, + sellQuantity, + sellTotalValue, + sellWap, + mutableMapOf(), + ) + + /** + * Total quantity purchased + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun buyQuantity(): Optional = buyQuantity.getOptional("buy_quantity") + + /** + * Total value of buy transactions + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun buyTotalValue(): Optional = buyTotalValue.getOptional("buy_total_value") + + /** + * Weighted Average Price for buy transactions + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun buyWap(): Optional = buyWap.getOptional("buy_wap") + + /** + * ISIN code of the security + * + * @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") + + /** + * Net amount payable/receivable for this security + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun netObligation(): Optional = netObligation.getOptional("net_obligation") + + /** + * Name of the security + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun securityName(): Optional = securityName.getOptional("security_name") + + /** + * Trading symbol + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun securitySymbol(): Optional = securitySymbol.getOptional("security_symbol") + + /** + * Total quantity sold + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun sellQuantity(): Optional = sellQuantity.getOptional("sell_quantity") + + /** + * Total value of sell transactions + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun sellTotalValue(): Optional = sellTotalValue.getOptional("sell_total_value") + + /** + * Weighted Average Price for sell transactions + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun sellWap(): Optional = sellWap.getOptional("sell_wap") + + /** + * Returns the raw JSON value of [buyQuantity]. + * + * Unlike [buyQuantity], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("buy_quantity") + @ExcludeMissing + fun _buyQuantity(): JsonField = buyQuantity + + /** + * Returns the raw JSON value of [buyTotalValue]. + * + * Unlike [buyTotalValue], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("buy_total_value") + @ExcludeMissing + fun _buyTotalValue(): JsonField = buyTotalValue + + /** + * Returns the raw JSON value of [buyWap]. + * + * Unlike [buyWap], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("buy_wap") @ExcludeMissing fun _buyWap(): JsonField = buyWap + + /** + * 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 + + /** + * Returns the raw JSON value of [netObligation]. + * + * Unlike [netObligation], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("net_obligation") + @ExcludeMissing + fun _netObligation(): JsonField = netObligation + + /** + * Returns the raw JSON value of [securityName]. + * + * Unlike [securityName], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("security_name") + @ExcludeMissing + fun _securityName(): JsonField = securityName + + /** + * Returns the raw JSON value of [securitySymbol]. + * + * Unlike [securitySymbol], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("security_symbol") + @ExcludeMissing + fun _securitySymbol(): JsonField = securitySymbol + + /** + * Returns the raw JSON value of [sellQuantity]. + * + * Unlike [sellQuantity], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("sell_quantity") + @ExcludeMissing + fun _sellQuantity(): JsonField = sellQuantity + + /** + * Returns the raw JSON value of [sellTotalValue]. + * + * Unlike [sellTotalValue], this method doesn't throw if the JSON field has an + * unexpected type. + */ + @JsonProperty("sell_total_value") + @ExcludeMissing + fun _sellTotalValue(): JsonField = sellTotalValue + + /** + * Returns the raw JSON value of [sellWap]. + * + * Unlike [sellWap], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sell_wap") @ExcludeMissing fun _sellWap(): JsonField = sellWap + + @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 [EquityTransaction]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [EquityTransaction]. */ + class Builder internal constructor() { + + private var buyQuantity: JsonField = JsonMissing.of() + private var buyTotalValue: JsonField = JsonMissing.of() + private var buyWap: JsonField = JsonMissing.of() + private var isin: JsonField = JsonMissing.of() + private var netObligation: JsonField = JsonMissing.of() + private var securityName: JsonField = JsonMissing.of() + private var securitySymbol: JsonField = JsonMissing.of() + private var sellQuantity: JsonField = JsonMissing.of() + private var sellTotalValue: JsonField = JsonMissing.of() + private var sellWap: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(equityTransaction: EquityTransaction) = apply { + buyQuantity = equityTransaction.buyQuantity + buyTotalValue = equityTransaction.buyTotalValue + buyWap = equityTransaction.buyWap + isin = equityTransaction.isin + netObligation = equityTransaction.netObligation + securityName = equityTransaction.securityName + securitySymbol = equityTransaction.securitySymbol + sellQuantity = equityTransaction.sellQuantity + sellTotalValue = equityTransaction.sellTotalValue + sellWap = equityTransaction.sellWap + additionalProperties = equityTransaction.additionalProperties.toMutableMap() + } + + /** Total quantity purchased */ + fun buyQuantity(buyQuantity: Float) = buyQuantity(JsonField.of(buyQuantity)) + + /** + * Sets [Builder.buyQuantity] to an arbitrary JSON value. + * + * You should usually call [Builder.buyQuantity] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun buyQuantity(buyQuantity: JsonField) = apply { + this.buyQuantity = buyQuantity + } + + /** Total value of buy transactions */ + fun buyTotalValue(buyTotalValue: Float) = buyTotalValue(JsonField.of(buyTotalValue)) + + /** + * Sets [Builder.buyTotalValue] to an arbitrary JSON value. + * + * You should usually call [Builder.buyTotalValue] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun buyTotalValue(buyTotalValue: JsonField) = apply { + this.buyTotalValue = buyTotalValue + } + + /** Weighted Average Price for buy transactions */ + fun buyWap(buyWap: Float) = buyWap(JsonField.of(buyWap)) + + /** + * Sets [Builder.buyWap] to an arbitrary JSON value. + * + * You should usually call [Builder.buyWap] with a well-typed [Float] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun buyWap(buyWap: JsonField) = apply { this.buyWap = buyWap } + + /** ISIN code of the security */ + fun isin(isin: String) = isin(JsonField.of(isin)) + + /** + * Sets [Builder.isin] to an arbitrary JSON value. + * + * 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. + */ + fun isin(isin: JsonField) = apply { this.isin = isin } + + /** Net amount payable/receivable for this security */ + fun netObligation(netObligation: Float) = netObligation(JsonField.of(netObligation)) + + /** + * Sets [Builder.netObligation] to an arbitrary JSON value. + * + * You should usually call [Builder.netObligation] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun netObligation(netObligation: JsonField) = apply { + this.netObligation = netObligation + } + + /** Name of the security */ + fun securityName(securityName: String) = securityName(JsonField.of(securityName)) + + /** + * Sets [Builder.securityName] to an arbitrary JSON value. + * + * You should usually call [Builder.securityName] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun securityName(securityName: JsonField) = apply { + this.securityName = securityName + } + + /** Trading symbol */ + fun securitySymbol(securitySymbol: String) = + securitySymbol(JsonField.of(securitySymbol)) + + /** + * Sets [Builder.securitySymbol] to an arbitrary JSON value. + * + * You should usually call [Builder.securitySymbol] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun securitySymbol(securitySymbol: JsonField) = apply { + this.securitySymbol = securitySymbol + } + + /** Total quantity sold */ + fun sellQuantity(sellQuantity: Float) = sellQuantity(JsonField.of(sellQuantity)) + + /** + * Sets [Builder.sellQuantity] to an arbitrary JSON value. + * + * You should usually call [Builder.sellQuantity] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun sellQuantity(sellQuantity: JsonField) = apply { + this.sellQuantity = sellQuantity + } + + /** Total value of sell transactions */ + fun sellTotalValue(sellTotalValue: Float) = + sellTotalValue(JsonField.of(sellTotalValue)) + + /** + * Sets [Builder.sellTotalValue] to an arbitrary JSON value. + * + * You should usually call [Builder.sellTotalValue] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun sellTotalValue(sellTotalValue: JsonField) = apply { + this.sellTotalValue = sellTotalValue + } + + /** Weighted Average Price for sell transactions */ + fun sellWap(sellWap: Float) = sellWap(JsonField.of(sellWap)) + + /** + * Sets [Builder.sellWap] to an arbitrary JSON value. + * + * You should usually call [Builder.sellWap] with a well-typed [Float] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun sellWap(sellWap: JsonField) = apply { this.sellWap = sellWap } + + 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 [EquityTransaction]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): EquityTransaction = + EquityTransaction( + buyQuantity, + buyTotalValue, + buyWap, + isin, + netObligation, + securityName, + securitySymbol, + sellQuantity, + sellTotalValue, + sellWap, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): EquityTransaction = apply { + if (validated) { + return@apply + } + + buyQuantity() + buyTotalValue() + buyWap() + isin() + netObligation() + securityName() + securitySymbol() + sellQuantity() + sellTotalValue() + sellWap() + 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 (buyQuantity.asKnown().isPresent) 1 else 0) + + (if (buyTotalValue.asKnown().isPresent) 1 else 0) + + (if (buyWap.asKnown().isPresent) 1 else 0) + + (if (isin.asKnown().isPresent) 1 else 0) + + (if (netObligation.asKnown().isPresent) 1 else 0) + + (if (securityName.asKnown().isPresent) 1 else 0) + + (if (securitySymbol.asKnown().isPresent) 1 else 0) + + (if (sellQuantity.asKnown().isPresent) 1 else 0) + + (if (sellTotalValue.asKnown().isPresent) 1 else 0) + + (if (sellWap.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is EquityTransaction && + buyQuantity == other.buyQuantity && + buyTotalValue == other.buyTotalValue && + buyWap == other.buyWap && + isin == other.isin && + netObligation == other.netObligation && + securityName == other.securityName && + securitySymbol == other.securitySymbol && + sellQuantity == other.sellQuantity && + sellTotalValue == other.sellTotalValue && + sellWap == other.sellWap && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + buyQuantity, + buyTotalValue, + buyWap, + isin, + netObligation, + securityName, + securitySymbol, + sellQuantity, + sellTotalValue, + sellWap, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "EquityTransaction{buyQuantity=$buyQuantity, buyTotalValue=$buyTotalValue, buyWap=$buyWap, isin=$isin, netObligation=$netObligation, securityName=$securityName, securitySymbol=$securitySymbol, sellQuantity=$sellQuantity, sellTotalValue=$sellTotalValue, sellWap=$sellWap, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Data && + brokerInfo == other.brokerInfo && + chargesSummary == other.chargesSummary && + clientInfo == other.clientInfo && + contractNoteInfo == other.contractNoteInfo && + derivativesTransactions == other.derivativesTransactions && + detailedTrades == other.detailedTrades && + equityTransactions == other.equityTransactions && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + brokerInfo, + chargesSummary, + clientInfo, + contractNoteInfo, + derivativesTransactions, + detailedTrades, + equityTransactions, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Data{brokerInfo=$brokerInfo, chargesSummary=$chargesSummary, clientInfo=$clientInfo, contractNoteInfo=$contractNoteInfo, derivativesTransactions=$derivativesTransactions, detailedTrades=$detailedTrades, equityTransactions=$equityTransactions, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ContractNoteParseResponse && + data == other.data && + msg == other.msg && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(data, msg, status, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ContractNoteParseResponse{data=$data, msg=$msg, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/credits/CreditCheckParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/credits/CreditCheckParams.kt new file mode 100644 index 0000000..aa45ce8 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/credits/CreditCheckParams.kt @@ -0,0 +1,219 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.credits + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +import java.util.Objects +import java.util.Optional + +/** + * Check your remaining API credits and usage for the current billing period. + * + * Returns: + * - Number of API calls used and remaining credits + * - Credit limit and reset date + * - List of enabled features for your plan + * + * Credits reset at the start of each billing period. + */ +class CreditCheckParams +private constructor( + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** 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 { + + @JvmStatic fun none(): CreditCheckParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [CreditCheckParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CreditCheckParams]. */ + class Builder internal constructor() { + + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(creditCheckParams: CreditCheckParams) = apply { + additionalHeaders = creditCheckParams.additionalHeaders.toBuilder() + additionalQueryParams = creditCheckParams.additionalQueryParams.toBuilder() + additionalBodyProperties = creditCheckParams.additionalBodyProperties.toMutableMap() + } + + 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) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [CreditCheckParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CreditCheckParams = + CreditCheckParams( + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CreditCheckParams && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash(additionalHeaders, additionalQueryParams, additionalBodyProperties) + + override fun toString() = + "CreditCheckParams{additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/credits/CreditCheckResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/credits/CreditCheckResponse.kt new file mode 100644 index 0000000..c5f263b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/credits/CreditCheckResponse.kt @@ -0,0 +1,395 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.credits + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class CreditCheckResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val enabledFeatures: JsonField>, + private val isUnlimited: JsonField, + private val limit: JsonField, + private val remaining: JsonField, + private val resetsAt: JsonField, + private val used: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("enabled_features") + @ExcludeMissing + enabledFeatures: JsonField> = JsonMissing.of(), + @JsonProperty("is_unlimited") + @ExcludeMissing + isUnlimited: JsonField = JsonMissing.of(), + @JsonProperty("limit") @ExcludeMissing limit: JsonField = JsonMissing.of(), + @JsonProperty("remaining") @ExcludeMissing remaining: JsonField = JsonMissing.of(), + @JsonProperty("resets_at") + @ExcludeMissing + resetsAt: JsonField = JsonMissing.of(), + @JsonProperty("used") @ExcludeMissing used: JsonField = JsonMissing.of(), + ) : this(enabledFeatures, isUnlimited, limit, remaining, resetsAt, used, mutableMapOf()) + + /** + * List of API features enabled for your plan + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun enabledFeatures(): Optional> = enabledFeatures.getOptional("enabled_features") + + /** + * Whether the account has unlimited credits + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun isUnlimited(): Optional = isUnlimited.getOptional("is_unlimited") + + /** + * Total credit limit for billing period + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun limit(): Optional = limit.getOptional("limit") + + /** + * Remaining credits (null if unlimited) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun remaining(): Optional = remaining.getOptional("remaining") + + /** + * When credits reset (ISO 8601) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun resetsAt(): Optional = resetsAt.getOptional("resets_at") + + /** + * Number of credits used this billing period + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun used(): Optional = used.getOptional("used") + + /** + * Returns the raw JSON value of [enabledFeatures]. + * + * Unlike [enabledFeatures], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("enabled_features") + @ExcludeMissing + fun _enabledFeatures(): JsonField> = enabledFeatures + + /** + * Returns the raw JSON value of [isUnlimited]. + * + * Unlike [isUnlimited], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("is_unlimited") + @ExcludeMissing + fun _isUnlimited(): JsonField = isUnlimited + + /** + * Returns the raw JSON value of [limit]. + * + * Unlike [limit], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("limit") @ExcludeMissing fun _limit(): JsonField = limit + + /** + * Returns the raw JSON value of [remaining]. + * + * Unlike [remaining], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("remaining") @ExcludeMissing fun _remaining(): JsonField = remaining + + /** + * Returns the raw JSON value of [resetsAt]. + * + * Unlike [resetsAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("resets_at") @ExcludeMissing fun _resetsAt(): JsonField = resetsAt + + /** + * Returns the raw JSON value of [used]. + * + * Unlike [used], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("used") @ExcludeMissing fun _used(): JsonField = used + + @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 [CreditCheckResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CreditCheckResponse]. */ + class Builder internal constructor() { + + private var enabledFeatures: JsonField>? = null + private var isUnlimited: JsonField = JsonMissing.of() + private var limit: JsonField = JsonMissing.of() + private var remaining: JsonField = JsonMissing.of() + private var resetsAt: JsonField = JsonMissing.of() + private var used: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(creditCheckResponse: CreditCheckResponse) = apply { + enabledFeatures = creditCheckResponse.enabledFeatures.map { it.toMutableList() } + isUnlimited = creditCheckResponse.isUnlimited + limit = creditCheckResponse.limit + remaining = creditCheckResponse.remaining + resetsAt = creditCheckResponse.resetsAt + used = creditCheckResponse.used + additionalProperties = creditCheckResponse.additionalProperties.toMutableMap() + } + + /** List of API features enabled for your plan */ + fun enabledFeatures(enabledFeatures: List) = + enabledFeatures(JsonField.of(enabledFeatures)) + + /** + * Sets [Builder.enabledFeatures] to an arbitrary JSON value. + * + * You should usually call [Builder.enabledFeatures] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun enabledFeatures(enabledFeatures: JsonField>) = apply { + this.enabledFeatures = enabledFeatures.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [enabledFeatures]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addEnabledFeature(enabledFeature: String) = apply { + enabledFeatures = + (enabledFeatures ?: JsonField.of(mutableListOf())).also { + checkKnown("enabledFeatures", it).add(enabledFeature) + } + } + + /** Whether the account has unlimited credits */ + fun isUnlimited(isUnlimited: Boolean) = isUnlimited(JsonField.of(isUnlimited)) + + /** + * Sets [Builder.isUnlimited] to an arbitrary JSON value. + * + * You should usually call [Builder.isUnlimited] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun isUnlimited(isUnlimited: JsonField) = apply { this.isUnlimited = isUnlimited } + + /** Total credit limit for billing period */ + fun limit(limit: Long) = limit(JsonField.of(limit)) + + /** + * Sets [Builder.limit] to an arbitrary JSON value. + * + * You should usually call [Builder.limit] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun limit(limit: JsonField) = apply { this.limit = limit } + + /** Remaining credits (null if unlimited) */ + fun remaining(remaining: Double?) = remaining(JsonField.ofNullable(remaining)) + + /** + * Alias for [Builder.remaining]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun remaining(remaining: Double) = remaining(remaining as Double?) + + /** Alias for calling [Builder.remaining] with `remaining.orElse(null)`. */ + fun remaining(remaining: Optional) = remaining(remaining.getOrNull()) + + /** + * Sets [Builder.remaining] to an arbitrary JSON value. + * + * You should usually call [Builder.remaining] with a well-typed [Double] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun remaining(remaining: JsonField) = apply { this.remaining = remaining } + + /** When credits reset (ISO 8601) */ + fun resetsAt(resetsAt: OffsetDateTime?) = resetsAt(JsonField.ofNullable(resetsAt)) + + /** Alias for calling [Builder.resetsAt] with `resetsAt.orElse(null)`. */ + fun resetsAt(resetsAt: Optional) = resetsAt(resetsAt.getOrNull()) + + /** + * Sets [Builder.resetsAt] to an arbitrary JSON value. + * + * You should usually call [Builder.resetsAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun resetsAt(resetsAt: JsonField) = apply { this.resetsAt = resetsAt } + + /** Number of credits used this billing period */ + fun used(used: Double) = used(JsonField.of(used)) + + /** + * Sets [Builder.used] to an arbitrary JSON value. + * + * You should usually call [Builder.used] with a well-typed [Double] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun used(used: JsonField) = apply { this.used = used } + + 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 [CreditCheckResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CreditCheckResponse = + CreditCheckResponse( + (enabledFeatures ?: JsonMissing.of()).map { it.toImmutable() }, + isUnlimited, + limit, + remaining, + resetsAt, + used, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): CreditCheckResponse = apply { + if (validated) { + return@apply + } + + enabledFeatures() + isUnlimited() + limit() + remaining() + resetsAt() + used() + 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 = + (enabledFeatures.asKnown().getOrNull()?.size ?: 0) + + (if (isUnlimited.asKnown().isPresent) 1 else 0) + + (if (limit.asKnown().isPresent) 1 else 0) + + (if (remaining.asKnown().isPresent) 1 else 0) + + (if (resetsAt.asKnown().isPresent) 1 else 0) + + (if (used.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is CreditCheckResponse && + enabledFeatures == other.enabledFeatures && + isUnlimited == other.isUnlimited && + limit == other.limit && + remaining == other.remaining && + resetsAt == other.resetsAt && + used == other.used && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + enabledFeatures, + isUnlimited, + limit, + remaining, + resetsAt, + used, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CreditCheckResponse{enabledFeatures=$enabledFeatures, isUnlimited=$isUnlimited, limit=$limit, remaining=$remaining, resetsAt=$resetsAt, used=$used, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateParams.kt new file mode 100644 index 0000000..d042ab7 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateParams.kt @@ -0,0 +1,1051 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +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.checkKnown +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +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 + +/** + * Create a dedicated inbound email address for collecting CAS statements via email forwarding. When + * an investor forwards a CAS email to this address, we verify the sender and make the file + * available to you. + * + * `callback_url` is **optional**: + * - **Set it** — we POST each parsed email to your webhook as it arrives. + * - **Omit it** — retrieve files via `GET /v4/inbound-email/{id}/files` without building a webhook + * consumer. + */ +class InboundEmailCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Optional custom email prefix (e.g. `john-portfolio@import.casparser.in`). 3-32 chars, + * alphanumeric + hyphens, must start/end with a letter or number. If omitted, a random ID is + * generated. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun alias(): Optional = body.alias() + + /** + * Filter emails by CAS provider. If omitted, accepts all providers. + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun allowedSources(): Optional> = body.allowedSources() + + /** + * Optional webhook URL where we POST parsed emails. Must be HTTPS in production (HTTP allowed + * for localhost). If omitted, retrieve files via `GET /v4/inbound-email/{id}/files`. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun callbackUrl(): Optional = body.callbackUrl() + + /** + * Optional key-value pairs (max 10) to include in webhook payload. Useful for passing context + * like plan_type, campaign_id, etc. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun metadata(): Optional = body.metadata() + + /** + * Your internal identifier (e.g., user_id, account_id). Returned in webhook payload for + * correlation. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun reference(): Optional = body.reference() + + /** + * Returns the raw JSON value of [alias]. + * + * Unlike [alias], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _alias(): JsonField = body._alias() + + /** + * Returns the raw JSON value of [allowedSources]. + * + * Unlike [allowedSources], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _allowedSources(): JsonField> = body._allowedSources() + + /** + * Returns the raw JSON value of [callbackUrl]. + * + * Unlike [callbackUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _callbackUrl(): JsonField = body._callbackUrl() + + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _metadata(): JsonField = body._metadata() + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _reference(): JsonField = body._reference() + + 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 { + + @JvmStatic fun none(): InboundEmailCreateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [InboundEmailCreateParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailCreateParams]. */ + 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(inboundEmailCreateParams: InboundEmailCreateParams) = apply { + body = inboundEmailCreateParams.body.toBuilder() + additionalHeaders = inboundEmailCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = inboundEmailCreateParams.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: + * - [alias] + * - [allowedSources] + * - [callbackUrl] + * - [metadata] + * - [reference] + * - etc. + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** + * Optional custom email prefix (e.g. `john-portfolio@import.casparser.in`). 3-32 chars, + * alphanumeric + hyphens, must start/end with a letter or number. If omitted, a random ID + * is generated. + */ + fun alias(alias: String) = apply { body.alias(alias) } + + /** + * Sets [Builder.alias] to an arbitrary JSON value. + * + * You should usually call [Builder.alias] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun alias(alias: JsonField) = apply { body.alias(alias) } + + /** + * Filter emails by CAS provider. If omitted, accepts all providers. + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + */ + fun allowedSources(allowedSources: List) = apply { + body.allowedSources(allowedSources) + } + + /** + * Sets [Builder.allowedSources] to an arbitrary JSON value. + * + * You should usually call [Builder.allowedSources] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun allowedSources(allowedSources: JsonField>) = apply { + body.allowedSources(allowedSources) + } + + /** + * Adds a single [AllowedSource] to [allowedSources]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAllowedSource(allowedSource: AllowedSource) = apply { + body.addAllowedSource(allowedSource) + } + + /** + * Optional webhook URL where we POST parsed emails. Must be HTTPS in production (HTTP + * allowed for localhost). If omitted, retrieve files via `GET + * /v4/inbound-email/{id}/files`. + */ + fun callbackUrl(callbackUrl: String?) = apply { body.callbackUrl(callbackUrl) } + + /** Alias for calling [Builder.callbackUrl] with `callbackUrl.orElse(null)`. */ + fun callbackUrl(callbackUrl: Optional) = callbackUrl(callbackUrl.getOrNull()) + + /** + * Sets [Builder.callbackUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.callbackUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun callbackUrl(callbackUrl: JsonField) = apply { body.callbackUrl(callbackUrl) } + + /** + * Optional key-value pairs (max 10) to include in webhook payload. Useful for passing + * context like plan_type, campaign_id, etc. + */ + fun metadata(metadata: Metadata) = apply { body.metadata(metadata) } + + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun metadata(metadata: JsonField) = apply { body.metadata(metadata) } + + /** + * Your internal identifier (e.g., user_id, account_id). Returned in webhook payload for + * correlation. + */ + fun reference(reference: String) = apply { body.reference(reference) } + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun reference(reference: JsonField) = apply { body.reference(reference) } + + 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 [InboundEmailCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailCreateParams = + InboundEmailCreateParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val alias: JsonField, + private val allowedSources: JsonField>, + private val callbackUrl: JsonField, + private val metadata: JsonField, + private val reference: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("alias") @ExcludeMissing alias: JsonField = JsonMissing.of(), + @JsonProperty("allowed_sources") + @ExcludeMissing + allowedSources: JsonField> = JsonMissing.of(), + @JsonProperty("callback_url") + @ExcludeMissing + callbackUrl: JsonField = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), + ) : this(alias, allowedSources, callbackUrl, metadata, reference, mutableMapOf()) + + /** + * Optional custom email prefix (e.g. `john-portfolio@import.casparser.in`). 3-32 chars, + * alphanumeric + hyphens, must start/end with a letter or number. If omitted, a random ID + * is generated. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun alias(): Optional = alias.getOptional("alias") + + /** + * Filter emails by CAS provider. If omitted, accepts all providers. + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun allowedSources(): Optional> = + allowedSources.getOptional("allowed_sources") + + /** + * Optional webhook URL where we POST parsed emails. Must be HTTPS in production (HTTP + * allowed for localhost). If omitted, retrieve files via `GET + * /v4/inbound-email/{id}/files`. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun callbackUrl(): Optional = callbackUrl.getOptional("callback_url") + + /** + * Optional key-value pairs (max 10) to include in webhook payload. Useful for passing + * context like plan_type, campaign_id, etc. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") + + /** + * Your internal identifier (e.g., user_id, account_id). Returned in webhook payload for + * correlation. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun reference(): Optional = reference.getOptional("reference") + + /** + * Returns the raw JSON value of [alias]. + * + * Unlike [alias], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("alias") @ExcludeMissing fun _alias(): JsonField = alias + + /** + * Returns the raw JSON value of [allowedSources]. + * + * Unlike [allowedSources], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("allowed_sources") + @ExcludeMissing + fun _allowedSources(): JsonField> = allowedSources + + /** + * Returns the raw JSON value of [callbackUrl]. + * + * Unlike [callbackUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("callback_url") + @ExcludeMissing + fun _callbackUrl(): JsonField = callbackUrl + + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("metadata") @ExcludeMissing fun _metadata(): JsonField = metadata + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reference") @ExcludeMissing fun _reference(): JsonField = reference + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var alias: JsonField = JsonMissing.of() + private var allowedSources: JsonField>? = null + private var callbackUrl: JsonField = JsonMissing.of() + private var metadata: JsonField = JsonMissing.of() + private var reference: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + alias = body.alias + allowedSources = body.allowedSources.map { it.toMutableList() } + callbackUrl = body.callbackUrl + metadata = body.metadata + reference = body.reference + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * Optional custom email prefix (e.g. `john-portfolio@import.casparser.in`). 3-32 chars, + * alphanumeric + hyphens, must start/end with a letter or number. If omitted, a random + * ID is generated. + */ + fun alias(alias: String) = alias(JsonField.of(alias)) + + /** + * Sets [Builder.alias] to an arbitrary JSON value. + * + * You should usually call [Builder.alias] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun alias(alias: JsonField) = apply { this.alias = alias } + + /** + * Filter emails by CAS provider. If omitted, accepts all providers. + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + */ + fun allowedSources(allowedSources: List) = + allowedSources(JsonField.of(allowedSources)) + + /** + * Sets [Builder.allowedSources] to an arbitrary JSON value. + * + * You should usually call [Builder.allowedSources] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun allowedSources(allowedSources: JsonField>) = apply { + this.allowedSources = allowedSources.map { it.toMutableList() } + } + + /** + * Adds a single [AllowedSource] to [allowedSources]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAllowedSource(allowedSource: AllowedSource) = apply { + allowedSources = + (allowedSources ?: JsonField.of(mutableListOf())).also { + checkKnown("allowedSources", it).add(allowedSource) + } + } + + /** + * Optional webhook URL where we POST parsed emails. Must be HTTPS in production (HTTP + * allowed for localhost). If omitted, retrieve files via `GET + * /v4/inbound-email/{id}/files`. + */ + fun callbackUrl(callbackUrl: String?) = callbackUrl(JsonField.ofNullable(callbackUrl)) + + /** Alias for calling [Builder.callbackUrl] with `callbackUrl.orElse(null)`. */ + fun callbackUrl(callbackUrl: Optional) = callbackUrl(callbackUrl.getOrNull()) + + /** + * Sets [Builder.callbackUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.callbackUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun callbackUrl(callbackUrl: JsonField) = apply { + this.callbackUrl = callbackUrl + } + + /** + * Optional key-value pairs (max 10) to include in webhook payload. Useful for passing + * context like plan_type, campaign_id, etc. + */ + fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) + + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } + + /** + * Your internal identifier (e.g., user_id, account_id). Returned in webhook payload for + * correlation. + */ + fun reference(reference: String) = reference(JsonField.of(reference)) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + 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. + */ + fun build(): Body = + Body( + alias, + (allowedSources ?: JsonMissing.of()).map { it.toImmutable() }, + callbackUrl, + metadata, + reference, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + alias() + allowedSources().ifPresent { it.forEach { it.validate() } } + callbackUrl() + metadata().ifPresent { it.validate() } + reference() + 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 (alias.asKnown().isPresent) 1 else 0) + + (allowedSources.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (callbackUrl.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + alias == other.alias && + allowedSources == other.allowedSources && + callbackUrl == other.callbackUrl && + metadata == other.metadata && + reference == other.reference && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + alias, + allowedSources, + callbackUrl, + metadata, + reference, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{alias=$alias, allowedSources=$allowedSources, callbackUrl=$callbackUrl, metadata=$metadata, reference=$reference, additionalProperties=$additionalProperties}" + } + + class AllowedSource @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 CDSL = of("cdsl") + + @JvmField val NSDL = of("nsdl") + + @JvmField val CAMS = of("cams") + + @JvmField val KFINTECH = of("kfintech") + + @JvmStatic fun of(value: String) = AllowedSource(JsonField.of(value)) + } + + /** An enum containing [AllowedSource]'s known values. */ + enum class Known { + CDSL, + NSDL, + CAMS, + KFINTECH, + } + + /** + * An enum containing [AllowedSource]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AllowedSource] 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 { + CDSL, + NSDL, + CAMS, + KFINTECH, + /** + * An enum member indicating that [AllowedSource] 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) { + CDSL -> Value.CDSL + NSDL -> Value.NSDL + CAMS -> Value.CAMS + KFINTECH -> Value.KFINTECH + 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) { + CDSL -> Known.CDSL + NSDL -> Known.NSDL + CAMS -> Known.CAMS + KFINTECH -> Known.KFINTECH + else -> throw CasParserInvalidDataException("Unknown AllowedSource: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): AllowedSource = 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 AllowedSource && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** + * Optional key-value pairs (max 10) to include in webhook payload. Useful for passing context + * like plan_type, campaign_id, etc. + */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Metadata]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } + + 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 [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Metadata = apply { + if (validated) { + return@apply + } + + 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Metadata && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboundEmailCreateParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InboundEmailCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateResponse.kt new file mode 100644 index 0000000..9ef158d --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateResponse.kt @@ -0,0 +1,923 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** An inbound email address for receiving forwarded CAS emails */ +class InboundEmailCreateResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val allowedSources: JsonField>, + private val callbackUrl: JsonField, + private val createdAt: JsonField, + private val email: JsonField, + private val inboundEmailId: JsonField, + private val metadata: JsonField, + private val reference: JsonField, + private val status: JsonField, + private val updatedAt: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("allowed_sources") + @ExcludeMissing + allowedSources: JsonField> = JsonMissing.of(), + @JsonProperty("callback_url") + @ExcludeMissing + callbackUrl: JsonField = JsonMissing.of(), + @JsonProperty("created_at") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("email") @ExcludeMissing email: JsonField = JsonMissing.of(), + @JsonProperty("inbound_email_id") + @ExcludeMissing + inboundEmailId: JsonField = JsonMissing.of(), + @JsonProperty("metadata") @ExcludeMissing metadata: JsonField = JsonMissing.of(), + @JsonProperty("reference") @ExcludeMissing reference: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("updated_at") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + ) : this( + allowedSources, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + mutableMapOf(), + ) + + /** + * Accepted CAS providers (empty = all) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun allowedSources(): Optional> = + allowedSources.getOptional("allowed_sources") + + /** + * Webhook URL for email notifications. If set, we POST each parsed email here. If omitted, + * files are only retrievable via `GET /v4/inbound-email/{id}/files`. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun callbackUrl(): Optional = callbackUrl.getOptional("callback_url") + + /** + * When the inbound email was created + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): Optional = createdAt.getOptional("created_at") + + /** + * The inbound email address to forward CAS statements to + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun email(): Optional = email.getOptional("email") + + /** + * Unique inbound email identifier + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun inboundEmailId(): Optional = inboundEmailId.getOptional("inbound_email_id") + + /** + * Custom key-value metadata + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") + + /** + * Your internal reference identifier + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun reference(): Optional = reference.getOptional("reference") + + /** + * Current inbound email lifecycle status + * + * @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") + + /** + * When the inbound email was last updated + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): Optional = updatedAt.getOptional("updated_at") + + /** + * Returns the raw JSON value of [allowedSources]. + * + * Unlike [allowedSources], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("allowed_sources") + @ExcludeMissing + fun _allowedSources(): JsonField> = allowedSources + + /** + * Returns the raw JSON value of [callbackUrl]. + * + * Unlike [callbackUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("callback_url") + @ExcludeMissing + fun _callbackUrl(): JsonField = callbackUrl + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("created_at") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * 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 [inboundEmailId]. + * + * Unlike [inboundEmailId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inbound_email_id") + @ExcludeMissing + fun _inboundEmailId(): JsonField = inboundEmailId + + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("metadata") @ExcludeMissing fun _metadata(): JsonField = metadata + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reference") @ExcludeMissing fun _reference(): JsonField = reference + + /** + * 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 + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updated_at") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + @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 [InboundEmailCreateResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailCreateResponse]. */ + class Builder internal constructor() { + + private var allowedSources: JsonField>? = null + private var callbackUrl: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var email: JsonField = JsonMissing.of() + private var inboundEmailId: JsonField = JsonMissing.of() + private var metadata: JsonField = JsonMissing.of() + private var reference: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboundEmailCreateResponse: InboundEmailCreateResponse) = apply { + allowedSources = inboundEmailCreateResponse.allowedSources.map { it.toMutableList() } + callbackUrl = inboundEmailCreateResponse.callbackUrl + createdAt = inboundEmailCreateResponse.createdAt + email = inboundEmailCreateResponse.email + inboundEmailId = inboundEmailCreateResponse.inboundEmailId + metadata = inboundEmailCreateResponse.metadata + reference = inboundEmailCreateResponse.reference + status = inboundEmailCreateResponse.status + updatedAt = inboundEmailCreateResponse.updatedAt + additionalProperties = inboundEmailCreateResponse.additionalProperties.toMutableMap() + } + + /** Accepted CAS providers (empty = all) */ + fun allowedSources(allowedSources: List) = + allowedSources(JsonField.of(allowedSources)) + + /** + * Sets [Builder.allowedSources] to an arbitrary JSON value. + * + * You should usually call [Builder.allowedSources] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun allowedSources(allowedSources: JsonField>) = apply { + this.allowedSources = allowedSources.map { it.toMutableList() } + } + + /** + * Adds a single [AllowedSource] to [allowedSources]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAllowedSource(allowedSource: AllowedSource) = apply { + allowedSources = + (allowedSources ?: JsonField.of(mutableListOf())).also { + checkKnown("allowedSources", it).add(allowedSource) + } + } + + /** + * Webhook URL for email notifications. If set, we POST each parsed email here. If omitted, + * files are only retrievable via `GET /v4/inbound-email/{id}/files`. + */ + fun callbackUrl(callbackUrl: String) = callbackUrl(JsonField.of(callbackUrl)) + + /** + * Sets [Builder.callbackUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.callbackUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun callbackUrl(callbackUrl: JsonField) = apply { this.callbackUrl = callbackUrl } + + /** When the inbound email was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** The inbound email address to forward CAS statements to */ + 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 } + + /** Unique inbound email identifier */ + fun inboundEmailId(inboundEmailId: String) = inboundEmailId(JsonField.of(inboundEmailId)) + + /** + * Sets [Builder.inboundEmailId] to an arbitrary JSON value. + * + * You should usually call [Builder.inboundEmailId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun inboundEmailId(inboundEmailId: JsonField) = apply { + this.inboundEmailId = inboundEmailId + } + + /** Custom key-value metadata */ + fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) + + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } + + /** Your internal reference identifier */ + fun reference(reference: String?) = reference(JsonField.ofNullable(reference)) + + /** Alias for calling [Builder.reference] with `reference.orElse(null)`. */ + fun reference(reference: Optional) = reference(reference.getOrNull()) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + /** Current inbound email lifecycle status */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] 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 } + + /** When the inbound email was last updated */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + 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 [InboundEmailCreateResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailCreateResponse = + InboundEmailCreateResponse( + (allowedSources ?: JsonMissing.of()).map { it.toImmutable() }, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboundEmailCreateResponse = apply { + if (validated) { + return@apply + } + + allowedSources().ifPresent { it.forEach { it.validate() } } + callbackUrl() + createdAt() + email() + inboundEmailId() + metadata().ifPresent { it.validate() } + reference() + status().ifPresent { it.validate() } + updatedAt() + 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 = + (allowedSources.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (callbackUrl.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (email.asKnown().isPresent) 1 else 0) + + (if (inboundEmailId.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (updatedAt.asKnown().isPresent) 1 else 0) + + class AllowedSource @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 CDSL = of("cdsl") + + @JvmField val NSDL = of("nsdl") + + @JvmField val CAMS = of("cams") + + @JvmField val KFINTECH = of("kfintech") + + @JvmStatic fun of(value: String) = AllowedSource(JsonField.of(value)) + } + + /** An enum containing [AllowedSource]'s known values. */ + enum class Known { + CDSL, + NSDL, + CAMS, + KFINTECH, + } + + /** + * An enum containing [AllowedSource]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AllowedSource] 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 { + CDSL, + NSDL, + CAMS, + KFINTECH, + /** + * An enum member indicating that [AllowedSource] 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) { + CDSL -> Value.CDSL + NSDL -> Value.NSDL + CAMS -> Value.CAMS + KFINTECH -> Value.KFINTECH + 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) { + CDSL -> Known.CDSL + NSDL -> Known.NSDL + CAMS -> Known.CAMS + KFINTECH -> Known.KFINTECH + else -> throw CasParserInvalidDataException("Unknown AllowedSource: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): AllowedSource = 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 AllowedSource && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Custom key-value metadata */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Metadata]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } + + 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 [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Metadata = apply { + if (validated) { + return@apply + } + + 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Metadata && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + /** Current inbound email lifecycle status */ + class Status @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 ACTIVE = of("active") + + @JvmField val PAUSED = of("paused") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + PAUSED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + PAUSED, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + PAUSED -> Value.PAUSED + 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) { + ACTIVE -> Known.ACTIVE + PAUSED -> Known.PAUSED + else -> throw CasParserInvalidDataException("Unknown Status: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = 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 Status && 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 InboundEmailCreateResponse && + allowedSources == other.allowedSources && + callbackUrl == other.callbackUrl && + createdAt == other.createdAt && + email == other.email && + inboundEmailId == other.inboundEmailId && + metadata == other.metadata && + reference == other.reference && + status == other.status && + updatedAt == other.updatedAt && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + allowedSources, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InboundEmailCreateResponse{allowedSources=$allowedSources, callbackUrl=$callbackUrl, createdAt=$createdAt, email=$email, inboundEmailId=$inboundEmailId, metadata=$metadata, reference=$reference, status=$status, updatedAt=$updatedAt, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteParams.kt new file mode 100644 index 0000000..1546fbf --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteParams.kt @@ -0,0 +1,241 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Permanently delete an inbound email address. It will stop accepting emails. + * + * **Note:** Deletion is immediate and cannot be undone. Any emails received after deletion will be + * rejected. + */ +class InboundEmailDeleteParams +private constructor( + private val inboundEmailId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun inboundEmailId(): Optional = Optional.ofNullable(inboundEmailId) + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** 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 { + + @JvmStatic fun none(): InboundEmailDeleteParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [InboundEmailDeleteParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailDeleteParams]. */ + class Builder internal constructor() { + + private var inboundEmailId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboundEmailDeleteParams: InboundEmailDeleteParams) = apply { + inboundEmailId = inboundEmailDeleteParams.inboundEmailId + additionalHeaders = inboundEmailDeleteParams.additionalHeaders.toBuilder() + additionalQueryParams = inboundEmailDeleteParams.additionalQueryParams.toBuilder() + additionalBodyProperties = + inboundEmailDeleteParams.additionalBodyProperties.toMutableMap() + } + + fun inboundEmailId(inboundEmailId: String?) = apply { this.inboundEmailId = inboundEmailId } + + /** Alias for calling [Builder.inboundEmailId] with `inboundEmailId.orElse(null)`. */ + fun inboundEmailId(inboundEmailId: Optional) = + inboundEmailId(inboundEmailId.getOrNull()) + + 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) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [InboundEmailDeleteParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailDeleteParams = + InboundEmailDeleteParams( + inboundEmailId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + fun _pathParam(index: Int): String = + when (index) { + 0 -> inboundEmailId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboundEmailDeleteParams && + inboundEmailId == other.inboundEmailId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash( + inboundEmailId, + additionalHeaders, + additionalQueryParams, + additionalBodyProperties, + ) + + override fun toString() = + "InboundEmailDeleteParams{inboundEmailId=$inboundEmailId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteResponse.kt new file mode 100644 index 0000000..a5ed525 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteResponse.kt @@ -0,0 +1,194 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +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 InboundEmailDeleteResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +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 [InboundEmailDeleteResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailDeleteResponse]. */ + 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(inboundEmailDeleteResponse: InboundEmailDeleteResponse) = apply { + msg = inboundEmailDeleteResponse.msg + status = inboundEmailDeleteResponse.status + additionalProperties = inboundEmailDeleteResponse.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 [InboundEmailDeleteResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailDeleteResponse = + InboundEmailDeleteResponse(msg, status, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboundEmailDeleteResponse = 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 InboundEmailDeleteResponse && + 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() = + "InboundEmailDeleteResponse{msg=$msg, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListParams.kt new file mode 100644 index 0000000..8b77162 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListParams.kt @@ -0,0 +1,390 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.Enum +import com.cas_parser.api.core.JsonField +import com.cas_parser.api.core.Params +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.JsonCreator +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * List all inbound emails associated with your API key. Returns active and paused inbound emails + * (deleted ones are excluded). + */ +class InboundEmailListParams +private constructor( + private val limit: Long?, + private val offset: Long?, + private val status: Status?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** Maximum number of inbound emails to return */ + fun limit(): Optional = Optional.ofNullable(limit) + + /** Pagination offset */ + fun offset(): Optional = Optional.ofNullable(offset) + + /** Filter by status */ + fun status(): Optional = Optional.ofNullable(status) + + /** 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 { + + @JvmStatic fun none(): InboundEmailListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [InboundEmailListParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailListParams]. */ + class Builder internal constructor() { + + private var limit: Long? = null + private var offset: Long? = null + private var status: Status? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(inboundEmailListParams: InboundEmailListParams) = apply { + limit = inboundEmailListParams.limit + offset = inboundEmailListParams.offset + status = inboundEmailListParams.status + additionalHeaders = inboundEmailListParams.additionalHeaders.toBuilder() + additionalQueryParams = inboundEmailListParams.additionalQueryParams.toBuilder() + } + + /** Maximum number of inbound emails to return */ + fun limit(limit: Long?) = apply { this.limit = limit } + + /** + * Alias for [Builder.limit]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun limit(limit: Long) = limit(limit as Long?) + + /** Alias for calling [Builder.limit] with `limit.orElse(null)`. */ + fun limit(limit: Optional) = limit(limit.getOrNull()) + + /** Pagination offset */ + fun offset(offset: Long?) = apply { this.offset = offset } + + /** + * Alias for [Builder.offset]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun offset(offset: Long) = offset(offset as Long?) + + /** Alias for calling [Builder.offset] with `offset.orElse(null)`. */ + fun offset(offset: Optional) = offset(offset.getOrNull()) + + /** Filter by status */ + fun status(status: Status?) = apply { this.status = status } + + /** Alias for calling [Builder.status] with `status.orElse(null)`. */ + fun status(status: Optional) = status(status.getOrNull()) + + 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 [InboundEmailListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailListParams = + InboundEmailListParams( + limit, + offset, + status, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = + QueryParams.builder() + .apply { + limit?.let { put("limit", it.toString()) } + offset?.let { put("offset", it.toString()) } + status?.let { put("status", it.toString()) } + putAll(additionalQueryParams) + } + .build() + + /** Filter by status */ + class Status @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 ACTIVE = of("active") + + @JvmField val PAUSED = of("paused") + + @JvmField val ALL = of("all") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + PAUSED, + ALL, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + PAUSED, + ALL, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + PAUSED -> Value.PAUSED + ALL -> Value.ALL + 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) { + ACTIVE -> Known.ACTIVE + PAUSED -> Known.PAUSED + ALL -> Known.ALL + else -> throw CasParserInvalidDataException("Unknown Status: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = 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 Status && 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 InboundEmailListParams && + limit == other.limit && + offset == other.offset && + status == other.status && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(limit, offset, status, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InboundEmailListParams{limit=$limit, offset=$offset, status=$status, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListResponse.kt new file mode 100644 index 0000000..e6f0048 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListResponse.kt @@ -0,0 +1,1245 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class InboundEmailListResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val inboundEmails: JsonField>, + private val limit: JsonField, + private val offset: JsonField, + private val status: JsonField, + private val total: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("inbound_emails") + @ExcludeMissing + inboundEmails: JsonField> = JsonMissing.of(), + @JsonProperty("limit") @ExcludeMissing limit: JsonField = JsonMissing.of(), + @JsonProperty("offset") @ExcludeMissing offset: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("total") @ExcludeMissing total: JsonField = JsonMissing.of(), + ) : this(inboundEmails, limit, offset, status, total, mutableMapOf()) + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun inboundEmails(): Optional> = inboundEmails.getOptional("inbound_emails") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun limit(): Optional = limit.getOptional("limit") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun offset(): Optional = offset.getOptional("offset") + + /** + * @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") + + /** + * Total number of inbound emails (for pagination) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun total(): Optional = total.getOptional("total") + + /** + * Returns the raw JSON value of [inboundEmails]. + * + * Unlike [inboundEmails], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inbound_emails") + @ExcludeMissing + fun _inboundEmails(): JsonField> = inboundEmails + + /** + * Returns the raw JSON value of [limit]. + * + * Unlike [limit], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("limit") @ExcludeMissing fun _limit(): JsonField = limit + + /** + * Returns the raw JSON value of [offset]. + * + * Unlike [offset], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("offset") @ExcludeMissing fun _offset(): JsonField = offset + + /** + * 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 + + /** + * Returns the raw JSON value of [total]. + * + * Unlike [total], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("total") @ExcludeMissing fun _total(): JsonField = total + + @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 [InboundEmailListResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailListResponse]. */ + class Builder internal constructor() { + + private var inboundEmails: JsonField>? = null + private var limit: JsonField = JsonMissing.of() + private var offset: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var total: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboundEmailListResponse: InboundEmailListResponse) = apply { + inboundEmails = inboundEmailListResponse.inboundEmails.map { it.toMutableList() } + limit = inboundEmailListResponse.limit + offset = inboundEmailListResponse.offset + status = inboundEmailListResponse.status + total = inboundEmailListResponse.total + additionalProperties = inboundEmailListResponse.additionalProperties.toMutableMap() + } + + fun inboundEmails(inboundEmails: List) = + inboundEmails(JsonField.of(inboundEmails)) + + /** + * Sets [Builder.inboundEmails] to an arbitrary JSON value. + * + * You should usually call [Builder.inboundEmails] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun inboundEmails(inboundEmails: JsonField>) = apply { + this.inboundEmails = inboundEmails.map { it.toMutableList() } + } + + /** + * Adds a single [InboundEmail] to [inboundEmails]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addInboundEmail(inboundEmail: InboundEmail) = apply { + inboundEmails = + (inboundEmails ?: JsonField.of(mutableListOf())).also { + checkKnown("inboundEmails", it).add(inboundEmail) + } + } + + fun limit(limit: Long) = limit(JsonField.of(limit)) + + /** + * Sets [Builder.limit] to an arbitrary JSON value. + * + * You should usually call [Builder.limit] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun limit(limit: JsonField) = apply { this.limit = limit } + + fun offset(offset: Long) = offset(JsonField.of(offset)) + + /** + * Sets [Builder.offset] to an arbitrary JSON value. + * + * You should usually call [Builder.offset] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun offset(offset: JsonField) = apply { this.offset = offset } + + 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 } + + /** Total number of inbound emails (for pagination) */ + fun total(total: Long) = total(JsonField.of(total)) + + /** + * Sets [Builder.total] to an arbitrary JSON value. + * + * You should usually call [Builder.total] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun total(total: JsonField) = apply { this.total = total } + + 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 [InboundEmailListResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailListResponse = + InboundEmailListResponse( + (inboundEmails ?: JsonMissing.of()).map { it.toImmutable() }, + limit, + offset, + status, + total, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboundEmailListResponse = apply { + if (validated) { + return@apply + } + + inboundEmails().ifPresent { it.forEach { it.validate() } } + limit() + offset() + status() + total() + 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 = + (inboundEmails.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (limit.asKnown().isPresent) 1 else 0) + + (if (offset.asKnown().isPresent) 1 else 0) + + (if (status.asKnown().isPresent) 1 else 0) + + (if (total.asKnown().isPresent) 1 else 0) + + /** An inbound email address for receiving forwarded CAS emails */ + class InboundEmail + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val allowedSources: JsonField>, + private val callbackUrl: JsonField, + private val createdAt: JsonField, + private val email: JsonField, + private val inboundEmailId: JsonField, + private val metadata: JsonField, + private val reference: JsonField, + private val status: JsonField, + private val updatedAt: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("allowed_sources") + @ExcludeMissing + allowedSources: JsonField> = JsonMissing.of(), + @JsonProperty("callback_url") + @ExcludeMissing + callbackUrl: JsonField = JsonMissing.of(), + @JsonProperty("created_at") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("email") @ExcludeMissing email: JsonField = JsonMissing.of(), + @JsonProperty("inbound_email_id") + @ExcludeMissing + inboundEmailId: JsonField = JsonMissing.of(), + @JsonProperty("metadata") + @ExcludeMissing + metadata: JsonField = JsonMissing.of(), + @JsonProperty("reference") + @ExcludeMissing + reference: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("updated_at") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + ) : this( + allowedSources, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + mutableMapOf(), + ) + + /** + * Accepted CAS providers (empty = all) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun allowedSources(): Optional> = + allowedSources.getOptional("allowed_sources") + + /** + * Webhook URL for email notifications. If set, we POST each parsed email here. If omitted, + * files are only retrievable via `GET /v4/inbound-email/{id}/files`. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun callbackUrl(): Optional = callbackUrl.getOptional("callback_url") + + /** + * When the inbound email was created + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun createdAt(): Optional = createdAt.getOptional("created_at") + + /** + * The inbound email address to forward CAS statements to + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun email(): Optional = email.getOptional("email") + + /** + * Unique inbound email identifier + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun inboundEmailId(): Optional = inboundEmailId.getOptional("inbound_email_id") + + /** + * Custom key-value metadata + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") + + /** + * Your internal reference identifier + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun reference(): Optional = reference.getOptional("reference") + + /** + * Current inbound email lifecycle status + * + * @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") + + /** + * When the inbound email was last updated + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun updatedAt(): Optional = updatedAt.getOptional("updated_at") + + /** + * Returns the raw JSON value of [allowedSources]. + * + * Unlike [allowedSources], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("allowed_sources") + @ExcludeMissing + fun _allowedSources(): JsonField> = allowedSources + + /** + * Returns the raw JSON value of [callbackUrl]. + * + * Unlike [callbackUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("callback_url") + @ExcludeMissing + fun _callbackUrl(): JsonField = callbackUrl + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("created_at") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * 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 [inboundEmailId]. + * + * Unlike [inboundEmailId], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("inbound_email_id") + @ExcludeMissing + fun _inboundEmailId(): JsonField = inboundEmailId + + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("metadata") @ExcludeMissing fun _metadata(): JsonField = metadata + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reference") @ExcludeMissing fun _reference(): JsonField = reference + + /** + * 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 + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updated_at") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + @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 [InboundEmail]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmail]. */ + class Builder internal constructor() { + + private var allowedSources: JsonField>? = null + private var callbackUrl: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var email: JsonField = JsonMissing.of() + private var inboundEmailId: JsonField = JsonMissing.of() + private var metadata: JsonField = JsonMissing.of() + private var reference: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboundEmail: InboundEmail) = apply { + allowedSources = inboundEmail.allowedSources.map { it.toMutableList() } + callbackUrl = inboundEmail.callbackUrl + createdAt = inboundEmail.createdAt + email = inboundEmail.email + inboundEmailId = inboundEmail.inboundEmailId + metadata = inboundEmail.metadata + reference = inboundEmail.reference + status = inboundEmail.status + updatedAt = inboundEmail.updatedAt + additionalProperties = inboundEmail.additionalProperties.toMutableMap() + } + + /** Accepted CAS providers (empty = all) */ + fun allowedSources(allowedSources: List) = + allowedSources(JsonField.of(allowedSources)) + + /** + * Sets [Builder.allowedSources] to an arbitrary JSON value. + * + * You should usually call [Builder.allowedSources] with a well-typed + * `List` value instead. This method is primarily for setting the field + * to an undocumented or not yet supported value. + */ + fun allowedSources(allowedSources: JsonField>) = apply { + this.allowedSources = allowedSources.map { it.toMutableList() } + } + + /** + * Adds a single [AllowedSource] to [allowedSources]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAllowedSource(allowedSource: AllowedSource) = apply { + allowedSources = + (allowedSources ?: JsonField.of(mutableListOf())).also { + checkKnown("allowedSources", it).add(allowedSource) + } + } + + /** + * Webhook URL for email notifications. If set, we POST each parsed email here. If + * omitted, files are only retrievable via `GET /v4/inbound-email/{id}/files`. + */ + fun callbackUrl(callbackUrl: String) = callbackUrl(JsonField.of(callbackUrl)) + + /** + * Sets [Builder.callbackUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.callbackUrl] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun callbackUrl(callbackUrl: JsonField) = apply { + this.callbackUrl = callbackUrl + } + + /** When the inbound email was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { + this.createdAt = createdAt + } + + /** The inbound email address to forward CAS statements to */ + 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 } + + /** Unique inbound email identifier */ + fun inboundEmailId(inboundEmailId: String) = + inboundEmailId(JsonField.of(inboundEmailId)) + + /** + * Sets [Builder.inboundEmailId] to an arbitrary JSON value. + * + * You should usually call [Builder.inboundEmailId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun inboundEmailId(inboundEmailId: JsonField) = apply { + this.inboundEmailId = inboundEmailId + } + + /** Custom key-value metadata */ + fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) + + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } + + /** Your internal reference identifier */ + fun reference(reference: String?) = reference(JsonField.ofNullable(reference)) + + /** Alias for calling [Builder.reference] with `reference.orElse(null)`. */ + fun reference(reference: Optional) = reference(reference.getOrNull()) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + /** Current inbound email lifecycle status */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] 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 } + + /** When the inbound email was last updated */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { + this.updatedAt = updatedAt + } + + 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 [InboundEmail]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmail = + InboundEmail( + (allowedSources ?: JsonMissing.of()).map { it.toImmutable() }, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboundEmail = apply { + if (validated) { + return@apply + } + + allowedSources().ifPresent { it.forEach { it.validate() } } + callbackUrl() + createdAt() + email() + inboundEmailId() + metadata().ifPresent { it.validate() } + reference() + status().ifPresent { it.validate() } + updatedAt() + 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 = + (allowedSources.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (callbackUrl.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (email.asKnown().isPresent) 1 else 0) + + (if (inboundEmailId.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (updatedAt.asKnown().isPresent) 1 else 0) + + class AllowedSource @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 CDSL = of("cdsl") + + @JvmField val NSDL = of("nsdl") + + @JvmField val CAMS = of("cams") + + @JvmField val KFINTECH = of("kfintech") + + @JvmStatic fun of(value: String) = AllowedSource(JsonField.of(value)) + } + + /** An enum containing [AllowedSource]'s known values. */ + enum class Known { + CDSL, + NSDL, + CAMS, + KFINTECH, + } + + /** + * An enum containing [AllowedSource]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AllowedSource] 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 { + CDSL, + NSDL, + CAMS, + KFINTECH, + /** + * An enum member indicating that [AllowedSource] 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) { + CDSL -> Value.CDSL + NSDL -> Value.NSDL + CAMS -> Value.CAMS + KFINTECH -> Value.KFINTECH + 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) { + CDSL -> Known.CDSL + NSDL -> Known.NSDL + CAMS -> Known.CAMS + KFINTECH -> Known.KFINTECH + else -> throw CasParserInvalidDataException("Unknown AllowedSource: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): AllowedSource = 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 AllowedSource && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Custom key-value metadata */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Metadata]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } + + 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 [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Metadata = apply { + if (validated) { + return@apply + } + + 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Metadata && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + /** Current inbound email lifecycle status */ + class Status @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 ACTIVE = of("active") + + @JvmField val PAUSED = of("paused") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + PAUSED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + PAUSED, + /** + * An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + PAUSED -> Value.PAUSED + 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) { + ACTIVE -> Known.ACTIVE + PAUSED -> Known.PAUSED + else -> throw CasParserInvalidDataException("Unknown Status: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): Status = 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 Status && 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 InboundEmail && + allowedSources == other.allowedSources && + callbackUrl == other.callbackUrl && + createdAt == other.createdAt && + email == other.email && + inboundEmailId == other.inboundEmailId && + metadata == other.metadata && + reference == other.reference && + status == other.status && + updatedAt == other.updatedAt && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + allowedSources, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InboundEmail{allowedSources=$allowedSources, callbackUrl=$callbackUrl, createdAt=$createdAt, email=$email, inboundEmailId=$inboundEmailId, metadata=$metadata, reference=$reference, status=$status, updatedAt=$updatedAt, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboundEmailListResponse && + inboundEmails == other.inboundEmails && + limit == other.limit && + offset == other.offset && + status == other.status && + total == other.total && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(inboundEmails, limit, offset, status, total, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InboundEmailListResponse{inboundEmails=$inboundEmails, limit=$limit, offset=$offset, status=$status, total=$total, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveParams.kt new file mode 100644 index 0000000..676faaa --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveParams.kt @@ -0,0 +1,197 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Retrieve details of a specific inbound email including statistics. */ +class InboundEmailRetrieveParams +private constructor( + private val inboundEmailId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun inboundEmailId(): Optional = Optional.ofNullable(inboundEmailId) + + /** 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 { + + @JvmStatic fun none(): InboundEmailRetrieveParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of [InboundEmailRetrieveParams]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailRetrieveParams]. */ + class Builder internal constructor() { + + private var inboundEmailId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(inboundEmailRetrieveParams: InboundEmailRetrieveParams) = apply { + inboundEmailId = inboundEmailRetrieveParams.inboundEmailId + additionalHeaders = inboundEmailRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = inboundEmailRetrieveParams.additionalQueryParams.toBuilder() + } + + fun inboundEmailId(inboundEmailId: String?) = apply { this.inboundEmailId = inboundEmailId } + + /** Alias for calling [Builder.inboundEmailId] with `inboundEmailId.orElse(null)`. */ + fun inboundEmailId(inboundEmailId: Optional) = + inboundEmailId(inboundEmailId.getOrNull()) + + 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 [InboundEmailRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailRetrieveParams = + InboundEmailRetrieveParams( + inboundEmailId, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> inboundEmailId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboundEmailRetrieveParams && + inboundEmailId == other.inboundEmailId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(inboundEmailId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InboundEmailRetrieveParams{inboundEmailId=$inboundEmailId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveResponse.kt new file mode 100644 index 0000000..3bbfbd9 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveResponse.kt @@ -0,0 +1,923 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** An inbound email address for receiving forwarded CAS emails */ +class InboundEmailRetrieveResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val allowedSources: JsonField>, + private val callbackUrl: JsonField, + private val createdAt: JsonField, + private val email: JsonField, + private val inboundEmailId: JsonField, + private val metadata: JsonField, + private val reference: JsonField, + private val status: JsonField, + private val updatedAt: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("allowed_sources") + @ExcludeMissing + allowedSources: JsonField> = JsonMissing.of(), + @JsonProperty("callback_url") + @ExcludeMissing + callbackUrl: JsonField = JsonMissing.of(), + @JsonProperty("created_at") + @ExcludeMissing + createdAt: JsonField = JsonMissing.of(), + @JsonProperty("email") @ExcludeMissing email: JsonField = JsonMissing.of(), + @JsonProperty("inbound_email_id") + @ExcludeMissing + inboundEmailId: JsonField = JsonMissing.of(), + @JsonProperty("metadata") @ExcludeMissing metadata: JsonField = JsonMissing.of(), + @JsonProperty("reference") @ExcludeMissing reference: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("updated_at") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + ) : this( + allowedSources, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + mutableMapOf(), + ) + + /** + * Accepted CAS providers (empty = all) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun allowedSources(): Optional> = + allowedSources.getOptional("allowed_sources") + + /** + * Webhook URL for email notifications. If set, we POST each parsed email here. If omitted, + * files are only retrievable via `GET /v4/inbound-email/{id}/files`. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun callbackUrl(): Optional = callbackUrl.getOptional("callback_url") + + /** + * When the inbound email was created + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun createdAt(): Optional = createdAt.getOptional("created_at") + + /** + * The inbound email address to forward CAS statements to + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun email(): Optional = email.getOptional("email") + + /** + * Unique inbound email identifier + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun inboundEmailId(): Optional = inboundEmailId.getOptional("inbound_email_id") + + /** + * Custom key-value metadata + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") + + /** + * Your internal reference identifier + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun reference(): Optional = reference.getOptional("reference") + + /** + * Current inbound email lifecycle status + * + * @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") + + /** + * When the inbound email was last updated + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun updatedAt(): Optional = updatedAt.getOptional("updated_at") + + /** + * Returns the raw JSON value of [allowedSources]. + * + * Unlike [allowedSources], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("allowed_sources") + @ExcludeMissing + fun _allowedSources(): JsonField> = allowedSources + + /** + * Returns the raw JSON value of [callbackUrl]. + * + * Unlike [callbackUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("callback_url") + @ExcludeMissing + fun _callbackUrl(): JsonField = callbackUrl + + /** + * Returns the raw JSON value of [createdAt]. + * + * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("created_at") + @ExcludeMissing + fun _createdAt(): JsonField = createdAt + + /** + * 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 [inboundEmailId]. + * + * Unlike [inboundEmailId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inbound_email_id") + @ExcludeMissing + fun _inboundEmailId(): JsonField = inboundEmailId + + /** + * Returns the raw JSON value of [metadata]. + * + * Unlike [metadata], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("metadata") @ExcludeMissing fun _metadata(): JsonField = metadata + + /** + * Returns the raw JSON value of [reference]. + * + * Unlike [reference], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reference") @ExcludeMissing fun _reference(): JsonField = reference + + /** + * 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 + + /** + * Returns the raw JSON value of [updatedAt]. + * + * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("updated_at") + @ExcludeMissing + fun _updatedAt(): JsonField = updatedAt + + @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 [InboundEmailRetrieveResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboundEmailRetrieveResponse]. */ + class Builder internal constructor() { + + private var allowedSources: JsonField>? = null + private var callbackUrl: JsonField = JsonMissing.of() + private var createdAt: JsonField = JsonMissing.of() + private var email: JsonField = JsonMissing.of() + private var inboundEmailId: JsonField = JsonMissing.of() + private var metadata: JsonField = JsonMissing.of() + private var reference: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboundEmailRetrieveResponse: InboundEmailRetrieveResponse) = apply { + allowedSources = inboundEmailRetrieveResponse.allowedSources.map { it.toMutableList() } + callbackUrl = inboundEmailRetrieveResponse.callbackUrl + createdAt = inboundEmailRetrieveResponse.createdAt + email = inboundEmailRetrieveResponse.email + inboundEmailId = inboundEmailRetrieveResponse.inboundEmailId + metadata = inboundEmailRetrieveResponse.metadata + reference = inboundEmailRetrieveResponse.reference + status = inboundEmailRetrieveResponse.status + updatedAt = inboundEmailRetrieveResponse.updatedAt + additionalProperties = inboundEmailRetrieveResponse.additionalProperties.toMutableMap() + } + + /** Accepted CAS providers (empty = all) */ + fun allowedSources(allowedSources: List) = + allowedSources(JsonField.of(allowedSources)) + + /** + * Sets [Builder.allowedSources] to an arbitrary JSON value. + * + * You should usually call [Builder.allowedSources] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun allowedSources(allowedSources: JsonField>) = apply { + this.allowedSources = allowedSources.map { it.toMutableList() } + } + + /** + * Adds a single [AllowedSource] to [allowedSources]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addAllowedSource(allowedSource: AllowedSource) = apply { + allowedSources = + (allowedSources ?: JsonField.of(mutableListOf())).also { + checkKnown("allowedSources", it).add(allowedSource) + } + } + + /** + * Webhook URL for email notifications. If set, we POST each parsed email here. If omitted, + * files are only retrievable via `GET /v4/inbound-email/{id}/files`. + */ + fun callbackUrl(callbackUrl: String) = callbackUrl(JsonField.of(callbackUrl)) + + /** + * Sets [Builder.callbackUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.callbackUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun callbackUrl(callbackUrl: JsonField) = apply { this.callbackUrl = callbackUrl } + + /** When the inbound email was created */ + fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt)) + + /** + * Sets [Builder.createdAt] to an arbitrary JSON value. + * + * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt } + + /** The inbound email address to forward CAS statements to */ + 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 } + + /** Unique inbound email identifier */ + fun inboundEmailId(inboundEmailId: String) = inboundEmailId(JsonField.of(inboundEmailId)) + + /** + * Sets [Builder.inboundEmailId] to an arbitrary JSON value. + * + * You should usually call [Builder.inboundEmailId] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun inboundEmailId(inboundEmailId: JsonField) = apply { + this.inboundEmailId = inboundEmailId + } + + /** Custom key-value metadata */ + fun metadata(metadata: Metadata) = metadata(JsonField.of(metadata)) + + /** + * Sets [Builder.metadata] to an arbitrary JSON value. + * + * You should usually call [Builder.metadata] with a well-typed [Metadata] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun metadata(metadata: JsonField) = apply { this.metadata = metadata } + + /** Your internal reference identifier */ + fun reference(reference: String?) = reference(JsonField.ofNullable(reference)) + + /** Alias for calling [Builder.reference] with `reference.orElse(null)`. */ + fun reference(reference: Optional) = reference(reference.getOrNull()) + + /** + * Sets [Builder.reference] to an arbitrary JSON value. + * + * You should usually call [Builder.reference] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun reference(reference: JsonField) = apply { this.reference = reference } + + /** Current inbound email lifecycle status */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] 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 } + + /** When the inbound email was last updated */ + fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt)) + + /** + * Sets [Builder.updatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt } + + 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 [InboundEmailRetrieveResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboundEmailRetrieveResponse = + InboundEmailRetrieveResponse( + (allowedSources ?: JsonMissing.of()).map { it.toImmutable() }, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboundEmailRetrieveResponse = apply { + if (validated) { + return@apply + } + + allowedSources().ifPresent { it.forEach { it.validate() } } + callbackUrl() + createdAt() + email() + inboundEmailId() + metadata().ifPresent { it.validate() } + reference() + status().ifPresent { it.validate() } + updatedAt() + 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 = + (allowedSources.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (callbackUrl.asKnown().isPresent) 1 else 0) + + (if (createdAt.asKnown().isPresent) 1 else 0) + + (if (email.asKnown().isPresent) 1 else 0) + + (if (inboundEmailId.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (reference.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (updatedAt.asKnown().isPresent) 1 else 0) + + class AllowedSource @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 CDSL = of("cdsl") + + @JvmField val NSDL = of("nsdl") + + @JvmField val CAMS = of("cams") + + @JvmField val KFINTECH = of("kfintech") + + @JvmStatic fun of(value: String) = AllowedSource(JsonField.of(value)) + } + + /** An enum containing [AllowedSource]'s known values. */ + enum class Known { + CDSL, + NSDL, + CAMS, + KFINTECH, + } + + /** + * An enum containing [AllowedSource]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [AllowedSource] 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 { + CDSL, + NSDL, + CAMS, + KFINTECH, + /** + * An enum member indicating that [AllowedSource] 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) { + CDSL -> Value.CDSL + NSDL -> Value.NSDL + CAMS -> Value.CAMS + KFINTECH -> Value.KFINTECH + 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) { + CDSL -> Known.CDSL + NSDL -> Known.NSDL + CAMS -> Known.CAMS + KFINTECH -> Known.KFINTECH + else -> throw CasParserInvalidDataException("Unknown AllowedSource: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): AllowedSource = 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 AllowedSource && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Custom key-value metadata */ + class Metadata + @JsonCreator + private constructor( + @com.fasterxml.jackson.annotation.JsonValue + private val additionalProperties: Map + ) { + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = additionalProperties + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Metadata]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Metadata]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(metadata: Metadata) = apply { + additionalProperties = metadata.additionalProperties.toMutableMap() + } + + 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 [Metadata]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Metadata = Metadata(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Metadata = apply { + if (validated) { + return@apply + } + + 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 = + additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Metadata && additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + /** Current inbound email lifecycle status */ + class Status @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 ACTIVE = of("active") + + @JvmField val PAUSED = of("paused") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + PAUSED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + PAUSED, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + PAUSED -> Value.PAUSED + 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) { + ACTIVE -> Known.ACTIVE + PAUSED -> Known.PAUSED + else -> throw CasParserInvalidDataException("Unknown Status: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = 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 Status && 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 InboundEmailRetrieveResponse && + allowedSources == other.allowedSources && + callbackUrl == other.callbackUrl && + createdAt == other.createdAt && + email == other.email && + inboundEmailId == other.inboundEmailId && + metadata == other.metadata && + reference == other.reference && + status == other.status && + updatedAt == other.updatedAt && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + allowedSources, + callbackUrl, + createdAt, + email, + inboundEmailId, + metadata, + reference, + status, + updatedAt, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InboundEmailRetrieveResponse{allowedSources=$allowedSources, callbackUrl=$callbackUrl, createdAt=$createdAt, email=$email, inboundEmailId=$inboundEmailId, metadata=$metadata, reference=$reference, status=$status, updatedAt=$updatedAt, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusParams.kt new file mode 100644 index 0000000..96b1fab --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusParams.kt @@ -0,0 +1,252 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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.core.toImmutable +import java.util.Objects +import java.util.Optional + +/** + * Verify if an `inbox_token` is still valid and check connection status. + * + * Use this to check if the user needs to re-authenticate (e.g., if they revoked access in their + * email provider settings). + */ +class InboxCheckConnectionStatusParams +private constructor( + private val xInboxToken: String, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun xInboxToken(): String = xInboxToken + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** 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 + * [InboxCheckConnectionStatusParams]. + * + * The following fields are required: + * ```java + * .xInboxToken() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxCheckConnectionStatusParams]. */ + class Builder internal constructor() { + + private var xInboxToken: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboxCheckConnectionStatusParams: InboxCheckConnectionStatusParams) = + apply { + xInboxToken = inboxCheckConnectionStatusParams.xInboxToken + additionalHeaders = inboxCheckConnectionStatusParams.additionalHeaders.toBuilder() + additionalQueryParams = + inboxCheckConnectionStatusParams.additionalQueryParams.toBuilder() + additionalBodyProperties = + inboxCheckConnectionStatusParams.additionalBodyProperties.toMutableMap() + } + + fun xInboxToken(xInboxToken: String) = apply { this.xInboxToken = xInboxToken } + + 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) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [InboxCheckConnectionStatusParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .xInboxToken() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InboxCheckConnectionStatusParams = + InboxCheckConnectionStatusParams( + checkRequired("xInboxToken", xInboxToken), + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + override fun _headers(): Headers = + Headers.builder() + .apply { + put("x-inbox-token", xInboxToken) + putAll(additionalHeaders) + } + .build() + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboxCheckConnectionStatusParams && + xInboxToken == other.xInboxToken && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash( + xInboxToken, + additionalHeaders, + additionalQueryParams, + additionalBodyProperties, + ) + + override fun toString() = + "InboxCheckConnectionStatusParams{xInboxToken=$xInboxToken, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusResponse.kt new file mode 100644 index 0000000..585828e --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusResponse.kt @@ -0,0 +1,273 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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 InboxCheckConnectionStatusResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val connected: JsonField, + private val email: JsonField, + private val provider: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("connected") @ExcludeMissing connected: JsonField = JsonMissing.of(), + @JsonProperty("email") @ExcludeMissing email: JsonField = JsonMissing.of(), + @JsonProperty("provider") @ExcludeMissing provider: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(connected, email, provider, status, mutableMapOf()) + + /** + * Whether the token is valid and usable + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun connected(): Optional = connected.getOptional("connected") + + /** + * Email address of the connected account + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun email(): Optional = email.getOptional("email") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun provider(): Optional = provider.getOptional("provider") + + /** + * @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 [connected]. + * + * Unlike [connected], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("connected") @ExcludeMissing fun _connected(): JsonField = connected + + /** + * 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 [provider]. + * + * Unlike [provider], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("provider") @ExcludeMissing fun _provider(): JsonField = provider + + /** + * 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 + * [InboxCheckConnectionStatusResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxCheckConnectionStatusResponse]. */ + class Builder internal constructor() { + + private var connected: JsonField = JsonMissing.of() + private var email: JsonField = JsonMissing.of() + private var provider: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboxCheckConnectionStatusResponse: InboxCheckConnectionStatusResponse) = + apply { + connected = inboxCheckConnectionStatusResponse.connected + email = inboxCheckConnectionStatusResponse.email + provider = inboxCheckConnectionStatusResponse.provider + status = inboxCheckConnectionStatusResponse.status + additionalProperties = + inboxCheckConnectionStatusResponse.additionalProperties.toMutableMap() + } + + /** Whether the token is valid and usable */ + fun connected(connected: Boolean) = connected(JsonField.of(connected)) + + /** + * Sets [Builder.connected] to an arbitrary JSON value. + * + * You should usually call [Builder.connected] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun connected(connected: JsonField) = apply { this.connected = connected } + + /** Email address of the connected account */ + 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 } + + fun provider(provider: String) = provider(JsonField.of(provider)) + + /** + * Sets [Builder.provider] to an arbitrary JSON value. + * + * You should usually call [Builder.provider] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun provider(provider: JsonField) = apply { this.provider = provider } + + 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 [InboxCheckConnectionStatusResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboxCheckConnectionStatusResponse = + InboxCheckConnectionStatusResponse( + connected, + email, + provider, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboxCheckConnectionStatusResponse = apply { + if (validated) { + return@apply + } + + connected() + email() + provider() + 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 (connected.asKnown().isPresent) 1 else 0) + + (if (email.asKnown().isPresent) 1 else 0) + + (if (provider.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 InboxCheckConnectionStatusResponse && + connected == other.connected && + email == other.email && + provider == other.provider && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(connected, email, provider, status, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InboxCheckConnectionStatusResponse{connected=$connected, email=$email, provider=$provider, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailParams.kt new file mode 100644 index 0000000..12bb059 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailParams.kt @@ -0,0 +1,515 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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 + +/** + * Initiate OAuth flow to connect user's email inbox. + * + * Returns an `oauth_url` that you should redirect the user to. After authorization, they are + * redirected back to your `redirect_uri` with the following query parameters: + * + * **On success:** + * - `inbox_token` - Encrypted token to store client-side + * - `email` - Email address of the connected account + * - `state` - Your original state parameter (for CSRF verification) + * + * **On error:** + * - `error` - Error code (e.g., `access_denied`, `token_exchange_failed`) + * - `state` - Your original state parameter + * + * **Store the `inbox_token` client-side** and use it for all subsequent inbox API calls. + */ +class InboxConnectEmailParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Your callback URL to receive the inbox_token (must be http or https) + * + * @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 redirectUri(): String = body.redirectUri() + + /** + * State parameter for CSRF protection (returned in redirect) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun state(): Optional = body.state() + + /** + * Returns the raw JSON value of [redirectUri]. + * + * Unlike [redirectUri], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _redirectUri(): JsonField = body._redirectUri() + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _state(): JsonField = body._state() + + 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 [InboxConnectEmailParams]. + * + * The following fields are required: + * ```java + * .redirectUri() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxConnectEmailParams]. */ + 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(inboxConnectEmailParams: InboxConnectEmailParams) = apply { + body = inboxConnectEmailParams.body.toBuilder() + additionalHeaders = inboxConnectEmailParams.additionalHeaders.toBuilder() + additionalQueryParams = inboxConnectEmailParams.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: + * - [redirectUri] + * - [state] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Your callback URL to receive the inbox_token (must be http or https) */ + fun redirectUri(redirectUri: String) = apply { body.redirectUri(redirectUri) } + + /** + * Sets [Builder.redirectUri] to an arbitrary JSON value. + * + * You should usually call [Builder.redirectUri] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun redirectUri(redirectUri: JsonField) = apply { body.redirectUri(redirectUri) } + + /** State parameter for CSRF protection (returned in redirect) */ + fun state(state: String) = apply { body.state(state) } + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun state(state: JsonField) = apply { body.state(state) } + + 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 [InboxConnectEmailParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .redirectUri() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InboxConnectEmailParams = + InboxConnectEmailParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val redirectUri: JsonField, + private val state: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("redirect_uri") + @ExcludeMissing + redirectUri: JsonField = JsonMissing.of(), + @JsonProperty("state") @ExcludeMissing state: JsonField = JsonMissing.of(), + ) : this(redirectUri, state, mutableMapOf()) + + /** + * Your callback URL to receive the inbox_token (must be http or https) + * + * @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 redirectUri(): String = redirectUri.getRequired("redirect_uri") + + /** + * State parameter for CSRF protection (returned in redirect) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun state(): Optional = state.getOptional("state") + + /** + * Returns the raw JSON value of [redirectUri]. + * + * Unlike [redirectUri], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("redirect_uri") + @ExcludeMissing + fun _redirectUri(): JsonField = redirectUri + + /** + * Returns the raw JSON value of [state]. + * + * Unlike [state], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("state") @ExcludeMissing fun _state(): JsonField = state + + @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 + * .redirectUri() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var redirectUri: JsonField? = null + private var state: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + redirectUri = body.redirectUri + state = body.state + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Your callback URL to receive the inbox_token (must be http or https) */ + fun redirectUri(redirectUri: String) = redirectUri(JsonField.of(redirectUri)) + + /** + * Sets [Builder.redirectUri] to an arbitrary JSON value. + * + * You should usually call [Builder.redirectUri] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun redirectUri(redirectUri: JsonField) = apply { + this.redirectUri = redirectUri + } + + /** State parameter for CSRF protection (returned in redirect) */ + fun state(state: String) = state(JsonField.of(state)) + + /** + * Sets [Builder.state] to an arbitrary JSON value. + * + * You should usually call [Builder.state] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun state(state: JsonField) = apply { this.state = state } + + 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 + * .redirectUri() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("redirectUri", redirectUri), + state, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + redirectUri() + state() + 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 (redirectUri.asKnown().isPresent) 1 else 0) + + (if (state.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + redirectUri == other.redirectUri && + state == other.state && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(redirectUri, state, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{redirectUri=$redirectUri, state=$state, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboxConnectEmailParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InboxConnectEmailParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailResponse.kt new file mode 100644 index 0000000..968248e --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailResponse.kt @@ -0,0 +1,238 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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 InboxConnectEmailResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val expiresIn: JsonField, + private val oauthUrl: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("expires_in") @ExcludeMissing expiresIn: JsonField = JsonMissing.of(), + @JsonProperty("oauth_url") @ExcludeMissing oauthUrl: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(expiresIn, oauthUrl, status, mutableMapOf()) + + /** + * Seconds until the OAuth URL expires (typically 10 minutes) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresIn(): Optional = expiresIn.getOptional("expires_in") + + /** + * Redirect user to this URL to start OAuth flow + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun oauthUrl(): Optional = oauthUrl.getOptional("oauth_url") + + /** + * @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 [expiresIn]. + * + * Unlike [expiresIn], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expires_in") @ExcludeMissing fun _expiresIn(): JsonField = expiresIn + + /** + * Returns the raw JSON value of [oauthUrl]. + * + * Unlike [oauthUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("oauth_url") @ExcludeMissing fun _oauthUrl(): JsonField = oauthUrl + + /** + * 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 [InboxConnectEmailResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxConnectEmailResponse]. */ + class Builder internal constructor() { + + private var expiresIn: JsonField = JsonMissing.of() + private var oauthUrl: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboxConnectEmailResponse: InboxConnectEmailResponse) = apply { + expiresIn = inboxConnectEmailResponse.expiresIn + oauthUrl = inboxConnectEmailResponse.oauthUrl + status = inboxConnectEmailResponse.status + additionalProperties = inboxConnectEmailResponse.additionalProperties.toMutableMap() + } + + /** Seconds until the OAuth URL expires (typically 10 minutes) */ + fun expiresIn(expiresIn: Long) = expiresIn(JsonField.of(expiresIn)) + + /** + * Sets [Builder.expiresIn] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresIn] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun expiresIn(expiresIn: JsonField) = apply { this.expiresIn = expiresIn } + + /** Redirect user to this URL to start OAuth flow */ + fun oauthUrl(oauthUrl: String) = oauthUrl(JsonField.of(oauthUrl)) + + /** + * Sets [Builder.oauthUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.oauthUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun oauthUrl(oauthUrl: JsonField) = apply { this.oauthUrl = oauthUrl } + + 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 [InboxConnectEmailResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboxConnectEmailResponse = + InboxConnectEmailResponse( + expiresIn, + oauthUrl, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboxConnectEmailResponse = apply { + if (validated) { + return@apply + } + + expiresIn() + oauthUrl() + 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 (expiresIn.asKnown().isPresent) 1 else 0) + + (if (oauthUrl.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 InboxConnectEmailResponse && + expiresIn == other.expiresIn && + oauthUrl == other.oauthUrl && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(expiresIn, oauthUrl, status, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InboxConnectEmailResponse{expiresIn=$expiresIn, oauthUrl=$oauthUrl, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailParams.kt new file mode 100644 index 0000000..6caa88e --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailParams.kt @@ -0,0 +1,251 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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.core.toImmutable +import java.util.Objects +import java.util.Optional + +/** + * Revoke email access and invalidate the token. + * + * This calls the provider's token revocation API (e.g., Google's revoke endpoint) to ensure the + * user's consent is properly removed. + * + * After calling this, the `inbox_token` becomes unusable. + */ +class InboxDisconnectEmailParams +private constructor( + private val xInboxToken: String, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun xInboxToken(): String = xInboxToken + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** 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 [InboxDisconnectEmailParams]. + * + * The following fields are required: + * ```java + * .xInboxToken() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxDisconnectEmailParams]. */ + class Builder internal constructor() { + + private var xInboxToken: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboxDisconnectEmailParams: InboxDisconnectEmailParams) = apply { + xInboxToken = inboxDisconnectEmailParams.xInboxToken + additionalHeaders = inboxDisconnectEmailParams.additionalHeaders.toBuilder() + additionalQueryParams = inboxDisconnectEmailParams.additionalQueryParams.toBuilder() + additionalBodyProperties = + inboxDisconnectEmailParams.additionalBodyProperties.toMutableMap() + } + + fun xInboxToken(xInboxToken: String) = apply { this.xInboxToken = xInboxToken } + + 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) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [InboxDisconnectEmailParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .xInboxToken() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InboxDisconnectEmailParams = + InboxDisconnectEmailParams( + checkRequired("xInboxToken", xInboxToken), + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + override fun _headers(): Headers = + Headers.builder() + .apply { + put("x-inbox-token", xInboxToken) + putAll(additionalHeaders) + } + .build() + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboxDisconnectEmailParams && + xInboxToken == other.xInboxToken && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash( + xInboxToken, + additionalHeaders, + additionalQueryParams, + additionalBodyProperties, + ) + + override fun toString() = + "InboxDisconnectEmailParams{xInboxToken=$xInboxToken, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailResponse.kt new file mode 100644 index 0000000..48cb354 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailResponse.kt @@ -0,0 +1,194 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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 InboxDisconnectEmailResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +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 [InboxDisconnectEmailResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxDisconnectEmailResponse]. */ + 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(inboxDisconnectEmailResponse: InboxDisconnectEmailResponse) = apply { + msg = inboxDisconnectEmailResponse.msg + status = inboxDisconnectEmailResponse.status + additionalProperties = inboxDisconnectEmailResponse.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 [InboxDisconnectEmailResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboxDisconnectEmailResponse = + InboxDisconnectEmailResponse(msg, status, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboxDisconnectEmailResponse = 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 InboxDisconnectEmailResponse && + 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() = + "InboxDisconnectEmailResponse{msg=$msg, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesParams.kt new file mode 100644 index 0000000..aa23aae --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesParams.kt @@ -0,0 +1,773 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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.checkKnown +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.core.toImmutable +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.time.LocalDate +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Search the user's email inbox for CAS files from known senders (CAMS, KFintech, CDSL, NSDL). + * + * Files are uploaded to temporary cloud storage. **URLs expire in 24 hours.** + * + * Optionally filter by CAS provider and date range. + * + * **Billing:** 0.2 credits per request (charged regardless of success or number of files found). + */ +class InboxListCasFilesParams +private constructor( + private val xInboxToken: String, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun xInboxToken(): String = xInboxToken + + /** + * Filter by CAS provider(s): + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun casTypes(): Optional> = body.casTypes() + + /** + * End date in ISO format (YYYY-MM-DD). Defaults to today. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun endDate(): Optional = body.endDate() + + /** + * Start date in ISO format (YYYY-MM-DD). Defaults to 30 days ago. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun startDate(): Optional = body.startDate() + + /** + * Returns the raw JSON value of [casTypes]. + * + * Unlike [casTypes], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _casTypes(): JsonField> = body._casTypes() + + /** + * Returns the raw JSON value of [endDate]. + * + * Unlike [endDate], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _endDate(): JsonField = body._endDate() + + /** + * Returns the raw JSON value of [startDate]. + * + * Unlike [startDate], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _startDate(): JsonField = body._startDate() + + 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 [InboxListCasFilesParams]. + * + * The following fields are required: + * ```java + * .xInboxToken() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxListCasFilesParams]. */ + class Builder internal constructor() { + + private var xInboxToken: String? = null + 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(inboxListCasFilesParams: InboxListCasFilesParams) = apply { + xInboxToken = inboxListCasFilesParams.xInboxToken + body = inboxListCasFilesParams.body.toBuilder() + additionalHeaders = inboxListCasFilesParams.additionalHeaders.toBuilder() + additionalQueryParams = inboxListCasFilesParams.additionalQueryParams.toBuilder() + } + + fun xInboxToken(xInboxToken: String) = apply { this.xInboxToken = xInboxToken } + + /** + * 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: + * - [casTypes] + * - [endDate] + * - [startDate] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** + * Filter by CAS provider(s): + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + */ + fun casTypes(casTypes: List) = apply { body.casTypes(casTypes) } + + /** + * Sets [Builder.casTypes] to an arbitrary JSON value. + * + * You should usually call [Builder.casTypes] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun casTypes(casTypes: JsonField>) = apply { body.casTypes(casTypes) } + + /** + * Adds a single [CasType] to [casTypes]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addCasType(casType: CasType) = apply { body.addCasType(casType) } + + /** End date in ISO format (YYYY-MM-DD). Defaults to today. */ + fun endDate(endDate: LocalDate) = apply { body.endDate(endDate) } + + /** + * Sets [Builder.endDate] to an arbitrary JSON value. + * + * You should usually call [Builder.endDate] with a well-typed [LocalDate] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun endDate(endDate: JsonField) = apply { body.endDate(endDate) } + + /** Start date in ISO format (YYYY-MM-DD). Defaults to 30 days ago. */ + fun startDate(startDate: LocalDate) = apply { body.startDate(startDate) } + + /** + * Sets [Builder.startDate] to an arbitrary JSON value. + * + * You should usually call [Builder.startDate] with a well-typed [LocalDate] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun startDate(startDate: JsonField) = apply { body.startDate(startDate) } + + 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 [InboxListCasFilesParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .xInboxToken() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InboxListCasFilesParams = + InboxListCasFilesParams( + checkRequired("xInboxToken", xInboxToken), + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = + Headers.builder() + .apply { + put("x-inbox-token", xInboxToken) + putAll(additionalHeaders) + } + .build() + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val casTypes: JsonField>, + private val endDate: JsonField, + private val startDate: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("cas_types") + @ExcludeMissing + casTypes: JsonField> = JsonMissing.of(), + @JsonProperty("end_date") + @ExcludeMissing + endDate: JsonField = JsonMissing.of(), + @JsonProperty("start_date") + @ExcludeMissing + startDate: JsonField = JsonMissing.of(), + ) : this(casTypes, endDate, startDate, mutableMapOf()) + + /** + * Filter by CAS provider(s): + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun casTypes(): Optional> = casTypes.getOptional("cas_types") + + /** + * End date in ISO format (YYYY-MM-DD). Defaults to today. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun endDate(): Optional = endDate.getOptional("end_date") + + /** + * Start date in ISO format (YYYY-MM-DD). Defaults to 30 days ago. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun startDate(): Optional = startDate.getOptional("start_date") + + /** + * Returns the raw JSON value of [casTypes]. + * + * Unlike [casTypes], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cas_types") + @ExcludeMissing + fun _casTypes(): JsonField> = casTypes + + /** + * Returns the raw JSON value of [endDate]. + * + * Unlike [endDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("end_date") @ExcludeMissing fun _endDate(): JsonField = endDate + + /** + * Returns the raw JSON value of [startDate]. + * + * Unlike [startDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("start_date") + @ExcludeMissing + fun _startDate(): JsonField = startDate + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var casTypes: JsonField>? = null + private var endDate: JsonField = JsonMissing.of() + private var startDate: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + casTypes = body.casTypes.map { it.toMutableList() } + endDate = body.endDate + startDate = body.startDate + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * Filter by CAS provider(s): + * - `cdsl` → eCAS@cdslstatement.com + * - `nsdl` → NSDL-CAS@nsdl.co.in + * - `cams` → donotreply@camsonline.com + * - `kfintech` → samfS@kfintech.com + */ + fun casTypes(casTypes: List) = casTypes(JsonField.of(casTypes)) + + /** + * Sets [Builder.casTypes] to an arbitrary JSON value. + * + * You should usually call [Builder.casTypes] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun casTypes(casTypes: JsonField>) = apply { + this.casTypes = casTypes.map { it.toMutableList() } + } + + /** + * Adds a single [CasType] to [casTypes]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addCasType(casType: CasType) = apply { + casTypes = + (casTypes ?: JsonField.of(mutableListOf())).also { + checkKnown("casTypes", it).add(casType) + } + } + + /** End date in ISO format (YYYY-MM-DD). Defaults to today. */ + fun endDate(endDate: LocalDate) = endDate(JsonField.of(endDate)) + + /** + * Sets [Builder.endDate] to an arbitrary JSON value. + * + * You should usually call [Builder.endDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun endDate(endDate: JsonField) = apply { this.endDate = endDate } + + /** Start date in ISO format (YYYY-MM-DD). Defaults to 30 days ago. */ + fun startDate(startDate: LocalDate) = startDate(JsonField.of(startDate)) + + /** + * Sets [Builder.startDate] to an arbitrary JSON value. + * + * You should usually call [Builder.startDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startDate(startDate: JsonField) = apply { this.startDate = startDate } + + 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. + */ + fun build(): Body = + Body( + (casTypes ?: JsonMissing.of()).map { it.toImmutable() }, + endDate, + startDate, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + casTypes().ifPresent { it.forEach { it.validate() } } + endDate() + startDate() + 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 = + (casTypes.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (endDate.asKnown().isPresent) 1 else 0) + + (if (startDate.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + casTypes == other.casTypes && + endDate == other.endDate && + startDate == other.startDate && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(casTypes, endDate, startDate, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{casTypes=$casTypes, endDate=$endDate, startDate=$startDate, additionalProperties=$additionalProperties}" + } + + class CasType @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 CDSL = of("cdsl") + + @JvmField val NSDL = of("nsdl") + + @JvmField val CAMS = of("cams") + + @JvmField val KFINTECH = of("kfintech") + + @JvmStatic fun of(value: String) = CasType(JsonField.of(value)) + } + + /** An enum containing [CasType]'s known values. */ + enum class Known { + CDSL, + NSDL, + CAMS, + KFINTECH, + } + + /** + * An enum containing [CasType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CasType] 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 { + CDSL, + NSDL, + CAMS, + KFINTECH, + /** An enum member indicating that [CasType] 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) { + CDSL -> Value.CDSL + NSDL -> Value.NSDL + CAMS -> Value.CAMS + KFINTECH -> Value.KFINTECH + 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) { + CDSL -> Known.CDSL + NSDL -> Known.NSDL + CAMS -> Known.CAMS + KFINTECH -> Known.KFINTECH + else -> throw CasParserInvalidDataException("Unknown CasType: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): CasType = 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 CasType && 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 InboxListCasFilesParams && + xInboxToken == other.xInboxToken && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(xInboxToken, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "InboxListCasFilesParams{xInboxToken=$xInboxToken, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesResponse.kt new file mode 100644 index 0000000..2240910 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesResponse.kt @@ -0,0 +1,905 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.LocalDate +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class InboxListCasFilesResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val count: JsonField, + private val files: JsonField>, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("count") @ExcludeMissing count: JsonField = JsonMissing.of(), + @JsonProperty("files") @ExcludeMissing files: JsonField> = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(count, files, status, mutableMapOf()) + + /** + * Number of CAS files found + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun count(): Optional = count.getOptional("count") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun files(): Optional> = files.getOptional("files") + + /** + * @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 [count]. + * + * Unlike [count], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("count") @ExcludeMissing fun _count(): JsonField = count + + /** + * Returns the raw JSON value of [files]. + * + * Unlike [files], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("files") @ExcludeMissing fun _files(): JsonField> = files + + /** + * 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 [InboxListCasFilesResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InboxListCasFilesResponse]. */ + class Builder internal constructor() { + + private var count: JsonField = JsonMissing.of() + private var files: JsonField>? = null + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inboxListCasFilesResponse: InboxListCasFilesResponse) = apply { + count = inboxListCasFilesResponse.count + files = inboxListCasFilesResponse.files.map { it.toMutableList() } + status = inboxListCasFilesResponse.status + additionalProperties = inboxListCasFilesResponse.additionalProperties.toMutableMap() + } + + /** Number of CAS files found */ + fun count(count: Long) = count(JsonField.of(count)) + + /** + * Sets [Builder.count] to an arbitrary JSON value. + * + * You should usually call [Builder.count] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun count(count: JsonField) = apply { this.count = count } + + fun files(files: List) = files(JsonField.of(files)) + + /** + * Sets [Builder.files] to an arbitrary JSON value. + * + * You should usually call [Builder.files] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun files(files: JsonField>) = apply { + this.files = files.map { it.toMutableList() } + } + + /** + * Adds a single [File] to [files]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addFile(file: File) = apply { + files = + (files ?: JsonField.of(mutableListOf())).also { checkKnown("files", it).add(file) } + } + + 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 [InboxListCasFilesResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InboxListCasFilesResponse = + InboxListCasFilesResponse( + count, + (files ?: JsonMissing.of()).map { it.toImmutable() }, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): InboxListCasFilesResponse = apply { + if (validated) { + return@apply + } + + count() + files().ifPresent { it.forEach { it.validate() } } + 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 (count.asKnown().isPresent) 1 else 0) + + (files.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (status.asKnown().isPresent) 1 else 0) + + /** A CAS file found in the user's email inbox */ + class File + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val casType: JsonField, + private val expiresIn: JsonField, + private val filename: JsonField, + private val messageDate: JsonField, + private val messageId: JsonField, + private val originalFilename: JsonField, + private val senderEmail: JsonField, + private val size: JsonField, + private val url: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("cas_type") + @ExcludeMissing + casType: JsonField = JsonMissing.of(), + @JsonProperty("expires_in") + @ExcludeMissing + expiresIn: JsonField = JsonMissing.of(), + @JsonProperty("filename") + @ExcludeMissing + filename: JsonField = JsonMissing.of(), + @JsonProperty("message_date") + @ExcludeMissing + messageDate: JsonField = JsonMissing.of(), + @JsonProperty("message_id") + @ExcludeMissing + messageId: JsonField = JsonMissing.of(), + @JsonProperty("original_filename") + @ExcludeMissing + originalFilename: JsonField = JsonMissing.of(), + @JsonProperty("sender_email") + @ExcludeMissing + senderEmail: JsonField = JsonMissing.of(), + @JsonProperty("size") @ExcludeMissing size: JsonField = JsonMissing.of(), + @JsonProperty("url") @ExcludeMissing url: JsonField = JsonMissing.of(), + ) : this( + casType, + expiresIn, + filename, + messageDate, + messageId, + originalFilename, + senderEmail, + size, + url, + mutableMapOf(), + ) + + /** + * Detected CAS provider based on sender email + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun casType(): Optional = casType.getOptional("cas_type") + + /** + * URL expiration time in seconds. Defaults vary by source: + * - Gmail Inbox Import: 86400 (24h) + * - Inbound Email with `callback_url` set: 172800 (48h) + * - Inbound Email without `callback_url`: aligned with the session TTL (~30 min) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun expiresIn(): Optional = expiresIn.getOptional("expires_in") + + /** + * Standardized filename (provider_YYYYMMDD_uniqueid.pdf) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun filename(): Optional = filename.getOptional("filename") + + /** + * Date the email was received + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun messageDate(): Optional = messageDate.getOptional("message_date") + + /** + * Unique identifier for the email message (use for subsequent API calls) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun messageId(): Optional = messageId.getOptional("message_id") + + /** + * Original attachment filename from the email + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun originalFilename(): Optional = originalFilename.getOptional("original_filename") + + /** + * Email address of the CAS authority (CDSL, NSDL, CAMS, or KFintech) who originally sent + * this statement + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun senderEmail(): Optional = senderEmail.getOptional("sender_email") + + /** + * File size in bytes + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun size(): Optional = size.getOptional("size") + + /** + * Direct download URL (presigned, expires based on expires_in) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun url(): Optional = url.getOptional("url") + + /** + * Returns the raw JSON value of [casType]. + * + * Unlike [casType], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cas_type") @ExcludeMissing fun _casType(): JsonField = casType + + /** + * Returns the raw JSON value of [expiresIn]. + * + * Unlike [expiresIn], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expires_in") @ExcludeMissing fun _expiresIn(): JsonField = expiresIn + + /** + * Returns the raw JSON value of [filename]. + * + * Unlike [filename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("filename") @ExcludeMissing fun _filename(): JsonField = filename + + /** + * Returns the raw JSON value of [messageDate]. + * + * Unlike [messageDate], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("message_date") + @ExcludeMissing + fun _messageDate(): JsonField = messageDate + + /** + * Returns the raw JSON value of [messageId]. + * + * Unlike [messageId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("message_id") @ExcludeMissing fun _messageId(): JsonField = messageId + + /** + * Returns the raw JSON value of [originalFilename]. + * + * Unlike [originalFilename], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("original_filename") + @ExcludeMissing + fun _originalFilename(): JsonField = originalFilename + + /** + * Returns the raw JSON value of [senderEmail]. + * + * Unlike [senderEmail], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("sender_email") + @ExcludeMissing + fun _senderEmail(): JsonField = senderEmail + + /** + * Returns the raw JSON value of [size]. + * + * Unlike [size], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("size") @ExcludeMissing fun _size(): JsonField = size + + /** + * Returns the raw JSON value of [url]. + * + * Unlike [url], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("url") @ExcludeMissing fun _url(): JsonField = url + + @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 [File]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [File]. */ + class Builder internal constructor() { + + private var casType: JsonField = JsonMissing.of() + private var expiresIn: JsonField = JsonMissing.of() + private var filename: JsonField = JsonMissing.of() + private var messageDate: JsonField = JsonMissing.of() + private var messageId: JsonField = JsonMissing.of() + private var originalFilename: JsonField = JsonMissing.of() + private var senderEmail: JsonField = JsonMissing.of() + private var size: JsonField = JsonMissing.of() + private var url: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(file: File) = apply { + casType = file.casType + expiresIn = file.expiresIn + filename = file.filename + messageDate = file.messageDate + messageId = file.messageId + originalFilename = file.originalFilename + senderEmail = file.senderEmail + size = file.size + url = file.url + additionalProperties = file.additionalProperties.toMutableMap() + } + + /** Detected CAS provider based on sender email */ + fun casType(casType: CasType) = casType(JsonField.of(casType)) + + /** + * Sets [Builder.casType] to an arbitrary JSON value. + * + * You should usually call [Builder.casType] with a well-typed [CasType] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun casType(casType: JsonField) = apply { this.casType = casType } + + /** + * URL expiration time in seconds. Defaults vary by source: + * - Gmail Inbox Import: 86400 (24h) + * - Inbound Email with `callback_url` set: 172800 (48h) + * - Inbound Email without `callback_url`: aligned with the session TTL (~30 min) + */ + fun expiresIn(expiresIn: Long) = expiresIn(JsonField.of(expiresIn)) + + /** + * Sets [Builder.expiresIn] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresIn] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresIn(expiresIn: JsonField) = apply { this.expiresIn = expiresIn } + + /** Standardized filename (provider_YYYYMMDD_uniqueid.pdf) */ + fun filename(filename: String) = filename(JsonField.of(filename)) + + /** + * Sets [Builder.filename] to an arbitrary JSON value. + * + * You should usually call [Builder.filename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun filename(filename: JsonField) = apply { this.filename = filename } + + /** Date the email was received */ + fun messageDate(messageDate: LocalDate) = messageDate(JsonField.of(messageDate)) + + /** + * Sets [Builder.messageDate] to an arbitrary JSON value. + * + * You should usually call [Builder.messageDate] with a well-typed [LocalDate] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun messageDate(messageDate: JsonField) = apply { + this.messageDate = messageDate + } + + /** Unique identifier for the email message (use for subsequent API calls) */ + fun messageId(messageId: String) = messageId(JsonField.of(messageId)) + + /** + * Sets [Builder.messageId] to an arbitrary JSON value. + * + * You should usually call [Builder.messageId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun messageId(messageId: JsonField) = apply { this.messageId = messageId } + + /** Original attachment filename from the email */ + fun originalFilename(originalFilename: String) = + originalFilename(JsonField.of(originalFilename)) + + /** + * Sets [Builder.originalFilename] to an arbitrary JSON value. + * + * You should usually call [Builder.originalFilename] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun originalFilename(originalFilename: JsonField) = apply { + this.originalFilename = originalFilename + } + + /** + * Email address of the CAS authority (CDSL, NSDL, CAMS, or KFintech) who originally + * sent this statement + */ + fun senderEmail(senderEmail: String) = senderEmail(JsonField.of(senderEmail)) + + /** + * Sets [Builder.senderEmail] to an arbitrary JSON value. + * + * You should usually call [Builder.senderEmail] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun senderEmail(senderEmail: JsonField) = apply { + this.senderEmail = senderEmail + } + + /** File size in bytes */ + fun size(size: Long) = size(JsonField.of(size)) + + /** + * Sets [Builder.size] to an arbitrary JSON value. + * + * You should usually call [Builder.size] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun size(size: JsonField) = apply { this.size = size } + + /** Direct download URL (presigned, expires based on expires_in) */ + fun url(url: String) = url(JsonField.of(url)) + + /** + * Sets [Builder.url] to an arbitrary JSON value. + * + * You should usually call [Builder.url] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun url(url: JsonField) = apply { this.url = url } + + 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 [File]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): File = + File( + casType, + expiresIn, + filename, + messageDate, + messageId, + originalFilename, + senderEmail, + size, + url, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): File = apply { + if (validated) { + return@apply + } + + casType().ifPresent { it.validate() } + expiresIn() + filename() + messageDate() + messageId() + originalFilename() + senderEmail() + size() + url() + 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 = + (casType.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresIn.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + + (if (messageDate.asKnown().isPresent) 1 else 0) + + (if (messageId.asKnown().isPresent) 1 else 0) + + (if (originalFilename.asKnown().isPresent) 1 else 0) + + (if (senderEmail.asKnown().isPresent) 1 else 0) + + (if (size.asKnown().isPresent) 1 else 0) + + (if (url.asKnown().isPresent) 1 else 0) + + /** Detected CAS provider based on sender email */ + class CasType @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 CDSL = of("cdsl") + + @JvmField val NSDL = of("nsdl") + + @JvmField val CAMS = of("cams") + + @JvmField val KFINTECH = of("kfintech") + + @JvmStatic fun of(value: String) = CasType(JsonField.of(value)) + } + + /** An enum containing [CasType]'s known values. */ + enum class Known { + CDSL, + NSDL, + CAMS, + KFINTECH, + } + + /** + * An enum containing [CasType]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [CasType] 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 { + CDSL, + NSDL, + CAMS, + KFINTECH, + /** + * An enum member indicating that [CasType] 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) { + CDSL -> Value.CDSL + NSDL -> Value.NSDL + CAMS -> Value.CAMS + KFINTECH -> Value.KFINTECH + 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) { + CDSL -> Known.CDSL + NSDL -> Known.NSDL + CAMS -> Known.CAMS + KFINTECH -> Known.KFINTECH + else -> throw CasParserInvalidDataException("Unknown CasType: $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 + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): CasType = 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 CasType && 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 File && + casType == other.casType && + expiresIn == other.expiresIn && + filename == other.filename && + messageDate == other.messageDate && + messageId == other.messageId && + originalFilename == other.originalFilename && + senderEmail == other.senderEmail && + size == other.size && + url == other.url && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + casType, + expiresIn, + filename, + messageDate, + messageId, + originalFilename, + senderEmail, + size, + url, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "File{casType=$casType, expiresIn=$expiresIn, filename=$filename, messageDate=$messageDate, messageId=$messageId, originalFilename=$originalFilename, senderEmail=$senderEmail, size=$size, url=$url, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InboxListCasFilesResponse && + count == other.count && + files == other.files && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(count, files, status, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InboxListCasFilesResponse{count=$count, files=$files, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasParams.kt new file mode 100644 index 0000000..b8a20e9 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasParams.kt @@ -0,0 +1,702 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.kfintech + +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 + +/** + * Generate CAS via KFintech mailback. The CAS PDF will be sent to the investor's email. + * + * This is an async operation - the investor receives the CAS via email within a few minutes. For + * instant CAS retrieval, use CDSL Fetch (`/v4/cdsl/fetch`). + */ +class KfintechGenerateCasParams +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 (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 for the 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 (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() + + /** + * PAN number (optional) + * + * @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 [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 [KfintechGenerateCasParams]. + * + * The following fields are required: + * ```java + * .email() + * .fromDate() + * .password() + * .toDate() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [KfintechGenerateCasParams]. */ + 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(kfintechGenerateCasParams: KfintechGenerateCasParams) = apply { + body = kfintechGenerateCasParams.body.toBuilder() + additionalHeaders = kfintechGenerateCasParams.additionalHeaders.toBuilder() + additionalQueryParams = kfintechGenerateCasParams.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] + * - [panNo] + * - 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 (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 for the 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 (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) } + + /** PAN number (optional) */ + 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 [KfintechGenerateCasParams]. + * + * 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(): KfintechGenerateCasParams = + KfintechGenerateCasParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val email: JsonField, + private val fromDate: JsonField, + private val password: JsonField, + private val toDate: 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("pan_no") @ExcludeMissing panNo: JsonField = JsonMissing.of(), + ) : this(email, fromDate, password, toDate, 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 (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 for the 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 (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") + + /** + * PAN number (optional) + * + * @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 [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 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 + 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 (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 for the 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 (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 } + + /** PAN number (optional) */ + 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), + panNo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + email() + fromDate() + password() + toDate() + 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) + + (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 && + panNo == other.panNo && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(email, fromDate, password, toDate, panNo, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{email=$email, fromDate=$fromDate, password=$password, toDate=$toDate, panNo=$panNo, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is KfintechGenerateCasParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "KfintechGenerateCasParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasResponse.kt new file mode 100644 index 0000000..e1d5883 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasResponse.kt @@ -0,0 +1,194 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.kfintech + +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 KfintechGenerateCasResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +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 [KfintechGenerateCasResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [KfintechGenerateCasResponse]. */ + 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(kfintechGenerateCasResponse: KfintechGenerateCasResponse) = apply { + msg = kfintechGenerateCasResponse.msg + status = kfintechGenerateCasResponse.status + additionalProperties = kfintechGenerateCasResponse.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 [KfintechGenerateCasResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): KfintechGenerateCasResponse = + KfintechGenerateCasResponse(msg, status, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): KfintechGenerateCasResponse = 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 KfintechGenerateCasResponse && + 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() = + "KfintechGenerateCasResponse{msg=$msg, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogCreateParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogCreateParams.kt new file mode 100644 index 0000000..fbd9946 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogCreateParams.kt @@ -0,0 +1,539 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +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.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.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional + +/** + * Retrieve detailed API usage logs for your account. + * + * Returns a list of API calls with timestamps, features used, status codes, and credits consumed. + * Useful for monitoring usage patterns and debugging. + * + * **Legacy path:** `/logs` (still supported) + */ +class LogCreateParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * End time filter (ISO 8601). Defaults to now. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun endTime(): Optional = body.endTime() + + /** + * Maximum number of logs to return + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun limit(): Optional = body.limit() + + /** + * Start time filter (ISO 8601). Defaults to 30 days ago. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun startTime(): Optional = body.startTime() + + /** + * Returns the raw JSON value of [endTime]. + * + * Unlike [endTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _endTime(): JsonField = body._endTime() + + /** + * Returns the raw JSON value of [limit]. + * + * Unlike [limit], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _limit(): JsonField = body._limit() + + /** + * Returns the raw JSON value of [startTime]. + * + * Unlike [startTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _startTime(): JsonField = body._startTime() + + 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 { + + @JvmStatic fun none(): LogCreateParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [LogCreateParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LogCreateParams]. */ + 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(logCreateParams: LogCreateParams) = apply { + body = logCreateParams.body.toBuilder() + additionalHeaders = logCreateParams.additionalHeaders.toBuilder() + additionalQueryParams = logCreateParams.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: + * - [endTime] + * - [limit] + * - [startTime] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** End time filter (ISO 8601). Defaults to now. */ + fun endTime(endTime: OffsetDateTime) = apply { body.endTime(endTime) } + + /** + * Sets [Builder.endTime] to an arbitrary JSON value. + * + * You should usually call [Builder.endTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun endTime(endTime: JsonField) = apply { body.endTime(endTime) } + + /** Maximum number of logs to return */ + fun limit(limit: Long) = apply { body.limit(limit) } + + /** + * Sets [Builder.limit] to an arbitrary JSON value. + * + * You should usually call [Builder.limit] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun limit(limit: JsonField) = apply { body.limit(limit) } + + /** Start time filter (ISO 8601). Defaults to 30 days ago. */ + fun startTime(startTime: OffsetDateTime) = apply { body.startTime(startTime) } + + /** + * Sets [Builder.startTime] to an arbitrary JSON value. + * + * You should usually call [Builder.startTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startTime(startTime: JsonField) = apply { body.startTime(startTime) } + + 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 [LogCreateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): LogCreateParams = + LogCreateParams(body.build(), additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val endTime: JsonField, + private val limit: JsonField, + private val startTime: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("end_time") + @ExcludeMissing + endTime: JsonField = JsonMissing.of(), + @JsonProperty("limit") @ExcludeMissing limit: JsonField = JsonMissing.of(), + @JsonProperty("start_time") + @ExcludeMissing + startTime: JsonField = JsonMissing.of(), + ) : this(endTime, limit, startTime, mutableMapOf()) + + /** + * End time filter (ISO 8601). Defaults to now. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun endTime(): Optional = endTime.getOptional("end_time") + + /** + * Maximum number of logs to return + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun limit(): Optional = limit.getOptional("limit") + + /** + * Start time filter (ISO 8601). Defaults to 30 days ago. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun startTime(): Optional = startTime.getOptional("start_time") + + /** + * Returns the raw JSON value of [endTime]. + * + * Unlike [endTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("end_time") + @ExcludeMissing + fun _endTime(): JsonField = endTime + + /** + * Returns the raw JSON value of [limit]. + * + * Unlike [limit], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("limit") @ExcludeMissing fun _limit(): JsonField = limit + + /** + * Returns the raw JSON value of [startTime]. + * + * Unlike [startTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("start_time") + @ExcludeMissing + fun _startTime(): JsonField = startTime + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var endTime: JsonField = JsonMissing.of() + private var limit: JsonField = JsonMissing.of() + private var startTime: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + endTime = body.endTime + limit = body.limit + startTime = body.startTime + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** End time filter (ISO 8601). Defaults to now. */ + fun endTime(endTime: OffsetDateTime) = endTime(JsonField.of(endTime)) + + /** + * Sets [Builder.endTime] to an arbitrary JSON value. + * + * You should usually call [Builder.endTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun endTime(endTime: JsonField) = apply { this.endTime = endTime } + + /** Maximum number of logs to return */ + fun limit(limit: Long) = limit(JsonField.of(limit)) + + /** + * Sets [Builder.limit] to an arbitrary JSON value. + * + * You should usually call [Builder.limit] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun limit(limit: JsonField) = apply { this.limit = limit } + + /** Start time filter (ISO 8601). Defaults to 30 days ago. */ + fun startTime(startTime: OffsetDateTime) = startTime(JsonField.of(startTime)) + + /** + * Sets [Builder.startTime] to an arbitrary JSON value. + * + * You should usually call [Builder.startTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startTime(startTime: JsonField) = apply { + this.startTime = startTime + } + + 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. + */ + fun build(): Body = Body(endTime, limit, startTime, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + endTime() + limit() + startTime() + 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 (endTime.asKnown().isPresent) 1 else 0) + + (if (limit.asKnown().isPresent) 1 else 0) + + (if (startTime.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + endTime == other.endTime && + limit == other.limit && + startTime == other.startTime && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(endTime, limit, startTime, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{endTime=$endTime, limit=$limit, startTime=$startTime, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LogCreateParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "LogCreateParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogCreateResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogCreateResponse.kt new file mode 100644 index 0000000..e545f51 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogCreateResponse.kt @@ -0,0 +1,595 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class LogCreateResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val count: JsonField, + private val logs: JsonField>, + private val status: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("count") @ExcludeMissing count: JsonField = JsonMissing.of(), + @JsonProperty("logs") @ExcludeMissing logs: JsonField> = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(count, logs, status, mutableMapOf()) + + /** + * Number of logs returned + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun count(): Optional = count.getOptional("count") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun logs(): Optional> = logs.getOptional("logs") + + /** + * @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 [count]. + * + * Unlike [count], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("count") @ExcludeMissing fun _count(): JsonField = count + + /** + * Returns the raw JSON value of [logs]. + * + * Unlike [logs], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("logs") @ExcludeMissing fun _logs(): JsonField> = logs + + /** + * 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 [LogCreateResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LogCreateResponse]. */ + class Builder internal constructor() { + + private var count: JsonField = JsonMissing.of() + private var logs: JsonField>? = null + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(logCreateResponse: LogCreateResponse) = apply { + count = logCreateResponse.count + logs = logCreateResponse.logs.map { it.toMutableList() } + status = logCreateResponse.status + additionalProperties = logCreateResponse.additionalProperties.toMutableMap() + } + + /** Number of logs returned */ + fun count(count: Long) = count(JsonField.of(count)) + + /** + * Sets [Builder.count] to an arbitrary JSON value. + * + * You should usually call [Builder.count] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun count(count: JsonField) = apply { this.count = count } + + fun logs(logs: List) = logs(JsonField.of(logs)) + + /** + * Sets [Builder.logs] to an arbitrary JSON value. + * + * You should usually call [Builder.logs] with a well-typed `List` value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun logs(logs: JsonField>) = apply { this.logs = logs.map { it.toMutableList() } } + + /** + * Adds a single [Log] to [logs]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addLog(log: Log) = apply { + logs = (logs ?: JsonField.of(mutableListOf())).also { checkKnown("logs", it).add(log) } + } + + 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 [LogCreateResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): LogCreateResponse = + LogCreateResponse( + count, + (logs ?: JsonMissing.of()).map { it.toImmutable() }, + status, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): LogCreateResponse = apply { + if (validated) { + return@apply + } + + count() + logs().ifPresent { it.forEach { it.validate() } } + 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 (count.asKnown().isPresent) 1 else 0) + + (logs.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (status.asKnown().isPresent) 1 else 0) + + class Log + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credits: JsonField, + private val feature: JsonField, + private val path: JsonField, + private val requestId: JsonField, + private val statusCode: JsonField, + private val timestamp: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("credits") @ExcludeMissing credits: JsonField = JsonMissing.of(), + @JsonProperty("feature") @ExcludeMissing feature: JsonField = JsonMissing.of(), + @JsonProperty("path") @ExcludeMissing path: JsonField = JsonMissing.of(), + @JsonProperty("request_id") + @ExcludeMissing + requestId: JsonField = JsonMissing.of(), + @JsonProperty("status_code") + @ExcludeMissing + statusCode: JsonField = JsonMissing.of(), + @JsonProperty("timestamp") + @ExcludeMissing + timestamp: JsonField = JsonMissing.of(), + ) : this(credits, feature, path, requestId, statusCode, timestamp, mutableMapOf()) + + /** + * Credits consumed for this request + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun credits(): Optional = credits.getOptional("credits") + + /** + * API feature used + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun feature(): Optional = feature.getOptional("feature") + + /** + * API endpoint path + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun path(): Optional = path.getOptional("path") + + /** + * Unique request identifier + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun requestId(): Optional = requestId.getOptional("request_id") + + /** + * HTTP response status code + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun statusCode(): Optional = statusCode.getOptional("status_code") + + /** + * When the request was made + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun timestamp(): Optional = timestamp.getOptional("timestamp") + + /** + * Returns the raw JSON value of [credits]. + * + * Unlike [credits], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("credits") @ExcludeMissing fun _credits(): JsonField = credits + + /** + * Returns the raw JSON value of [feature]. + * + * Unlike [feature], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("feature") @ExcludeMissing fun _feature(): JsonField = feature + + /** + * Returns the raw JSON value of [path]. + * + * Unlike [path], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("path") @ExcludeMissing fun _path(): JsonField = path + + /** + * Returns the raw JSON value of [requestId]. + * + * Unlike [requestId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("request_id") @ExcludeMissing fun _requestId(): JsonField = requestId + + /** + * Returns the raw JSON value of [statusCode]. + * + * Unlike [statusCode], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status_code") @ExcludeMissing fun _statusCode(): JsonField = statusCode + + /** + * Returns the raw JSON value of [timestamp]. + * + * Unlike [timestamp], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("timestamp") + @ExcludeMissing + fun _timestamp(): JsonField = timestamp + + @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 [Log]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Log]. */ + class Builder internal constructor() { + + private var credits: JsonField = JsonMissing.of() + private var feature: JsonField = JsonMissing.of() + private var path: JsonField = JsonMissing.of() + private var requestId: JsonField = JsonMissing.of() + private var statusCode: JsonField = JsonMissing.of() + private var timestamp: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(log: Log) = apply { + credits = log.credits + feature = log.feature + path = log.path + requestId = log.requestId + statusCode = log.statusCode + timestamp = log.timestamp + additionalProperties = log.additionalProperties.toMutableMap() + } + + /** Credits consumed for this request */ + fun credits(credits: Double) = credits(JsonField.of(credits)) + + /** + * Sets [Builder.credits] to an arbitrary JSON value. + * + * You should usually call [Builder.credits] with a well-typed [Double] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun credits(credits: JsonField) = apply { this.credits = credits } + + /** API feature used */ + fun feature(feature: String) = feature(JsonField.of(feature)) + + /** + * Sets [Builder.feature] to an arbitrary JSON value. + * + * You should usually call [Builder.feature] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun feature(feature: JsonField) = apply { this.feature = feature } + + /** API endpoint path */ + fun path(path: String) = path(JsonField.of(path)) + + /** + * Sets [Builder.path] to an arbitrary JSON value. + * + * You should usually call [Builder.path] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun path(path: JsonField) = apply { this.path = path } + + /** Unique request identifier */ + fun requestId(requestId: String) = requestId(JsonField.of(requestId)) + + /** + * Sets [Builder.requestId] to an arbitrary JSON value. + * + * You should usually call [Builder.requestId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun requestId(requestId: JsonField) = apply { this.requestId = requestId } + + /** HTTP response status code */ + fun statusCode(statusCode: Long) = statusCode(JsonField.of(statusCode)) + + /** + * Sets [Builder.statusCode] to an arbitrary JSON value. + * + * You should usually call [Builder.statusCode] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun statusCode(statusCode: JsonField) = apply { this.statusCode = statusCode } + + /** When the request was made */ + fun timestamp(timestamp: OffsetDateTime) = timestamp(JsonField.of(timestamp)) + + /** + * Sets [Builder.timestamp] to an arbitrary JSON value. + * + * You should usually call [Builder.timestamp] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun timestamp(timestamp: JsonField) = apply { + this.timestamp = timestamp + } + + 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 [Log]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Log = + Log( + credits, + feature, + path, + requestId, + statusCode, + timestamp, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Log = apply { + if (validated) { + return@apply + } + + credits() + feature() + path() + requestId() + statusCode() + timestamp() + 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 (credits.asKnown().isPresent) 1 else 0) + + (if (feature.asKnown().isPresent) 1 else 0) + + (if (path.asKnown().isPresent) 1 else 0) + + (if (requestId.asKnown().isPresent) 1 else 0) + + (if (statusCode.asKnown().isPresent) 1 else 0) + + (if (timestamp.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Log && + credits == other.credits && + feature == other.feature && + path == other.path && + requestId == other.requestId && + statusCode == other.statusCode && + timestamp == other.timestamp && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + credits, + feature, + path, + requestId, + statusCode, + timestamp, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Log{credits=$credits, feature=$feature, path=$path, requestId=$requestId, statusCode=$statusCode, timestamp=$timestamp, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LogCreateResponse && + count == other.count && + logs == other.logs && + status == other.status && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(count, logs, status, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "LogCreateResponse{count=$count, logs=$logs, status=$status, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogGetSummaryParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogGetSummaryParams.kt new file mode 100644 index 0000000..b645d12 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogGetSummaryParams.kt @@ -0,0 +1,479 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +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.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.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional + +/** + * Get aggregated usage statistics grouped by feature. + * + * Useful for understanding which API features are being used most and tracking usage trends. + * + * **Legacy path:** `/logs/summary` (still supported) + */ +class LogGetSummaryParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * End time filter (ISO 8601). Defaults to now. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun endTime(): Optional = body.endTime() + + /** + * Start time filter (ISO 8601). Defaults to start of current month. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun startTime(): Optional = body.startTime() + + /** + * Returns the raw JSON value of [endTime]. + * + * Unlike [endTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _endTime(): JsonField = body._endTime() + + /** + * Returns the raw JSON value of [startTime]. + * + * Unlike [startTime], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _startTime(): JsonField = body._startTime() + + 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 { + + @JvmStatic fun none(): LogGetSummaryParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [LogGetSummaryParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LogGetSummaryParams]. */ + 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(logGetSummaryParams: LogGetSummaryParams) = apply { + body = logGetSummaryParams.body.toBuilder() + additionalHeaders = logGetSummaryParams.additionalHeaders.toBuilder() + additionalQueryParams = logGetSummaryParams.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: + * - [endTime] + * - [startTime] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** End time filter (ISO 8601). Defaults to now. */ + fun endTime(endTime: OffsetDateTime) = apply { body.endTime(endTime) } + + /** + * Sets [Builder.endTime] to an arbitrary JSON value. + * + * You should usually call [Builder.endTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun endTime(endTime: JsonField) = apply { body.endTime(endTime) } + + /** Start time filter (ISO 8601). Defaults to start of current month. */ + fun startTime(startTime: OffsetDateTime) = apply { body.startTime(startTime) } + + /** + * Sets [Builder.startTime] to an arbitrary JSON value. + * + * You should usually call [Builder.startTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startTime(startTime: JsonField) = apply { body.startTime(startTime) } + + 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 [LogGetSummaryParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): LogGetSummaryParams = + LogGetSummaryParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val endTime: JsonField, + private val startTime: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("end_time") + @ExcludeMissing + endTime: JsonField = JsonMissing.of(), + @JsonProperty("start_time") + @ExcludeMissing + startTime: JsonField = JsonMissing.of(), + ) : this(endTime, startTime, mutableMapOf()) + + /** + * End time filter (ISO 8601). Defaults to now. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun endTime(): Optional = endTime.getOptional("end_time") + + /** + * Start time filter (ISO 8601). Defaults to start of current month. + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun startTime(): Optional = startTime.getOptional("start_time") + + /** + * Returns the raw JSON value of [endTime]. + * + * Unlike [endTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("end_time") + @ExcludeMissing + fun _endTime(): JsonField = endTime + + /** + * Returns the raw JSON value of [startTime]. + * + * Unlike [startTime], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("start_time") + @ExcludeMissing + fun _startTime(): JsonField = startTime + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var endTime: JsonField = JsonMissing.of() + private var startTime: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + endTime = body.endTime + startTime = body.startTime + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** End time filter (ISO 8601). Defaults to now. */ + fun endTime(endTime: OffsetDateTime) = endTime(JsonField.of(endTime)) + + /** + * Sets [Builder.endTime] to an arbitrary JSON value. + * + * You should usually call [Builder.endTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun endTime(endTime: JsonField) = apply { this.endTime = endTime } + + /** Start time filter (ISO 8601). Defaults to start of current month. */ + fun startTime(startTime: OffsetDateTime) = startTime(JsonField.of(startTime)) + + /** + * Sets [Builder.startTime] to an arbitrary JSON value. + * + * You should usually call [Builder.startTime] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startTime(startTime: JsonField) = apply { + this.startTime = startTime + } + + 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. + */ + fun build(): Body = Body(endTime, startTime, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + endTime() + startTime() + 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 (endTime.asKnown().isPresent) 1 else 0) + + (if (startTime.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + endTime == other.endTime && + startTime == other.startTime && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(endTime, startTime, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{endTime=$endTime, startTime=$startTime, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LogGetSummaryParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "LogGetSummaryParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogGetSummaryResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogGetSummaryResponse.kt new file mode 100644 index 0000000..892fb73 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/logs/LogGetSummaryResponse.kt @@ -0,0 +1,690 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +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.checkKnown +import com.cas_parser.api.core.toImmutable +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 + +class LogGetSummaryResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val status: JsonField, + private val summary: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("summary") @ExcludeMissing summary: JsonField = JsonMissing.of(), + ) : this(status, summary, mutableMapOf()) + + /** + * @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") + + /** + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun summary(): Optional = summary.getOptional("summary") + + /** + * 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 + + /** + * Returns the raw JSON value of [summary]. + * + * Unlike [summary], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("summary") @ExcludeMissing fun _summary(): JsonField = summary + + @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 [LogGetSummaryResponse]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [LogGetSummaryResponse]. */ + class Builder internal constructor() { + + private var status: JsonField = JsonMissing.of() + private var summary: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(logGetSummaryResponse: LogGetSummaryResponse) = apply { + status = logGetSummaryResponse.status + summary = logGetSummaryResponse.summary + additionalProperties = logGetSummaryResponse.additionalProperties.toMutableMap() + } + + 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 summary(summary: Summary) = summary(JsonField.of(summary)) + + /** + * Sets [Builder.summary] to an arbitrary JSON value. + * + * You should usually call [Builder.summary] with a well-typed [Summary] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun summary(summary: JsonField) = apply { this.summary = summary } + + 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 [LogGetSummaryResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): LogGetSummaryResponse = + LogGetSummaryResponse(status, summary, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): LogGetSummaryResponse = apply { + if (validated) { + return@apply + } + + status() + summary().ifPresent { it.validate() } + 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 (status.asKnown().isPresent) 1 else 0) + + (summary.asKnown().getOrNull()?.validity() ?: 0) + + class Summary + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val byFeature: JsonField>, + private val totalCredits: JsonField, + private val totalRequests: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("by_feature") + @ExcludeMissing + byFeature: JsonField> = JsonMissing.of(), + @JsonProperty("total_credits") + @ExcludeMissing + totalCredits: JsonField = JsonMissing.of(), + @JsonProperty("total_requests") + @ExcludeMissing + totalRequests: JsonField = JsonMissing.of(), + ) : this(byFeature, totalCredits, totalRequests, mutableMapOf()) + + /** + * Usage breakdown by feature + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun byFeature(): Optional> = byFeature.getOptional("by_feature") + + /** + * Total credits consumed in the period + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun totalCredits(): Optional = totalCredits.getOptional("total_credits") + + /** + * Total API requests made in the period + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun totalRequests(): Optional = totalRequests.getOptional("total_requests") + + /** + * Returns the raw JSON value of [byFeature]. + * + * Unlike [byFeature], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("by_feature") + @ExcludeMissing + fun _byFeature(): JsonField> = byFeature + + /** + * Returns the raw JSON value of [totalCredits]. + * + * Unlike [totalCredits], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("total_credits") + @ExcludeMissing + fun _totalCredits(): JsonField = totalCredits + + /** + * Returns the raw JSON value of [totalRequests]. + * + * Unlike [totalRequests], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("total_requests") + @ExcludeMissing + fun _totalRequests(): JsonField = totalRequests + + @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 [Summary]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Summary]. */ + class Builder internal constructor() { + + private var byFeature: JsonField>? = null + private var totalCredits: JsonField = JsonMissing.of() + private var totalRequests: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(summary: Summary) = apply { + byFeature = summary.byFeature.map { it.toMutableList() } + totalCredits = summary.totalCredits + totalRequests = summary.totalRequests + additionalProperties = summary.additionalProperties.toMutableMap() + } + + /** Usage breakdown by feature */ + fun byFeature(byFeature: List) = byFeature(JsonField.of(byFeature)) + + /** + * Sets [Builder.byFeature] to an arbitrary JSON value. + * + * You should usually call [Builder.byFeature] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun byFeature(byFeature: JsonField>) = apply { + this.byFeature = byFeature.map { it.toMutableList() } + } + + /** + * Adds a single [ByFeature] to [Builder.byFeature]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addByFeature(byFeature: ByFeature) = apply { + this.byFeature = + (this.byFeature ?: JsonField.of(mutableListOf())).also { + checkKnown("byFeature", it).add(byFeature) + } + } + + /** Total credits consumed in the period */ + fun totalCredits(totalCredits: Double) = totalCredits(JsonField.of(totalCredits)) + + /** + * Sets [Builder.totalCredits] to an arbitrary JSON value. + * + * You should usually call [Builder.totalCredits] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun totalCredits(totalCredits: JsonField) = apply { + this.totalCredits = totalCredits + } + + /** Total API requests made in the period */ + fun totalRequests(totalRequests: Long) = totalRequests(JsonField.of(totalRequests)) + + /** + * Sets [Builder.totalRequests] to an arbitrary JSON value. + * + * You should usually call [Builder.totalRequests] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun totalRequests(totalRequests: JsonField) = apply { + this.totalRequests = totalRequests + } + + 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 [Summary]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Summary = + Summary( + (byFeature ?: JsonMissing.of()).map { it.toImmutable() }, + totalCredits, + totalRequests, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Summary = apply { + if (validated) { + return@apply + } + + byFeature().ifPresent { it.forEach { it.validate() } } + totalCredits() + totalRequests() + 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 = + (byFeature.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (totalCredits.asKnown().isPresent) 1 else 0) + + (if (totalRequests.asKnown().isPresent) 1 else 0) + + class ByFeature + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val credits: JsonField, + private val feature: JsonField, + private val requests: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("credits") + @ExcludeMissing + credits: JsonField = JsonMissing.of(), + @JsonProperty("feature") + @ExcludeMissing + feature: JsonField = JsonMissing.of(), + @JsonProperty("requests") + @ExcludeMissing + requests: JsonField = JsonMissing.of(), + ) : this(credits, feature, requests, mutableMapOf()) + + /** + * Credits consumed by this feature + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun credits(): Optional = credits.getOptional("credits") + + /** + * API feature name + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun feature(): Optional = feature.getOptional("feature") + + /** + * Number of requests for this feature + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. + * if the server responded with an unexpected value). + */ + fun requests(): Optional = requests.getOptional("requests") + + /** + * Returns the raw JSON value of [credits]. + * + * Unlike [credits], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("credits") @ExcludeMissing fun _credits(): JsonField = credits + + /** + * Returns the raw JSON value of [feature]. + * + * Unlike [feature], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("feature") @ExcludeMissing fun _feature(): JsonField = feature + + /** + * Returns the raw JSON value of [requests]. + * + * Unlike [requests], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("requests") @ExcludeMissing fun _requests(): JsonField = requests + + @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 [ByFeature]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ByFeature]. */ + class Builder internal constructor() { + + private var credits: JsonField = JsonMissing.of() + private var feature: JsonField = JsonMissing.of() + private var requests: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(byFeature: ByFeature) = apply { + credits = byFeature.credits + feature = byFeature.feature + requests = byFeature.requests + additionalProperties = byFeature.additionalProperties.toMutableMap() + } + + /** Credits consumed by this feature */ + fun credits(credits: Double) = credits(JsonField.of(credits)) + + /** + * Sets [Builder.credits] to an arbitrary JSON value. + * + * You should usually call [Builder.credits] with a well-typed [Double] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun credits(credits: JsonField) = apply { this.credits = credits } + + /** API feature name */ + fun feature(feature: String) = feature(JsonField.of(feature)) + + /** + * Sets [Builder.feature] to an arbitrary JSON value. + * + * You should usually call [Builder.feature] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun feature(feature: JsonField) = apply { this.feature = feature } + + /** Number of requests for this feature */ + fun requests(requests: Long) = requests(JsonField.of(requests)) + + /** + * Sets [Builder.requests] to an arbitrary JSON value. + * + * You should usually call [Builder.requests] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun requests(requests: JsonField) = apply { this.requests = requests } + + 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 [ByFeature]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ByFeature = + ByFeature(credits, feature, requests, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing + * fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match + * its expected type. + */ + fun validate(): ByFeature = apply { + if (validated) { + return@apply + } + + credits() + feature() + requests() + 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 (credits.asKnown().isPresent) 1 else 0) + + (if (feature.asKnown().isPresent) 1 else 0) + + (if (requests.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ByFeature && + credits == other.credits && + feature == other.feature && + requests == other.requests && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(credits, feature, requests, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ByFeature{credits=$credits, feature=$feature, requests=$requests, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Summary && + byFeature == other.byFeature && + totalCredits == other.totalCredits && + totalRequests == other.totalRequests && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(byFeature, totalCredits, totalRequests, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Summary{byFeature=$byFeature, totalCredits=$totalCredits, totalRequests=$totalRequests, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is LogGetSummaryResponse && + status == other.status && + summary == other.summary && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(status, summary, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "LogGetSummaryResponse{status=$status, summary=$summary, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/nsdl/NsdlParseParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/nsdl/NsdlParseParams.kt new file mode 100644 index 0000000..96eb267 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/nsdl/NsdlParseParams.kt @@ -0,0 +1,507 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.nsdl + +import com.cas_parser.api.core.ExcludeMissing +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.MultipartField +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +import com.cas_parser.api.errors.CasParserInvalidDataException +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonProperty +import java.util.Collections +import java.util.Objects +import java.util.Optional + +/** + * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and returns + * data in a unified format. Use this endpoint when you know the PDF is from NSDL. + */ +class NsdlParseParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun password(): Optional = body.password() + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfFile(): Optional = body.pdfFile() + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfUrl(): Optional = body.pdfUrl() + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _password(): MultipartField = body._password() + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfFile(): MultipartField = body._pdfFile() + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfUrl(): MultipartField = body._pdfUrl() + + 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 { + + @JvmStatic fun none(): NsdlParseParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [NsdlParseParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [NsdlParseParams]. */ + 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(nsdlParseParams: NsdlParseParams) = apply { + body = nsdlParseParams.body.toBuilder() + additionalHeaders = nsdlParseParams.additionalHeaders.toBuilder() + additionalQueryParams = nsdlParseParams.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: + * - [password] + * - [pdfFile] + * - [pdfUrl] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Password for the PDF file (if required) */ + fun password(password: String) = apply { body.password(password) } + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { body.password(password) } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = apply { body.pdfFile(pdfFile) } + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { body.pdfFile(pdfFile) } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = apply { body.pdfUrl(pdfUrl) } + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { body.pdfUrl(pdfUrl) } + + 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 [NsdlParseParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): NsdlParseParams = + NsdlParseParams(body.build(), additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _body(): Map> = + (mapOf("password" to _password(), "pdf_file" to _pdfFile(), "pdf_url" to _pdfUrl()) + + _additionalBodyProperties().mapValues { (_, value) -> MultipartField.of(value) }) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** Provide either `pdf_file` OR `pdf_url` (one is required) */ + class Body + private constructor( + private val password: MultipartField, + private val pdfFile: MultipartField, + private val pdfUrl: MultipartField, + private val additionalProperties: MutableMap, + ) { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun password(): Optional = password.value.getOptional("password") + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfFile(): Optional = pdfFile.value.getOptional("pdf_file") + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfUrl(): Optional = pdfUrl.value.getOptional("pdf_url") + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("password") @ExcludeMissing fun _password(): MultipartField = password + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("pdf_file") @ExcludeMissing fun _pdfFile(): MultipartField = pdfFile + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + @JsonProperty("pdf_url") @ExcludeMissing fun _pdfUrl(): MultipartField = pdfUrl + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var password: MultipartField = MultipartField.of(null) + private var pdfFile: MultipartField = MultipartField.of(null) + private var pdfUrl: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + password = body.password + pdfFile = body.pdfFile + pdfUrl = body.pdfUrl + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Password for the PDF file (if required) */ + fun password(password: String) = password(MultipartField.of(password)) + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { this.password = password } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = pdfFile(MultipartField.of(pdfFile)) + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { this.pdfFile = pdfFile } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = pdfUrl(MultipartField.of(pdfUrl)) + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { this.pdfUrl = pdfUrl } + + 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. + */ + fun build(): Body = Body(password, pdfFile, pdfUrl, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + password() + pdfFile() + pdfUrl() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CasParserInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + password == other.password && + pdfFile == other.pdfFile && + pdfUrl == other.pdfUrl && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(password, pdfFile, pdfUrl, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{password=$password, pdfFile=$pdfFile, pdfUrl=$pdfUrl, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is NsdlParseParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "NsdlParseParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/smart/SmartParseCasPdfParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/smart/SmartParseCasPdfParams.kt new file mode 100644 index 0000000..730a047 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/smart/SmartParseCasPdfParams.kt @@ -0,0 +1,512 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.smart + +import com.cas_parser.api.core.ExcludeMissing +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.MultipartField +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +import com.cas_parser.api.errors.CasParserInvalidDataException +import com.fasterxml.jackson.annotation.JsonAnyGetter +import com.fasterxml.jackson.annotation.JsonAnySetter +import com.fasterxml.jackson.annotation.JsonProperty +import java.util.Collections +import java.util.Objects +import java.util.Optional + +/** + * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or + * CAMS/KFintech and returns data in a unified format. It auto-detects the CAS type and transforms + * the data into a consistent structure regardless of the source. + */ +class SmartParseCasPdfParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun password(): Optional = body.password() + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfFile(): Optional = body.pdfFile() + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun pdfUrl(): Optional = body.pdfUrl() + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _password(): MultipartField = body._password() + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfFile(): MultipartField = body._pdfFile() + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + fun _pdfUrl(): MultipartField = body._pdfUrl() + + 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 { + + @JvmStatic fun none(): SmartParseCasPdfParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [SmartParseCasPdfParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [SmartParseCasPdfParams]. */ + 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(smartParseCasPdfParams: SmartParseCasPdfParams) = apply { + body = smartParseCasPdfParams.body.toBuilder() + additionalHeaders = smartParseCasPdfParams.additionalHeaders.toBuilder() + additionalQueryParams = smartParseCasPdfParams.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: + * - [password] + * - [pdfFile] + * - [pdfUrl] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Password for the PDF file (if required) */ + fun password(password: String) = apply { body.password(password) } + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { body.password(password) } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = apply { body.pdfFile(pdfFile) } + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { body.pdfFile(pdfFile) } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = apply { body.pdfUrl(pdfUrl) } + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { body.pdfUrl(pdfUrl) } + + 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 [SmartParseCasPdfParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): SmartParseCasPdfParams = + SmartParseCasPdfParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Map> = + (mapOf("password" to _password(), "pdf_file" to _pdfFile(), "pdf_url" to _pdfUrl()) + + _additionalBodyProperties().mapValues { (_, value) -> MultipartField.of(value) }) + .toImmutable() + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** Provide either `pdf_file` OR `pdf_url` (one is required) */ + class Body + private constructor( + private val password: MultipartField, + private val pdfFile: MultipartField, + private val pdfUrl: MultipartField, + private val additionalProperties: MutableMap, + ) { + + /** + * Password for the PDF file (if required) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun password(): Optional = password.value.getOptional("password") + + /** + * Base64 encoded CAS PDF file (required if pdf_url not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfFile(): Optional = pdfFile.value.getOptional("pdf_file") + + /** + * URL to the CAS PDF file (required if pdf_file not provided) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun pdfUrl(): Optional = pdfUrl.value.getOptional("pdf_url") + + /** + * Returns the raw multipart value of [password]. + * + * Unlike [password], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("password") @ExcludeMissing fun _password(): MultipartField = password + + /** + * Returns the raw multipart value of [pdfFile]. + * + * Unlike [pdfFile], this method doesn't throw if the multipart field has an unexpected + * type. + */ + @JsonProperty("pdf_file") @ExcludeMissing fun _pdfFile(): MultipartField = pdfFile + + /** + * Returns the raw multipart value of [pdfUrl]. + * + * Unlike [pdfUrl], this method doesn't throw if the multipart field has an unexpected type. + */ + @JsonProperty("pdf_url") @ExcludeMissing fun _pdfUrl(): MultipartField = pdfUrl + + @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]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var password: MultipartField = MultipartField.of(null) + private var pdfFile: MultipartField = MultipartField.of(null) + private var pdfUrl: MultipartField = MultipartField.of(null) + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + password = body.password + pdfFile = body.pdfFile + pdfUrl = body.pdfUrl + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Password for the PDF file (if required) */ + fun password(password: String) = password(MultipartField.of(password)) + + /** + * Sets [Builder.password] to an arbitrary multipart 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: MultipartField) = apply { this.password = password } + + /** Base64 encoded CAS PDF file (required if pdf_url not provided) */ + fun pdfFile(pdfFile: String) = pdfFile(MultipartField.of(pdfFile)) + + /** + * Sets [Builder.pdfFile] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfFile] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfFile(pdfFile: MultipartField) = apply { this.pdfFile = pdfFile } + + /** URL to the CAS PDF file (required if pdf_file not provided) */ + fun pdfUrl(pdfUrl: String) = pdfUrl(MultipartField.of(pdfUrl)) + + /** + * Sets [Builder.pdfUrl] to an arbitrary multipart value. + * + * You should usually call [Builder.pdfUrl] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun pdfUrl(pdfUrl: MultipartField) = apply { this.pdfUrl = pdfUrl } + + 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. + */ + fun build(): Body = Body(password, pdfFile, pdfUrl, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + password() + pdfFile() + pdfUrl() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CasParserInvalidDataException) { + false + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + password == other.password && + pdfFile == other.pdfFile && + pdfUrl == other.pdfUrl && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(password, pdfFile, pdfUrl, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{password=$password, pdfFile=$pdfFile, pdfUrl=$pdfUrl, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is SmartParseCasPdfParams && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = Objects.hash(body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "SmartParseCasPdfParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyParams.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyParams.kt new file mode 100644 index 0000000..aac1a81 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyParams.kt @@ -0,0 +1,211 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.verifytoken + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.Params +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.http.QueryParams +import com.cas_parser.api.core.toImmutable +import java.util.Objects +import java.util.Optional + +/** Verify an access token and check if it's still valid. Useful for debugging token issues. */ +class VerifyTokenVerifyParams +private constructor( + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** 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 { + + @JvmStatic fun none(): VerifyTokenVerifyParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [VerifyTokenVerifyParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VerifyTokenVerifyParams]. */ + class Builder internal constructor() { + + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(verifyTokenVerifyParams: VerifyTokenVerifyParams) = apply { + additionalHeaders = verifyTokenVerifyParams.additionalHeaders.toBuilder() + additionalQueryParams = verifyTokenVerifyParams.additionalQueryParams.toBuilder() + additionalBodyProperties = + verifyTokenVerifyParams.additionalBodyProperties.toMutableMap() + } + + 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) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [VerifyTokenVerifyParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): VerifyTokenVerifyParams = + VerifyTokenVerifyParams( + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is VerifyTokenVerifyParams && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash(additionalHeaders, additionalQueryParams, additionalBodyProperties) + + override fun toString() = + "VerifyTokenVerifyParams{additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyResponse.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyResponse.kt new file mode 100644 index 0000000..f34e4bd --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyResponse.kt @@ -0,0 +1,248 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.verifytoken + +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 VerifyTokenVerifyResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val error: JsonField, + private val maskedApiKey: JsonField, + private val valid: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("error") @ExcludeMissing error: JsonField = JsonMissing.of(), + @JsonProperty("masked_api_key") + @ExcludeMissing + maskedApiKey: JsonField = JsonMissing.of(), + @JsonProperty("valid") @ExcludeMissing valid: JsonField = JsonMissing.of(), + ) : this(error, maskedApiKey, valid, mutableMapOf()) + + /** + * Error message (only shown if invalid) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun error(): Optional = error.getOptional("error") + + /** + * Masked API key (only shown if valid) + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun maskedApiKey(): Optional = maskedApiKey.getOptional("masked_api_key") + + /** + * Whether the token is valid + * + * @throws CasParserInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun valid(): Optional = valid.getOptional("valid") + + /** + * Returns the raw JSON value of [error]. + * + * Unlike [error], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("error") @ExcludeMissing fun _error(): JsonField = error + + /** + * Returns the raw JSON value of [maskedApiKey]. + * + * Unlike [maskedApiKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("masked_api_key") + @ExcludeMissing + fun _maskedApiKey(): JsonField = maskedApiKey + + /** + * Returns the raw JSON value of [valid]. + * + * Unlike [valid], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("valid") @ExcludeMissing fun _valid(): JsonField = valid + + @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 [VerifyTokenVerifyResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [VerifyTokenVerifyResponse]. */ + class Builder internal constructor() { + + private var error: JsonField = JsonMissing.of() + private var maskedApiKey: JsonField = JsonMissing.of() + private var valid: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(verifyTokenVerifyResponse: VerifyTokenVerifyResponse) = apply { + error = verifyTokenVerifyResponse.error + maskedApiKey = verifyTokenVerifyResponse.maskedApiKey + valid = verifyTokenVerifyResponse.valid + additionalProperties = verifyTokenVerifyResponse.additionalProperties.toMutableMap() + } + + /** Error message (only shown if invalid) */ + fun error(error: String) = error(JsonField.of(error)) + + /** + * Sets [Builder.error] to an arbitrary JSON value. + * + * You should usually call [Builder.error] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun error(error: JsonField) = apply { this.error = error } + + /** Masked API key (only shown if valid) */ + fun maskedApiKey(maskedApiKey: String) = maskedApiKey(JsonField.of(maskedApiKey)) + + /** + * Sets [Builder.maskedApiKey] to an arbitrary JSON value. + * + * You should usually call [Builder.maskedApiKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun maskedApiKey(maskedApiKey: JsonField) = apply { + this.maskedApiKey = maskedApiKey + } + + /** Whether the token is valid */ + fun valid(valid: Boolean) = valid(JsonField.of(valid)) + + /** + * Sets [Builder.valid] to an arbitrary JSON value. + * + * You should usually call [Builder.valid] with a well-typed [Boolean] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun valid(valid: JsonField) = apply { this.valid = valid } + + 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 [VerifyTokenVerifyResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): VerifyTokenVerifyResponse = + VerifyTokenVerifyResponse( + error, + maskedApiKey, + valid, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CasParserInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): VerifyTokenVerifyResponse = apply { + if (validated) { + return@apply + } + + error() + maskedApiKey() + valid() + 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 (error.asKnown().isPresent) 1 else 0) + + (if (maskedApiKey.asKnown().isPresent) 1 else 0) + + (if (valid.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is VerifyTokenVerifyResponse && + error == other.error && + maskedApiKey == other.maskedApiKey && + valid == other.valid && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(error, maskedApiKey, valid, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "VerifyTokenVerifyResponse{error=$error, maskedApiKey=$maskedApiKey, valid=$valid, additionalProperties=$additionalProperties}" +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsync.kt new file mode 100644 index 0000000..6f0e9db --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsync.kt @@ -0,0 +1,104 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.accesstoken.AccessTokenCreateParams +import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +interface AccessTokenServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): AccessTokenServiceAsync + + /** + * Generate a short-lived access token from your API key. + * + * **Use this endpoint from your backend** to create tokens that can be safely passed to + * frontend/SDK. + * + * **Legacy path:** `/v1/access-token` (still supported) + * + * Access tokens: + * - Are prefixed with `at_` for easy identification + * - Valid for up to 60 minutes + * - Can be used in place of API keys on all v4 endpoints + * - Cannot be used to generate other access tokens + */ + fun create(): CompletableFuture = + create(AccessTokenCreateParams.none()) + + /** @see create */ + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see create */ + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none() + ): CompletableFuture = create(params, RequestOptions.none()) + + /** @see create */ + fun create(requestOptions: RequestOptions): CompletableFuture = + create(AccessTokenCreateParams.none(), requestOptions) + + /** + * A view of [AccessTokenServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): AccessTokenServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/token`, but is otherwise the same as + * [AccessTokenServiceAsync.create]. + */ + fun create(): CompletableFuture> = + create(AccessTokenCreateParams.none()) + + /** @see create */ + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see create */ + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none() + ): CompletableFuture> = + create(params, RequestOptions.none()) + + /** @see create */ + fun create( + requestOptions: RequestOptions + ): CompletableFuture> = + create(AccessTokenCreateParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncImpl.kt new file mode 100644 index 0000000..a36cbb7 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncImpl.kt @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.accesstoken.AccessTokenCreateParams +import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +class AccessTokenServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + AccessTokenServiceAsync { + + private val withRawResponse: AccessTokenServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): AccessTokenServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): AccessTokenServiceAsync = + AccessTokenServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun create( + params: AccessTokenCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v1/token + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AccessTokenServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): AccessTokenServiceAsync.WithRawResponse = + AccessTokenServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: AccessTokenCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "token") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsync.kt new file mode 100644 index 0000000..e8d7cc2 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsync.kt @@ -0,0 +1,90 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.CamsKfintechParseParams +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface CamsKfintechServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CamsKfintechServiceAsync + + /** + * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF + * files and returns data in a unified format. Use this endpoint when you know the PDF is from + * CAMS or KFintech. + */ + fun parse(): CompletableFuture = parse(CamsKfintechParseParams.none()) + + /** @see parse */ + fun parse( + params: CamsKfintechParseParams = CamsKfintechParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see parse */ + fun parse( + params: CamsKfintechParseParams = CamsKfintechParseParams.none() + ): CompletableFuture = parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse(requestOptions: RequestOptions): CompletableFuture = + parse(CamsKfintechParseParams.none(), requestOptions) + + /** + * A view of [CamsKfintechServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): CamsKfintechServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/cams_kfintech/parse`, but is otherwise the same + * as [CamsKfintechServiceAsync.parse]. + */ + fun parse(): CompletableFuture> = + parse(CamsKfintechParseParams.none()) + + /** @see parse */ + fun parse( + params: CamsKfintechParseParams = CamsKfintechParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see parse */ + fun parse( + params: CamsKfintechParseParams = CamsKfintechParseParams.none() + ): CompletableFuture> = + parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse( + requestOptions: RequestOptions + ): CompletableFuture> = + parse(CamsKfintechParseParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncImpl.kt new file mode 100644 index 0000000..df53226 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.camskfintech.CamsKfintechParseParams +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class CamsKfintechServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + CamsKfintechServiceAsync { + + private val withRawResponse: CamsKfintechServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): CamsKfintechServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CamsKfintechServiceAsync = + CamsKfintechServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parse( + params: CamsKfintechParseParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/cams_kfintech/parse + withRawResponse().parse(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CamsKfintechServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): CamsKfintechServiceAsync.WithRawResponse = + CamsKfintechServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parse( + params: CamsKfintechParseParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cams_kfintech", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { parseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsync.kt new file mode 100644 index 0000000..dd5e7d3 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsync.kt @@ -0,0 +1,97 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.cdsl.CdslParsePdfParams +import com.cas_parser.api.services.async.cdsl.FetchServiceAsync +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface CdslServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CdslServiceAsync + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ + fun fetch(): FetchServiceAsync + + /** + * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and + * returns data in a unified format. Use this endpoint when you know the PDF is from CDSL. + */ + fun parsePdf(): CompletableFuture = parsePdf(CdslParsePdfParams.none()) + + /** @see parsePdf */ + fun parsePdf( + params: CdslParsePdfParams = CdslParsePdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see parsePdf */ + fun parsePdf( + params: CdslParsePdfParams = CdslParsePdfParams.none() + ): CompletableFuture = parsePdf(params, RequestOptions.none()) + + /** @see parsePdf */ + fun parsePdf(requestOptions: RequestOptions): CompletableFuture = + parsePdf(CdslParsePdfParams.none(), requestOptions) + + /** A view of [CdslServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CdslServiceAsync.WithRawResponse + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via + * OTP authentication. + */ + fun fetch(): FetchServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/cdsl/parse`, but is otherwise the same as + * [CdslServiceAsync.parsePdf]. + */ + fun parsePdf(): CompletableFuture> = + parsePdf(CdslParsePdfParams.none()) + + /** @see parsePdf */ + fun parsePdf( + params: CdslParsePdfParams = CdslParsePdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see parsePdf */ + fun parsePdf( + params: CdslParsePdfParams = CdslParsePdfParams.none() + ): CompletableFuture> = + parsePdf(params, RequestOptions.none()) + + /** @see parsePdf */ + fun parsePdf( + requestOptions: RequestOptions + ): CompletableFuture> = + parsePdf(CdslParsePdfParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncImpl.kt new file mode 100644 index 0000000..0bb2f66 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncImpl.kt @@ -0,0 +1,107 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.cdsl.CdslParsePdfParams +import com.cas_parser.api.services.async.cdsl.FetchServiceAsync +import com.cas_parser.api.services.async.cdsl.FetchServiceAsyncImpl +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class CdslServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + CdslServiceAsync { + + private val withRawResponse: CdslServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val fetch: FetchServiceAsync by lazy { FetchServiceAsyncImpl(clientOptions) } + + override fun withRawResponse(): CdslServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CdslServiceAsync = + CdslServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ + override fun fetch(): FetchServiceAsync = fetch + + override fun parsePdf( + params: CdslParsePdfParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/cdsl/parse + withRawResponse().parsePdf(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CdslServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val fetch: FetchServiceAsync.WithRawResponse by lazy { + FetchServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: Consumer + ): CdslServiceAsync.WithRawResponse = + CdslServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via + * OTP authentication. + */ + override fun fetch(): FetchServiceAsync.WithRawResponse = fetch + + private val parsePdfHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parsePdf( + params: CdslParsePdfParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cdsl", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { parsePdfHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsync.kt new file mode 100644 index 0000000..2fee7ca --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsync.kt @@ -0,0 +1,117 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.contractnote.ContractNoteParseParams +import com.cas_parser.api.models.contractnote.ContractNoteParseResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ +interface ContractNoteServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): ContractNoteServiceAsync + + /** + * This endpoint parses Contract Note PDF files from various brokers including Zerodha, Groww, + * Upstox, ICICI Securities, and others. + * + * **What is a Contract Note?** A contract note is a legal document that provides details of all + * trades executed by an investor. It includes: + * - Trade details with timestamps, quantities, and prices + * - Brokerage and charges breakdown + * - Settlement information + * - Regulatory compliance details + * + * **Supported Brokers:** + * - Zerodha Broking Limited + * - Groww Invest Tech Private Limited + * - Upstox (RKSV Securities) + * - ICICI Securities Limited + * - Auto-detection for unknown brokers + * + * **Key Features:** + * - **Auto-detection**: Automatically identifies broker type from PDF content + * - **Comprehensive parsing**: Extracts equity transactions, derivatives transactions, detailed + * trades, and charges + * - **Flexible input**: Accepts both file upload and URL-based PDF input + * - **Password protection**: Supports password-protected PDFs + * + * The API returns structured data including contract note information, client details, + * transaction summaries, and detailed trade-by-trade breakdowns. + */ + fun parse(): CompletableFuture = + parse(ContractNoteParseParams.none()) + + /** @see parse */ + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see parse */ + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none() + ): CompletableFuture = parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse(requestOptions: RequestOptions): CompletableFuture = + parse(ContractNoteParseParams.none(), requestOptions) + + /** + * A view of [ContractNoteServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): ContractNoteServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/contract_note/parse`, but is otherwise the same + * as [ContractNoteServiceAsync.parse]. + */ + fun parse(): CompletableFuture> = + parse(ContractNoteParseParams.none()) + + /** @see parse */ + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see parse */ + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none() + ): CompletableFuture> = + parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse( + requestOptions: RequestOptions + ): CompletableFuture> = + parse(ContractNoteParseParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncImpl.kt new file mode 100644 index 0000000..b9c1014 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncImpl.kt @@ -0,0 +1,90 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.contractnote.ContractNoteParseParams +import com.cas_parser.api.models.contractnote.ContractNoteParseResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ +class ContractNoteServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + ContractNoteServiceAsync { + + private val withRawResponse: ContractNoteServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ContractNoteServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): ContractNoteServiceAsync = + ContractNoteServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parse( + params: ContractNoteParseParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/contract_note/parse + withRawResponse().parse(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ContractNoteServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): ContractNoteServiceAsync.WithRawResponse = + ContractNoteServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parse( + params: ContractNoteParseParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "contract_note", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { parseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsync.kt new file mode 100644 index 0000000..e737edd --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsync.kt @@ -0,0 +1,97 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.credits.CreditCheckParams +import com.cas_parser.api.models.credits.CreditCheckResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +interface CreditServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CreditServiceAsync + + /** + * Check your remaining API credits and usage for the current billing period. + * + * Returns: + * - Number of API calls used and remaining credits + * - Credit limit and reset date + * - List of enabled features for your plan + * + * Credits reset at the start of each billing period. + */ + fun check(): CompletableFuture = check(CreditCheckParams.none()) + + /** @see check */ + fun check( + params: CreditCheckParams = CreditCheckParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see check */ + fun check( + params: CreditCheckParams = CreditCheckParams.none() + ): CompletableFuture = check(params, RequestOptions.none()) + + /** @see check */ + fun check(requestOptions: RequestOptions): CompletableFuture = + check(CreditCheckParams.none(), requestOptions) + + /** + * A view of [CreditServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): CreditServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/credits`, but is otherwise the same as + * [CreditServiceAsync.check]. + */ + fun check(): CompletableFuture> = + check(CreditCheckParams.none()) + + /** @see check */ + fun check( + params: CreditCheckParams = CreditCheckParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see check */ + fun check( + params: CreditCheckParams = CreditCheckParams.none() + ): CompletableFuture> = + check(params, RequestOptions.none()) + + /** @see check */ + fun check( + requestOptions: RequestOptions + ): CompletableFuture> = + check(CreditCheckParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncImpl.kt new file mode 100644 index 0000000..e585dd5 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncImpl.kt @@ -0,0 +1,90 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.credits.CreditCheckParams +import com.cas_parser.api.models.credits.CreditCheckResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +class CreditServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + CreditServiceAsync { + + private val withRawResponse: CreditServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): CreditServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CreditServiceAsync = + CreditServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun check( + params: CreditCheckParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v1/credits + withRawResponse().check(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CreditServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): CreditServiceAsync.WithRawResponse = + CreditServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val checkHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun check( + params: CreditCheckParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "credits") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { checkHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsync.kt new file mode 100644 index 0000000..cfd29f4 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsync.kt @@ -0,0 +1,334 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.inboundemail.InboundEmailCreateParams +import com.cas_parser.api.models.inboundemail.InboundEmailCreateResponse +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteParams +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteResponse +import com.cas_parser.api.models.inboundemail.InboundEmailListParams +import com.cas_parser.api.models.inboundemail.InboundEmailListResponse +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveParams +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file + * upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ +interface InboundEmailServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): InboundEmailServiceAsync + + /** + * Create a dedicated inbound email address for collecting CAS statements via email forwarding. + * When an investor forwards a CAS email to this address, we verify the sender and make the file + * available to you. + * + * `callback_url` is **optional**: + * - **Set it** — we POST each parsed email to your webhook as it arrives. + * - **Omit it** — retrieve files via `GET /v4/inbound-email/{id}/files` without building a + * webhook consumer. + */ + fun create(): CompletableFuture = + create(InboundEmailCreateParams.none()) + + /** @see create */ + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see create */ + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none() + ): CompletableFuture = create(params, RequestOptions.none()) + + /** @see create */ + fun create(requestOptions: RequestOptions): CompletableFuture = + create(InboundEmailCreateParams.none(), requestOptions) + + /** Retrieve details of a specific inbound email including statistics. */ + fun retrieve(inboundEmailId: String): CompletableFuture = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none()) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + ): CompletableFuture = + retrieve(inboundEmailId, params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see retrieve */ + fun retrieve( + params: InboundEmailRetrieveParams + ): CompletableFuture = retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none(), requestOptions) + + /** + * List all inbound emails associated with your API key. Returns active and paused inbound + * emails (deleted ones are excluded). + */ + fun list(): CompletableFuture = list(InboundEmailListParams.none()) + + /** @see list */ + fun list( + params: InboundEmailListParams = InboundEmailListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see list */ + fun list( + params: InboundEmailListParams = InboundEmailListParams.none() + ): CompletableFuture = list(params, RequestOptions.none()) + + /** @see list */ + fun list(requestOptions: RequestOptions): CompletableFuture = + list(InboundEmailListParams.none(), requestOptions) + + /** + * Permanently delete an inbound email address. It will stop accepting emails. + * + * **Note:** Deletion is immediate and cannot be undone. Any emails received after deletion will + * be rejected. + */ + fun delete(inboundEmailId: String): CompletableFuture = + delete(inboundEmailId, InboundEmailDeleteParams.none()) + + /** @see delete */ + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see delete */ + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + ): CompletableFuture = + delete(inboundEmailId, params, RequestOptions.none()) + + /** @see delete */ + fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see delete */ + fun delete(params: InboundEmailDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see delete */ + fun delete( + inboundEmailId: String, + requestOptions: RequestOptions, + ): CompletableFuture = + delete(inboundEmailId, InboundEmailDeleteParams.none(), requestOptions) + + /** + * A view of [InboundEmailServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): InboundEmailServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/inbound-email`, but is otherwise the same as + * [InboundEmailServiceAsync.create]. + */ + fun create(): CompletableFuture> = + create(InboundEmailCreateParams.none()) + + /** @see create */ + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see create */ + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none() + ): CompletableFuture> = + create(params, RequestOptions.none()) + + /** @see create */ + fun create( + requestOptions: RequestOptions + ): CompletableFuture> = + create(InboundEmailCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /v4/inbound-email/{inbound_email_id}`, but is + * otherwise the same as [InboundEmailServiceAsync.retrieve]. + */ + fun retrieve( + inboundEmailId: String + ): CompletableFuture> = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none()) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + ): CompletableFuture> = + retrieve(inboundEmailId, params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see retrieve */ + fun retrieve( + params: InboundEmailRetrieveParams + ): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /v4/inbound-email`, but is otherwise the same as + * [InboundEmailServiceAsync.list]. + */ + fun list(): CompletableFuture> = + list(InboundEmailListParams.none()) + + /** @see list */ + fun list( + params: InboundEmailListParams = InboundEmailListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see list */ + fun list( + params: InboundEmailListParams = InboundEmailListParams.none() + ): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see list */ + fun list( + requestOptions: RequestOptions + ): CompletableFuture> = + list(InboundEmailListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /v4/inbound-email/{inbound_email_id}`, but is + * otherwise the same as [InboundEmailServiceAsync.delete]. + */ + fun delete( + inboundEmailId: String + ): CompletableFuture> = + delete(inboundEmailId, InboundEmailDeleteParams.none()) + + /** @see delete */ + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + delete(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see delete */ + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + ): CompletableFuture> = + delete(inboundEmailId, params, RequestOptions.none()) + + /** @see delete */ + fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see delete */ + fun delete( + params: InboundEmailDeleteParams + ): CompletableFuture> = + delete(params, RequestOptions.none()) + + /** @see delete */ + fun delete( + inboundEmailId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + delete(inboundEmailId, InboundEmailDeleteParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncImpl.kt new file mode 100644 index 0000000..9131ae8 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncImpl.kt @@ -0,0 +1,235 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.inboundemail.InboundEmailCreateParams +import com.cas_parser.api.models.inboundemail.InboundEmailCreateResponse +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteParams +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteResponse +import com.cas_parser.api.models.inboundemail.InboundEmailListParams +import com.cas_parser.api.models.inboundemail.InboundEmailListResponse +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveParams +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull + +/** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file + * upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ +class InboundEmailServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + InboundEmailServiceAsync { + + private val withRawResponse: InboundEmailServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InboundEmailServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): InboundEmailServiceAsync = + InboundEmailServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun create( + params: InboundEmailCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/inbound-email + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + override fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // get /v4/inbound-email/{inbound_email_id} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } + + override fun list( + params: InboundEmailListParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // get /v4/inbound-email + withRawResponse().list(params, requestOptions).thenApply { it.parse() } + + override fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // delete /v4/inbound-email/{inbound_email_id} + withRawResponse().delete(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InboundEmailServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): InboundEmailServiceAsync.WithRawResponse = + InboundEmailServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: InboundEmailCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("inboundEmailId", params.inboundEmailId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email", params._pathParam(0)) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: InboundEmailListParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("inboundEmailId", params.inboundEmailId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email", params._pathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsync.kt new file mode 100644 index 0000000..d2a2a85 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsync.kt @@ -0,0 +1,205 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusParams +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusResponse +import com.cas_parser.api.models.inbox.InboxConnectEmailParams +import com.cas_parser.api.models.inbox.InboxConnectEmailResponse +import com.cas_parser.api.models.inbox.InboxDisconnectEmailParams +import com.cas_parser.api.models.inbox.InboxDisconnectEmailResponse +import com.cas_parser.api.models.inbox.InboxListCasFilesParams +import com.cas_parser.api.models.inbox.InboxListCasFilesResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ +interface InboxServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): InboxServiceAsync + + /** + * Verify if an `inbox_token` is still valid and check connection status. + * + * Use this to check if the user needs to re-authenticate (e.g., if they revoked access in their + * email provider settings). + */ + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams + ): CompletableFuture = + checkConnectionStatus(params, RequestOptions.none()) + + /** @see checkConnectionStatus */ + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * Initiate OAuth flow to connect user's email inbox. + * + * Returns an `oauth_url` that you should redirect the user to. After authorization, they are + * redirected back to your `redirect_uri` with the following query parameters: + * + * **On success:** + * - `inbox_token` - Encrypted token to store client-side + * - `email` - Email address of the connected account + * - `state` - Your original state parameter (for CSRF verification) + * + * **On error:** + * - `error` - Error code (e.g., `access_denied`, `token_exchange_failed`) + * - `state` - Your original state parameter + * + * **Store the `inbox_token` client-side** and use it for all subsequent inbox API calls. + */ + fun connectEmail( + params: InboxConnectEmailParams + ): CompletableFuture = connectEmail(params, RequestOptions.none()) + + /** @see connectEmail */ + fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * Revoke email access and invalidate the token. + * + * This calls the provider's token revocation API (e.g., Google's revoke endpoint) to ensure the + * user's consent is properly removed. + * + * After calling this, the `inbox_token` becomes unusable. + */ + fun disconnectEmail( + params: InboxDisconnectEmailParams + ): CompletableFuture = + disconnectEmail(params, RequestOptions.none()) + + /** @see disconnectEmail */ + fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * Search the user's email inbox for CAS files from known senders (CAMS, KFintech, CDSL, NSDL). + * + * Files are uploaded to temporary cloud storage. **URLs expire in 24 hours.** + * + * Optionally filter by CAS provider and date range. + * + * **Billing:** 0.2 credits per request (charged regardless of success or number of files + * found). + */ + fun listCasFiles( + params: InboxListCasFilesParams + ): CompletableFuture = listCasFiles(params, RequestOptions.none()) + + /** @see listCasFiles */ + fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** A view of [InboxServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): InboxServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/inbox/status`, but is otherwise the same as + * [InboxServiceAsync.checkConnectionStatus]. + */ + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams + ): CompletableFuture> = + checkConnectionStatus(params, RequestOptions.none()) + + /** @see checkConnectionStatus */ + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /v4/inbox/connect`, but is otherwise the same as + * [InboxServiceAsync.connectEmail]. + */ + fun connectEmail( + params: InboxConnectEmailParams + ): CompletableFuture> = + connectEmail(params, RequestOptions.none()) + + /** @see connectEmail */ + fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /v4/inbox/disconnect`, but is otherwise the same as + * [InboxServiceAsync.disconnectEmail]. + */ + fun disconnectEmail( + params: InboxDisconnectEmailParams + ): CompletableFuture> = + disconnectEmail(params, RequestOptions.none()) + + /** @see disconnectEmail */ + fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /v4/inbox/cas`, but is otherwise the same as + * [InboxServiceAsync.listCasFiles]. + */ + fun listCasFiles( + params: InboxListCasFilesParams + ): CompletableFuture> = + listCasFiles(params, RequestOptions.none()) + + /** @see listCasFiles */ + fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncImpl.kt new file mode 100644 index 0000000..f3f2599 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncImpl.kt @@ -0,0 +1,223 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusParams +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusResponse +import com.cas_parser.api.models.inbox.InboxConnectEmailParams +import com.cas_parser.api.models.inbox.InboxConnectEmailResponse +import com.cas_parser.api.models.inbox.InboxDisconnectEmailParams +import com.cas_parser.api.models.inbox.InboxDisconnectEmailResponse +import com.cas_parser.api.models.inbox.InboxListCasFilesParams +import com.cas_parser.api.models.inbox.InboxListCasFilesResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ +class InboxServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + InboxServiceAsync { + + private val withRawResponse: InboxServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InboxServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): InboxServiceAsync = + InboxServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/inbox/status + withRawResponse().checkConnectionStatus(params, requestOptions).thenApply { it.parse() } + + override fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/inbox/connect + withRawResponse().connectEmail(params, requestOptions).thenApply { it.parse() } + + override fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/inbox/disconnect + withRawResponse().disconnectEmail(params, requestOptions).thenApply { it.parse() } + + override fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/inbox/cas + withRawResponse().listCasFiles(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InboxServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): InboxServiceAsync.WithRawResponse = + InboxServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val checkConnectionStatusHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "status") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { checkConnectionStatusHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val connectEmailHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "connect") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { connectEmailHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val disconnectEmailHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "disconnect") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { disconnectEmailHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listCasFilesHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "cas") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { listCasFilesHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsync.kt new file mode 100644 index 0000000..e2bfdc7 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsync.kt @@ -0,0 +1,73 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.kfintech.KfintechGenerateCasParams +import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for generating new CAS documents via email mailback (KFintech). */ +interface KfintechServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): KfintechServiceAsync + + /** + * Generate CAS via KFintech mailback. The CAS PDF will be sent to the investor's email. + * + * This is an async operation - the investor receives the CAS via email within a few minutes. + * For instant CAS retrieval, use CDSL Fetch (`/v4/cdsl/fetch`). + */ + fun generateCas( + params: KfintechGenerateCasParams + ): CompletableFuture = generateCas(params, RequestOptions.none()) + + /** @see generateCas */ + fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * A view of [KfintechServiceAsync] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): KfintechServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/kfintech/generate`, but is otherwise the same + * as [KfintechServiceAsync.generateCas]. + */ + fun generateCas( + params: KfintechGenerateCasParams + ): CompletableFuture> = + generateCas(params, RequestOptions.none()) + + /** @see generateCas */ + fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncImpl.kt new file mode 100644 index 0000000..df04e64 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.kfintech.KfintechGenerateCasParams +import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for generating new CAS documents via email mailback (KFintech). */ +class KfintechServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + KfintechServiceAsync { + + private val withRawResponse: KfintechServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): KfintechServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): KfintechServiceAsync = + KfintechServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/kfintech/generate + withRawResponse().generateCas(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + KfintechServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): KfintechServiceAsync.WithRawResponse = + KfintechServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val generateCasHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "kfintech", "generate") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { generateCasHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsync.kt new file mode 100644 index 0000000..c74e947 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsync.kt @@ -0,0 +1,143 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.logs.LogCreateParams +import com.cas_parser.api.models.logs.LogCreateResponse +import com.cas_parser.api.models.logs.LogGetSummaryParams +import com.cas_parser.api.models.logs.LogGetSummaryResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +interface LogServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): LogServiceAsync + + /** + * Retrieve detailed API usage logs for your account. + * + * Returns a list of API calls with timestamps, features used, status codes, and credits + * consumed. Useful for monitoring usage patterns and debugging. + * + * **Legacy path:** `/logs` (still supported) + */ + fun create(): CompletableFuture = create(LogCreateParams.none()) + + /** @see create */ + fun create( + params: LogCreateParams = LogCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see create */ + fun create( + params: LogCreateParams = LogCreateParams.none() + ): CompletableFuture = create(params, RequestOptions.none()) + + /** @see create */ + fun create(requestOptions: RequestOptions): CompletableFuture = + create(LogCreateParams.none(), requestOptions) + + /** + * Get aggregated usage statistics grouped by feature. + * + * Useful for understanding which API features are being used most and tracking usage trends. + * + * **Legacy path:** `/logs/summary` (still supported) + */ + fun getSummary(): CompletableFuture = + getSummary(LogGetSummaryParams.none()) + + /** @see getSummary */ + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see getSummary */ + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none() + ): CompletableFuture = getSummary(params, RequestOptions.none()) + + /** @see getSummary */ + fun getSummary(requestOptions: RequestOptions): CompletableFuture = + getSummary(LogGetSummaryParams.none(), requestOptions) + + /** A view of [LogServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): LogServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/usage`, but is otherwise the same as + * [LogServiceAsync.create]. + */ + fun create(): CompletableFuture> = + create(LogCreateParams.none()) + + /** @see create */ + fun create( + params: LogCreateParams = LogCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see create */ + fun create( + params: LogCreateParams = LogCreateParams.none() + ): CompletableFuture> = + create(params, RequestOptions.none()) + + /** @see create */ + fun create( + requestOptions: RequestOptions + ): CompletableFuture> = + create(LogCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /v1/usage/summary`, but is otherwise the same as + * [LogServiceAsync.getSummary]. + */ + fun getSummary(): CompletableFuture> = + getSummary(LogGetSummaryParams.none()) + + /** @see getSummary */ + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see getSummary */ + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none() + ): CompletableFuture> = + getSummary(params, RequestOptions.none()) + + /** @see getSummary */ + fun getSummary( + requestOptions: RequestOptions + ): CompletableFuture> = + getSummary(LogGetSummaryParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsyncImpl.kt new file mode 100644 index 0000000..f2cc6a7 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsyncImpl.kt @@ -0,0 +1,130 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.logs.LogCreateParams +import com.cas_parser.api.models.logs.LogCreateResponse +import com.cas_parser.api.models.logs.LogGetSummaryParams +import com.cas_parser.api.models.logs.LogGetSummaryResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +class LogServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + LogServiceAsync { + + private val withRawResponse: LogServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): LogServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): LogServiceAsync = + LogServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun create( + params: LogCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v1/usage + withRawResponse().create(params, requestOptions).thenApply { it.parse() } + + override fun getSummary( + params: LogGetSummaryParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v1/usage/summary + withRawResponse().getSummary(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + LogServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): LogServiceAsync.WithRawResponse = + LogServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: LogCreateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "usage") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val getSummaryHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun getSummary( + params: LogGetSummaryParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "usage", "summary") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { getSummaryHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsync.kt new file mode 100644 index 0000000..cb33723 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsync.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.nsdl.NsdlParseParams +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface NsdlServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): NsdlServiceAsync + + /** + * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and + * returns data in a unified format. Use this endpoint when you know the PDF is from NSDL. + */ + fun parse(): CompletableFuture = parse(NsdlParseParams.none()) + + /** @see parse */ + fun parse( + params: NsdlParseParams = NsdlParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see parse */ + fun parse( + params: NsdlParseParams = NsdlParseParams.none() + ): CompletableFuture = parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse(requestOptions: RequestOptions): CompletableFuture = + parse(NsdlParseParams.none(), requestOptions) + + /** A view of [NsdlServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): NsdlServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/nsdl/parse`, but is otherwise the same as + * [NsdlServiceAsync.parse]. + */ + fun parse(): CompletableFuture> = + parse(NsdlParseParams.none()) + + /** @see parse */ + fun parse( + params: NsdlParseParams = NsdlParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see parse */ + fun parse( + params: NsdlParseParams = NsdlParseParams.none() + ): CompletableFuture> = + parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse( + requestOptions: RequestOptions + ): CompletableFuture> = + parse(NsdlParseParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncImpl.kt new file mode 100644 index 0000000..ee79b39 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.nsdl.NsdlParseParams +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class NsdlServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + NsdlServiceAsync { + + private val withRawResponse: NsdlServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): NsdlServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): NsdlServiceAsync = + NsdlServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parse( + params: NsdlParseParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/nsdl/parse + withRawResponse().parse(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + NsdlServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): NsdlServiceAsync.WithRawResponse = + NsdlServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parse( + params: NsdlParseParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "nsdl", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { parseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsync.kt new file mode 100644 index 0000000..af7ed57 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsync.kt @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.smart.SmartParseCasPdfParams +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface SmartServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): SmartServiceAsync + + /** + * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or + * CAMS/KFintech and returns data in a unified format. It auto-detects the CAS type and + * transforms the data into a consistent structure regardless of the source. + */ + fun parseCasPdf(): CompletableFuture = + parseCasPdf(SmartParseCasPdfParams.none()) + + /** @see parseCasPdf */ + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see parseCasPdf */ + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none() + ): CompletableFuture = parseCasPdf(params, RequestOptions.none()) + + /** @see parseCasPdf */ + fun parseCasPdf(requestOptions: RequestOptions): CompletableFuture = + parseCasPdf(SmartParseCasPdfParams.none(), requestOptions) + + /** A view of [SmartServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): SmartServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/smart/parse`, but is otherwise the same as + * [SmartServiceAsync.parseCasPdf]. + */ + fun parseCasPdf(): CompletableFuture> = + parseCasPdf(SmartParseCasPdfParams.none()) + + /** @see parseCasPdf */ + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see parseCasPdf */ + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none() + ): CompletableFuture> = + parseCasPdf(params, RequestOptions.none()) + + /** @see parseCasPdf */ + fun parseCasPdf( + requestOptions: RequestOptions + ): CompletableFuture> = + parseCasPdf(SmartParseCasPdfParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncImpl.kt new file mode 100644 index 0000000..02f8c0a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.smart.SmartParseCasPdfParams +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class SmartServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + SmartServiceAsync { + + private val withRawResponse: SmartServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): SmartServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): SmartServiceAsync = + SmartServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parseCasPdf( + params: SmartParseCasPdfParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/smart/parse + withRawResponse().parseCasPdf(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + SmartServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): SmartServiceAsync.WithRawResponse = + SmartServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseCasPdfHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parseCasPdf( + params: SmartParseCasPdfParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "smart", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { parseCasPdfHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsync.kt new file mode 100644 index 0000000..fa58dfc --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsync.kt @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyParams +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +interface VerifyTokenServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): VerifyTokenServiceAsync + + /** Verify an access token and check if it's still valid. Useful for debugging token issues. */ + fun verify(): CompletableFuture = + verify(VerifyTokenVerifyParams.none()) + + /** @see verify */ + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** @see verify */ + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none() + ): CompletableFuture = verify(params, RequestOptions.none()) + + /** @see verify */ + fun verify(requestOptions: RequestOptions): CompletableFuture = + verify(VerifyTokenVerifyParams.none(), requestOptions) + + /** + * A view of [VerifyTokenServiceAsync] that provides access to raw HTTP responses for each + * method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): VerifyTokenServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/token/verify`, but is otherwise the same as + * [VerifyTokenServiceAsync.verify]. + */ + fun verify(): CompletableFuture> = + verify(VerifyTokenVerifyParams.none()) + + /** @see verify */ + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see verify */ + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none() + ): CompletableFuture> = + verify(params, RequestOptions.none()) + + /** @see verify */ + fun verify( + requestOptions: RequestOptions + ): CompletableFuture> = + verify(VerifyTokenVerifyParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncImpl.kt new file mode 100644 index 0000000..cf826af --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncImpl.kt @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyParams +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +class VerifyTokenServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + VerifyTokenServiceAsync { + + private val withRawResponse: VerifyTokenServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): VerifyTokenServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): VerifyTokenServiceAsync = + VerifyTokenServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun verify( + params: VerifyTokenVerifyParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v1/token/verify + withRawResponse().verify(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + VerifyTokenServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): VerifyTokenServiceAsync.WithRawResponse = + VerifyTokenServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val verifyHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun verify( + params: VerifyTokenVerifyParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "token", "verify") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { verifyHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsync.kt new file mode 100644 index 0000000..a60287d --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsync.kt @@ -0,0 +1,139 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async.cdsl + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpResponse +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer + +/** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ +interface FetchServiceAsync { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): FetchServiceAsync + + /** + * **Step 1 of 2**: Request OTP for CDSL CAS fetch. + * + * This endpoint: + * 1. Solves reCAPTCHA automatically (~15-20 seconds) + * 2. Submits login credentials to CDSL portal + * 3. Triggers OTP to user's registered mobile number + * + * After user receives OTP, call `/v4/cdsl/fetch/{session_id}/verify` to complete. + */ + fun requestOtp(params: FetchRequestOtpParams): CompletableFuture = + requestOtp(params, RequestOptions.none()) + + /** @see requestOtp */ + fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * **Step 2 of 2**: Verify OTP and retrieve CDSL CAS files. + * + * After successful verification, CAS PDFs are fetched from CDSL portal, uploaded to cloud + * storage, and returned as direct download URLs. + */ + fun verifyOtp( + sessionId: String, + params: FetchVerifyOtpParams, + ): CompletableFuture = + verifyOtp(sessionId, params, RequestOptions.none()) + + /** @see verifyOtp */ + fun verifyOtp( + sessionId: String, + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + verifyOtp(params.toBuilder().sessionId(sessionId).build(), requestOptions) + + /** @see verifyOtp */ + fun verifyOtp(params: FetchVerifyOtpParams): CompletableFuture = + verifyOtp(params, RequestOptions.none()) + + /** @see verifyOtp */ + fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** A view of [FetchServiceAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): FetchServiceAsync.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/cdsl/fetch`, but is otherwise the same as + * [FetchServiceAsync.requestOtp]. + */ + fun requestOtp( + params: FetchRequestOtpParams + ): CompletableFuture> = + requestOtp(params, RequestOptions.none()) + + /** @see requestOtp */ + fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `post /v4/cdsl/fetch/{session_id}/verify`, but is + * otherwise the same as [FetchServiceAsync.verifyOtp]. + */ + fun verifyOtp( + sessionId: String, + params: FetchVerifyOtpParams, + ): CompletableFuture> = + verifyOtp(sessionId, params, RequestOptions.none()) + + /** @see verifyOtp */ + fun verifyOtp( + sessionId: String, + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + verifyOtp(params.toBuilder().sessionId(sessionId).build(), requestOptions) + + /** @see verifyOtp */ + fun verifyOtp( + params: FetchVerifyOtpParams + ): CompletableFuture> = + verifyOtp(params, RequestOptions.none()) + + /** @see verifyOtp */ + fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncImpl.kt new file mode 100644 index 0000000..9e9453a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncImpl.kt @@ -0,0 +1,135 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async.cdsl + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepareAsync +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpResponse +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpResponse +import java.util.concurrent.CompletableFuture +import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull + +/** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ +class FetchServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) : + FetchServiceAsync { + + private val withRawResponse: FetchServiceAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): FetchServiceAsync.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): FetchServiceAsync = + FetchServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/cdsl/fetch + withRawResponse().requestOtp(params, requestOptions).thenApply { it.parse() } + + override fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /v4/cdsl/fetch/{session_id}/verify + withRawResponse().verifyOtp(params, requestOptions).thenApply { it.parse() } + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FetchServiceAsync.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): FetchServiceAsync.WithRawResponse = + FetchServiceAsyncImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val requestOtpHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cdsl", "fetch") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { requestOtpHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val verifyOtpHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("sessionId", params.sessionId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cdsl", "fetch", params._pathParam(0), "verify") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { verifyOtpHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenService.kt new file mode 100644 index 0000000..cef38eb --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenService.kt @@ -0,0 +1,103 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.accesstoken.AccessTokenCreateParams +import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +interface AccessTokenService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): AccessTokenService + + /** + * Generate a short-lived access token from your API key. + * + * **Use this endpoint from your backend** to create tokens that can be safely passed to + * frontend/SDK. + * + * **Legacy path:** `/v1/access-token` (still supported) + * + * Access tokens: + * - Are prefixed with `at_` for easy identification + * - Valid for up to 60 minutes + * - Can be used in place of API keys on all v4 endpoints + * - Cannot be used to generate other access tokens + */ + fun create(): AccessTokenCreateResponse = create(AccessTokenCreateParams.none()) + + /** @see create */ + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): AccessTokenCreateResponse + + /** @see create */ + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none() + ): AccessTokenCreateResponse = create(params, RequestOptions.none()) + + /** @see create */ + fun create(requestOptions: RequestOptions): AccessTokenCreateResponse = + create(AccessTokenCreateParams.none(), requestOptions) + + /** + * A view of [AccessTokenService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): AccessTokenService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/token`, but is otherwise the same as + * [AccessTokenService.create]. + */ + @MustBeClosed + fun create(): HttpResponseFor = + create(AccessTokenCreateParams.none()) + + /** @see create */ + @MustBeClosed + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see create */ + @MustBeClosed + fun create( + params: AccessTokenCreateParams = AccessTokenCreateParams.none() + ): HttpResponseFor = create(params, RequestOptions.none()) + + /** @see create */ + @MustBeClosed + fun create(requestOptions: RequestOptions): HttpResponseFor = + create(AccessTokenCreateParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceImpl.kt new file mode 100644 index 0000000..cc2c4c0 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.accesstoken.AccessTokenCreateParams +import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +class AccessTokenServiceImpl internal constructor(private val clientOptions: ClientOptions) : + AccessTokenService { + + private val withRawResponse: AccessTokenService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): AccessTokenService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): AccessTokenService = + AccessTokenServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun create( + params: AccessTokenCreateParams, + requestOptions: RequestOptions, + ): AccessTokenCreateResponse = + // post /v1/token + withRawResponse().create(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + AccessTokenService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): AccessTokenService.WithRawResponse = + AccessTokenServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: AccessTokenCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "token") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechService.kt new file mode 100644 index 0000000..c8841a5 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechService.kt @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.CamsKfintechParseParams +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface CamsKfintechService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CamsKfintechService + + /** + * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF + * files and returns data in a unified format. Use this endpoint when you know the PDF is from + * CAMS or KFintech. + */ + fun parse(): UnifiedResponse = parse(CamsKfintechParseParams.none()) + + /** @see parse */ + fun parse( + params: CamsKfintechParseParams = CamsKfintechParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UnifiedResponse + + /** @see parse */ + fun parse(params: CamsKfintechParseParams = CamsKfintechParseParams.none()): UnifiedResponse = + parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse(requestOptions: RequestOptions): UnifiedResponse = + parse(CamsKfintechParseParams.none(), requestOptions) + + /** + * A view of [CamsKfintechService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): CamsKfintechService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/cams_kfintech/parse`, but is otherwise the same + * as [CamsKfintechService.parse]. + */ + @MustBeClosed + fun parse(): HttpResponseFor = parse(CamsKfintechParseParams.none()) + + /** @see parse */ + @MustBeClosed + fun parse( + params: CamsKfintechParseParams = CamsKfintechParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see parse */ + @MustBeClosed + fun parse( + params: CamsKfintechParseParams = CamsKfintechParseParams.none() + ): HttpResponseFor = parse(params, RequestOptions.none()) + + /** @see parse */ + @MustBeClosed + fun parse(requestOptions: RequestOptions): HttpResponseFor = + parse(CamsKfintechParseParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceImpl.kt new file mode 100644 index 0000000..7796a20 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceImpl.kt @@ -0,0 +1,83 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.camskfintech.CamsKfintechParseParams +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class CamsKfintechServiceImpl internal constructor(private val clientOptions: ClientOptions) : + CamsKfintechService { + + private val withRawResponse: CamsKfintechService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): CamsKfintechService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CamsKfintechService = + CamsKfintechServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parse( + params: CamsKfintechParseParams, + requestOptions: RequestOptions, + ): UnifiedResponse = + // post /v4/cams_kfintech/parse + withRawResponse().parse(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CamsKfintechService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): CamsKfintechService.WithRawResponse = + CamsKfintechServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parse( + params: CamsKfintechParseParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cams_kfintech", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { parseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslService.kt new file mode 100644 index 0000000..7514c91 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslService.kt @@ -0,0 +1,96 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.cdsl.CdslParsePdfParams +import com.cas_parser.api.services.blocking.cdsl.FetchService +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface CdslService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CdslService + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ + fun fetch(): FetchService + + /** + * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and + * returns data in a unified format. Use this endpoint when you know the PDF is from CDSL. + */ + fun parsePdf(): UnifiedResponse = parsePdf(CdslParsePdfParams.none()) + + /** @see parsePdf */ + fun parsePdf( + params: CdslParsePdfParams = CdslParsePdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UnifiedResponse + + /** @see parsePdf */ + fun parsePdf(params: CdslParsePdfParams = CdslParsePdfParams.none()): UnifiedResponse = + parsePdf(params, RequestOptions.none()) + + /** @see parsePdf */ + fun parsePdf(requestOptions: RequestOptions): UnifiedResponse = + parsePdf(CdslParsePdfParams.none(), requestOptions) + + /** A view of [CdslService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CdslService.WithRawResponse + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via + * OTP authentication. + */ + fun fetch(): FetchService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/cdsl/parse`, but is otherwise the same as + * [CdslService.parsePdf]. + */ + @MustBeClosed + fun parsePdf(): HttpResponseFor = parsePdf(CdslParsePdfParams.none()) + + /** @see parsePdf */ + @MustBeClosed + fun parsePdf( + params: CdslParsePdfParams = CdslParsePdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see parsePdf */ + @MustBeClosed + fun parsePdf( + params: CdslParsePdfParams = CdslParsePdfParams.none() + ): HttpResponseFor = parsePdf(params, RequestOptions.none()) + + /** @see parsePdf */ + @MustBeClosed + fun parsePdf(requestOptions: RequestOptions): HttpResponseFor = + parsePdf(CdslParsePdfParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslServiceImpl.kt new file mode 100644 index 0000000..7bbc6a3 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslServiceImpl.kt @@ -0,0 +1,102 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.cdsl.CdslParsePdfParams +import com.cas_parser.api.services.blocking.cdsl.FetchService +import com.cas_parser.api.services.blocking.cdsl.FetchServiceImpl +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class CdslServiceImpl internal constructor(private val clientOptions: ClientOptions) : CdslService { + + private val withRawResponse: CdslService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val fetch: FetchService by lazy { FetchServiceImpl(clientOptions) } + + override fun withRawResponse(): CdslService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CdslService = + CdslServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ + override fun fetch(): FetchService = fetch + + override fun parsePdf( + params: CdslParsePdfParams, + requestOptions: RequestOptions, + ): UnifiedResponse = + // post /v4/cdsl/parse + withRawResponse().parsePdf(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CdslService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + private val fetch: FetchService.WithRawResponse by lazy { + FetchServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun withOptions( + modifier: Consumer + ): CdslService.WithRawResponse = + CdslServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + /** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via + * OTP authentication. + */ + override fun fetch(): FetchService.WithRawResponse = fetch + + private val parsePdfHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parsePdf( + params: CdslParsePdfParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cdsl", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { parsePdfHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteService.kt new file mode 100644 index 0000000..bb49da3 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteService.kt @@ -0,0 +1,116 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.contractnote.ContractNoteParseParams +import com.cas_parser.api.models.contractnote.ContractNoteParseResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ +interface ContractNoteService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): ContractNoteService + + /** + * This endpoint parses Contract Note PDF files from various brokers including Zerodha, Groww, + * Upstox, ICICI Securities, and others. + * + * **What is a Contract Note?** A contract note is a legal document that provides details of all + * trades executed by an investor. It includes: + * - Trade details with timestamps, quantities, and prices + * - Brokerage and charges breakdown + * - Settlement information + * - Regulatory compliance details + * + * **Supported Brokers:** + * - Zerodha Broking Limited + * - Groww Invest Tech Private Limited + * - Upstox (RKSV Securities) + * - ICICI Securities Limited + * - Auto-detection for unknown brokers + * + * **Key Features:** + * - **Auto-detection**: Automatically identifies broker type from PDF content + * - **Comprehensive parsing**: Extracts equity transactions, derivatives transactions, detailed + * trades, and charges + * - **Flexible input**: Accepts both file upload and URL-based PDF input + * - **Password protection**: Supports password-protected PDFs + * + * The API returns structured data including contract note information, client details, + * transaction summaries, and detailed trade-by-trade breakdowns. + */ + fun parse(): ContractNoteParseResponse = parse(ContractNoteParseParams.none()) + + /** @see parse */ + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): ContractNoteParseResponse + + /** @see parse */ + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none() + ): ContractNoteParseResponse = parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse(requestOptions: RequestOptions): ContractNoteParseResponse = + parse(ContractNoteParseParams.none(), requestOptions) + + /** + * A view of [ContractNoteService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): ContractNoteService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/contract_note/parse`, but is otherwise the same + * as [ContractNoteService.parse]. + */ + @MustBeClosed + fun parse(): HttpResponseFor = + parse(ContractNoteParseParams.none()) + + /** @see parse */ + @MustBeClosed + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see parse */ + @MustBeClosed + fun parse( + params: ContractNoteParseParams = ContractNoteParseParams.none() + ): HttpResponseFor = parse(params, RequestOptions.none()) + + /** @see parse */ + @MustBeClosed + fun parse(requestOptions: RequestOptions): HttpResponseFor = + parse(ContractNoteParseParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceImpl.kt new file mode 100644 index 0000000..7d5555f --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceImpl.kt @@ -0,0 +1,86 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.contractnote.ContractNoteParseParams +import com.cas_parser.api.models.contractnote.ContractNoteParseResponse +import java.util.function.Consumer + +/** + * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww, + * Upstox, ICICI etc. + */ +class ContractNoteServiceImpl internal constructor(private val clientOptions: ClientOptions) : + ContractNoteService { + + private val withRawResponse: ContractNoteService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): ContractNoteService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): ContractNoteService = + ContractNoteServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parse( + params: ContractNoteParseParams, + requestOptions: RequestOptions, + ): ContractNoteParseResponse = + // post /v4/contract_note/parse + withRawResponse().parse(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + ContractNoteService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): ContractNoteService.WithRawResponse = + ContractNoteServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parse( + params: ContractNoteParseParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "contract_note", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { parseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditService.kt new file mode 100644 index 0000000..d4459c9 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditService.kt @@ -0,0 +1,92 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.credits.CreditCheckParams +import com.cas_parser.api.models.credits.CreditCheckResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +interface CreditService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CreditService + + /** + * Check your remaining API credits and usage for the current billing period. + * + * Returns: + * - Number of API calls used and remaining credits + * - Credit limit and reset date + * - List of enabled features for your plan + * + * Credits reset at the start of each billing period. + */ + fun check(): CreditCheckResponse = check(CreditCheckParams.none()) + + /** @see check */ + fun check( + params: CreditCheckParams = CreditCheckParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CreditCheckResponse + + /** @see check */ + fun check(params: CreditCheckParams = CreditCheckParams.none()): CreditCheckResponse = + check(params, RequestOptions.none()) + + /** @see check */ + fun check(requestOptions: RequestOptions): CreditCheckResponse = + check(CreditCheckParams.none(), requestOptions) + + /** A view of [CreditService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): CreditService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/credits`, but is otherwise the same as + * [CreditService.check]. + */ + @MustBeClosed + fun check(): HttpResponseFor = check(CreditCheckParams.none()) + + /** @see check */ + @MustBeClosed + fun check( + params: CreditCheckParams = CreditCheckParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see check */ + @MustBeClosed + fun check( + params: CreditCheckParams = CreditCheckParams.none() + ): HttpResponseFor = check(params, RequestOptions.none()) + + /** @see check */ + @MustBeClosed + fun check(requestOptions: RequestOptions): HttpResponseFor = + check(CreditCheckParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditServiceImpl.kt new file mode 100644 index 0000000..6425e16 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditServiceImpl.kt @@ -0,0 +1,86 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.credits.CreditCheckParams +import com.cas_parser.api.models.credits.CreditCheckResponse +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +class CreditServiceImpl internal constructor(private val clientOptions: ClientOptions) : + CreditService { + + private val withRawResponse: CreditService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): CreditService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): CreditService = + CreditServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun check( + params: CreditCheckParams, + requestOptions: RequestOptions, + ): CreditCheckResponse = + // post /v1/credits + withRawResponse().check(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + CreditService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): CreditService.WithRawResponse = + CreditServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val checkHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun check( + params: CreditCheckParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "credits") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { checkHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailService.kt new file mode 100644 index 0000000..ab31236 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailService.kt @@ -0,0 +1,332 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.inboundemail.InboundEmailCreateParams +import com.cas_parser.api.models.inboundemail.InboundEmailCreateResponse +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteParams +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteResponse +import com.cas_parser.api.models.inboundemail.InboundEmailListParams +import com.cas_parser.api.models.inboundemail.InboundEmailListResponse +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveParams +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file + * upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ +interface InboundEmailService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): InboundEmailService + + /** + * Create a dedicated inbound email address for collecting CAS statements via email forwarding. + * When an investor forwards a CAS email to this address, we verify the sender and make the file + * available to you. + * + * `callback_url` is **optional**: + * - **Set it** — we POST each parsed email to your webhook as it arrives. + * - **Omit it** — retrieve files via `GET /v4/inbound-email/{id}/files` without building a + * webhook consumer. + */ + fun create(): InboundEmailCreateResponse = create(InboundEmailCreateParams.none()) + + /** @see create */ + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): InboundEmailCreateResponse + + /** @see create */ + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none() + ): InboundEmailCreateResponse = create(params, RequestOptions.none()) + + /** @see create */ + fun create(requestOptions: RequestOptions): InboundEmailCreateResponse = + create(InboundEmailCreateParams.none(), requestOptions) + + /** Retrieve details of a specific inbound email including statistics. */ + fun retrieve(inboundEmailId: String): InboundEmailRetrieveResponse = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none()) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): InboundEmailRetrieveResponse = + retrieve(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + ): InboundEmailRetrieveResponse = retrieve(inboundEmailId, params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InboundEmailRetrieveResponse + + /** @see retrieve */ + fun retrieve(params: InboundEmailRetrieveParams): InboundEmailRetrieveResponse = + retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + inboundEmailId: String, + requestOptions: RequestOptions, + ): InboundEmailRetrieveResponse = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none(), requestOptions) + + /** + * List all inbound emails associated with your API key. Returns active and paused inbound + * emails (deleted ones are excluded). + */ + fun list(): InboundEmailListResponse = list(InboundEmailListParams.none()) + + /** @see list */ + fun list( + params: InboundEmailListParams = InboundEmailListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): InboundEmailListResponse + + /** @see list */ + fun list( + params: InboundEmailListParams = InboundEmailListParams.none() + ): InboundEmailListResponse = list(params, RequestOptions.none()) + + /** @see list */ + fun list(requestOptions: RequestOptions): InboundEmailListResponse = + list(InboundEmailListParams.none(), requestOptions) + + /** + * Permanently delete an inbound email address. It will stop accepting emails. + * + * **Note:** Deletion is immediate and cannot be undone. Any emails received after deletion will + * be rejected. + */ + fun delete(inboundEmailId: String): InboundEmailDeleteResponse = + delete(inboundEmailId, InboundEmailDeleteParams.none()) + + /** @see delete */ + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): InboundEmailDeleteResponse = + delete(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see delete */ + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + ): InboundEmailDeleteResponse = delete(inboundEmailId, params, RequestOptions.none()) + + /** @see delete */ + fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InboundEmailDeleteResponse + + /** @see delete */ + fun delete(params: InboundEmailDeleteParams): InboundEmailDeleteResponse = + delete(params, RequestOptions.none()) + + /** @see delete */ + fun delete(inboundEmailId: String, requestOptions: RequestOptions): InboundEmailDeleteResponse = + delete(inboundEmailId, InboundEmailDeleteParams.none(), requestOptions) + + /** + * A view of [InboundEmailService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): InboundEmailService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/inbound-email`, but is otherwise the same as + * [InboundEmailService.create]. + */ + @MustBeClosed + fun create(): HttpResponseFor = + create(InboundEmailCreateParams.none()) + + /** @see create */ + @MustBeClosed + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see create */ + @MustBeClosed + fun create( + params: InboundEmailCreateParams = InboundEmailCreateParams.none() + ): HttpResponseFor = create(params, RequestOptions.none()) + + /** @see create */ + @MustBeClosed + fun create(requestOptions: RequestOptions): HttpResponseFor = + create(InboundEmailCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /v4/inbound-email/{inbound_email_id}`, but is + * otherwise the same as [InboundEmailService.retrieve]. + */ + @MustBeClosed + fun retrieve(inboundEmailId: String): HttpResponseFor = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none()) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + inboundEmailId: String, + params: InboundEmailRetrieveParams = InboundEmailRetrieveParams.none(), + ): HttpResponseFor = + retrieve(inboundEmailId, params, RequestOptions.none()) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: InboundEmailRetrieveParams + ): HttpResponseFor = retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + inboundEmailId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + retrieve(inboundEmailId, InboundEmailRetrieveParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /v4/inbound-email`, but is otherwise the same as + * [InboundEmailService.list]. + */ + @MustBeClosed + fun list(): HttpResponseFor = list(InboundEmailListParams.none()) + + /** @see list */ + @MustBeClosed + fun list( + params: InboundEmailListParams = InboundEmailListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see list */ + @MustBeClosed + fun list( + params: InboundEmailListParams = InboundEmailListParams.none() + ): HttpResponseFor = list(params, RequestOptions.none()) + + /** @see list */ + @MustBeClosed + fun list(requestOptions: RequestOptions): HttpResponseFor = + list(InboundEmailListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /v4/inbound-email/{inbound_email_id}`, but is + * otherwise the same as [InboundEmailService.delete]. + */ + @MustBeClosed + fun delete(inboundEmailId: String): HttpResponseFor = + delete(inboundEmailId, InboundEmailDeleteParams.none()) + + /** @see delete */ + @MustBeClosed + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + delete(params.toBuilder().inboundEmailId(inboundEmailId).build(), requestOptions) + + /** @see delete */ + @MustBeClosed + fun delete( + inboundEmailId: String, + params: InboundEmailDeleteParams = InboundEmailDeleteParams.none(), + ): HttpResponseFor = + delete(inboundEmailId, params, RequestOptions.none()) + + /** @see delete */ + @MustBeClosed + fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see delete */ + @MustBeClosed + fun delete(params: InboundEmailDeleteParams): HttpResponseFor = + delete(params, RequestOptions.none()) + + /** @see delete */ + @MustBeClosed + fun delete( + inboundEmailId: String, + requestOptions: RequestOptions, + ): HttpResponseFor = + delete(inboundEmailId, InboundEmailDeleteParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceImpl.kt new file mode 100644 index 0000000..704f560 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceImpl.kt @@ -0,0 +1,222 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.inboundemail.InboundEmailCreateParams +import com.cas_parser.api.models.inboundemail.InboundEmailCreateResponse +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteParams +import com.cas_parser.api.models.inboundemail.InboundEmailDeleteResponse +import com.cas_parser.api.models.inboundemail.InboundEmailListParams +import com.cas_parser.api.models.inboundemail.InboundEmailListResponse +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveParams +import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveResponse +import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull + +/** + * Create dedicated inbound email addresses for investors to forward their CAS statements. + * + * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file + * upload. + * + * **How it works:** + * 1. Call `POST /v4/inbound-email` to create a unique inbound email address + * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in" + * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook + * 4. Your webhook receives email metadata + attachment download URLs + * + * **Sender Validation:** + * - Only emails from verified CAS authorities are processed: + * - CDSL: `eCAS@cdslstatement.com` + * - NSDL: `NSDL-CAS@nsdl.co.in` + * - CAMS: `donotreply@camsonline.com` + * - KFintech: `samfS@kfintech.com` + * - Emails failing SPF/DKIM/DMARC are rejected + * - Forwarded emails must contain the original sender in headers + * + * **Billing:** 0.2 credits per successfully processed valid email + */ +class InboundEmailServiceImpl internal constructor(private val clientOptions: ClientOptions) : + InboundEmailService { + + private val withRawResponse: InboundEmailService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InboundEmailService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): InboundEmailService = + InboundEmailServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun create( + params: InboundEmailCreateParams, + requestOptions: RequestOptions, + ): InboundEmailCreateResponse = + // post /v4/inbound-email + withRawResponse().create(params, requestOptions).parse() + + override fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions, + ): InboundEmailRetrieveResponse = + // get /v4/inbound-email/{inbound_email_id} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun list( + params: InboundEmailListParams, + requestOptions: RequestOptions, + ): InboundEmailListResponse = + // get /v4/inbound-email + withRawResponse().list(params, requestOptions).parse() + + override fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions, + ): InboundEmailDeleteResponse = + // delete /v4/inbound-email/{inbound_email_id} + withRawResponse().delete(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InboundEmailService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): InboundEmailService.WithRawResponse = + InboundEmailServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: InboundEmailCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: InboundEmailRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("inboundEmailId", params.inboundEmailId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email", params._pathParam(0)) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun list( + params: InboundEmailListParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val deleteHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun delete( + params: InboundEmailDeleteParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("inboundEmailId", params.inboundEmailId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbound-email", params._pathParam(0)) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { deleteHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxService.kt new file mode 100644 index 0000000..051365a --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxService.kt @@ -0,0 +1,204 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusParams +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusResponse +import com.cas_parser.api.models.inbox.InboxConnectEmailParams +import com.cas_parser.api.models.inbox.InboxConnectEmailResponse +import com.cas_parser.api.models.inbox.InboxDisconnectEmailParams +import com.cas_parser.api.models.inbox.InboxDisconnectEmailResponse +import com.cas_parser.api.models.inbox.InboxListCasFilesParams +import com.cas_parser.api.models.inbox.InboxListCasFilesResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ +interface InboxService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): InboxService + + /** + * Verify if an `inbox_token` is still valid and check connection status. + * + * Use this to check if the user needs to re-authenticate (e.g., if they revoked access in their + * email provider settings). + */ + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams + ): InboxCheckConnectionStatusResponse = checkConnectionStatus(params, RequestOptions.none()) + + /** @see checkConnectionStatus */ + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InboxCheckConnectionStatusResponse + + /** + * Initiate OAuth flow to connect user's email inbox. + * + * Returns an `oauth_url` that you should redirect the user to. After authorization, they are + * redirected back to your `redirect_uri` with the following query parameters: + * + * **On success:** + * - `inbox_token` - Encrypted token to store client-side + * - `email` - Email address of the connected account + * - `state` - Your original state parameter (for CSRF verification) + * + * **On error:** + * - `error` - Error code (e.g., `access_denied`, `token_exchange_failed`) + * - `state` - Your original state parameter + * + * **Store the `inbox_token` client-side** and use it for all subsequent inbox API calls. + */ + fun connectEmail(params: InboxConnectEmailParams): InboxConnectEmailResponse = + connectEmail(params, RequestOptions.none()) + + /** @see connectEmail */ + fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InboxConnectEmailResponse + + /** + * Revoke email access and invalidate the token. + * + * This calls the provider's token revocation API (e.g., Google's revoke endpoint) to ensure the + * user's consent is properly removed. + * + * After calling this, the `inbox_token` becomes unusable. + */ + fun disconnectEmail(params: InboxDisconnectEmailParams): InboxDisconnectEmailResponse = + disconnectEmail(params, RequestOptions.none()) + + /** @see disconnectEmail */ + fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InboxDisconnectEmailResponse + + /** + * Search the user's email inbox for CAS files from known senders (CAMS, KFintech, CDSL, NSDL). + * + * Files are uploaded to temporary cloud storage. **URLs expire in 24 hours.** + * + * Optionally filter by CAS provider and date range. + * + * **Billing:** 0.2 credits per request (charged regardless of success or number of files + * found). + */ + fun listCasFiles(params: InboxListCasFilesParams): InboxListCasFilesResponse = + listCasFiles(params, RequestOptions.none()) + + /** @see listCasFiles */ + fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): InboxListCasFilesResponse + + /** A view of [InboxService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): InboxService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/inbox/status`, but is otherwise the same as + * [InboxService.checkConnectionStatus]. + */ + @MustBeClosed + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams + ): HttpResponseFor = + checkConnectionStatus(params, RequestOptions.none()) + + /** @see checkConnectionStatus */ + @MustBeClosed + fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /v4/inbox/connect`, but is otherwise the same as + * [InboxService.connectEmail]. + */ + @MustBeClosed + fun connectEmail( + params: InboxConnectEmailParams + ): HttpResponseFor = connectEmail(params, RequestOptions.none()) + + /** @see connectEmail */ + @MustBeClosed + fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /v4/inbox/disconnect`, but is otherwise the same as + * [InboxService.disconnectEmail]. + */ + @MustBeClosed + fun disconnectEmail( + params: InboxDisconnectEmailParams + ): HttpResponseFor = + disconnectEmail(params, RequestOptions.none()) + + /** @see disconnectEmail */ + @MustBeClosed + fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /v4/inbox/cas`, but is otherwise the same as + * [InboxService.listCasFiles]. + */ + @MustBeClosed + fun listCasFiles( + params: InboxListCasFilesParams + ): HttpResponseFor = listCasFiles(params, RequestOptions.none()) + + /** @see listCasFiles */ + @MustBeClosed + fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxServiceImpl.kt new file mode 100644 index 0000000..4abb1e6 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxServiceImpl.kt @@ -0,0 +1,210 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusParams +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusResponse +import com.cas_parser.api.models.inbox.InboxConnectEmailParams +import com.cas_parser.api.models.inbox.InboxConnectEmailResponse +import com.cas_parser.api.models.inbox.InboxDisconnectEmailParams +import com.cas_parser.api.models.inbox.InboxDisconnectEmailResponse +import com.cas_parser.api.models.inbox.InboxListCasFilesParams +import com.cas_parser.api.models.inbox.InboxListCasFilesResponse +import java.util.function.Consumer + +/** + * Endpoints for importing CAS files directly from user email inboxes. + * + * **Supported Providers:** Gmail (more coming soon) + * + * **How it works:** + * 1. Call `POST /v4/inbox/connect` to get an OAuth URL + * 2. Redirect user to the OAuth URL for consent + * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token` + * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`) + * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours) + * + * **Security:** + * - Read-only access (we cannot send emails) + * - Tokens are encrypted with server-side secret + * - User can revoke access anytime via `/v4/inbox/disconnect` + */ +class InboxServiceImpl internal constructor(private val clientOptions: ClientOptions) : + InboxService { + + private val withRawResponse: InboxService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): InboxService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): InboxService = + InboxServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions, + ): InboxCheckConnectionStatusResponse = + // post /v4/inbox/status + withRawResponse().checkConnectionStatus(params, requestOptions).parse() + + override fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions, + ): InboxConnectEmailResponse = + // post /v4/inbox/connect + withRawResponse().connectEmail(params, requestOptions).parse() + + override fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions, + ): InboxDisconnectEmailResponse = + // post /v4/inbox/disconnect + withRawResponse().disconnectEmail(params, requestOptions).parse() + + override fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions, + ): InboxListCasFilesResponse = + // post /v4/inbox/cas + withRawResponse().listCasFiles(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + InboxService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): InboxService.WithRawResponse = + InboxServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val checkConnectionStatusHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun checkConnectionStatus( + params: InboxCheckConnectionStatusParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "status") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { checkConnectionStatusHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val connectEmailHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun connectEmail( + params: InboxConnectEmailParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "connect") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { connectEmailHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val disconnectEmailHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun disconnectEmail( + params: InboxDisconnectEmailParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "disconnect") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { disconnectEmailHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listCasFilesHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun listCasFiles( + params: InboxListCasFilesParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "inbox", "cas") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listCasFilesHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechService.kt new file mode 100644 index 0000000..b4ac124 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechService.kt @@ -0,0 +1,69 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.kfintech.KfintechGenerateCasParams +import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** Endpoints for generating new CAS documents via email mailback (KFintech). */ +interface KfintechService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): KfintechService + + /** + * Generate CAS via KFintech mailback. The CAS PDF will be sent to the investor's email. + * + * This is an async operation - the investor receives the CAS via email within a few minutes. + * For instant CAS retrieval, use CDSL Fetch (`/v4/cdsl/fetch`). + */ + fun generateCas(params: KfintechGenerateCasParams): KfintechGenerateCasResponse = + generateCas(params, RequestOptions.none()) + + /** @see generateCas */ + fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): KfintechGenerateCasResponse + + /** A view of [KfintechService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): KfintechService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/kfintech/generate`, but is otherwise the same + * as [KfintechService.generateCas]. + */ + @MustBeClosed + fun generateCas( + params: KfintechGenerateCasParams + ): HttpResponseFor = generateCas(params, RequestOptions.none()) + + /** @see generateCas */ + @MustBeClosed + fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechServiceImpl.kt new file mode 100644 index 0000000..aeccbb9 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechServiceImpl.kt @@ -0,0 +1,83 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.kfintech.KfintechGenerateCasParams +import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse +import java.util.function.Consumer + +/** Endpoints for generating new CAS documents via email mailback (KFintech). */ +class KfintechServiceImpl internal constructor(private val clientOptions: ClientOptions) : + KfintechService { + + private val withRawResponse: KfintechService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): KfintechService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): KfintechService = + KfintechServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions, + ): KfintechGenerateCasResponse = + // post /v4/kfintech/generate + withRawResponse().generateCas(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + KfintechService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): KfintechService.WithRawResponse = + KfintechServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val generateCasHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun generateCas( + params: KfintechGenerateCasParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "kfintech", "generate") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { generateCasHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogService.kt new file mode 100644 index 0000000..6c7468b --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogService.kt @@ -0,0 +1,142 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.logs.LogCreateParams +import com.cas_parser.api.models.logs.LogCreateResponse +import com.cas_parser.api.models.logs.LogGetSummaryParams +import com.cas_parser.api.models.logs.LogGetSummaryResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +interface LogService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): LogService + + /** + * Retrieve detailed API usage logs for your account. + * + * Returns a list of API calls with timestamps, features used, status codes, and credits + * consumed. Useful for monitoring usage patterns and debugging. + * + * **Legacy path:** `/logs` (still supported) + */ + fun create(): LogCreateResponse = create(LogCreateParams.none()) + + /** @see create */ + fun create( + params: LogCreateParams = LogCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): LogCreateResponse + + /** @see create */ + fun create(params: LogCreateParams = LogCreateParams.none()): LogCreateResponse = + create(params, RequestOptions.none()) + + /** @see create */ + fun create(requestOptions: RequestOptions): LogCreateResponse = + create(LogCreateParams.none(), requestOptions) + + /** + * Get aggregated usage statistics grouped by feature. + * + * Useful for understanding which API features are being used most and tracking usage trends. + * + * **Legacy path:** `/logs/summary` (still supported) + */ + fun getSummary(): LogGetSummaryResponse = getSummary(LogGetSummaryParams.none()) + + /** @see getSummary */ + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): LogGetSummaryResponse + + /** @see getSummary */ + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none() + ): LogGetSummaryResponse = getSummary(params, RequestOptions.none()) + + /** @see getSummary */ + fun getSummary(requestOptions: RequestOptions): LogGetSummaryResponse = + getSummary(LogGetSummaryParams.none(), requestOptions) + + /** A view of [LogService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): LogService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/usage`, but is otherwise the same as + * [LogService.create]. + */ + @MustBeClosed + fun create(): HttpResponseFor = create(LogCreateParams.none()) + + /** @see create */ + @MustBeClosed + fun create( + params: LogCreateParams = LogCreateParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see create */ + @MustBeClosed + fun create( + params: LogCreateParams = LogCreateParams.none() + ): HttpResponseFor = create(params, RequestOptions.none()) + + /** @see create */ + @MustBeClosed + fun create(requestOptions: RequestOptions): HttpResponseFor = + create(LogCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `post /v1/usage/summary`, but is otherwise the same as + * [LogService.getSummary]. + */ + @MustBeClosed + fun getSummary(): HttpResponseFor = + getSummary(LogGetSummaryParams.none()) + + /** @see getSummary */ + @MustBeClosed + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see getSummary */ + @MustBeClosed + fun getSummary( + params: LogGetSummaryParams = LogGetSummaryParams.none() + ): HttpResponseFor = getSummary(params, RequestOptions.none()) + + /** @see getSummary */ + @MustBeClosed + fun getSummary(requestOptions: RequestOptions): HttpResponseFor = + getSummary(LogGetSummaryParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogServiceImpl.kt new file mode 100644 index 0000000..d481476 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogServiceImpl.kt @@ -0,0 +1,122 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.logs.LogCreateParams +import com.cas_parser.api.models.logs.LogCreateResponse +import com.cas_parser.api.models.logs.LogGetSummaryParams +import com.cas_parser.api.models.logs.LogGetSummaryResponse +import java.util.function.Consumer + +/** + * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API + * usage and remaining quota. + */ +class LogServiceImpl internal constructor(private val clientOptions: ClientOptions) : LogService { + + private val withRawResponse: LogService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): LogService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): LogService = + LogServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun create( + params: LogCreateParams, + requestOptions: RequestOptions, + ): LogCreateResponse = + // post /v1/usage + withRawResponse().create(params, requestOptions).parse() + + override fun getSummary( + params: LogGetSummaryParams, + requestOptions: RequestOptions, + ): LogGetSummaryResponse = + // post /v1/usage/summary + withRawResponse().getSummary(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + LogService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): LogService.WithRawResponse = + LogServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val createHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun create( + params: LogCreateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "usage") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { createHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val getSummaryHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun getSummary( + params: LogGetSummaryParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "usage", "summary") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { getSummaryHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlService.kt new file mode 100644 index 0000000..786d115 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlService.kt @@ -0,0 +1,82 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.nsdl.NsdlParseParams +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface NsdlService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): NsdlService + + /** + * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and + * returns data in a unified format. Use this endpoint when you know the PDF is from NSDL. + */ + fun parse(): UnifiedResponse = parse(NsdlParseParams.none()) + + /** @see parse */ + fun parse( + params: NsdlParseParams = NsdlParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UnifiedResponse + + /** @see parse */ + fun parse(params: NsdlParseParams = NsdlParseParams.none()): UnifiedResponse = + parse(params, RequestOptions.none()) + + /** @see parse */ + fun parse(requestOptions: RequestOptions): UnifiedResponse = + parse(NsdlParseParams.none(), requestOptions) + + /** A view of [NsdlService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): NsdlService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/nsdl/parse`, but is otherwise the same as + * [NsdlService.parse]. + */ + @MustBeClosed fun parse(): HttpResponseFor = parse(NsdlParseParams.none()) + + /** @see parse */ + @MustBeClosed + fun parse( + params: NsdlParseParams = NsdlParseParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see parse */ + @MustBeClosed + fun parse( + params: NsdlParseParams = NsdlParseParams.none() + ): HttpResponseFor = parse(params, RequestOptions.none()) + + /** @see parse */ + @MustBeClosed + fun parse(requestOptions: RequestOptions): HttpResponseFor = + parse(NsdlParseParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlServiceImpl.kt new file mode 100644 index 0000000..582c6df --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlServiceImpl.kt @@ -0,0 +1,79 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.nsdl.NsdlParseParams +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class NsdlServiceImpl internal constructor(private val clientOptions: ClientOptions) : NsdlService { + + private val withRawResponse: NsdlService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): NsdlService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): NsdlService = + NsdlServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parse(params: NsdlParseParams, requestOptions: RequestOptions): UnifiedResponse = + // post /v4/nsdl/parse + withRawResponse().parse(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + NsdlService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): NsdlService.WithRawResponse = + NsdlServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parse( + params: NsdlParseParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "nsdl", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { parseHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartService.kt new file mode 100644 index 0000000..79198c1 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartService.kt @@ -0,0 +1,86 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.smart.SmartParseCasPdfParams +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +interface SmartService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): SmartService + + /** + * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or + * CAMS/KFintech and returns data in a unified format. It auto-detects the CAS type and + * transforms the data into a consistent structure regardless of the source. + */ + fun parseCasPdf(): UnifiedResponse = parseCasPdf(SmartParseCasPdfParams.none()) + + /** @see parseCasPdf */ + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): UnifiedResponse + + /** @see parseCasPdf */ + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none() + ): UnifiedResponse = parseCasPdf(params, RequestOptions.none()) + + /** @see parseCasPdf */ + fun parseCasPdf(requestOptions: RequestOptions): UnifiedResponse = + parseCasPdf(SmartParseCasPdfParams.none(), requestOptions) + + /** A view of [SmartService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): SmartService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/smart/parse`, but is otherwise the same as + * [SmartService.parseCasPdf]. + */ + @MustBeClosed + fun parseCasPdf(): HttpResponseFor = + parseCasPdf(SmartParseCasPdfParams.none()) + + /** @see parseCasPdf */ + @MustBeClosed + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see parseCasPdf */ + @MustBeClosed + fun parseCasPdf( + params: SmartParseCasPdfParams = SmartParseCasPdfParams.none() + ): HttpResponseFor = parseCasPdf(params, RequestOptions.none()) + + /** @see parseCasPdf */ + @MustBeClosed + fun parseCasPdf(requestOptions: RequestOptions): HttpResponseFor = + parseCasPdf(SmartParseCasPdfParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartServiceImpl.kt new file mode 100644 index 0000000..2c8c2d8 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartServiceImpl.kt @@ -0,0 +1,83 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.multipartFormData +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.camskfintech.UnifiedResponse +import com.cas_parser.api.models.smart.SmartParseCasPdfParams +import java.util.function.Consumer + +/** Endpoints for parsing CAS PDF files from different sources. */ +class SmartServiceImpl internal constructor(private val clientOptions: ClientOptions) : + SmartService { + + private val withRawResponse: SmartService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): SmartService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): SmartService = + SmartServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun parseCasPdf( + params: SmartParseCasPdfParams, + requestOptions: RequestOptions, + ): UnifiedResponse = + // post /v4/smart/parse + withRawResponse().parseCasPdf(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + SmartService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): SmartService.WithRawResponse = + SmartServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val parseCasPdfHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun parseCasPdf( + params: SmartParseCasPdfParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "smart", "parse") + .body(multipartFormData(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { parseCasPdfHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenService.kt new file mode 100644 index 0000000..468e98d --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenService.kt @@ -0,0 +1,90 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyParams +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +interface VerifyTokenService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): VerifyTokenService + + /** Verify an access token and check if it's still valid. Useful for debugging token issues. */ + fun verify(): VerifyTokenVerifyResponse = verify(VerifyTokenVerifyParams.none()) + + /** @see verify */ + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): VerifyTokenVerifyResponse + + /** @see verify */ + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none() + ): VerifyTokenVerifyResponse = verify(params, RequestOptions.none()) + + /** @see verify */ + fun verify(requestOptions: RequestOptions): VerifyTokenVerifyResponse = + verify(VerifyTokenVerifyParams.none(), requestOptions) + + /** + * A view of [VerifyTokenService] that provides access to raw HTTP responses for each method. + */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions( + modifier: Consumer + ): VerifyTokenService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v1/token/verify`, but is otherwise the same as + * [VerifyTokenService.verify]. + */ + @MustBeClosed + fun verify(): HttpResponseFor = + verify(VerifyTokenVerifyParams.none()) + + /** @see verify */ + @MustBeClosed + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** @see verify */ + @MustBeClosed + fun verify( + params: VerifyTokenVerifyParams = VerifyTokenVerifyParams.none() + ): HttpResponseFor = verify(params, RequestOptions.none()) + + /** @see verify */ + @MustBeClosed + fun verify(requestOptions: RequestOptions): HttpResponseFor = + verify(VerifyTokenVerifyParams.none(), requestOptions) + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceImpl.kt new file mode 100644 index 0000000..e76ff7d --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceImpl.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyParams +import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse +import java.util.function.Consumer + +/** + * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate + * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access + * tokens can be used in place of API keys on all v4 endpoints. + */ +class VerifyTokenServiceImpl internal constructor(private val clientOptions: ClientOptions) : + VerifyTokenService { + + private val withRawResponse: VerifyTokenService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): VerifyTokenService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): VerifyTokenService = + VerifyTokenServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun verify( + params: VerifyTokenVerifyParams, + requestOptions: RequestOptions, + ): VerifyTokenVerifyResponse = + // post /v1/token/verify + withRawResponse().verify(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + VerifyTokenService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): VerifyTokenService.WithRawResponse = + VerifyTokenServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val verifyHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun verify( + params: VerifyTokenVerifyParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v1", "token", "verify") + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { verifyHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchService.kt new file mode 100644 index 0000000..b8f7006 --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchService.kt @@ -0,0 +1,136 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking.cdsl + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpResponse +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpResponse +import com.google.errorprone.annotations.MustBeClosed +import java.util.function.Consumer + +/** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ +interface FetchService { + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): FetchService + + /** + * **Step 1 of 2**: Request OTP for CDSL CAS fetch. + * + * This endpoint: + * 1. Solves reCAPTCHA automatically (~15-20 seconds) + * 2. Submits login credentials to CDSL portal + * 3. Triggers OTP to user's registered mobile number + * + * After user receives OTP, call `/v4/cdsl/fetch/{session_id}/verify` to complete. + */ + fun requestOtp(params: FetchRequestOtpParams): FetchRequestOtpResponse = + requestOtp(params, RequestOptions.none()) + + /** @see requestOtp */ + fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FetchRequestOtpResponse + + /** + * **Step 2 of 2**: Verify OTP and retrieve CDSL CAS files. + * + * After successful verification, CAS PDFs are fetched from CDSL portal, uploaded to cloud + * storage, and returned as direct download URLs. + */ + fun verifyOtp(sessionId: String, params: FetchVerifyOtpParams): FetchVerifyOtpResponse = + verifyOtp(sessionId, params, RequestOptions.none()) + + /** @see verifyOtp */ + fun verifyOtp( + sessionId: String, + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FetchVerifyOtpResponse = + verifyOtp(params.toBuilder().sessionId(sessionId).build(), requestOptions) + + /** @see verifyOtp */ + fun verifyOtp(params: FetchVerifyOtpParams): FetchVerifyOtpResponse = + verifyOtp(params, RequestOptions.none()) + + /** @see verifyOtp */ + fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): FetchVerifyOtpResponse + + /** A view of [FetchService] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + /** + * Returns a view of this service with the given option modifications applied. + * + * The original service is not modified. + */ + fun withOptions(modifier: Consumer): FetchService.WithRawResponse + + /** + * Returns a raw HTTP response for `post /v4/cdsl/fetch`, but is otherwise the same as + * [FetchService.requestOtp]. + */ + @MustBeClosed + fun requestOtp(params: FetchRequestOtpParams): HttpResponseFor = + requestOtp(params, RequestOptions.none()) + + /** @see requestOtp */ + @MustBeClosed + fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `post /v4/cdsl/fetch/{session_id}/verify`, but is + * otherwise the same as [FetchService.verifyOtp]. + */ + @MustBeClosed + fun verifyOtp( + sessionId: String, + params: FetchVerifyOtpParams, + ): HttpResponseFor = + verifyOtp(sessionId, params, RequestOptions.none()) + + /** @see verifyOtp */ + @MustBeClosed + fun verifyOtp( + sessionId: String, + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + verifyOtp(params.toBuilder().sessionId(sessionId).build(), requestOptions) + + /** @see verifyOtp */ + @MustBeClosed + fun verifyOtp(params: FetchVerifyOtpParams): HttpResponseFor = + verifyOtp(params, RequestOptions.none()) + + /** @see verifyOtp */ + @MustBeClosed + fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + } +} diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceImpl.kt new file mode 100644 index 0000000..9c8db9f --- /dev/null +++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceImpl.kt @@ -0,0 +1,128 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking.cdsl + +import com.cas_parser.api.core.ClientOptions +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.checkRequired +import com.cas_parser.api.core.handlers.errorBodyHandler +import com.cas_parser.api.core.handlers.errorHandler +import com.cas_parser.api.core.handlers.jsonHandler +import com.cas_parser.api.core.http.HttpMethod +import com.cas_parser.api.core.http.HttpRequest +import com.cas_parser.api.core.http.HttpResponse +import com.cas_parser.api.core.http.HttpResponse.Handler +import com.cas_parser.api.core.http.HttpResponseFor +import com.cas_parser.api.core.http.json +import com.cas_parser.api.core.http.parseable +import com.cas_parser.api.core.prepare +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpResponse +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpResponse +import java.util.function.Consumer +import kotlin.jvm.optionals.getOrNull + +/** + * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP + * authentication. + */ +class FetchServiceImpl internal constructor(private val clientOptions: ClientOptions) : + FetchService { + + private val withRawResponse: FetchService.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + override fun withRawResponse(): FetchService.WithRawResponse = withRawResponse + + override fun withOptions(modifier: Consumer): FetchService = + FetchServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build()) + + override fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions, + ): FetchRequestOtpResponse = + // post /v4/cdsl/fetch + withRawResponse().requestOtp(params, requestOptions).parse() + + override fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions, + ): FetchVerifyOtpResponse = + // post /v4/cdsl/fetch/{session_id}/verify + withRawResponse().verifyOtp(params, requestOptions).parse() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + FetchService.WithRawResponse { + + private val errorHandler: Handler = + errorHandler(errorBodyHandler(clientOptions.jsonMapper)) + + override fun withOptions( + modifier: Consumer + ): FetchService.WithRawResponse = + FetchServiceImpl.WithRawResponseImpl( + clientOptions.toBuilder().apply(modifier::accept).build() + ) + + private val requestOtpHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun requestOtp( + params: FetchRequestOtpParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cdsl", "fetch") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { requestOtpHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val verifyOtpHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun verifyOtp( + params: FetchVerifyOtpParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("sessionId", params.sessionId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("v4", "cdsl", "fetch", params._pathParam(0), "verify") + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { verifyOtpHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } +} diff --git a/cas-parser-java-core/src/main/resources/META-INF/proguard/cas-parser-java-core.pro b/cas-parser-java-core/src/main/resources/META-INF/proguard/cas-parser-java-core.pro new file mode 100644 index 0000000..8ee82ce --- /dev/null +++ b/cas-parser-java-core/src/main/resources/META-INF/proguard/cas-parser-java-core.pro @@ -0,0 +1,32 @@ +# Jackson uses reflection and depends heavily on runtime attributes. +-keepattributes Exceptions,InnerClasses,Signature,Deprecated,*Annotation* + +# Jackson uses Kotlin reflection utilities, which themselves use reflection to access things. +-keep class kotlin.reflect.** { *; } +-keep class kotlin.Metadata { *; } + +# Jackson uses reflection to access enum members (e.g. via `java.lang.Class.getEnumConstants()`). +-keepclassmembers class com.fasterxml.jackson.** extends java.lang.Enum { + ; + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# Jackson uses reflection to access annotation members. +-keepclassmembers @interface com.fasterxml.jackson.annotation.** { + *; +} + +# Jackson uses reified type information to serialize and deserialize our classes (via `TypeReference`). +-keep class com.fasterxml.jackson.core.type.TypeReference { *; } +-keep class * extends com.fasterxml.jackson.core.type.TypeReference { *; } + +# Jackson uses reflection to access our class serializers and deserializers. +-keep @com.fasterxml.jackson.databind.annotation.JsonSerialize class com.cas_parser.api.** { *; } +-keep @com.fasterxml.jackson.databind.annotation.JsonDeserialize class com.cas_parser.api.** { *; } + +# Jackson uses reflection to serialize and deserialize our classes based on their constructors and annotated members. +-keepclassmembers class com.cas_parser.api.** { + (...); + @com.fasterxml.jackson.annotation.* *; +} \ No newline at end of file diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ClientOptionsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ClientOptionsTest.kt new file mode 100644 index 0000000..4e9fb6e --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ClientOptionsTest.kt @@ -0,0 +1,56 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core + +import com.cas_parser.api.core.http.HttpClient +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +@ExtendWith(MockitoExtension::class) +internal class ClientOptionsTest { + + private val httpClient = mock() + + @Test + fun putHeader_canOverwriteDefaultHeader() { + val clientOptions = + ClientOptions.builder() + .httpClient(httpClient) + .putHeader("User-Agent", "My User Agent") + .apiKey("My API Key") + .build() + + assertThat(clientOptions.headers.values("User-Agent")).containsExactly("My User Agent") + } + + @Test + fun toBuilder_apiKeyAuthCanBeUpdated() { + var clientOptions = + ClientOptions.builder().httpClient(httpClient).apiKey("My API Key").build() + + clientOptions = clientOptions.toBuilder().apiKey("another My API Key").build() + + assertThat(clientOptions.headers.values("x-api-key")).containsExactly("another My API Key") + } + + @Test + fun toBuilder_whenOriginalClientOptionsGarbageCollected_doesNotCloseOriginalClient() { + var clientOptions = + ClientOptions.builder().httpClient(httpClient).apiKey("My API Key").build() + verify(httpClient, never()).close() + + // Overwrite the `clientOptions` variable so that the original `ClientOptions` is GC'd. + clientOptions = clientOptions.toBuilder().build() + System.gc() + Thread.sleep(100) + + verify(httpClient, never()).close() + // This exists so that `clientOptions` is still reachable. + assertThat(clientOptions).isEqualTo(clientOptions) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ObjectMappersTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ObjectMappersTest.kt new file mode 100644 index 0000000..8010442 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ObjectMappersTest.kt @@ -0,0 +1,117 @@ +package com.cas_parser.api.core + +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.exc.MismatchedInputException +import com.fasterxml.jackson.module.kotlin.readValue +import java.time.LocalDate +import java.time.LocalTime +import java.time.OffsetDateTime +import java.time.ZoneOffset +import kotlin.reflect.KClass +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.catchThrowable +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource +import org.junitpioneer.jupiter.cartesian.CartesianTest + +internal class ObjectMappersTest { + + internal class ClassWithBooleanFieldPrefixedWithIs(private val isActive: JsonField) { + + @JsonProperty("is_active") @ExcludeMissing fun _isActive() = isActive + } + + @Test + fun write_whenFieldPrefixedWithIs_keepsPrefix() { + val value = ClassWithBooleanFieldPrefixedWithIs(JsonField.of(true)) + + val json = jsonMapper().writeValueAsString(value) + + assertThat(json).isEqualTo("{\"is_active\":true}") + } + + internal class Class(@get:JsonProperty("field") @JsonProperty("field") val field: String) + + enum class ShapeTestCase(val value: Any, val kClass: KClass<*>) { + STRING("Hello World!", String::class), + BOOLEAN(true, Boolean::class), + FLOAT(3.14F, Float::class), + DOUBLE(3.14, Double::class), + INTEGER(42, Int::class), + LONG(42L, Long::class), + MAP(mapOf("property" to "value"), Map::class), + CLASS(Class("Hello World!"), Class::class), + LIST(listOf(1, 2, 3), List::class); + + companion object { + val VALID_CONVERSIONS = + listOf( + FLOAT to DOUBLE, + DOUBLE to FLOAT, + INTEGER to FLOAT, + INTEGER to DOUBLE, + INTEGER to LONG, + LONG to FLOAT, + LONG to DOUBLE, + LONG to INTEGER, + CLASS to MAP, + ) + } + } + + @CartesianTest + fun read(@CartesianTest.Enum shape1: ShapeTestCase, @CartesianTest.Enum shape2: ShapeTestCase) { + val jsonMapper = jsonMapper() + val json = jsonMapper.writeValueAsString(shape1.value) + + val e = catchThrowable { jsonMapper.readValue(json, shape2.kClass.java) } + + if (shape1 == shape2 || shape1 to shape2 in ShapeTestCase.VALID_CONVERSIONS) { + assertThat(e).isNull() + } else { + assertThat(e).isInstanceOf(MismatchedInputException::class.java) + } + } + + enum class LenientOffsetDateTimeTestCase( + val string: String, + val expectedOffsetDateTime: OffsetDateTime, + ) { + DATE( + "1998-04-21", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(0, 0), ZoneOffset.UTC), + ), + DATE_TIME( + "1998-04-21T04:00:00", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(4, 0), ZoneOffset.UTC), + ), + ZONED_DATE_TIME_1( + "1998-04-21T04:00:00+03:00", + expectedOffsetDateTime = + OffsetDateTime.of( + LocalDate.of(1998, 4, 21), + LocalTime.of(4, 0), + ZoneOffset.ofHours(3), + ), + ), + ZONED_DATE_TIME_2( + "1998-04-21T04:00:00Z", + expectedOffsetDateTime = + OffsetDateTime.of(LocalDate.of(1998, 4, 21), LocalTime.of(4, 0), ZoneOffset.UTC), + ), + } + + @ParameterizedTest + @EnumSource + fun readOffsetDateTime_lenient(testCase: LenientOffsetDateTimeTestCase) { + val jsonMapper = jsonMapper() + val json = jsonMapper.writeValueAsString(testCase.string) + + val offsetDateTime = jsonMapper().readValue(json) + + assertThat(offsetDateTime).isEqualTo(testCase.expectedOffsetDateTime) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/PhantomReachableTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/PhantomReachableTest.kt new file mode 100644 index 0000000..db10ab6 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/PhantomReachableTest.kt @@ -0,0 +1,27 @@ +package com.cas_parser.api.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class PhantomReachableTest { + + @Test + fun closeWhenPhantomReachable_whenObservedIsGarbageCollected_closesCloseable() { + var closed = false + val closeable = AutoCloseable { closed = true } + + closeWhenPhantomReachable( + // Pass an inline object for the object to observe so that it becomes immediately + // unreachable. + Any(), + closeable, + ) + + assertThat(closed).isFalse() + + System.gc() + Thread.sleep(100) + + assertThat(closed).isTrue() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/UtilsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/UtilsTest.kt new file mode 100644 index 0000000..fe6240b --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/UtilsTest.kt @@ -0,0 +1,33 @@ +package com.cas_parser.api.core + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UtilsTest { + @Test + fun contentDeepEquals() { + assertThat(42 contentEquals 42).isTrue() + assertThat(42 contentEquals "Hello World!").isFalse() + assertThat(byteArrayOf(1, 2, 3) contentEquals byteArrayOf(1, 2, 3)).isTrue() + assertThat(byteArrayOf(1, 2, 3) contentEquals byteArrayOf(1, 2, 4)).isFalse() + assertThat( + arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) contentEquals + arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) + ) + .isTrue() + assertThat( + arrayOf(byteArrayOf(1, 2), byteArrayOf(3)) contentEquals + arrayOf(byteArrayOf(1), byteArrayOf(2, 3)) + ) + .isFalse() + } + + @Test + fun contentToString() { + assertThat((42).contentToString()).isEqualTo("42") + assertThat("Hello World!".contentToString()).isEqualTo("Hello World!") + assertThat(byteArrayOf(1, 2, 3).contentToString()).isEqualTo("[1, 2, 3]") + assertThat(arrayOf(byteArrayOf(1, 2), byteArrayOf(3)).contentToString()) + .isEqualTo("[[1, 2], [3]]") + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ValuesTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ValuesTest.kt new file mode 100644 index 0000000..8fd7b86 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/ValuesTest.kt @@ -0,0 +1,144 @@ +package com.cas_parser.api.core + +import java.util.Optional +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class ValuesTest { + companion object { + private val NON_JSON = Any() + } + + enum class TestCase( + val value: JsonField<*>, + val expectedIsMissing: Boolean = false, + val expectedIsNull: Boolean = false, + val expectedAsKnown: Optional<*> = Optional.empty(), + val expectedAsBoolean: Optional = Optional.empty(), + val expectedAsNumber: Optional = Optional.empty(), + val expectedAsString: Optional = Optional.empty(), + val expectedAsArray: Optional> = Optional.empty(), + val expectedAsObject: Optional> = Optional.empty(), + ) { + MISSING(JsonMissing.of(), expectedIsMissing = true), + NULL(JsonNull.of(), expectedIsNull = true), + KNOWN(KnownValue.of(NON_JSON), expectedAsKnown = Optional.of(NON_JSON)), + KNOWN_BOOLEAN( + KnownValue.of(true), + expectedAsKnown = Optional.of(true), + expectedAsBoolean = Optional.of(true), + ), + BOOLEAN(JsonBoolean.of(true), expectedAsBoolean = Optional.of(true)), + KNOWN_NUMBER( + KnownValue.of(42), + expectedAsKnown = Optional.of(42), + expectedAsNumber = Optional.of(42), + ), + NUMBER(JsonNumber.of(42), expectedAsNumber = Optional.of(42)), + KNOWN_STRING( + KnownValue.of("hello"), + expectedAsKnown = Optional.of("hello"), + expectedAsString = Optional.of("hello"), + ), + STRING(JsonString.of("hello"), expectedAsString = Optional.of("hello")), + KNOWN_ARRAY_NOT_ALL_JSON( + KnownValue.of(listOf("a", "b", NON_JSON)), + expectedAsKnown = Optional.of(listOf("a", "b", NON_JSON)), + ), + KNOWN_ARRAY( + KnownValue.of(listOf("a", "b", "c")), + expectedAsKnown = Optional.of(listOf("a", "b", "c")), + expectedAsArray = + Optional.of(listOf(JsonString.of("a"), JsonString.of("b"), JsonString.of("c"))), + ), + ARRAY( + JsonArray.of(listOf(JsonString.of("a"), JsonString.of("b"), JsonString.of("c"))), + expectedAsArray = + Optional.of(listOf(JsonString.of("a"), JsonString.of("b"), JsonString.of("c"))), + ), + KNOWN_OBJECT_NOT_ALL_STRING_KEYS( + KnownValue.of(mapOf("a" to "b", 42 to "c")), + expectedAsKnown = Optional.of(mapOf("a" to "b", 42 to "c")), + ), + KNOWN_OBJECT_NOT_ALL_JSON( + KnownValue.of(mapOf("a" to "b", "b" to NON_JSON)), + expectedAsKnown = Optional.of(mapOf("a" to "b", "b" to NON_JSON)), + ), + KNOWN_OBJECT( + KnownValue.of(mapOf("a" to "b", "b" to "c")), + expectedAsKnown = Optional.of(mapOf("a" to "b", "b" to "c")), + expectedAsObject = + Optional.of(mapOf("a" to JsonString.of("b"), "b" to JsonString.of("c"))), + ), + OBJECT( + JsonObject.of(mapOf("a" to JsonString.of("b"), "b" to JsonString.of("c"))), + expectedAsObject = + Optional.of(mapOf("a" to JsonString.of("b"), "b" to JsonString.of("c"))), + ), + } + + @ParameterizedTest + @EnumSource + fun isMissing(testCase: TestCase) { + val isMissing = testCase.value.isMissing() + + assertThat(isMissing).isEqualTo(testCase.expectedIsMissing) + } + + @ParameterizedTest + @EnumSource + fun isNull(testCase: TestCase) { + val isNull = testCase.value.isNull() + + assertThat(isNull).isEqualTo(testCase.expectedIsNull) + } + + @ParameterizedTest + @EnumSource + fun asKnown(testCase: TestCase) { + val known = testCase.value.asKnown() + + assertThat(known).isEqualTo(testCase.expectedAsKnown) + } + + @ParameterizedTest + @EnumSource + fun asBoolean(testCase: TestCase) { + val boolean = testCase.value.asBoolean() + + assertThat(boolean).isEqualTo(testCase.expectedAsBoolean) + } + + @ParameterizedTest + @EnumSource + fun asNumber(testCase: TestCase) { + val number = testCase.value.asNumber() + + assertThat(number).isEqualTo(testCase.expectedAsNumber) + } + + @ParameterizedTest + @EnumSource + fun asString(testCase: TestCase) { + val string = testCase.value.asString() + + assertThat(string).isEqualTo(testCase.expectedAsString) + } + + @ParameterizedTest + @EnumSource + fun asArray(testCase: TestCase) { + val array = testCase.value.asArray() + + assertThat(array).isEqualTo(testCase.expectedAsArray) + } + + @ParameterizedTest + @EnumSource + fun asObject(testCase: TestCase) { + val obj = testCase.value.asObject() + + assertThat(obj).isEqualTo(testCase.expectedAsObject) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/AsyncStreamResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/AsyncStreamResponseTest.kt new file mode 100644 index 0000000..6f3ec90 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/AsyncStreamResponseTest.kt @@ -0,0 +1,268 @@ +package com.cas_parser.api.core.http + +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executor +import java.util.stream.Stream +import kotlin.streams.asStream +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.catchThrowable +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertDoesNotThrow +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.* + +@ExtendWith(MockitoExtension::class) +internal class AsyncStreamResponseTest { + + companion object { + private val ERROR = RuntimeException("ERROR!") + } + + private val streamResponse = + spy> { + doReturn(Stream.of("chunk1", "chunk2", "chunk3")).whenever(it).stream() + } + private val erroringStreamResponse = + spy> { + doReturn( + sequence { + yield("chunk1") + yield("chunk2") + throw ERROR + } + .asStream() + ) + .whenever(it) + .stream() + } + private val executor = + spy { + doAnswer { invocation -> invocation.getArgument(0).run() } + .whenever(it) + .execute(any()) + } + private val handler = mock>() + + @Test + fun subscribe_whenAlreadySubscribed_throws() { + val asyncStreamResponse = CompletableFuture>().toAsync(executor) + asyncStreamResponse.subscribe {} + + val throwable = catchThrowable { asyncStreamResponse.subscribe {} } + + assertThat(throwable).isInstanceOf(IllegalStateException::class.java) + assertThat(throwable).hasMessage("Cannot subscribe more than once") + verify(executor, never()).execute(any()) + } + + @Test + fun subscribe_whenClosed_throws() { + val asyncStreamResponse = CompletableFuture>().toAsync(executor) + asyncStreamResponse.close() + + val throwable = catchThrowable { asyncStreamResponse.subscribe {} } + + assertThat(throwable).isInstanceOf(IllegalStateException::class.java) + assertThat(throwable).hasMessage("Cannot subscribe after the response is closed") + verify(executor, never()).execute(any()) + } + + @Test + fun subscribe_whenFutureCompletesAfterClose_doesNothing() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe(handler) + asyncStreamResponse.close() + + future.complete(streamResponse) + + verify(handler, never()).onNext(any()) + verify(handler, never()).onComplete(any()) + verify(executor, times(1)).execute(any()) + } + + @Test + fun subscribe_whenFutureErrors_callsOnComplete() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe(handler) + + future.completeExceptionally(ERROR) + + verify(handler, never()).onNext(any()) + verify(handler, times(1)).onComplete(Optional.of(ERROR)) + verify(executor, times(1)).execute(any()) + } + + @Test + fun subscribe_whenFutureCompletes_runsHandler() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe(handler) + + future.complete(streamResponse) + + inOrder(handler, streamResponse) { + verify(handler, times(1)).onNext("chunk1") + verify(handler, times(1)).onNext("chunk2") + verify(handler, times(1)).onNext("chunk3") + verify(handler, times(1)).onComplete(Optional.empty()) + verify(streamResponse, times(1)).close() + } + verify(executor, times(1)).execute(any()) + } + + @Test + fun subscribe_whenStreamErrors_callsOnCompleteEarly() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe(handler) + + future.complete(erroringStreamResponse) + + inOrder(handler, erroringStreamResponse) { + verify(handler, times(1)).onNext("chunk1") + verify(handler, times(1)).onNext("chunk2") + verify(handler, times(1)).onComplete(Optional.of(ERROR)) + verify(erroringStreamResponse, times(1)).close() + } + verify(executor, times(1)).execute(any()) + } + + @Test + fun onCompleteFuture_whenStreamResponseFutureNotCompleted_onCompleteFutureNotCompleted() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isNotCompleted + } + + @Test + fun onCompleteFuture_whenStreamResponseFutureErrors_onCompleteFutureCompletedExceptionally() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + future.completeExceptionally(ERROR) + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isCompletedExceptionally + } + + @Test + fun onCompleteFuture_whenStreamResponseFutureCompletedButStillStreaming_onCompleteFutureNotCompleted() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + future.complete(streamResponse) + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isNotCompleted + } + + @Test + fun onCompleteFuture_whenStreamResponseFutureCompletedAndStreamErrors_onCompleteFutureCompletedExceptionally() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe(handler) + future.complete(erroringStreamResponse) + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isCompletedExceptionally + } + + @Test + fun onCompleteFuture_whenStreamResponseFutureCompletedAndStreamCompleted_onCompleteFutureCompleted() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe(handler) + future.complete(streamResponse) + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isCompleted + } + + @Test + fun onCompleteFuture_whenHandlerOnCompleteWithoutThrowableThrows_onCompleteFutureCompleted() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe( + object : AsyncStreamResponse.Handler { + override fun onNext(value: String) {} + + override fun onComplete(error: Optional) = throw ERROR + } + ) + future.complete(streamResponse) + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isCompleted + } + + @Test + fun onCompleteFuture_whenHandlerOnCompleteWithThrowableThrows_onCompleteFutureCompletedExceptionally() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.subscribe( + object : AsyncStreamResponse.Handler { + override fun onNext(value: String) {} + + override fun onComplete(error: Optional) = throw ERROR + } + ) + future.complete(erroringStreamResponse) + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isCompletedExceptionally + } + + @Test + fun onCompleteFuture_whenClosed_onCompleteFutureCompleted() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.close() + + val onCompletableFuture = asyncStreamResponse.onCompleteFuture() + + assertThat(onCompletableFuture).isCompleted + } + + @Test + fun close_whenNotClosed_closesStreamResponse() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + + asyncStreamResponse.close() + future.complete(streamResponse) + + verify(streamResponse, times(1)).close() + } + + @Test + fun close_whenAlreadyClosed_doesNothing() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.close() + future.complete(streamResponse) + + asyncStreamResponse.close() + + verify(streamResponse, times(1)).close() + } + + @Test + fun close_whenFutureErrors_doesNothing() { + val future = CompletableFuture>() + val asyncStreamResponse = future.toAsync(executor) + asyncStreamResponse.close() + + assertDoesNotThrow { future.completeExceptionally(ERROR) } + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HeadersTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HeadersTest.kt new file mode 100644 index 0000000..0105fd1 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HeadersTest.kt @@ -0,0 +1,242 @@ +package com.cas_parser.api.core.http + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class HeadersTest { + + enum class TestCase( + val headers: Headers, + val expectedMap: Map>, + val expectedSize: Int, + ) { + EMPTY(Headers.builder().build(), expectedMap = mapOf(), expectedSize = 0), + PUT_ONE( + Headers.builder().put("name", "value").build(), + expectedMap = mapOf("name" to listOf("value")), + expectedSize = 1, + ), + PUT_MULTIPLE( + Headers.builder().put("name", listOf("value1", "value2")).build(), + expectedMap = mapOf("name" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT( + Headers.builder().put("name1", "value").put("name2", "value").build(), + expectedMap = mapOf("name1" to listOf("value"), "name2" to listOf("value")), + expectedSize = 2, + ), + MULTIPLE_PUT_SAME_NAME( + Headers.builder().put("name", "value1").put("name", "value2").build(), + expectedMap = mapOf("name" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT_MULTIPLE( + Headers.builder() + .put("name", listOf("value1", "value2")) + .put("name", listOf("value1", "value2")) + .build(), + expectedMap = mapOf("name" to listOf("value1", "value2", "value1", "value2")), + expectedSize = 4, + ), + PUT_CASE_INSENSITIVE( + Headers.builder() + .put("name", "value1") + .put("NAME", "value2") + .put("nAmE", "value3") + .build(), + expectedMap = mapOf("name" to listOf("value1", "value2", "value3")), + expectedSize = 3, + ), + PUT_ALL_MAP( + Headers.builder() + .putAll( + mapOf( + "name1" to listOf("value1", "value2"), + "name2" to listOf("value1", "value2"), + ) + ) + .build(), + expectedMap = + mapOf("name1" to listOf("value1", "value2"), "name2" to listOf("value1", "value2")), + expectedSize = 4, + ), + PUT_ALL_HEADERS( + Headers.builder().putAll(Headers.builder().put("name", "value").build()).build(), + expectedMap = mapOf("name" to listOf("value")), + expectedSize = 1, + ), + PUT_ALL_CASE_INSENSITIVE( + Headers.builder() + .putAll( + mapOf( + "name" to listOf("value1"), + "NAME" to listOf("value2"), + "nAmE" to listOf("value3"), + ) + ) + .build(), + expectedMap = mapOf("name" to listOf("value1", "value2", "value3")), + expectedSize = 3, + ), + REMOVE_ABSENT( + Headers.builder().remove("name").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_ONE( + Headers.builder().put("name", "value").remove("name").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_MULTIPLE( + Headers.builder().put("name", listOf("value1", "value2")).remove("name").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_CASE_INSENSITIVE( + Headers.builder().put("name", listOf("value1", "value2")).remove("NAME").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_ALL( + Headers.builder() + .put("name1", "value") + .put("name3", "value") + .removeAll(setOf("name1", "name2", "name3")) + .build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_ALL_CASE_INSENSITIVE( + Headers.builder() + .put("name1", "value") + .put("name3", "value") + .removeAll(setOf("NAME1", "nAmE3")) + .build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + CLEAR( + Headers.builder().put("name1", "value").put("name2", "value").clear().build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REPLACE_ONE_ABSENT( + Headers.builder().replace("name", "value").build(), + expectedMap = mapOf("name" to listOf("value")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_ONE( + Headers.builder().put("name", "value1").replace("name", "value2").build(), + expectedMap = mapOf("name" to listOf("value2")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_MULTIPLE( + Headers.builder() + .put("name", listOf("value1", "value2")) + .replace("name", "value3") + .build(), + expectedMap = mapOf("name" to listOf("value3")), + expectedSize = 1, + ), + REPLACE_MULTIPLE_ABSENT( + Headers.builder().replace("name", listOf("value1", "value2")).build(), + expectedMap = mapOf("name" to listOf("value1", "value2")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_ONE( + Headers.builder() + .put("name", "value1") + .replace("name", listOf("value2", "value3")) + .build(), + expectedMap = mapOf("name" to listOf("value2", "value3")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_MULTIPLE( + Headers.builder() + .put("name", listOf("value1", "value2")) + .replace("name", listOf("value3", "value4")) + .build(), + expectedMap = mapOf("name" to listOf("value3", "value4")), + expectedSize = 2, + ), + REPLACE_CASE_INSENSITIVE( + Headers.builder() + .put("name", "value1") + .replace("NAME", listOf("value2", "value3")) + .build(), + expectedMap = mapOf("NAME" to listOf("value2", "value3")), + expectedSize = 2, + ), + REPLACE_ALL_MAP( + Headers.builder() + .put("name1", "value1") + .put("name2", "value1") + .put("name3", "value1") + .replaceAll(mapOf("name1" to listOf("value2"), "name3" to listOf("value2"))) + .build(), + expectedMap = + mapOf( + "name1" to listOf("value2"), + "name2" to listOf("value1"), + "name3" to listOf("value2"), + ), + expectedSize = 3, + ), + REPLACE_ALL_HEADERS( + Headers.builder() + .put("name1", "value1") + .put("name2", "value1") + .put("name3", "value1") + .replaceAll(Headers.builder().put("name1", "value2").put("name3", "value2").build()) + .build(), + expectedMap = + mapOf( + "name1" to listOf("value2"), + "name2" to listOf("value1"), + "name3" to listOf("value2"), + ), + expectedSize = 3, + ), + REPLACE_ALL_CASE_INSENSITIVE( + Headers.builder() + .put("name1", "value1") + .put("name2", "value1") + .replaceAll(mapOf("NAME1" to listOf("value2"), "nAmE2" to listOf("value2"))) + .build(), + expectedMap = mapOf("NAME1" to listOf("value2"), "nAmE2" to listOf("value2")), + expectedSize = 2, + ), + } + + @ParameterizedTest + @EnumSource + fun namesAndValues(testCase: TestCase) { + val map = mutableMapOf>() + val headers = testCase.headers + headers.names().forEach { name -> map[name] = headers.values(name) } + + assertThat(map).isEqualTo(testCase.expectedMap) + } + + @ParameterizedTest + @EnumSource + fun caseInsensitiveNames(testCase: TestCase) { + val headers = testCase.headers + + for (name in headers.names()) { + assertThat(headers.values(name)).isEqualTo(headers.values(name.lowercase())) + assertThat(headers.values(name)).isEqualTo(headers.values(name.uppercase())) + } + } + + @ParameterizedTest + @EnumSource + fun size(testCase: TestCase) { + val size = testCase.headers.size + + assertThat(size).isEqualTo(testCase.expectedSize) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestBodiesTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestBodiesTest.kt new file mode 100644 index 0000000..8b9e0fd --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestBodiesTest.kt @@ -0,0 +1,729 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.MultipartField +import com.cas_parser.api.core.jsonMapper +import java.io.ByteArrayOutputStream +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class HttpRequestBodiesTest { + + @Test + fun multipartFormData_serializesFieldWithFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "file" to + MultipartField.builder() + .value("hello") + .filename("hello.txt") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(output.size().toLong()).isEqualTo(body.contentLength()) + val boundary = body.contentType()!!.substringAfter("multipart/form-data; boundary=") + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="file"; filename="hello.txt" + |Content-Type: text/plain + | + |hello + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesFieldWithoutFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field" to + MultipartField.builder() + .value("value") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(output.size().toLong()).isEqualTo(body.contentLength()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="field" + |Content-Type: text/plain + | + |value + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesInputStream() { + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code + // path. + val inputStream = "stream content".byteInputStream().buffered() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder() + .value(inputStream) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data" + |Content-Type: application/octet-stream + | + |stream content + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesByteArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "binary" to + MultipartField.builder() + .value("abc".toByteArray()) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="binary" + |Content-Type: application/octet-stream + | + |abc + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesBooleanValue() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "flag" to + MultipartField.builder() + .value(true) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="flag" + |Content-Type: text/plain + | + |true + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNumberValue() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "count" to + MultipartField.builder().value(42).contentType("text/plain").build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="count" + |Content-Type: text/plain + | + |42 + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNullValueAsNoParts() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "present" to + MultipartField.builder() + .value("yes") + .contentType("text/plain") + .build(), + "absent" to + MultipartField.builder() + .value(null as String?) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="present" + |Content-Type: text/plain + | + |yes + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "items" to + MultipartField.builder>() + .value(listOf("alpha", "beta", "gamma")) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="items" + |Content-Type: text/plain + | + |alpha,beta,gamma + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesObjectAsNestedParts() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "meta" to + MultipartField.builder>() + .value(mapOf("key1" to "val1", "key2" to "val2")) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="meta[key1]" + |Content-Type: text/plain + | + |val1 + |--$boundary + |Content-Disposition: form-data; name="meta[key2]" + |Content-Type: text/plain + | + |val2 + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesMultipleFields() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "name" to + MultipartField.builder() + .value("Alice") + .contentType("text/plain") + .build(), + "age" to + MultipartField.builder().value(30).contentType("text/plain").build(), + "file" to + MultipartField.builder() + .value("file contents") + .filename("doc.txt") + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="name" + |Content-Type: text/plain + | + |Alice + |--$boundary + |Content-Disposition: form-data; name="age" + |Content-Type: text/plain + | + |30 + |--$boundary + |Content-Disposition: form-data; name="file"; filename="doc.txt" + |Content-Type: text/plain + | + |file contents + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_quotesSpecialCharactersInNameAndFilename() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field\nname" to + MultipartField.builder() + .value("value") + .filename("file\r\"name.txt") + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="field%0Aname"; filename="file%0D%22name.txt" + |Content-Type: text/plain + | + |value + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_writeIsRepeatable() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "field" to + MultipartField.builder() + .value("repeatable") + .contentType("text/plain") + .build() + ), + ) + + val output1 = ByteArrayOutputStream() + body.writeTo(output1) + val output2 = ByteArrayOutputStream() + body.writeTo(output2) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output1.size().toLong()) + val boundary = boundary(body) + val expected = + """ + |--$boundary + |Content-Disposition: form-data; name="field" + |Content-Type: text/plain + | + |repeatable + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + assertThat(output1.toString("UTF-8")).isEqualTo(expected) + assertThat(output2.toString("UTF-8")).isEqualTo(expected) + } + + @Test + fun multipartFormData_serializesByteArrayInputStream() { + // ByteArrayInputStream is specifically handled as repeatable with known content length. + val inputStream = "byte array stream".byteInputStream() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder() + .value(inputStream) + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data" + |Content-Type: application/octet-stream + | + |byte array stream + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesInputStreamWithFilename() { + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code + // path. + val inputStream = "file data".byteInputStream().buffered() + val body = + multipartFormData( + jsonMapper(), + mapOf( + "upload" to + MultipartField.builder() + .value(inputStream) + .filename("upload.bin") + .contentType("application/octet-stream") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="upload"; filename="upload.bin" + |Content-Type: application/octet-stream + | + |file data + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesNestedArrayInObject() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "data" to + MultipartField.builder>>() + .value(mapOf("tags" to listOf("a", "b"))) + .contentType("text/plain") + .build() + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="data[tags]" + |Content-Type: text/plain + | + |a,b + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_contentLengthIsUnknownWhenInputStreamPresent() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "text" to + MultipartField.builder() + .value("hello") + .contentType("text/plain") + .build(), + "stream" to + MultipartField.builder() + // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the + // non-repeatable code path. + .value("data".byteInputStream().buffered()) + .contentType("application/octet-stream") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isFalse() + assertThat(body.contentLength()).isEqualTo(-1L) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="text" + |Content-Type: text/plain + | + |hello + |--$boundary + |Content-Disposition: form-data; name="stream" + |Content-Type: application/octet-stream + | + |data + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesEmptyArray() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "required" to + MultipartField.builder() + .value("present") + .contentType("text/plain") + .build(), + "items" to + MultipartField.builder>() + .value(emptyList()) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="required" + |Content-Type: text/plain + | + |present + |--$boundary + |Content-Disposition: form-data; name="items" + |Content-Type: text/plain + | + | + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + @Test + fun multipartFormData_serializesEmptyObject() { + val body = + multipartFormData( + jsonMapper(), + mapOf( + "required" to + MultipartField.builder() + .value("present") + .contentType("text/plain") + .build(), + "meta" to + MultipartField.builder>() + .value(emptyMap()) + .contentType("text/plain") + .build(), + ), + ) + + val output = ByteArrayOutputStream() + body.writeTo(output) + + assertThat(body.repeatable()).isTrue() + assertThat(body.contentLength()).isEqualTo(output.size().toLong()) + val boundary = boundary(body) + assertThat(output.toString("UTF-8")) + .isEqualTo( + """ + |--$boundary + |Content-Disposition: form-data; name="required" + |Content-Type: text/plain + | + |present + |--$boundary-- + | + """ + .trimMargin() + .replace("\n", "\r\n") + ) + } + + private fun boundary(body: HttpRequestBody): String = + body.contentType()!!.substringAfter("multipart/form-data; boundary=") +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestTest.kt new file mode 100644 index 0000000..06e315d --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestTest.kt @@ -0,0 +1,110 @@ +package com.cas_parser.api.core.http + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class HttpRequestTest { + + enum class UrlTestCase(val request: HttpRequest, val expectedUrl: String) { + BASE_URL_ONLY( + HttpRequest.builder().method(HttpMethod.GET).baseUrl("https://api.example.com").build(), + expectedUrl = "https://api.example.com", + ), + BASE_URL_WITH_TRAILING_SLASH( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com/") + .build(), + expectedUrl = "https://api.example.com/", + ), + SINGLE_PATH_SEGMENT( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .build(), + expectedUrl = "https://api.example.com/users", + ), + MULTIPLE_PATH_SEGMENTS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegments("users", "123", "profile") + .build(), + expectedUrl = "https://api.example.com/users/123/profile", + ), + PATH_SEGMENT_WITH_SPECIAL_CHARS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("user name") + .build(), + expectedUrl = "https://api.example.com/user+name", + ), + SINGLE_QUERY_PARAM( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParam("limit", "10") + .build(), + expectedUrl = "https://api.example.com/users?limit=10", + ), + MULTIPLE_QUERY_PARAMS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParam("limit", "10") + .putQueryParam("offset", "20") + .build(), + expectedUrl = "https://api.example.com/users?limit=10&offset=20", + ), + QUERY_PARAM_WITH_SPECIAL_CHARS( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("search") + .putQueryParam("q", "hello world") + .build(), + expectedUrl = "https://api.example.com/search?q=hello+world", + ), + MULTIPLE_VALUES_SAME_PARAM( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("users") + .putQueryParams("tags", listOf("admin", "user")) + .build(), + expectedUrl = "https://api.example.com/users?tags=admin&tags=user", + ), + BASE_URL_WITH_TRAILING_SLASH_AND_PATH( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com/") + .addPathSegment("users") + .build(), + expectedUrl = "https://api.example.com/users", + ), + COMPLEX_URL( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl("https://api.example.com") + .addPathSegments("v1", "users", "123") + .putQueryParams("include", listOf("profile", "settings")) + .putQueryParam("format", "json") + .build(), + expectedUrl = + "https://api.example.com/v1/users/123?include=profile&include=settings&format=json", + ), + } + + @ParameterizedTest + @EnumSource + fun url(testCase: UrlTestCase) { + val actualUrl = testCase.request.url() + + assertThat(actualUrl).isEqualTo(testCase.expectedUrl) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/LoggingHttpClientTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/LoggingHttpClientTest.kt new file mode 100644 index 0000000..ad94107 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/LoggingHttpClientTest.kt @@ -0,0 +1,1000 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core.http + +import com.cas_parser.api.core.LogLevel +import com.cas_parser.api.core.RequestOptions +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.io.PrintStream +import java.nio.charset.StandardCharsets +import java.time.Clock +import java.time.Instant +import java.time.ZoneOffset +import java.util.concurrent.CompletableFuture +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +@ResourceLock("stderr") +internal class LoggingHttpClientTest { + + private lateinit var originalErr: PrintStream + private lateinit var errContent: ByteArrayOutputStream + + @BeforeEach + fun beforeEach() { + originalErr = System.err + errContent = ByteArrayOutputStream() + System.setErr(PrintStream(errContent)) + } + + @AfterEach + fun afterEach() { + System.setErr(originalErr) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun offLevel_noOutput(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.OFF) + + val response = client.execute(simpleGetRequest(), async).apply { body().readBytes() } + + assertThat(response.statusCode()).isEqualTo(200) + assertThat(stderrOutput()).isEmpty() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsGetRequest(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.INFO) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsPostRequestWithBodySize(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.INFO) + + client.execute(postRequestWithBody("""{"key":"value"}"""), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> POST https://api.example.com/v1/resources (15-byte body) + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsRequestWithUnknownLengthBody(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.INFO) + + client + .execute(postRequestWithBody("""{"key":"value"}""", contentLength = -1L), async) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> POST https://api.example.com/v1/resources (unknown-length body) + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsResponseStatusAndDuration(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:00:01.234Z"), + ) + val client = loggingClient(fakeHttpClient(statusCode = 201), LogLevel.INFO, clock) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 201 (1s 234ms) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_logsResponseContentLength(async: Boolean) { + val headers = + Headers.builder().put("Content-Length", "42").put("Content-Type", "text/plain").build() + val client = loggingClient(fakeHttpClient(responseHeaders = headers), LogLevel.INFO) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (0s, 42-byte body) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_doesNotLogHeaders(async: Boolean) { + val headers = Headers.builder().put("X-Custom", "visible").build() + val client = loggingClient(fakeHttpClient(responseHeaders = headers), LogLevel.INFO) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("v1") + .putHeader("X-Request-Custom", "req-value") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1 + |<-- 200 (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsGetWithEndMarker(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.DEBUG) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsRequestAndResponseHeaders(async: Boolean) { + val responseHeaders = + Headers.builder() + .put("X-Response-Id", "abc-123") + .put("Content-Type", "text/plain") + .build() + val client = + loggingClient(fakeHttpClient(responseHeaders = responseHeaders), LogLevel.DEBUG) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("test") + .putHeader("X-Custom", "my-value") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/test + |X-Custom: my-value + |--> END GET + | + |<-- 200 (0s) + |Content-Type: text/plain + |X-Response-Id: abc-123 + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_redactsSensitiveHeaders(async: Boolean) { + val client = + loggingClient( + fakeHttpClient(), + LogLevel.DEBUG, + redactedHeaders = setOf("Authorization", "X-Secret"), + ) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("test") + .putHeader("Authorization", "Bearer token-123") + .putHeader("X-Secret", "secret-value") + .putHeader("X-Public", "public-value") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/test + |Authorization: ██ + |X-Public: public-value + |X-Secret: ██ + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_redactsHeadersCaseInsensitively(async: Boolean) { + val client = + loggingClient( + fakeHttpClient(), + LogLevel.DEBUG, + redactedHeaders = setOf("Authorization"), + ) + + client + .execute( + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("test") + .putHeader("authorization", "Bearer secret") + .build(), + async, + ) + .body() + .readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/test + |authorization: ██ + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsRequestBody(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.DEBUG) + val body = """{"name":"test","value":42}""" + + client.execute(postRequestWithBody(body), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> POST https://api.example.com/v1/resources (26-byte body) + | + |{"name":"test","value":42} + |--> END POST (26-byte body) + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsResponseBody(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + val response = client.execute(simpleGetRequest(), async) + val body = response.body().readBytes().toString(StandardCharsets.UTF_8) + + assertThat(body).isEqualTo(responseBody) + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + | + |{"id":1,"status":"ok"} + |<-- END HTTP (22-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsBinaryResponseBodyAsOmitted(async: Boolean) { + val binaryBody = ByteArray(256) { it.toByte() } + val client = loggingClient(fakeHttpClient(responseBody = binaryBody), LogLevel.DEBUG) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + | + |(binary body omitted) + |<-- END HTTP (256-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsMultilineResponseBody(async: Boolean) { + val multilineBody = "line1\nline2\nline3" + val headers = Headers.builder().put("Content-Type", "text/plain; charset=utf-8").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = multilineBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: text/plain; charset=utf-8 + | + |line1 + |line2 + |line3 + |<-- END HTTP (17-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsResponseBodyWithExplicitCharset(async: Boolean) { + val responseBody = "héllo wörld" + val headers = Headers.builder().put("Content-Type", "text/plain; charset=utf-8").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: text/plain; charset=utf-8 + | + |héllo wörld + |<-- END HTTP (13-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsResponseBodyWithNoContentType(async: Boolean) { + val responseBody = "plain text body" + val client = + loggingClient( + fakeHttpClient(responseBody = responseBody.toByteArray(StandardCharsets.UTF_8)), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + | + |plain text body + |<-- END HTTP (15-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEmptyResponseBody(async: Boolean) { + val client = loggingClient(fakeHttpClient(), LogLevel.DEBUG) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |<-- END HTTP (0-byte body) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEndHttpMarkerOnEarlyClose(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + val body = client.execute(simpleGetRequest(), async).body() + body.read(ByteArray(5)) + body.close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + | + |{"id" + |<-- END HTTP (5-byte body, closed early) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEndHttpMarkerOnCloseWithoutReading(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).body().close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + |<-- END HTTP (0-byte body, closed early) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsEndHttpMarkerWhenResponseClosedAfterPartialRead(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + val response = client.execute(simpleGetRequest(), async) + response.body().read(ByteArray(5)) + response.close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + | + |{"id" + |<-- END HTTP (5-byte body, closed early) + | + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_doesNotLogEndHttpMarkerWhenResponseClosedWithoutBodyAccess(async: Boolean) { + val responseBody = """{"id":1,"status":"ok"}""" + val headers = Headers.builder().put("Content-Type", "application/json").build() + val client = + loggingClient( + fakeHttpClient( + responseHeaders = headers, + responseBody = responseBody.toByteArray(StandardCharsets.UTF_8), + ), + LogLevel.DEBUG, + ) + + client.execute(simpleGetRequest(), async).close() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- 200 (0s) + |Content-Type: application/json + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun errorLevel_logsRequestFailure(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:00:01.234Z"), + ) + val client = + loggingClient( + failingHttpClient(IOException("Connection refused")), + LogLevel.ERROR, + clock, + ) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- !! IOException: Connection refused (1s 234ms) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun infoLevel_doesNotLogRequestFailure(async: Boolean) { + val client = + loggingClient(failingHttpClient(IOException("Connection refused")), LogLevel.INFO) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun debugLevel_logsRequestFailureAfterHeaders(async: Boolean) { + val client = + loggingClient(failingHttpClient(IOException("Connection refused")), LogLevel.DEBUG) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |--> END GET + | + |<-- !! IOException: Connection refused (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun errorLevel_logsRequestFailureWithoutMessage(async: Boolean) { + val client = loggingClient(failingHttpClient(IOException()), LogLevel.ERROR) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- !! IOException (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun offLevel_doesNotLogRequestFailure(async: Boolean) { + val client = + loggingClient(failingHttpClient(IOException("Connection refused")), LogLevel.OFF) + + assertThatThrownBy { client.execute(simpleGetRequest(), async) } + + assertThat(stderrOutput()).isEmpty() + } + + @Test + fun errorLevel_logsExecuteAsyncSynchronousThrow() { + val error = IOException("Connection refused") + val client = + loggingClient( + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = throw UnsupportedOperationException() + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = throw error + + override fun close() {} + }, + LogLevel.ERROR, + ) + + assertThatThrownBy { client.execute(simpleGetRequest(), async = true) }.isSameAs(error) + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- !! IOException: Connection refused (0s) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun durationFormat_seconds(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:00:02.500Z"), + ) + val client = loggingClient(fakeHttpClient(), LogLevel.INFO, clock) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (2s 500ms) + |""" + .trimMargin() + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun durationFormat_minutesAndSeconds(async: Boolean) { + val clock = + clockFrom( + Instant.parse("1998-04-21T00:00:00Z"), + Instant.parse("1998-04-21T00:01:40.467Z"), + ) + val client = loggingClient(fakeHttpClient(), LogLevel.INFO, clock) + + client.execute(simpleGetRequest(), async).body().readBytes() + + assertThat(stderrOutput()) + .isEqualTo( + """ + |--> GET https://api.example.com/v1/resources + |<-- 200 (1m 40s 467ms) + |""" + .trimMargin() + ) + } + + @Test + fun builder_toBuilder_roundtrips() { + val delegate = fakeHttpClient() + val clock = Clock.fixed(Instant.parse("1998-04-21T00:00:00Z"), ZoneOffset.UTC) + val client = + LoggingHttpClient.builder() + .httpClient(delegate) + .level(LogLevel.DEBUG) + .redactedHeaders(setOf("X-Secret")) + .clock(clock) + .build() + + val rebuilt = client.toBuilder().build() + + assertThat(rebuilt.httpClient).isSameAs(delegate) + assertThat(rebuilt.level).isEqualTo(LogLevel.DEBUG) + assertThat(rebuilt.redactedHeaders).containsExactly("X-Secret") + assertThat(rebuilt.clock).isEqualTo(clock) + } + + @Test + fun close_delegatesToUnderlyingClient() { + var closed = false + val delegate = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = throw UnsupportedOperationException() + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = throw UnsupportedOperationException() + + override fun close() { + closed = true + } + } + val client = loggingClient(delegate, LogLevel.OFF) + + client.close() + + assertThat(closed).isTrue() + } + + private fun stderrOutput(): String = errContent.toString("UTF-8") + + private fun loggingClient( + httpClient: HttpClient, + level: LogLevel, + clock: Clock = clockFrom(Instant.parse("1998-04-21T00:00:00Z")), + redactedHeaders: Set = + setOf("authorization", "api-key", "x-api-key", "cookie", "set-cookie"), + ): LoggingHttpClient = + LoggingHttpClient.builder() + .httpClient(httpClient) + .level(level) + .clock(clock) + .redactedHeaders(redactedHeaders) + .build() + + private fun simpleGetRequest(): HttpRequest = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl("https://api.example.com") + .addPathSegment("v1") + .addPathSegment("resources") + .build() + + private fun postRequestWithBody( + body: String, + contentType: String = "application/json", + contentLength: Long? = null, + ): HttpRequest = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl("https://api.example.com") + .addPathSegment("v1") + .addPathSegment("resources") + .body( + object : HttpRequestBody { + private val bytes = body.toByteArray(StandardCharsets.UTF_8) + + override fun writeTo(outputStream: OutputStream) { + outputStream.write(bytes) + } + + override fun contentType(): String = contentType + + override fun contentLength(): Long = contentLength ?: bytes.size.toLong() + + override fun repeatable(): Boolean = true + + override fun close() {} + } + ) + .build() + + private fun fakeHttpClient( + statusCode: Int = 200, + responseHeaders: Headers = Headers.builder().build(), + responseBody: ByteArray = ByteArray(0), + ): HttpClient = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + // Consume the request body if present to trigger logging. + request.body?.let { + val out = ByteArrayOutputStream() + it.writeTo(out) + } + return fakeResponse(statusCode, responseHeaders, responseBody) + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = + CompletableFuture.completedFuture(execute(request, requestOptions)) + + override fun close() {} + } + + private fun failingHttpClient(error: Throwable): HttpClient = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + request.body?.let { + val out = ByteArrayOutputStream() + it.writeTo(out) + } + throw error + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val future = CompletableFuture() + future.completeExceptionally(error) + return future + } + + override fun close() {} + } + + private fun fakeResponse(statusCode: Int, headers: Headers, body: ByteArray): HttpResponse = + object : HttpResponse { + override fun statusCode(): Int = statusCode + + override fun headers(): Headers = headers + + override fun body(): InputStream = ByteArrayInputStream(body) + + override fun close() {} + } + + private fun clockFrom(vararg instants: Instant): Clock = + object : Clock() { + private var index = 0 + + override fun getZone() = ZoneOffset.UTC + + override fun withZone(zone: java.time.ZoneId?) = this + + override fun instant(): Instant { + val instant = instants[index % instants.size] + index++ + return instant + } + } + + private fun HttpClient.execute(request: HttpRequest, async: Boolean): HttpResponse = + if (async) executeAsync(request).get() else execute(request) +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/QueryParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/QueryParamsTest.kt new file mode 100644 index 0000000..8ad1fbc --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/QueryParamsTest.kt @@ -0,0 +1,180 @@ +package com.cas_parser.api.core.http + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class QueryParamsTest { + + enum class TestCase( + val queryParams: QueryParams, + val expectedMap: Map>, + val expectedSize: Int, + ) { + EMPTY(QueryParams.builder().build(), expectedMap = mapOf(), expectedSize = 0), + PUT_ONE( + QueryParams.builder().put("key", "value").build(), + expectedMap = mapOf("key" to listOf("value")), + expectedSize = 1, + ), + PUT_MULTIPLE( + QueryParams.builder().put("key", listOf("value1", "value2")).build(), + expectedMap = mapOf("key" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT( + QueryParams.builder().put("key1", "value").put("key2", "value").build(), + expectedMap = mapOf("key1" to listOf("value"), "key2" to listOf("value")), + expectedSize = 2, + ), + MULTIPLE_PUT_SAME_NAME( + QueryParams.builder().put("key", "value1").put("key", "value2").build(), + expectedMap = mapOf("key" to listOf("value1", "value2")), + expectedSize = 2, + ), + MULTIPLE_PUT_MULTIPLE( + QueryParams.builder() + .put("key", listOf("value1", "value2")) + .put("key", listOf("value1", "value2")) + .build(), + expectedMap = mapOf("key" to listOf("value1", "value2", "value1", "value2")), + expectedSize = 4, + ), + PUT_ALL_MAP( + QueryParams.builder() + .putAll( + mapOf( + "key1" to listOf("value1", "value2"), + "key2" to listOf("value1", "value2"), + ) + ) + .build(), + expectedMap = + mapOf("key1" to listOf("value1", "value2"), "key2" to listOf("value1", "value2")), + expectedSize = 4, + ), + PUT_ALL_HEADERS( + QueryParams.builder().putAll(QueryParams.builder().put("key", "value").build()).build(), + expectedMap = mapOf("key" to listOf("value")), + expectedSize = 1, + ), + REMOVE_ABSENT( + QueryParams.builder().remove("key").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_ONE( + QueryParams.builder().put("key", "value").remove("key").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_PRESENT_MULTIPLE( + QueryParams.builder().put("key", listOf("value1", "value2")).remove("key").build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REMOVE_ALL( + QueryParams.builder() + .put("key1", "value") + .put("key3", "value") + .removeAll(setOf("key1", "key2", "key3")) + .build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + CLEAR( + QueryParams.builder().put("key1", "value").put("key2", "value").clear().build(), + expectedMap = mapOf(), + expectedSize = 0, + ), + REPLACE_ONE_ABSENT( + QueryParams.builder().replace("key", "value").build(), + expectedMap = mapOf("key" to listOf("value")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_ONE( + QueryParams.builder().put("key", "value1").replace("key", "value2").build(), + expectedMap = mapOf("key" to listOf("value2")), + expectedSize = 1, + ), + REPLACE_ONE_PRESENT_MULTIPLE( + QueryParams.builder() + .put("key", listOf("value1", "value2")) + .replace("key", "value3") + .build(), + expectedMap = mapOf("key" to listOf("value3")), + expectedSize = 1, + ), + REPLACE_MULTIPLE_ABSENT( + QueryParams.builder().replace("key", listOf("value1", "value2")).build(), + expectedMap = mapOf("key" to listOf("value1", "value2")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_ONE( + QueryParams.builder() + .put("key", "value1") + .replace("key", listOf("value2", "value3")) + .build(), + expectedMap = mapOf("key" to listOf("value2", "value3")), + expectedSize = 2, + ), + REPLACE_MULTIPLE_PRESENT_MULTIPLE( + QueryParams.builder() + .put("key", listOf("value1", "value2")) + .replace("key", listOf("value3", "value4")) + .build(), + expectedMap = mapOf("key" to listOf("value3", "value4")), + expectedSize = 2, + ), + REPLACE_ALL_MAP( + QueryParams.builder() + .put("key1", "value1") + .put("key2", "value1") + .put("key3", "value1") + .replaceAll(mapOf("key1" to listOf("value2"), "key3" to listOf("value2"))) + .build(), + expectedMap = + mapOf( + "key1" to listOf("value2"), + "key2" to listOf("value1"), + "key3" to listOf("value2"), + ), + expectedSize = 3, + ), + REPLACE_ALL_HEADERS( + QueryParams.builder() + .put("key1", "value1") + .put("key2", "value1") + .put("key3", "value1") + .replaceAll( + QueryParams.builder().put("key1", "value2").put("key3", "value2").build() + ) + .build(), + expectedMap = + mapOf( + "key1" to listOf("value2"), + "key2" to listOf("value1"), + "key3" to listOf("value2"), + ), + expectedSize = 3, + ), + } + + @ParameterizedTest + @EnumSource + fun keysAndValues(testCase: TestCase) { + val map = mutableMapOf>() + val queryParams = testCase.queryParams + queryParams.keys().forEach { key -> map[key] = queryParams.values(key) } + + assertThat(map).isEqualTo(testCase.expectedMap) + } + + @ParameterizedTest + @EnumSource + fun size(testCase: TestCase) { + val size = testCase.queryParams.size + + assertThat(size).isEqualTo(testCase.expectedSize) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/RetryingHttpClientTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/RetryingHttpClientTest.kt new file mode 100644 index 0000000..720423c --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/RetryingHttpClientTest.kt @@ -0,0 +1,525 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.core.http + +import com.cas_parser.api.client.okhttp.OkHttpClient +import com.cas_parser.api.core.RequestOptions +import com.cas_parser.api.core.Sleeper +import com.cas_parser.api.errors.CasParserRetryableException +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.matching +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.resetAllScenarios +import com.github.tomakehurst.wiremock.client.WireMock.serviceUnavailable +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo +import com.github.tomakehurst.wiremock.client.WireMock.verify +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import com.github.tomakehurst.wiremock.stubbing.Scenario +import java.io.InputStream +import java.time.Clock +import java.time.Duration +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.util.concurrent.CompletableFuture +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.parallel.ResourceLock +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class RetryingHttpClientTest { + + private var openResponseCount = 0 + private lateinit var baseUrl: String + private lateinit var httpClient: HttpClient + + private class RecordingSleeper : Sleeper { + val durations = mutableListOf() + + override fun sleep(duration: Duration) { + durations.add(duration) + } + + override fun sleepAsync(duration: Duration): CompletableFuture { + durations.add(duration) + return CompletableFuture.completedFuture(null) + } + + override fun close() {} + } + + @BeforeEach + fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { + baseUrl = wmRuntimeInfo.httpBaseUrl + val okHttpClient = OkHttpClient.builder().build() + httpClient = + object : HttpClient { + + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse = trackClose(okHttpClient.execute(request, requestOptions)) + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture = + okHttpClient.executeAsync(request, requestOptions).thenApply { trackClose(it) } + + override fun close() = okHttpClient.close() + + private fun trackClose(response: HttpResponse): HttpResponse { + openResponseCount++ + return object : HttpResponse { + + private var isClosed = false + + override fun statusCode(): Int = response.statusCode() + + override fun headers(): Headers = response.headers() + + override fun body(): InputStream = response.body() + + override fun close() { + response.close() + if (isClosed) { + return + } + openResponseCount-- + isClosed = true + } + } + } + } + resetAllScenarios() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).isEmpty() + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withIdempotencyHeader(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .withHeader("X-Some-Header", matching("stainless-java-retry-.+")) + .willReturn(ok()) + ) + val sleeper = RecordingSleeper() + val retryingClient = + retryingHttpClientBuilder(sleeper) + .maxRetries(2) + .idempotencyHeader("X-Some-Header") + .build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify(1, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).isEmpty() + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterHeader(async: Boolean) { + val retryAfterDate = "Wed, 21 Oct 2015 07:28:00 GMT" + stubFor( + post(urlPathEqualTo("/something")) + // First we fail with a retry after header given as a date + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn(serviceUnavailable().withHeader("Retry-After", retryAfterDate)) + .willSetStateTo("RETRY_AFTER_DATE") + ) + stubFor( + post(urlPathEqualTo("/something")) + // Then we fail with a retry after header given as a delay + .inScenario("foo") + .whenScenarioStateIs("RETRY_AFTER_DATE") + .willReturn(serviceUnavailable().withHeader("Retry-After", "1.234")) + .willSetStateTo("RETRY_AFTER_DELAY") + ) + stubFor( + post(urlPathEqualTo("/something")) + // Then we return a success + .inScenario("foo") + .whenScenarioStateIs("RETRY_AFTER_DELAY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + // Fix the clock to 5 seconds before the Retry-After date so the date-based backoff is + // deterministic. + val retryAfterDateTime = + OffsetDateTime.parse(retryAfterDate, DateTimeFormatter.RFC_1123_DATE_TIME) + val clock = Clock.fixed(retryAfterDateTime.minusSeconds(5).toInstant(), ZoneOffset.UTC) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper, clock).maxRetries(2).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("0")), + ) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("1")), + ) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("2")), + ) + assertThat(sleeper.durations) + .containsExactly(Duration.ofSeconds(5), Duration.ofMillis(1234)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withOverwrittenRetryCountHeader(async: Boolean) { + val retryAfterDate = "Wed, 21 Oct 2015 07:28:00 GMT" + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") // first we fail with a retry after header given as a date + .whenScenarioStateIs(Scenario.STARTED) + .willReturn(serviceUnavailable().withHeader("Retry-After", retryAfterDate)) + .willSetStateTo("RETRY_AFTER_DATE") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") // then we return a success + .whenScenarioStateIs("RETRY_AFTER_DATE") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val retryAfterDateTime = + OffsetDateTime.parse(retryAfterDate, DateTimeFormatter.RFC_1123_DATE_TIME) + val clock = Clock.fixed(retryAfterDateTime.minusSeconds(5).toInstant(), ZoneOffset.UTC) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper, clock).maxRetries(2).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .putHeader("x-stainless-retry-count", "42") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify( + 2, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("42")), + ) + assertThat(sleeper.durations).containsExactly(Duration.ofSeconds(5)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterMsHeader(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn(serviceUnavailable().withHeader("Retry-After-Ms", "10")) + .willSetStateTo("RETRY_AFTER_DELAY") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") // then we return a success + .whenScenarioStateIs("RETRY_AFTER_DELAY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(1).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify(2, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).containsExactly(Duration.ofMillis(10)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryableException(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(ok())) + + var callCount = 0 + val failingHttpClient = + object : HttpClient { + override fun execute( + request: HttpRequest, + requestOptions: RequestOptions, + ): HttpResponse { + callCount++ + if (callCount == 1) { + throw CasParserRetryableException("Simulated retryable failure") + } + return httpClient.execute(request, requestOptions) + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + callCount++ + if (callCount == 1) { + val future = CompletableFuture() + future.completeExceptionally( + CasParserRetryableException("Simulated retryable failure") + ) + return future + } + return httpClient.executeAsync(request, requestOptions) + } + + override fun close() = httpClient.close() + } + + val sleeper = RecordingSleeper() + val retryingClient = + RetryingHttpClient.builder() + .httpClient(failingHttpClient) + .maxRetries(2) + .sleeper(sleeper) + .build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + verify( + 1, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("1")), + ) + verify( + 0, + postRequestedFor(urlPathEqualTo("/something")) + .withHeader("x-stainless-retry-count", equalTo("0")), + ) + // Exponential backoff with jitter: 0.5s * jitter where jitter is in [0.75, 1.0]. + assertThat(sleeper.durations).hasSize(1) + assertThat(sleeper.durations[0]).isBetween(Duration.ofMillis(375), Duration.ofMillis(500)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withExponentialBackoff(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(serviceUnavailable())) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(3).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + // All retries exhausted; the last 503 response is returned. + assertThat(response.statusCode()).isEqualTo(503) + verify(4, postRequestedFor(urlPathEqualTo("/something"))) + // Exponential backoff with jitter: backoff = min(0.5 * 2^(retries-1), 8) * jitter where + // jitter is in [0.75, 1.0]. + assertThat(sleeper.durations).hasSize(3) + // retries=1: 0.5s * [0.75, 1.0] + assertThat(sleeper.durations[0]).isBetween(Duration.ofMillis(375), Duration.ofMillis(500)) + // retries=2: 1s * [0.75, 1.0] + assertThat(sleeper.durations[1]).isBetween(Duration.ofMillis(750), Duration.ofMillis(1000)) + // retries=3: 2s * [0.75, 1.0] + assertThat(sleeper.durations[2]).isBetween(Duration.ofMillis(1500), Duration.ofMillis(2000)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withExponentialBackoffCap(async: Boolean) { + stubFor(post(urlPathEqualTo("/something")).willReturn(serviceUnavailable())) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(6).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(503) + verify(7, postRequestedFor(urlPathEqualTo("/something"))) + assertThat(sleeper.durations).hasSize(6) + // retries=5: backoff hits the 8s cap * [0.75, 1.0] + assertThat(sleeper.durations[4]).isBetween(Duration.ofMillis(6000), Duration.ofMillis(8000)) + // retries=6: still capped at 8s * [0.75, 1.0] + assertThat(sleeper.durations[5]).isBetween(Duration.ofMillis(6000), Duration.ofMillis(8000)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterMsPriorityOverRetryAfter(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn( + serviceUnavailable() + .withHeader("Retry-After-Ms", "50") + .withHeader("Retry-After", "2") + ) + .willSetStateTo("RETRY") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs("RETRY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(1).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + // Retry-After-Ms (50ms) takes priority over Retry-After (2s). + assertThat(sleeper.durations).containsExactly(Duration.ofMillis(50)) + assertNoResponseLeaks() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun execute_withRetryAfterUnparseable(async: Boolean) { + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs(Scenario.STARTED) + .willReturn(serviceUnavailable().withHeader("Retry-After", "not-a-date-or-number")) + .willSetStateTo("RETRY") + ) + stubFor( + post(urlPathEqualTo("/something")) + .inScenario("foo") + .whenScenarioStateIs("RETRY") + .willReturn(ok()) + .willSetStateTo("COMPLETED") + ) + val sleeper = RecordingSleeper() + val retryingClient = retryingHttpClientBuilder(sleeper).maxRetries(1).build() + + val response = + retryingClient.execute( + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(baseUrl) + .addPathSegment("something") + .build(), + async, + ) + + assertThat(response.statusCode()).isEqualTo(200) + // Unparseable Retry-After falls through to exponential backoff. + assertThat(sleeper.durations).hasSize(1) + assertThat(sleeper.durations[0]).isBetween(Duration.ofMillis(375), Duration.ofMillis(500)) + assertNoResponseLeaks() + } + + private fun retryingHttpClientBuilder( + sleeper: RecordingSleeper, + clock: Clock = Clock.systemUTC(), + ) = RetryingHttpClient.builder().httpClient(httpClient).sleeper(sleeper).clock(clock) + + private fun HttpClient.execute(request: HttpRequest, async: Boolean): HttpResponse = + if (async) executeAsync(request).get() else execute(request) + + // When retrying, all failed responses should be closed. Only the final returned response should + // be open. + private fun assertNoResponseLeaks() = assertThat(openResponseCount).isEqualTo(1) +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateParamsTest.kt new file mode 100644 index 0000000..a6a696b --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateParamsTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.accesstoken + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class AccessTokenCreateParamsTest { + + @Test + fun create() { + AccessTokenCreateParams.builder().expiryMinutes(60L).build() + } + + @Test + fun body() { + val params = AccessTokenCreateParams.builder().expiryMinutes(60L).build() + + val body = params._body() + + assertThat(body.expiryMinutes()).contains(60L) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = AccessTokenCreateParams.builder().build() + + val body = params._body() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateResponseTest.kt new file mode 100644 index 0000000..09382b7 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/accesstoken/AccessTokenCreateResponseTest.kt @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.accesstoken + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class AccessTokenCreateResponseTest { + + @Test + fun create() { + val accessTokenCreateResponse = + AccessTokenCreateResponse.builder() + .accessToken("at_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") + .expiresIn(3600L) + .tokenType("api_key") + .build() + + assertThat(accessTokenCreateResponse.accessToken()) + .contains("at_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") + assertThat(accessTokenCreateResponse.expiresIn()).contains(3600L) + assertThat(accessTokenCreateResponse.tokenType()).contains("api_key") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val accessTokenCreateResponse = + AccessTokenCreateResponse.builder() + .accessToken("at_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...") + .expiresIn(3600L) + .tokenType("api_key") + .build() + + val roundtrippedAccessTokenCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(accessTokenCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedAccessTokenCreateResponse).isEqualTo(accessTokenCreateResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/CamsKfintechParseParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/CamsKfintechParseParamsTest.kt new file mode 100644 index 0000000..4cf0889 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/CamsKfintechParseParamsTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +import com.cas_parser.api.core.MultipartField +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CamsKfintechParseParamsTest { + + @Test + fun create() { + CamsKfintechParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + } + + @Test + fun body() { + val params = + CamsKfintechParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }) + .usingRecursiveComparison() + // TODO(AssertJ): Replace this and the `mapValues` below with: + // https://github.com/assertj/assertj/issues/3165 + .withEqualsForType( + { a, b -> a.readBytes() contentEquals b.readBytes() }, + InputStream::class.java, + ) + .isEqualTo( + mapOf( + "password" to MultipartField.of("password"), + "pdf_file" to MultipartField.of("pdf_file"), + "pdf_url" to MultipartField.of("https://example.com"), + ) + .mapValues { (_, field) -> + field.map { (it as? ByteArray)?.inputStream() ?: it } + } + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = CamsKfintechParseParams.builder().build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }).isEmpty() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/LinkedHolderTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/LinkedHolderTest.kt new file mode 100644 index 0000000..7382244 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/LinkedHolderTest.kt @@ -0,0 +1,33 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class LinkedHolderTest { + + @Test + fun create() { + val linkedHolder = LinkedHolder.builder().name("name").pan("pan").build() + + assertThat(linkedHolder.name()).contains("name") + assertThat(linkedHolder.pan()).contains("pan") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val linkedHolder = LinkedHolder.builder().name("name").pan("pan").build() + + val roundtrippedLinkedHolder = + jsonMapper.readValue( + jsonMapper.writeValueAsString(linkedHolder), + jacksonTypeRef(), + ) + + assertThat(roundtrippedLinkedHolder).isEqualTo(linkedHolder) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/TransactionTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/TransactionTest.kt new file mode 100644 index 0000000..e133f0c --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/TransactionTest.kt @@ -0,0 +1,94 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class TransactionTest { + + @Test + fun create() { + val transaction = + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + + assertThat(transaction.additionalInfo()) + .contains( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + assertThat(transaction.amount()).contains(0.0f) + assertThat(transaction.balance()).contains(0.0f) + assertThat(transaction.date()).contains(LocalDate.parse("2019-12-27")) + assertThat(transaction.description()).contains("description") + assertThat(transaction.dividendRate()).contains(0.0f) + assertThat(transaction.nav()).contains(0.0f) + assertThat(transaction.type()).contains(Transaction.Type.PURCHASE) + assertThat(transaction.units()).contains(0.0f) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val transaction = + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + + val roundtrippedTransaction = + jsonMapper.readValue( + jsonMapper.writeValueAsString(transaction), + jacksonTypeRef(), + ) + + assertThat(roundtrippedTransaction).isEqualTo(transaction) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/UnifiedResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/UnifiedResponseTest.kt new file mode 100644 index 0000000..ab37900 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/camskfintech/UnifiedResponseTest.kt @@ -0,0 +1,1199 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.camskfintech + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.LocalDate +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class UnifiedResponseTest { + + @Test + fun create() { + val unifiedResponse = + UnifiedResponse.builder() + .addDematAccount( + UnifiedResponse.DematAccount.builder() + .additionalInfo( + UnifiedResponse.DematAccount.AdditionalInfo.builder() + .boStatus("bo_status") + .boSubStatus("bo_sub_status") + .boType("bo_type") + .bsda("bsda") + .email("dev@stainless.com") + .addLinkedPan("string") + .nominee("nominee") + .status("status") + .build() + ) + .boId("bo_id") + .clientId("client_id") + .dematType(UnifiedResponse.DematAccount.DematType.NSDL) + .dpId("dp_id") + .dpName("dp_name") + .holdings( + UnifiedResponse.DematAccount.Holdings.builder() + .addAif( + UnifiedResponse.DematAccount.Holdings.Aif.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.Aif.AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addCorporateBond( + UnifiedResponse.DematAccount.Holdings.CorporateBond.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.CorporateBond + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addDematMutualFund( + UnifiedResponse.DematAccount.Holdings.DematMutualFund.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.DematMutualFund + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addEquity( + UnifiedResponse.DematAccount.Holdings.Equity.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.Equity + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addGovernmentSecurity( + UnifiedResponse.DematAccount.Holdings.GovernmentSecurity + .builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.GovernmentSecurity + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .build() + ) + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .value(0.0f) + .build() + ) + .insurance( + UnifiedResponse.Insurance.builder() + .addLifeInsurancePolicy( + UnifiedResponse.Insurance.LifeInsurancePolicy.builder() + .additionalInfo(JsonValue.from(mapOf())) + .lifeAssured("life_assured") + .policyName("policy_name") + .policyNumber("policy_number") + .premiumAmount(0.0f) + .premiumFrequency("premium_frequency") + .provider("provider") + .status("status") + .sumAssured(0.0f) + .build() + ) + .build() + ) + .investor( + UnifiedResponse.Investor.builder() + .address("address") + .casId("cas_id") + .email("dev@stainless.com") + .mobile("mobile") + .name("name") + .pan("pan") + .pincode("pincode") + .build() + ) + .meta( + UnifiedResponse.Meta.builder() + .casType(UnifiedResponse.Meta.CasType.NSDL) + .generatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .statementPeriod( + UnifiedResponse.Meta.StatementPeriod.builder() + .from(LocalDate.parse("2019-12-27")) + .to(LocalDate.parse("2019-12-27")) + .build() + ) + .build() + ) + .addMutualFund( + UnifiedResponse.MutualFund.builder() + .additionalInfo( + UnifiedResponse.MutualFund.AdditionalInfo.builder() + .kyc("kyc") + .pan("pan") + .pankyc("pankyc") + .build() + ) + .amc("amc") + .folioNumber("folio_number") + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .registrar("registrar") + .addScheme( + UnifiedResponse.MutualFund.Scheme.builder() + .additionalInfo( + UnifiedResponse.MutualFund.Scheme.AdditionalInfo.builder() + .advisor("advisor") + .amfi("amfi") + .closeUnits(0.0f) + .openUnits(0.0f) + .rtaCode("rta_code") + .build() + ) + .cost(0.0f) + .gain( + UnifiedResponse.MutualFund.Scheme.Gain.builder() + .absolute(0.0f) + .percentage(0.0f) + .build() + ) + .isin("isin") + .name("name") + .nav(0.0f) + .addNominee("string") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .type(UnifiedResponse.MutualFund.Scheme.Type.EQUITY) + .units(0.0f) + .value(0.0f) + .build() + ) + .value(0.0f) + .build() + ) + .addNp( + UnifiedResponse.Np.builder() + .additionalInfo(JsonValue.from(mapOf())) + .cra("cra") + .addFund( + UnifiedResponse.Np.Fund.builder() + .additionalInfo( + UnifiedResponse.Np.Fund.AdditionalInfo.builder() + .manager("manager") + .tier(UnifiedResponse.Np.Fund.AdditionalInfo.Tier._1) + .build() + ) + .cost(0.0f) + .name("name") + .nav(0.0f) + .units(0.0f) + .value(0.0f) + .build() + ) + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .pran("pran") + .value(0.0f) + .build() + ) + .summary( + UnifiedResponse.Summary.builder() + .accounts( + UnifiedResponse.Summary.Accounts.builder() + .demat( + UnifiedResponse.Summary.Accounts.Demat.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .insurance( + UnifiedResponse.Summary.Accounts.Insurance.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .mutualFunds( + UnifiedResponse.Summary.Accounts.MutualFunds.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .nps( + UnifiedResponse.Summary.Accounts.Nps.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .build() + ) + .totalValue(0.0f) + .build() + ) + .build() + + assertThat(unifiedResponse.dematAccounts().getOrNull()) + .containsExactly( + UnifiedResponse.DematAccount.builder() + .additionalInfo( + UnifiedResponse.DematAccount.AdditionalInfo.builder() + .boStatus("bo_status") + .boSubStatus("bo_sub_status") + .boType("bo_type") + .bsda("bsda") + .email("dev@stainless.com") + .addLinkedPan("string") + .nominee("nominee") + .status("status") + .build() + ) + .boId("bo_id") + .clientId("client_id") + .dematType(UnifiedResponse.DematAccount.DematType.NSDL) + .dpId("dp_id") + .dpName("dp_name") + .holdings( + UnifiedResponse.DematAccount.Holdings.builder() + .addAif( + UnifiedResponse.DematAccount.Holdings.Aif.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.Aif.AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addCorporateBond( + UnifiedResponse.DematAccount.Holdings.CorporateBond.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.CorporateBond + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addDematMutualFund( + UnifiedResponse.DematAccount.Holdings.DematMutualFund.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.DematMutualFund + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addEquity( + UnifiedResponse.DematAccount.Holdings.Equity.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.Equity.AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addGovernmentSecurity( + UnifiedResponse.DematAccount.Holdings.GovernmentSecurity.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.GovernmentSecurity + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .build() + ) + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .value(0.0f) + .build() + ) + assertThat(unifiedResponse.insurance()) + .contains( + UnifiedResponse.Insurance.builder() + .addLifeInsurancePolicy( + UnifiedResponse.Insurance.LifeInsurancePolicy.builder() + .additionalInfo(JsonValue.from(mapOf())) + .lifeAssured("life_assured") + .policyName("policy_name") + .policyNumber("policy_number") + .premiumAmount(0.0f) + .premiumFrequency("premium_frequency") + .provider("provider") + .status("status") + .sumAssured(0.0f) + .build() + ) + .build() + ) + assertThat(unifiedResponse.investor()) + .contains( + UnifiedResponse.Investor.builder() + .address("address") + .casId("cas_id") + .email("dev@stainless.com") + .mobile("mobile") + .name("name") + .pan("pan") + .pincode("pincode") + .build() + ) + assertThat(unifiedResponse.meta()) + .contains( + UnifiedResponse.Meta.builder() + .casType(UnifiedResponse.Meta.CasType.NSDL) + .generatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .statementPeriod( + UnifiedResponse.Meta.StatementPeriod.builder() + .from(LocalDate.parse("2019-12-27")) + .to(LocalDate.parse("2019-12-27")) + .build() + ) + .build() + ) + assertThat(unifiedResponse.mutualFunds().getOrNull()) + .containsExactly( + UnifiedResponse.MutualFund.builder() + .additionalInfo( + UnifiedResponse.MutualFund.AdditionalInfo.builder() + .kyc("kyc") + .pan("pan") + .pankyc("pankyc") + .build() + ) + .amc("amc") + .folioNumber("folio_number") + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .registrar("registrar") + .addScheme( + UnifiedResponse.MutualFund.Scheme.builder() + .additionalInfo( + UnifiedResponse.MutualFund.Scheme.AdditionalInfo.builder() + .advisor("advisor") + .amfi("amfi") + .closeUnits(0.0f) + .openUnits(0.0f) + .rtaCode("rta_code") + .build() + ) + .cost(0.0f) + .gain( + UnifiedResponse.MutualFund.Scheme.Gain.builder() + .absolute(0.0f) + .percentage(0.0f) + .build() + ) + .isin("isin") + .name("name") + .nav(0.0f) + .addNominee("string") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .type(UnifiedResponse.MutualFund.Scheme.Type.EQUITY) + .units(0.0f) + .value(0.0f) + .build() + ) + .value(0.0f) + .build() + ) + assertThat(unifiedResponse.nps().getOrNull()) + .containsExactly( + UnifiedResponse.Np.builder() + .additionalInfo(JsonValue.from(mapOf())) + .cra("cra") + .addFund( + UnifiedResponse.Np.Fund.builder() + .additionalInfo( + UnifiedResponse.Np.Fund.AdditionalInfo.builder() + .manager("manager") + .tier(UnifiedResponse.Np.Fund.AdditionalInfo.Tier._1) + .build() + ) + .cost(0.0f) + .name("name") + .nav(0.0f) + .units(0.0f) + .value(0.0f) + .build() + ) + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .pran("pran") + .value(0.0f) + .build() + ) + assertThat(unifiedResponse.summary()) + .contains( + UnifiedResponse.Summary.builder() + .accounts( + UnifiedResponse.Summary.Accounts.builder() + .demat( + UnifiedResponse.Summary.Accounts.Demat.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .insurance( + UnifiedResponse.Summary.Accounts.Insurance.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .mutualFunds( + UnifiedResponse.Summary.Accounts.MutualFunds.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .nps( + UnifiedResponse.Summary.Accounts.Nps.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .build() + ) + .totalValue(0.0f) + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val unifiedResponse = + UnifiedResponse.builder() + .addDematAccount( + UnifiedResponse.DematAccount.builder() + .additionalInfo( + UnifiedResponse.DematAccount.AdditionalInfo.builder() + .boStatus("bo_status") + .boSubStatus("bo_sub_status") + .boType("bo_type") + .bsda("bsda") + .email("dev@stainless.com") + .addLinkedPan("string") + .nominee("nominee") + .status("status") + .build() + ) + .boId("bo_id") + .clientId("client_id") + .dematType(UnifiedResponse.DematAccount.DematType.NSDL) + .dpId("dp_id") + .dpName("dp_name") + .holdings( + UnifiedResponse.DematAccount.Holdings.builder() + .addAif( + UnifiedResponse.DematAccount.Holdings.Aif.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.Aif.AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addCorporateBond( + UnifiedResponse.DematAccount.Holdings.CorporateBond.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.CorporateBond + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addDematMutualFund( + UnifiedResponse.DematAccount.Holdings.DematMutualFund.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.DematMutualFund + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addEquity( + UnifiedResponse.DematAccount.Holdings.Equity.builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.Equity + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .addGovernmentSecurity( + UnifiedResponse.DematAccount.Holdings.GovernmentSecurity + .builder() + .additionalInfo( + UnifiedResponse.DematAccount.Holdings.GovernmentSecurity + .AdditionalInfo + .builder() + .closeUnits(0.0f) + .openUnits(0.0f) + .build() + ) + .isin("isin") + .name("name") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .units(0.0f) + .value(0.0f) + .build() + ) + .build() + ) + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .value(0.0f) + .build() + ) + .insurance( + UnifiedResponse.Insurance.builder() + .addLifeInsurancePolicy( + UnifiedResponse.Insurance.LifeInsurancePolicy.builder() + .additionalInfo(JsonValue.from(mapOf())) + .lifeAssured("life_assured") + .policyName("policy_name") + .policyNumber("policy_number") + .premiumAmount(0.0f) + .premiumFrequency("premium_frequency") + .provider("provider") + .status("status") + .sumAssured(0.0f) + .build() + ) + .build() + ) + .investor( + UnifiedResponse.Investor.builder() + .address("address") + .casId("cas_id") + .email("dev@stainless.com") + .mobile("mobile") + .name("name") + .pan("pan") + .pincode("pincode") + .build() + ) + .meta( + UnifiedResponse.Meta.builder() + .casType(UnifiedResponse.Meta.CasType.NSDL) + .generatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .statementPeriod( + UnifiedResponse.Meta.StatementPeriod.builder() + .from(LocalDate.parse("2019-12-27")) + .to(LocalDate.parse("2019-12-27")) + .build() + ) + .build() + ) + .addMutualFund( + UnifiedResponse.MutualFund.builder() + .additionalInfo( + UnifiedResponse.MutualFund.AdditionalInfo.builder() + .kyc("kyc") + .pan("pan") + .pankyc("pankyc") + .build() + ) + .amc("amc") + .folioNumber("folio_number") + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .registrar("registrar") + .addScheme( + UnifiedResponse.MutualFund.Scheme.builder() + .additionalInfo( + UnifiedResponse.MutualFund.Scheme.AdditionalInfo.builder() + .advisor("advisor") + .amfi("amfi") + .closeUnits(0.0f) + .openUnits(0.0f) + .rtaCode("rta_code") + .build() + ) + .cost(0.0f) + .gain( + UnifiedResponse.MutualFund.Scheme.Gain.builder() + .absolute(0.0f) + .percentage(0.0f) + .build() + ) + .isin("isin") + .name("name") + .nav(0.0f) + .addNominee("string") + .addTransaction( + Transaction.builder() + .additionalInfo( + Transaction.AdditionalInfo.builder() + .capitalWithdrawal(0.0f) + .credit(0.0f) + .debit(0.0f) + .incomeDistribution(0.0f) + .orderNo("order_no") + .price(0.0f) + .stampDuty(0.0f) + .build() + ) + .amount(0.0f) + .balance(0.0f) + .date(LocalDate.parse("2019-12-27")) + .description("description") + .dividendRate(0.0f) + .nav(0.0f) + .type(Transaction.Type.PURCHASE) + .units(0.0f) + .build() + ) + .type(UnifiedResponse.MutualFund.Scheme.Type.EQUITY) + .units(0.0f) + .value(0.0f) + .build() + ) + .value(0.0f) + .build() + ) + .addNp( + UnifiedResponse.Np.builder() + .additionalInfo(JsonValue.from(mapOf())) + .cra("cra") + .addFund( + UnifiedResponse.Np.Fund.builder() + .additionalInfo( + UnifiedResponse.Np.Fund.AdditionalInfo.builder() + .manager("manager") + .tier(UnifiedResponse.Np.Fund.AdditionalInfo.Tier._1) + .build() + ) + .cost(0.0f) + .name("name") + .nav(0.0f) + .units(0.0f) + .value(0.0f) + .build() + ) + .addLinkedHolder(LinkedHolder.builder().name("name").pan("pan").build()) + .pran("pran") + .value(0.0f) + .build() + ) + .summary( + UnifiedResponse.Summary.builder() + .accounts( + UnifiedResponse.Summary.Accounts.builder() + .demat( + UnifiedResponse.Summary.Accounts.Demat.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .insurance( + UnifiedResponse.Summary.Accounts.Insurance.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .mutualFunds( + UnifiedResponse.Summary.Accounts.MutualFunds.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .nps( + UnifiedResponse.Summary.Accounts.Nps.builder() + .count(0L) + .totalValue(0.0f) + .build() + ) + .build() + ) + .totalValue(0.0f) + .build() + ) + .build() + + val roundtrippedUnifiedResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(unifiedResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedUnifiedResponse).isEqualTo(unifiedResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/CdslParsePdfParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/CdslParsePdfParamsTest.kt new file mode 100644 index 0000000..d848d88 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/CdslParsePdfParamsTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl + +import com.cas_parser.api.core.MultipartField +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CdslParsePdfParamsTest { + + @Test + fun create() { + CdslParsePdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + } + + @Test + fun body() { + val params = + CdslParsePdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }) + .usingRecursiveComparison() + // TODO(AssertJ): Replace this and the `mapValues` below with: + // https://github.com/assertj/assertj/issues/3165 + .withEqualsForType( + { a, b -> a.readBytes() contentEquals b.readBytes() }, + InputStream::class.java, + ) + .isEqualTo( + mapOf( + "password" to MultipartField.of("password"), + "pdf_file" to MultipartField.of("pdf_file"), + "pdf_url" to MultipartField.of("https://example.com"), + ) + .mapValues { (_, field) -> + field.map { (it as? ByteArray)?.inputStream() ?: it } + } + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = CdslParsePdfParams.builder().build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }).isEmpty() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpParamsTest.kt new file mode 100644 index 0000000..3f56894 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpParamsTest.kt @@ -0,0 +1,34 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class FetchRequestOtpParamsTest { + + @Test + fun create() { + FetchRequestOtpParams.builder() + .boId("1234567890123456") + .dob("1990-01-15") + .pan("ABCDE1234F") + .build() + } + + @Test + fun body() { + val params = + FetchRequestOtpParams.builder() + .boId("1234567890123456") + .dob("1990-01-15") + .pan("ABCDE1234F") + .build() + + val body = params._body() + + assertThat(body.boId()).isEqualTo("1234567890123456") + assertThat(body.dob()).isEqualTo("1990-01-15") + assertThat(body.pan()).isEqualTo("ABCDE1234F") + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpResponseTest.kt new file mode 100644 index 0000000..a63b5e2 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchRequestOtpResponseTest.kt @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class FetchRequestOtpResponseTest { + + @Test + fun create() { + val fetchRequestOtpResponse = + FetchRequestOtpResponse.builder() + .msg("OTP sent to registered mobile") + .sessionId("550e8400-e29b-41d4-a716-446655440000") + .status("success") + .build() + + assertThat(fetchRequestOtpResponse.msg()).contains("OTP sent to registered mobile") + assertThat(fetchRequestOtpResponse.sessionId()) + .contains("550e8400-e29b-41d4-a716-446655440000") + assertThat(fetchRequestOtpResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fetchRequestOtpResponse = + FetchRequestOtpResponse.builder() + .msg("OTP sent to registered mobile") + .sessionId("550e8400-e29b-41d4-a716-446655440000") + .status("success") + .build() + + val roundtrippedFetchRequestOtpResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fetchRequestOtpResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFetchRequestOtpResponse).isEqualTo(fetchRequestOtpResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpParamsTest.kt new file mode 100644 index 0000000..5b4f38d --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpParamsTest.kt @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class FetchVerifyOtpParamsTest { + + @Test + fun create() { + FetchVerifyOtpParams.builder().sessionId("session_id").otp("123456").numPeriods(6L).build() + } + + @Test + fun pathParams() { + val params = FetchVerifyOtpParams.builder().sessionId("session_id").otp("123456").build() + + assertThat(params._pathParam(0)).isEqualTo("session_id") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } + + @Test + fun body() { + val params = + FetchVerifyOtpParams.builder() + .sessionId("session_id") + .otp("123456") + .numPeriods(6L) + .build() + + val body = params._body() + + assertThat(body.otp()).isEqualTo("123456") + assertThat(body.numPeriods()).contains(6L) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = FetchVerifyOtpParams.builder().sessionId("session_id").otp("123456").build() + + val body = params._body() + + assertThat(body.otp()).isEqualTo("123456") + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpResponseTest.kt new file mode 100644 index 0000000..fbad184 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/cdsl/fetch/FetchVerifyOtpResponseTest.kt @@ -0,0 +1,67 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.cdsl.fetch + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class FetchVerifyOtpResponseTest { + + @Test + fun create() { + val fetchVerifyOtpResponse = + FetchVerifyOtpResponse.builder() + .addFile( + FetchVerifyOtpResponse.File.builder() + .filename("CDSL_CAS_1234567890123456_NOV2025.pdf") + .url( + "https://cdn.casparser.in/cdsl-cas/session-id/CDSL_CAS_1234567890123456_NOV2025.pdf" + ) + .build() + ) + .msg("Fetched 6 CAS files") + .status("success") + .build() + + assertThat(fetchVerifyOtpResponse.files().getOrNull()) + .containsExactly( + FetchVerifyOtpResponse.File.builder() + .filename("CDSL_CAS_1234567890123456_NOV2025.pdf") + .url( + "https://cdn.casparser.in/cdsl-cas/session-id/CDSL_CAS_1234567890123456_NOV2025.pdf" + ) + .build() + ) + assertThat(fetchVerifyOtpResponse.msg()).contains("Fetched 6 CAS files") + assertThat(fetchVerifyOtpResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val fetchVerifyOtpResponse = + FetchVerifyOtpResponse.builder() + .addFile( + FetchVerifyOtpResponse.File.builder() + .filename("CDSL_CAS_1234567890123456_NOV2025.pdf") + .url( + "https://cdn.casparser.in/cdsl-cas/session-id/CDSL_CAS_1234567890123456_NOV2025.pdf" + ) + .build() + ) + .msg("Fetched 6 CAS files") + .status("success") + .build() + + val roundtrippedFetchVerifyOtpResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(fetchVerifyOtpResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedFetchVerifyOtpResponse).isEqualTo(fetchVerifyOtpResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseParamsTest.kt new file mode 100644 index 0000000..71e01fd --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseParamsTest.kt @@ -0,0 +1,65 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.contractnote + +import com.cas_parser.api.core.MultipartField +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ContractNoteParseParamsTest { + + @Test + fun create() { + ContractNoteParseParams.builder() + .brokerType(ContractNoteParseParams.BrokerType.ZERODHA) + .password("FAXAK2545F") + .pdfFile("JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo...") + .pdfUrl("https://example.com/contract_note.pdf") + .build() + } + + @Test + fun body() { + val params = + ContractNoteParseParams.builder() + .brokerType(ContractNoteParseParams.BrokerType.ZERODHA) + .password("FAXAK2545F") + .pdfFile("JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo...") + .pdfUrl("https://example.com/contract_note.pdf") + .build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }) + .usingRecursiveComparison() + // TODO(AssertJ): Replace this and the `mapValues` below with: + // https://github.com/assertj/assertj/issues/3165 + .withEqualsForType( + { a, b -> a.readBytes() contentEquals b.readBytes() }, + InputStream::class.java, + ) + .isEqualTo( + mapOf( + "broker_type" to + MultipartField.of(ContractNoteParseParams.BrokerType.ZERODHA), + "password" to MultipartField.of("FAXAK2545F"), + "pdf_file" to + MultipartField.of("JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo..."), + "pdf_url" to MultipartField.of("https://example.com/contract_note.pdf"), + ) + .mapValues { (_, field) -> + field.map { (it as? ByteArray)?.inputStream() ?: it } + } + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = ContractNoteParseParams.builder().build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }).isEmpty() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseResponseTest.kt new file mode 100644 index 0000000..9d524f4 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/contractnote/ContractNoteParseResponseTest.kt @@ -0,0 +1,304 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.contractnote + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.LocalDate +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ContractNoteParseResponseTest { + + @Test + fun create() { + val contractNoteParseResponse = + ContractNoteParseResponse.builder() + .data( + ContractNoteParseResponse.Data.builder() + .brokerInfo( + ContractNoteParseResponse.Data.BrokerInfo.builder() + .brokerType( + ContractNoteParseResponse.Data.BrokerInfo.BrokerType.ZERODHA + ) + .name("Zerodha Broking Limited") + .sebiRegistration("INZ000031633") + .build() + ) + .chargesSummary( + ContractNoteParseResponse.Data.ChargesSummary.builder() + .cgst(0.0f) + .exchangeTransactionCharges(0.0f) + .igst(0.0f) + .netAmountReceivablePayable(0.0f) + .payInPayOutObligation(0.0f) + .sebiTurnoverFees(0.0f) + .securitiesTransactionTax(0.0f) + .sgst(0.0f) + .stampDuty(0.0f) + .taxableValueBrokerage(0.0f) + .build() + ) + .clientInfo( + ContractNoteParseResponse.Data.ClientInfo.builder() + .address("address") + .gstStateCode("7") + .name("VIRENDER KUMAR") + .pan("FAXAK2545F") + .placeOfSupply("DELHI") + .ucc("YS3654") + .build() + ) + .contractNoteInfo( + ContractNoteParseResponse.Data.ContractNoteInfo.builder() + .contractNoteNumber("CNT-25/26-73436720") + .settlementDate(LocalDate.parse("2025-08-06")) + .settlementNumber("2025149") + .tradeDate(LocalDate.parse("2025-08-05")) + .build() + ) + .addDerivativesTransaction( + ContractNoteParseResponse.Data.DerivativesTransaction.builder() + .brokeragePerUnit(0.0f) + .buySellBfCf("B") + .closingRatePerUnit(0.0f) + .contractDescription("NIFTY24802410DPE") + .netTotal(0.0f) + .quantity(0.0f) + .wapPerUnit(0.0f) + .build() + ) + .addDetailedTrade( + ContractNoteParseResponse.Data.DetailedTrade.builder() + .brokerage(0.0f) + .buySell("B") + .closingRatePerUnit(0.0f) + .exchange("NSE") + .netRatePerUnit(0.0f) + .netTotal(0.0f) + .orderNumber("1000000042939390") + .orderTime("13:13:13") + .quantity(0.0f) + .remarks("remarks") + .securityDescription("CASTROLIND-EQ/INE172A01027") + .tradeNumber("4006567") + .tradeTime("13:13:13") + .build() + ) + .addEquityTransaction( + ContractNoteParseResponse.Data.EquityTransaction.builder() + .buyQuantity(0.0f) + .buyTotalValue(0.0f) + .buyWap(0.0f) + .isin("INE172A01027") + .netObligation(0.0f) + .securityName("CASTROLIND") + .securitySymbol("CASTROLIND") + .sellQuantity(0.0f) + .sellTotalValue(0.0f) + .sellWap(0.0f) + .build() + ) + .build() + ) + .msg("success") + .status("success") + .build() + + assertThat(contractNoteParseResponse.data()) + .contains( + ContractNoteParseResponse.Data.builder() + .brokerInfo( + ContractNoteParseResponse.Data.BrokerInfo.builder() + .brokerType( + ContractNoteParseResponse.Data.BrokerInfo.BrokerType.ZERODHA + ) + .name("Zerodha Broking Limited") + .sebiRegistration("INZ000031633") + .build() + ) + .chargesSummary( + ContractNoteParseResponse.Data.ChargesSummary.builder() + .cgst(0.0f) + .exchangeTransactionCharges(0.0f) + .igst(0.0f) + .netAmountReceivablePayable(0.0f) + .payInPayOutObligation(0.0f) + .sebiTurnoverFees(0.0f) + .securitiesTransactionTax(0.0f) + .sgst(0.0f) + .stampDuty(0.0f) + .taxableValueBrokerage(0.0f) + .build() + ) + .clientInfo( + ContractNoteParseResponse.Data.ClientInfo.builder() + .address("address") + .gstStateCode("7") + .name("VIRENDER KUMAR") + .pan("FAXAK2545F") + .placeOfSupply("DELHI") + .ucc("YS3654") + .build() + ) + .contractNoteInfo( + ContractNoteParseResponse.Data.ContractNoteInfo.builder() + .contractNoteNumber("CNT-25/26-73436720") + .settlementDate(LocalDate.parse("2025-08-06")) + .settlementNumber("2025149") + .tradeDate(LocalDate.parse("2025-08-05")) + .build() + ) + .addDerivativesTransaction( + ContractNoteParseResponse.Data.DerivativesTransaction.builder() + .brokeragePerUnit(0.0f) + .buySellBfCf("B") + .closingRatePerUnit(0.0f) + .contractDescription("NIFTY24802410DPE") + .netTotal(0.0f) + .quantity(0.0f) + .wapPerUnit(0.0f) + .build() + ) + .addDetailedTrade( + ContractNoteParseResponse.Data.DetailedTrade.builder() + .brokerage(0.0f) + .buySell("B") + .closingRatePerUnit(0.0f) + .exchange("NSE") + .netRatePerUnit(0.0f) + .netTotal(0.0f) + .orderNumber("1000000042939390") + .orderTime("13:13:13") + .quantity(0.0f) + .remarks("remarks") + .securityDescription("CASTROLIND-EQ/INE172A01027") + .tradeNumber("4006567") + .tradeTime("13:13:13") + .build() + ) + .addEquityTransaction( + ContractNoteParseResponse.Data.EquityTransaction.builder() + .buyQuantity(0.0f) + .buyTotalValue(0.0f) + .buyWap(0.0f) + .isin("INE172A01027") + .netObligation(0.0f) + .securityName("CASTROLIND") + .securitySymbol("CASTROLIND") + .sellQuantity(0.0f) + .sellTotalValue(0.0f) + .sellWap(0.0f) + .build() + ) + .build() + ) + assertThat(contractNoteParseResponse.msg()).contains("success") + assertThat(contractNoteParseResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val contractNoteParseResponse = + ContractNoteParseResponse.builder() + .data( + ContractNoteParseResponse.Data.builder() + .brokerInfo( + ContractNoteParseResponse.Data.BrokerInfo.builder() + .brokerType( + ContractNoteParseResponse.Data.BrokerInfo.BrokerType.ZERODHA + ) + .name("Zerodha Broking Limited") + .sebiRegistration("INZ000031633") + .build() + ) + .chargesSummary( + ContractNoteParseResponse.Data.ChargesSummary.builder() + .cgst(0.0f) + .exchangeTransactionCharges(0.0f) + .igst(0.0f) + .netAmountReceivablePayable(0.0f) + .payInPayOutObligation(0.0f) + .sebiTurnoverFees(0.0f) + .securitiesTransactionTax(0.0f) + .sgst(0.0f) + .stampDuty(0.0f) + .taxableValueBrokerage(0.0f) + .build() + ) + .clientInfo( + ContractNoteParseResponse.Data.ClientInfo.builder() + .address("address") + .gstStateCode("7") + .name("VIRENDER KUMAR") + .pan("FAXAK2545F") + .placeOfSupply("DELHI") + .ucc("YS3654") + .build() + ) + .contractNoteInfo( + ContractNoteParseResponse.Data.ContractNoteInfo.builder() + .contractNoteNumber("CNT-25/26-73436720") + .settlementDate(LocalDate.parse("2025-08-06")) + .settlementNumber("2025149") + .tradeDate(LocalDate.parse("2025-08-05")) + .build() + ) + .addDerivativesTransaction( + ContractNoteParseResponse.Data.DerivativesTransaction.builder() + .brokeragePerUnit(0.0f) + .buySellBfCf("B") + .closingRatePerUnit(0.0f) + .contractDescription("NIFTY24802410DPE") + .netTotal(0.0f) + .quantity(0.0f) + .wapPerUnit(0.0f) + .build() + ) + .addDetailedTrade( + ContractNoteParseResponse.Data.DetailedTrade.builder() + .brokerage(0.0f) + .buySell("B") + .closingRatePerUnit(0.0f) + .exchange("NSE") + .netRatePerUnit(0.0f) + .netTotal(0.0f) + .orderNumber("1000000042939390") + .orderTime("13:13:13") + .quantity(0.0f) + .remarks("remarks") + .securityDescription("CASTROLIND-EQ/INE172A01027") + .tradeNumber("4006567") + .tradeTime("13:13:13") + .build() + ) + .addEquityTransaction( + ContractNoteParseResponse.Data.EquityTransaction.builder() + .buyQuantity(0.0f) + .buyTotalValue(0.0f) + .buyWap(0.0f) + .isin("INE172A01027") + .netObligation(0.0f) + .securityName("CASTROLIND") + .securitySymbol("CASTROLIND") + .sellQuantity(0.0f) + .sellTotalValue(0.0f) + .sellWap(0.0f) + .build() + ) + .build() + ) + .msg("success") + .status("success") + .build() + + val roundtrippedContractNoteParseResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(contractNoteParseResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedContractNoteParseResponse).isEqualTo(contractNoteParseResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/credits/CreditCheckParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/credits/CreditCheckParamsTest.kt new file mode 100644 index 0000000..b26664b --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/credits/CreditCheckParamsTest.kt @@ -0,0 +1,13 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.credits + +import org.junit.jupiter.api.Test + +internal class CreditCheckParamsTest { + + @Test + fun create() { + CreditCheckParams.builder().build() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/credits/CreditCheckResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/credits/CreditCheckResponseTest.kt new file mode 100644 index 0000000..17d52f9 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/credits/CreditCheckResponseTest.kt @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.credits + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class CreditCheckResponseTest { + + @Test + fun create() { + val creditCheckResponse = + CreditCheckResponse.builder() + .enabledFeatures( + listOf("cams_kfintech_cas_parser", "cdsl_cas_parser", "nsdl_cas_parser") + ) + .isUnlimited(false) + .limit(50L) + .remaining(35.0) + .resetsAt(OffsetDateTime.parse("2026-02-15T00:00:00Z")) + .used(15.0) + .build() + + assertThat(creditCheckResponse.enabledFeatures().getOrNull()) + .containsExactly("cams_kfintech_cas_parser", "cdsl_cas_parser", "nsdl_cas_parser") + assertThat(creditCheckResponse.isUnlimited()).contains(false) + assertThat(creditCheckResponse.limit()).contains(50L) + assertThat(creditCheckResponse.remaining()).contains(35.0) + assertThat(creditCheckResponse.resetsAt()) + .contains(OffsetDateTime.parse("2026-02-15T00:00:00Z")) + assertThat(creditCheckResponse.used()).contains(15.0) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val creditCheckResponse = + CreditCheckResponse.builder() + .enabledFeatures( + listOf("cams_kfintech_cas_parser", "cdsl_cas_parser", "nsdl_cas_parser") + ) + .isUnlimited(false) + .limit(50L) + .remaining(35.0) + .resetsAt(OffsetDateTime.parse("2026-02-15T00:00:00Z")) + .used(15.0) + .build() + + val roundtrippedCreditCheckResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(creditCheckResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCreditCheckResponse).isEqualTo(creditCheckResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateParamsTest.kt new file mode 100644 index 0000000..876cff9 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateParamsTest.kt @@ -0,0 +1,71 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.JsonValue +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailCreateParamsTest { + + @Test + fun create() { + InboundEmailCreateParams.builder() + .alias("john-portfolio") + .addAllowedSource(InboundEmailCreateParams.AllowedSource.CDSL) + .addAllowedSource(InboundEmailCreateParams.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .metadata( + InboundEmailCreateParams.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .putAdditionalProperty("source", JsonValue.from("onboarding")) + .build() + ) + .reference("user_12345") + .build() + } + + @Test + fun body() { + val params = + InboundEmailCreateParams.builder() + .alias("john-portfolio") + .addAllowedSource(InboundEmailCreateParams.AllowedSource.CDSL) + .addAllowedSource(InboundEmailCreateParams.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .metadata( + InboundEmailCreateParams.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .putAdditionalProperty("source", JsonValue.from("onboarding")) + .build() + ) + .reference("user_12345") + .build() + + val body = params._body() + + assertThat(body.alias()).contains("john-portfolio") + assertThat(body.allowedSources().getOrNull()) + .containsExactly( + InboundEmailCreateParams.AllowedSource.CDSL, + InboundEmailCreateParams.AllowedSource.NSDL, + ) + assertThat(body.callbackUrl()).contains("https://api.yourapp.com/webhooks/cas-email") + assertThat(body.metadata()) + .contains( + InboundEmailCreateParams.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .putAdditionalProperty("source", JsonValue.from("onboarding")) + .build() + ) + assertThat(body.reference()).contains("user_12345") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = InboundEmailCreateParams.builder().build() + + val body = params._body() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateResponseTest.kt new file mode 100644 index 0000000..1fbdbd1 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailCreateResponseTest.kt @@ -0,0 +1,89 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailCreateResponseTest { + + @Test + fun create() { + val inboundEmailCreateResponse = + InboundEmailCreateResponse.builder() + .addAllowedSource(InboundEmailCreateResponse.AllowedSource.CDSL) + .addAllowedSource(InboundEmailCreateResponse.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .createdAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .email("ie_a1b2c3d4e5f6@import.casparser.in") + .inboundEmailId("ie_a1b2c3d4e5f6") + .metadata( + InboundEmailCreateResponse.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + .reference("user_12345") + .status(InboundEmailCreateResponse.Status.ACTIVE) + .updatedAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .build() + + assertThat(inboundEmailCreateResponse.allowedSources().getOrNull()) + .containsExactly( + InboundEmailCreateResponse.AllowedSource.CDSL, + InboundEmailCreateResponse.AllowedSource.NSDL, + ) + assertThat(inboundEmailCreateResponse.callbackUrl()) + .contains("https://api.yourapp.com/webhooks/cas-email") + assertThat(inboundEmailCreateResponse.createdAt()) + .contains(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + assertThat(inboundEmailCreateResponse.email()) + .contains("ie_a1b2c3d4e5f6@import.casparser.in") + assertThat(inboundEmailCreateResponse.inboundEmailId()).contains("ie_a1b2c3d4e5f6") + assertThat(inboundEmailCreateResponse.metadata()) + .contains( + InboundEmailCreateResponse.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + assertThat(inboundEmailCreateResponse.reference()).contains("user_12345") + assertThat(inboundEmailCreateResponse.status()) + .contains(InboundEmailCreateResponse.Status.ACTIVE) + assertThat(inboundEmailCreateResponse.updatedAt()) + .contains(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboundEmailCreateResponse = + InboundEmailCreateResponse.builder() + .addAllowedSource(InboundEmailCreateResponse.AllowedSource.CDSL) + .addAllowedSource(InboundEmailCreateResponse.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .createdAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .email("ie_a1b2c3d4e5f6@import.casparser.in") + .inboundEmailId("ie_a1b2c3d4e5f6") + .metadata( + InboundEmailCreateResponse.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + .reference("user_12345") + .status(InboundEmailCreateResponse.Status.ACTIVE) + .updatedAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .build() + + val roundtrippedInboundEmailCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboundEmailCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboundEmailCreateResponse).isEqualTo(inboundEmailCreateResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteParamsTest.kt new file mode 100644 index 0000000..917b4ee --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailDeleteParamsTest { + + @Test + fun create() { + InboundEmailDeleteParams.builder().inboundEmailId("inbound_email_id").build() + } + + @Test + fun pathParams() { + val params = InboundEmailDeleteParams.builder().inboundEmailId("inbound_email_id").build() + + assertThat(params._pathParam(0)).isEqualTo("inbound_email_id") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteResponseTest.kt new file mode 100644 index 0000000..476763e --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailDeleteResponseTest.kt @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailDeleteResponseTest { + + @Test + fun create() { + val inboundEmailDeleteResponse = + InboundEmailDeleteResponse.builder() + .msg("Inbound email deleted successfully") + .status("success") + .build() + + assertThat(inboundEmailDeleteResponse.msg()).contains("Inbound email deleted successfully") + assertThat(inboundEmailDeleteResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboundEmailDeleteResponse = + InboundEmailDeleteResponse.builder() + .msg("Inbound email deleted successfully") + .status("success") + .build() + + val roundtrippedInboundEmailDeleteResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboundEmailDeleteResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboundEmailDeleteResponse).isEqualTo(inboundEmailDeleteResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListParamsTest.kt new file mode 100644 index 0000000..b0dfb9d --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListParamsTest.kt @@ -0,0 +1,49 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.http.QueryParams +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailListParamsTest { + + @Test + fun create() { + InboundEmailListParams.builder() + .limit(1L) + .offset(0L) + .status(InboundEmailListParams.Status.ACTIVE) + .build() + } + + @Test + fun queryParams() { + val params = + InboundEmailListParams.builder() + .limit(1L) + .offset(0L) + .status(InboundEmailListParams.Status.ACTIVE) + .build() + + val queryParams = params._queryParams() + + assertThat(queryParams) + .isEqualTo( + QueryParams.builder() + .put("limit", "1") + .put("offset", "0") + .put("status", "active") + .build() + ) + } + + @Test + fun queryParamsWithoutOptionalFields() { + val params = InboundEmailListParams.builder().build() + + val queryParams = params._queryParams() + + assertThat(queryParams).isEqualTo(QueryParams.builder().build()) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListResponseTest.kt new file mode 100644 index 0000000..ff243b8 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailListResponseTest.kt @@ -0,0 +1,105 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailListResponseTest { + + @Test + fun create() { + val inboundEmailListResponse = + InboundEmailListResponse.builder() + .addInboundEmail( + InboundEmailListResponse.InboundEmail.builder() + .addAllowedSource(InboundEmailListResponse.InboundEmail.AllowedSource.CDSL) + .addAllowedSource(InboundEmailListResponse.InboundEmail.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .createdAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .email("ie_a1b2c3d4e5f6@import.casparser.in") + .inboundEmailId("ie_a1b2c3d4e5f6") + .metadata( + InboundEmailListResponse.InboundEmail.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + .reference("user_12345") + .status(InboundEmailListResponse.InboundEmail.Status.ACTIVE) + .updatedAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .build() + ) + .limit(50L) + .offset(0L) + .status("success") + .total(15L) + .build() + + assertThat(inboundEmailListResponse.inboundEmails().getOrNull()) + .containsExactly( + InboundEmailListResponse.InboundEmail.builder() + .addAllowedSource(InboundEmailListResponse.InboundEmail.AllowedSource.CDSL) + .addAllowedSource(InboundEmailListResponse.InboundEmail.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .createdAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .email("ie_a1b2c3d4e5f6@import.casparser.in") + .inboundEmailId("ie_a1b2c3d4e5f6") + .metadata( + InboundEmailListResponse.InboundEmail.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + .reference("user_12345") + .status(InboundEmailListResponse.InboundEmail.Status.ACTIVE) + .updatedAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .build() + ) + assertThat(inboundEmailListResponse.limit()).contains(50L) + assertThat(inboundEmailListResponse.offset()).contains(0L) + assertThat(inboundEmailListResponse.status()).contains("success") + assertThat(inboundEmailListResponse.total()).contains(15L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboundEmailListResponse = + InboundEmailListResponse.builder() + .addInboundEmail( + InboundEmailListResponse.InboundEmail.builder() + .addAllowedSource(InboundEmailListResponse.InboundEmail.AllowedSource.CDSL) + .addAllowedSource(InboundEmailListResponse.InboundEmail.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .createdAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .email("ie_a1b2c3d4e5f6@import.casparser.in") + .inboundEmailId("ie_a1b2c3d4e5f6") + .metadata( + InboundEmailListResponse.InboundEmail.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + .reference("user_12345") + .status(InboundEmailListResponse.InboundEmail.Status.ACTIVE) + .updatedAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .build() + ) + .limit(50L) + .offset(0L) + .status("success") + .total(15L) + .build() + + val roundtrippedInboundEmailListResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboundEmailListResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboundEmailListResponse).isEqualTo(inboundEmailListResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveParamsTest.kt new file mode 100644 index 0000000..8d7abc0 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveParamsTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailRetrieveParamsTest { + + @Test + fun create() { + InboundEmailRetrieveParams.builder().inboundEmailId("inbound_email_id").build() + } + + @Test + fun pathParams() { + val params = InboundEmailRetrieveParams.builder().inboundEmailId("inbound_email_id").build() + + assertThat(params._pathParam(0)).isEqualTo("inbound_email_id") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveResponseTest.kt new file mode 100644 index 0000000..c6e9872 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inboundemail/InboundEmailRetrieveResponseTest.kt @@ -0,0 +1,89 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inboundemail + +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboundEmailRetrieveResponseTest { + + @Test + fun create() { + val inboundEmailRetrieveResponse = + InboundEmailRetrieveResponse.builder() + .addAllowedSource(InboundEmailRetrieveResponse.AllowedSource.CDSL) + .addAllowedSource(InboundEmailRetrieveResponse.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .createdAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .email("ie_a1b2c3d4e5f6@import.casparser.in") + .inboundEmailId("ie_a1b2c3d4e5f6") + .metadata( + InboundEmailRetrieveResponse.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + .reference("user_12345") + .status(InboundEmailRetrieveResponse.Status.ACTIVE) + .updatedAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .build() + + assertThat(inboundEmailRetrieveResponse.allowedSources().getOrNull()) + .containsExactly( + InboundEmailRetrieveResponse.AllowedSource.CDSL, + InboundEmailRetrieveResponse.AllowedSource.NSDL, + ) + assertThat(inboundEmailRetrieveResponse.callbackUrl()) + .contains("https://api.yourapp.com/webhooks/cas-email") + assertThat(inboundEmailRetrieveResponse.createdAt()) + .contains(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + assertThat(inboundEmailRetrieveResponse.email()) + .contains("ie_a1b2c3d4e5f6@import.casparser.in") + assertThat(inboundEmailRetrieveResponse.inboundEmailId()).contains("ie_a1b2c3d4e5f6") + assertThat(inboundEmailRetrieveResponse.metadata()) + .contains( + InboundEmailRetrieveResponse.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + assertThat(inboundEmailRetrieveResponse.reference()).contains("user_12345") + assertThat(inboundEmailRetrieveResponse.status()) + .contains(InboundEmailRetrieveResponse.Status.ACTIVE) + assertThat(inboundEmailRetrieveResponse.updatedAt()) + .contains(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboundEmailRetrieveResponse = + InboundEmailRetrieveResponse.builder() + .addAllowedSource(InboundEmailRetrieveResponse.AllowedSource.CDSL) + .addAllowedSource(InboundEmailRetrieveResponse.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .createdAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .email("ie_a1b2c3d4e5f6@import.casparser.in") + .inboundEmailId("ie_a1b2c3d4e5f6") + .metadata( + InboundEmailRetrieveResponse.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .build() + ) + .reference("user_12345") + .status(InboundEmailRetrieveResponse.Status.ACTIVE) + .updatedAt(OffsetDateTime.parse("2025-02-21T10:30:00Z")) + .build() + + val roundtrippedInboundEmailRetrieveResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboundEmailRetrieveResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboundEmailRetrieveResponse).isEqualTo(inboundEmailRetrieveResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusParamsTest.kt new file mode 100644 index 0000000..082807a --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusParamsTest.kt @@ -0,0 +1,25 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import com.cas_parser.api.core.http.Headers +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxCheckConnectionStatusParamsTest { + + @Test + fun create() { + InboxCheckConnectionStatusParams.builder().xInboxToken("x-inbox-token").build() + } + + @Test + fun headers() { + val params = InboxCheckConnectionStatusParams.builder().xInboxToken("x-inbox-token").build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo(Headers.builder().put("x-inbox-token", "x-inbox-token").build()) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusResponseTest.kt new file mode 100644 index 0000000..1c78969 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxCheckConnectionStatusResponseTest.kt @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxCheckConnectionStatusResponseTest { + + @Test + fun create() { + val inboxCheckConnectionStatusResponse = + InboxCheckConnectionStatusResponse.builder() + .connected(true) + .email("user@gmail.com") + .provider("gmail") + .status("success") + .build() + + assertThat(inboxCheckConnectionStatusResponse.connected()).contains(true) + assertThat(inboxCheckConnectionStatusResponse.email()).contains("user@gmail.com") + assertThat(inboxCheckConnectionStatusResponse.provider()).contains("gmail") + assertThat(inboxCheckConnectionStatusResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboxCheckConnectionStatusResponse = + InboxCheckConnectionStatusResponse.builder() + .connected(true) + .email("user@gmail.com") + .provider("gmail") + .status("success") + .build() + + val roundtrippedInboxCheckConnectionStatusResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboxCheckConnectionStatusResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboxCheckConnectionStatusResponse) + .isEqualTo(inboxCheckConnectionStatusResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailParamsTest.kt new file mode 100644 index 0000000..568051f --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailParamsTest.kt @@ -0,0 +1,43 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxConnectEmailParamsTest { + + @Test + fun create() { + InboxConnectEmailParams.builder() + .redirectUri("https://yourapp.com/oauth-callback") + .state("abc123") + .build() + } + + @Test + fun body() { + val params = + InboxConnectEmailParams.builder() + .redirectUri("https://yourapp.com/oauth-callback") + .state("abc123") + .build() + + val body = params._body() + + assertThat(body.redirectUri()).isEqualTo("https://yourapp.com/oauth-callback") + assertThat(body.state()).contains("abc123") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + InboxConnectEmailParams.builder() + .redirectUri("https://yourapp.com/oauth-callback") + .build() + + val body = params._body() + + assertThat(body.redirectUri()).isEqualTo("https://yourapp.com/oauth-callback") + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailResponseTest.kt new file mode 100644 index 0000000..2a2f511 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxConnectEmailResponseTest.kt @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxConnectEmailResponseTest { + + @Test + fun create() { + val inboxConnectEmailResponse = + InboxConnectEmailResponse.builder() + .expiresIn(600L) + .oauthUrl("https://accounts.google.com/o/oauth2/v2/auth?client_id=...") + .status("success") + .build() + + assertThat(inboxConnectEmailResponse.expiresIn()).contains(600L) + assertThat(inboxConnectEmailResponse.oauthUrl()) + .contains("https://accounts.google.com/o/oauth2/v2/auth?client_id=...") + assertThat(inboxConnectEmailResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboxConnectEmailResponse = + InboxConnectEmailResponse.builder() + .expiresIn(600L) + .oauthUrl("https://accounts.google.com/o/oauth2/v2/auth?client_id=...") + .status("success") + .build() + + val roundtrippedInboxConnectEmailResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboxConnectEmailResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboxConnectEmailResponse).isEqualTo(inboxConnectEmailResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailParamsTest.kt new file mode 100644 index 0000000..90269ad --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailParamsTest.kt @@ -0,0 +1,25 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import com.cas_parser.api.core.http.Headers +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxDisconnectEmailParamsTest { + + @Test + fun create() { + InboxDisconnectEmailParams.builder().xInboxToken("x-inbox-token").build() + } + + @Test + fun headers() { + val params = InboxDisconnectEmailParams.builder().xInboxToken("x-inbox-token").build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo(Headers.builder().put("x-inbox-token", "x-inbox-token").build()) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailResponseTest.kt new file mode 100644 index 0000000..ea186c6 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxDisconnectEmailResponseTest.kt @@ -0,0 +1,41 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxDisconnectEmailResponseTest { + + @Test + fun create() { + val inboxDisconnectEmailResponse = + InboxDisconnectEmailResponse.builder() + .msg("Email disconnected successfully") + .status("success") + .build() + + assertThat(inboxDisconnectEmailResponse.msg()).contains("Email disconnected successfully") + assertThat(inboxDisconnectEmailResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboxDisconnectEmailResponse = + InboxDisconnectEmailResponse.builder() + .msg("Email disconnected successfully") + .status("success") + .build() + + val roundtrippedInboxDisconnectEmailResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboxDisconnectEmailResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboxDisconnectEmailResponse).isEqualTo(inboxDisconnectEmailResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesParamsTest.kt new file mode 100644 index 0000000..1d56f77 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesParamsTest.kt @@ -0,0 +1,79 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import com.cas_parser.api.core.http.Headers +import java.time.LocalDate +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxListCasFilesParamsTest { + + @Test + fun create() { + InboxListCasFilesParams.builder() + .xInboxToken("x-inbox-token") + .addCasType(InboxListCasFilesParams.CasType.CDSL) + .addCasType(InboxListCasFilesParams.CasType.NSDL) + .endDate(LocalDate.parse("2025-12-31")) + .startDate(LocalDate.parse("2025-12-01")) + .build() + } + + @Test + fun headers() { + val params = + InboxListCasFilesParams.builder() + .xInboxToken("x-inbox-token") + .addCasType(InboxListCasFilesParams.CasType.CDSL) + .addCasType(InboxListCasFilesParams.CasType.NSDL) + .endDate(LocalDate.parse("2025-12-31")) + .startDate(LocalDate.parse("2025-12-01")) + .build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo(Headers.builder().put("x-inbox-token", "x-inbox-token").build()) + } + + @Test + fun headersWithoutOptionalFields() { + val params = InboxListCasFilesParams.builder().xInboxToken("x-inbox-token").build() + + val headers = params._headers() + + assertThat(headers) + .isEqualTo(Headers.builder().put("x-inbox-token", "x-inbox-token").build()) + } + + @Test + fun body() { + val params = + InboxListCasFilesParams.builder() + .xInboxToken("x-inbox-token") + .addCasType(InboxListCasFilesParams.CasType.CDSL) + .addCasType(InboxListCasFilesParams.CasType.NSDL) + .endDate(LocalDate.parse("2025-12-31")) + .startDate(LocalDate.parse("2025-12-01")) + .build() + + val body = params._body() + + assertThat(body.casTypes().getOrNull()) + .containsExactly( + InboxListCasFilesParams.CasType.CDSL, + InboxListCasFilesParams.CasType.NSDL, + ) + assertThat(body.endDate()).contains(LocalDate.parse("2025-12-31")) + assertThat(body.startDate()).contains(LocalDate.parse("2025-12-01")) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = InboxListCasFilesParams.builder().xInboxToken("x-inbox-token").build() + + val body = params._body() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesResponseTest.kt new file mode 100644 index 0000000..a491c06 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/inbox/InboxListCasFilesResponseTest.kt @@ -0,0 +1,87 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.inbox + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.LocalDate +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class InboxListCasFilesResponseTest { + + @Test + fun create() { + val inboxListCasFilesResponse = + InboxListCasFilesResponse.builder() + .count(5L) + .addFile( + InboxListCasFilesResponse.File.builder() + .casType(InboxListCasFilesResponse.File.CasType.CDSL) + .expiresIn(86400L) + .filename("cdsl_20250115_a1b2c3d4.pdf") + .messageDate(LocalDate.parse("2025-01-15")) + .messageId("18d4a2b3c4d5e6f7") + .originalFilename("CDSL_CAS_Statement.pdf") + .senderEmail("eCAS@cdslstatement.com") + .size(245000L) + .url( + "https://cdn.casparser.in/email-cas/user123/cdsl_20250115_a1b2c3d4.pdf" + ) + .build() + ) + .status("success") + .build() + + assertThat(inboxListCasFilesResponse.count()).contains(5L) + assertThat(inboxListCasFilesResponse.files().getOrNull()) + .containsExactly( + InboxListCasFilesResponse.File.builder() + .casType(InboxListCasFilesResponse.File.CasType.CDSL) + .expiresIn(86400L) + .filename("cdsl_20250115_a1b2c3d4.pdf") + .messageDate(LocalDate.parse("2025-01-15")) + .messageId("18d4a2b3c4d5e6f7") + .originalFilename("CDSL_CAS_Statement.pdf") + .senderEmail("eCAS@cdslstatement.com") + .size(245000L) + .url("https://cdn.casparser.in/email-cas/user123/cdsl_20250115_a1b2c3d4.pdf") + .build() + ) + assertThat(inboxListCasFilesResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val inboxListCasFilesResponse = + InboxListCasFilesResponse.builder() + .count(5L) + .addFile( + InboxListCasFilesResponse.File.builder() + .casType(InboxListCasFilesResponse.File.CasType.CDSL) + .expiresIn(86400L) + .filename("cdsl_20250115_a1b2c3d4.pdf") + .messageDate(LocalDate.parse("2025-01-15")) + .messageId("18d4a2b3c4d5e6f7") + .originalFilename("CDSL_CAS_Statement.pdf") + .senderEmail("eCAS@cdslstatement.com") + .size(245000L) + .url( + "https://cdn.casparser.in/email-cas/user123/cdsl_20250115_a1b2c3d4.pdf" + ) + .build() + ) + .status("success") + .build() + + val roundtrippedInboxListCasFilesResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(inboxListCasFilesResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedInboxListCasFilesResponse).isEqualTo(inboxListCasFilesResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasParamsTest.kt new file mode 100644 index 0000000..bed6b13 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasParamsTest.kt @@ -0,0 +1,58 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.kfintech + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class KfintechGenerateCasParamsTest { + + @Test + fun create() { + KfintechGenerateCasParams.builder() + .email("user@example.com") + .fromDate("2023-01-01") + .password("Abcdefghi12\$") + .toDate("2023-12-31") + .panNo("ABCDE1234F") + .build() + } + + @Test + fun body() { + val params = + KfintechGenerateCasParams.builder() + .email("user@example.com") + .fromDate("2023-01-01") + .password("Abcdefghi12\$") + .toDate("2023-12-31") + .panNo("ABCDE1234F") + .build() + + val body = params._body() + + assertThat(body.email()).isEqualTo("user@example.com") + assertThat(body.fromDate()).isEqualTo("2023-01-01") + assertThat(body.password()).isEqualTo("Abcdefghi12\$") + assertThat(body.toDate()).isEqualTo("2023-12-31") + assertThat(body.panNo()).contains("ABCDE1234F") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + KfintechGenerateCasParams.builder() + .email("user@example.com") + .fromDate("2023-01-01") + .password("Abcdefghi12\$") + .toDate("2023-12-31") + .build() + + val body = params._body() + + assertThat(body.email()).isEqualTo("user@example.com") + assertThat(body.fromDate()).isEqualTo("2023-01-01") + assertThat(body.password()).isEqualTo("Abcdefghi12\$") + assertThat(body.toDate()).isEqualTo("2023-12-31") + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasResponseTest.kt new file mode 100644 index 0000000..57b2d09 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/kfintech/KfintechGenerateCasResponseTest.kt @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.kfintech + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class KfintechGenerateCasResponseTest { + + @Test + fun create() { + val kfintechGenerateCasResponse = + KfintechGenerateCasResponse.builder() + .msg("CAS request submitted. Check email shortly.") + .status("success") + .build() + + assertThat(kfintechGenerateCasResponse.msg()) + .contains("CAS request submitted. Check email shortly.") + assertThat(kfintechGenerateCasResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val kfintechGenerateCasResponse = + KfintechGenerateCasResponse.builder() + .msg("CAS request submitted. Check email shortly.") + .status("success") + .build() + + val roundtrippedKfintechGenerateCasResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(kfintechGenerateCasResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedKfintechGenerateCasResponse).isEqualTo(kfintechGenerateCasResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogCreateParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogCreateParamsTest.kt new file mode 100644 index 0000000..e7212ba --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogCreateParamsTest.kt @@ -0,0 +1,42 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class LogCreateParamsTest { + + @Test + fun create() { + LogCreateParams.builder() + .endTime(OffsetDateTime.parse("2026-01-31T23:59:59Z")) + .limit(1L) + .startTime(OffsetDateTime.parse("2026-01-01T00:00:00Z")) + .build() + } + + @Test + fun body() { + val params = + LogCreateParams.builder() + .endTime(OffsetDateTime.parse("2026-01-31T23:59:59Z")) + .limit(1L) + .startTime(OffsetDateTime.parse("2026-01-01T00:00:00Z")) + .build() + + val body = params._body() + + assertThat(body.endTime()).contains(OffsetDateTime.parse("2026-01-31T23:59:59Z")) + assertThat(body.limit()).contains(1L) + assertThat(body.startTime()).contains(OffsetDateTime.parse("2026-01-01T00:00:00Z")) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = LogCreateParams.builder().build() + + val body = params._body() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogCreateResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogCreateResponseTest.kt new file mode 100644 index 0000000..0f24a21 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogCreateResponseTest.kt @@ -0,0 +1,74 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.OffsetDateTime +import kotlin.jvm.optionals.getOrNull +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class LogCreateResponseTest { + + @Test + fun create() { + val logCreateResponse = + LogCreateResponse.builder() + .count(25L) + .addLog( + LogCreateResponse.Log.builder() + .credits(1.0) + .feature("cdsl_cas_parser") + .path("/v4/cdsl/parse") + .requestId("req_2xYz7KpL8mN3Ab") + .statusCode(200L) + .timestamp(OffsetDateTime.parse("2026-01-15T14:30:00Z")) + .build() + ) + .status("success") + .build() + + assertThat(logCreateResponse.count()).contains(25L) + assertThat(logCreateResponse.logs().getOrNull()) + .containsExactly( + LogCreateResponse.Log.builder() + .credits(1.0) + .feature("cdsl_cas_parser") + .path("/v4/cdsl/parse") + .requestId("req_2xYz7KpL8mN3Ab") + .statusCode(200L) + .timestamp(OffsetDateTime.parse("2026-01-15T14:30:00Z")) + .build() + ) + assertThat(logCreateResponse.status()).contains("success") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val logCreateResponse = + LogCreateResponse.builder() + .count(25L) + .addLog( + LogCreateResponse.Log.builder() + .credits(1.0) + .feature("cdsl_cas_parser") + .path("/v4/cdsl/parse") + .requestId("req_2xYz7KpL8mN3Ab") + .statusCode(200L) + .timestamp(OffsetDateTime.parse("2026-01-15T14:30:00Z")) + .build() + ) + .status("success") + .build() + + val roundtrippedLogCreateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(logCreateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedLogCreateResponse).isEqualTo(logCreateResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogGetSummaryParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogGetSummaryParamsTest.kt new file mode 100644 index 0000000..a2cfd4a --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogGetSummaryParamsTest.kt @@ -0,0 +1,39 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class LogGetSummaryParamsTest { + + @Test + fun create() { + LogGetSummaryParams.builder() + .endTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .startTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + } + + @Test + fun body() { + val params = + LogGetSummaryParams.builder() + .endTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .startTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + + val body = params._body() + + assertThat(body.endTime()).contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(body.startTime()).contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = LogGetSummaryParams.builder().build() + + val body = params._body() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogGetSummaryResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogGetSummaryResponseTest.kt new file mode 100644 index 0000000..94e8bc7 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/logs/LogGetSummaryResponseTest.kt @@ -0,0 +1,78 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.logs + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class LogGetSummaryResponseTest { + + @Test + fun create() { + val logGetSummaryResponse = + LogGetSummaryResponse.builder() + .status("success") + .summary( + LogGetSummaryResponse.Summary.builder() + .addByFeature( + LogGetSummaryResponse.Summary.ByFeature.builder() + .credits(15.0) + .feature("cdsl_cas_parser") + .requests(15L) + .build() + ) + .totalCredits(45.5) + .totalRequests(42L) + .build() + ) + .build() + + assertThat(logGetSummaryResponse.status()).contains("success") + assertThat(logGetSummaryResponse.summary()) + .contains( + LogGetSummaryResponse.Summary.builder() + .addByFeature( + LogGetSummaryResponse.Summary.ByFeature.builder() + .credits(15.0) + .feature("cdsl_cas_parser") + .requests(15L) + .build() + ) + .totalCredits(45.5) + .totalRequests(42L) + .build() + ) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val logGetSummaryResponse = + LogGetSummaryResponse.builder() + .status("success") + .summary( + LogGetSummaryResponse.Summary.builder() + .addByFeature( + LogGetSummaryResponse.Summary.ByFeature.builder() + .credits(15.0) + .feature("cdsl_cas_parser") + .requests(15L) + .build() + ) + .totalCredits(45.5) + .totalRequests(42L) + .build() + ) + .build() + + val roundtrippedLogGetSummaryResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(logGetSummaryResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedLogGetSummaryResponse).isEqualTo(logGetSummaryResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/nsdl/NsdlParseParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/nsdl/NsdlParseParamsTest.kt new file mode 100644 index 0000000..a7631d5 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/nsdl/NsdlParseParamsTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.nsdl + +import com.cas_parser.api.core.MultipartField +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class NsdlParseParamsTest { + + @Test + fun create() { + NsdlParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + } + + @Test + fun body() { + val params = + NsdlParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }) + .usingRecursiveComparison() + // TODO(AssertJ): Replace this and the `mapValues` below with: + // https://github.com/assertj/assertj/issues/3165 + .withEqualsForType( + { a, b -> a.readBytes() contentEquals b.readBytes() }, + InputStream::class.java, + ) + .isEqualTo( + mapOf( + "password" to MultipartField.of("password"), + "pdf_file" to MultipartField.of("pdf_file"), + "pdf_url" to MultipartField.of("https://example.com"), + ) + .mapValues { (_, field) -> + field.map { (it as? ByteArray)?.inputStream() ?: it } + } + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = NsdlParseParams.builder().build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }).isEmpty() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/smart/SmartParseCasPdfParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/smart/SmartParseCasPdfParamsTest.kt new file mode 100644 index 0000000..1c1b2c2 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/smart/SmartParseCasPdfParamsTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.smart + +import com.cas_parser.api.core.MultipartField +import java.io.InputStream +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class SmartParseCasPdfParamsTest { + + @Test + fun create() { + SmartParseCasPdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + } + + @Test + fun body() { + val params = + SmartParseCasPdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }) + .usingRecursiveComparison() + // TODO(AssertJ): Replace this and the `mapValues` below with: + // https://github.com/assertj/assertj/issues/3165 + .withEqualsForType( + { a, b -> a.readBytes() contentEquals b.readBytes() }, + InputStream::class.java, + ) + .isEqualTo( + mapOf( + "password" to MultipartField.of("password"), + "pdf_file" to MultipartField.of("pdf_file"), + "pdf_url" to MultipartField.of("https://example.com"), + ) + .mapValues { (_, field) -> + field.map { (it as? ByteArray)?.inputStream() ?: it } + } + ) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = SmartParseCasPdfParams.builder().build() + + val body = params._body() + + assertThat(body.filterValues { !it.value.isNull() }).isEmpty() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyParamsTest.kt new file mode 100644 index 0000000..6e75d40 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyParamsTest.kt @@ -0,0 +1,13 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.verifytoken + +import org.junit.jupiter.api.Test + +internal class VerifyTokenVerifyParamsTest { + + @Test + fun create() { + VerifyTokenVerifyParams.builder().build() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyResponseTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyResponseTest.kt new file mode 100644 index 0000000..813bc45 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/models/verifytoken/VerifyTokenVerifyResponseTest.kt @@ -0,0 +1,44 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.models.verifytoken + +import com.cas_parser.api.core.jsonMapper +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class VerifyTokenVerifyResponseTest { + + @Test + fun create() { + val verifyTokenVerifyResponse = + VerifyTokenVerifyResponse.builder() + .error("Token has expired") + .maskedApiKey("abc1****ef23") + .valid(true) + .build() + + assertThat(verifyTokenVerifyResponse.error()).contains("Token has expired") + assertThat(verifyTokenVerifyResponse.maskedApiKey()).contains("abc1****ef23") + assertThat(verifyTokenVerifyResponse.valid()).contains(true) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val verifyTokenVerifyResponse = + VerifyTokenVerifyResponse.builder() + .error("Token has expired") + .maskedApiKey("abc1****ef23") + .valid(true) + .build() + + val roundtrippedVerifyTokenVerifyResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(verifyTokenVerifyResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedVerifyTokenVerifyResponse).isEqualTo(verifyTokenVerifyResponse) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/ErrorHandlingTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/ErrorHandlingTest.kt new file mode 100644 index 0000000..f58190a --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/ErrorHandlingTest.kt @@ -0,0 +1,349 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services + +import com.cas_parser.api.client.CasParserClient +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.core.http.Headers +import com.cas_parser.api.core.jsonMapper +import com.cas_parser.api.errors.BadRequestException +import com.cas_parser.api.errors.CasParserException +import com.cas_parser.api.errors.InternalServerException +import com.cas_parser.api.errors.NotFoundException +import com.cas_parser.api.errors.PermissionDeniedException +import com.cas_parser.api.errors.RateLimitException +import com.cas_parser.api.errors.UnauthorizedException +import com.cas_parser.api.errors.UnexpectedStatusCodeException +import com.cas_parser.api.errors.UnprocessableEntityException +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.status +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.entry +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.parallel.ResourceLock + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class ErrorHandlingTest { + + companion object { + + private val ERROR_JSON: JsonValue = JsonValue.from(mapOf("errorProperty" to "42")) + + private val ERROR_JSON_BYTES: ByteArray = jsonMapper().writeValueAsBytes(ERROR_JSON) + + private const val HEADER_NAME: String = "Error-Header" + + private const val HEADER_VALUE: String = "42" + + private const val NOT_JSON: String = "Not JSON" + } + + private lateinit var client: CasParserClient + + @BeforeEach + fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { + client = + CasParserOkHttpClient.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .apiKey("My API Key") + .build() + } + + @Test + fun creditsCheck400() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(400).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(400) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck400WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(400).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(400) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck401() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(401).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(401) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck401WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(401).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(401) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck403() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(403).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(403) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck403WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(403).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(403) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck404() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(404).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(404) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck404WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(404).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(404) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck422() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(422).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(422) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck422WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(422).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(422) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck429() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(429).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(429) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck429WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(429).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(429) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck500() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(500).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(500) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck500WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(500).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(500) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck999() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn( + status(999).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(999) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheck999WithRawResponse() { + val creditService = client.credits().withRawResponse() + stubFor( + post(anyUrl()) + .willReturn( + status(999).withHeader(HEADER_NAME, HEADER_VALUE).withBody(ERROR_JSON_BYTES) + ) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e.statusCode()).isEqualTo(999) + assertThat(e.headers().toMap()).contains(entry(HEADER_NAME, listOf(HEADER_VALUE))) + assertThat(e.body()).isEqualTo(ERROR_JSON) + } + + @Test + fun creditsCheckInvalidJsonBody() { + val creditService = client.credits() + stubFor( + post(anyUrl()) + .willReturn(status(200).withHeader(HEADER_NAME, HEADER_VALUE).withBody(NOT_JSON)) + ) + + val e = assertThrows { creditService.check() } + + assertThat(e).hasMessage("Error reading response") + } + + private fun Headers.toMap(): Map> = + mutableMapOf>().also { map -> + names().forEach { map[it] = values(it) } + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/ServiceParamsTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/ServiceParamsTest.kt new file mode 100644 index 0000000..4cdd376 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/ServiceParamsTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services + +import com.cas_parser.api.client.CasParserClient +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.models.credits.CreditCheckParams +import com.github.tomakehurst.wiremock.client.WireMock.anyUrl +import com.github.tomakehurst.wiremock.client.WireMock.equalTo +import com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath +import com.github.tomakehurst.wiremock.client.WireMock.ok +import com.github.tomakehurst.wiremock.client.WireMock.post +import com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor +import com.github.tomakehurst.wiremock.client.WireMock.stubFor +import com.github.tomakehurst.wiremock.client.WireMock.verify +import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo +import com.github.tomakehurst.wiremock.junit5.WireMockTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.parallel.ResourceLock + +@WireMockTest +@ResourceLock("https://github.com/wiremock/wiremock/issues/169") +internal class ServiceParamsTest { + + private lateinit var client: CasParserClient + + @BeforeEach + fun beforeEach(wmRuntimeInfo: WireMockRuntimeInfo) { + client = + CasParserOkHttpClient.builder() + .baseUrl(wmRuntimeInfo.httpBaseUrl) + .apiKey("My API Key") + .build() + } + + @Disabled("Mock server tests are disabled") + @Test + fun check() { + val creditService = client.credits() + stubFor(post(anyUrl()).willReturn(ok("{}"))) + + creditService.check( + CreditCheckParams.builder() + .putAdditionalHeader("Secret-Header", "42") + .putAdditionalQueryParam("secret_query_param", "42") + .putAdditionalBodyProperty("secretProperty", JsonValue.from("42")) + .build() + ) + + verify( + postRequestedFor(anyUrl()) + .withHeader("Secret-Header", equalTo("42")) + .withQueryParam("secret_query_param", equalTo("42")) + .withRequestBody(matchingJsonPath("$.secretProperty", equalTo("42"))) + ) + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncTest.kt new file mode 100644 index 0000000..c795a5c --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncTest.kt @@ -0,0 +1,26 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.accesstoken.AccessTokenCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class AccessTokenServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun create() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val accessTokenServiceAsync = client.accessToken() + + val accessTokenFuture = + accessTokenServiceAsync.create( + AccessTokenCreateParams.builder().expiryMinutes(60L).build() + ) + + val accessToken = accessTokenFuture.get() + accessToken.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncTest.kt new file mode 100644 index 0000000..046850e --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.camskfintech.CamsKfintechParseParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class CamsKfintechServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parse() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val camsKfintechServiceAsync = client.camsKfintech() + + val unifiedResponseFuture = + camsKfintechServiceAsync.parse( + CamsKfintechParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + val unifiedResponse = unifiedResponseFuture.get() + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncTest.kt new file mode 100644 index 0000000..35d78b1 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.cdsl.CdslParsePdfParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class CdslServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parsePdf() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val cdslServiceAsync = client.cdsl() + + val unifiedResponseFuture = + cdslServiceAsync.parsePdf( + CdslParsePdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + val unifiedResponse = unifiedResponseFuture.get() + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncTest.kt new file mode 100644 index 0000000..1d51efe --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncTest.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.contractnote.ContractNoteParseParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class ContractNoteServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parse() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val contractNoteServiceAsync = client.contractNote() + + val responseFuture = + contractNoteServiceAsync.parse( + ContractNoteParseParams.builder() + .brokerType(ContractNoteParseParams.BrokerType.ZERODHA) + .password("FAXAK2545F") + .pdfFile("JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo...") + .pdfUrl("https://example.com/contract_note.pdf") + .build() + ) + + val response = responseFuture.get() + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncTest.kt new file mode 100644 index 0000000..6c9e4d8 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncTest.kt @@ -0,0 +1,22 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class CreditServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun check() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val creditServiceAsync = client.credits() + + val responseFuture = creditServiceAsync.check() + + val response = responseFuture.get() + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncTest.kt new file mode 100644 index 0000000..386e7ca --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncTest.kt @@ -0,0 +1,83 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.models.inboundemail.InboundEmailCreateParams +import com.cas_parser.api.models.inboundemail.InboundEmailListParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class InboundEmailServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun create() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboundEmailServiceAsync = client.inboundEmail() + + val inboundEmailFuture = + inboundEmailServiceAsync.create( + InboundEmailCreateParams.builder() + .alias("john-portfolio") + .addAllowedSource(InboundEmailCreateParams.AllowedSource.CDSL) + .addAllowedSource(InboundEmailCreateParams.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .metadata( + InboundEmailCreateParams.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .putAdditionalProperty("source", JsonValue.from("onboarding")) + .build() + ) + .reference("user_12345") + .build() + ) + + val inboundEmail = inboundEmailFuture.get() + inboundEmail.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun retrieve() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboundEmailServiceAsync = client.inboundEmail() + + val inboundEmailFuture = inboundEmailServiceAsync.retrieve("inbound_email_id") + + val inboundEmail = inboundEmailFuture.get() + inboundEmail.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun list() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboundEmailServiceAsync = client.inboundEmail() + + val inboundEmailsFuture = + inboundEmailServiceAsync.list( + InboundEmailListParams.builder() + .limit(1L) + .offset(0L) + .status(InboundEmailListParams.Status.ACTIVE) + .build() + ) + + val inboundEmails = inboundEmailsFuture.get() + inboundEmails.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun delete() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboundEmailServiceAsync = client.inboundEmail() + + val inboundEmailFuture = inboundEmailServiceAsync.delete("inbound_email_id") + + val inboundEmail = inboundEmailFuture.get() + inboundEmail.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncTest.kt new file mode 100644 index 0000000..47362c1 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncTest.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusParams +import com.cas_parser.api.models.inbox.InboxConnectEmailParams +import com.cas_parser.api.models.inbox.InboxDisconnectEmailParams +import com.cas_parser.api.models.inbox.InboxListCasFilesParams +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class InboxServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun checkConnectionStatus() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboxServiceAsync = client.inbox() + + val responseFuture = + inboxServiceAsync.checkConnectionStatus( + InboxCheckConnectionStatusParams.builder().xInboxToken("x-inbox-token").build() + ) + + val response = responseFuture.get() + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun connectEmail() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboxServiceAsync = client.inbox() + + val responseFuture = + inboxServiceAsync.connectEmail( + InboxConnectEmailParams.builder() + .redirectUri("https://yourapp.com/oauth-callback") + .state("abc123") + .build() + ) + + val response = responseFuture.get() + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun disconnectEmail() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboxServiceAsync = client.inbox() + + val responseFuture = + inboxServiceAsync.disconnectEmail( + InboxDisconnectEmailParams.builder().xInboxToken("x-inbox-token").build() + ) + + val response = responseFuture.get() + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun listCasFiles() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val inboxServiceAsync = client.inbox() + + val responseFuture = + inboxServiceAsync.listCasFiles( + InboxListCasFilesParams.builder() + .xInboxToken("x-inbox-token") + .addCasType(InboxListCasFilesParams.CasType.CDSL) + .addCasType(InboxListCasFilesParams.CasType.NSDL) + .endDate(LocalDate.parse("2025-12-31")) + .startDate(LocalDate.parse("2025-12-01")) + .build() + ) + + val response = responseFuture.get() + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncTest.kt new file mode 100644 index 0000000..0632267 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncTest.kt @@ -0,0 +1,32 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.kfintech.KfintechGenerateCasParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class KfintechServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun generateCas() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val kfintechServiceAsync = client.kfintech() + + val responseFuture = + kfintechServiceAsync.generateCas( + KfintechGenerateCasParams.builder() + .email("user@example.com") + .fromDate("2023-01-01") + .password("Abcdefghi12\$") + .toDate("2023-12-31") + .panNo("ABCDE1234F") + .build() + ) + + val response = responseFuture.get() + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/LogServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/LogServiceAsyncTest.kt new file mode 100644 index 0000000..532be8d --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/LogServiceAsyncTest.kt @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.logs.LogCreateParams +import com.cas_parser.api.models.logs.LogGetSummaryParams +import java.time.OffsetDateTime +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class LogServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun create() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val logServiceAsync = client.logs() + + val logFuture = + logServiceAsync.create( + LogCreateParams.builder() + .endTime(OffsetDateTime.parse("2026-01-31T23:59:59Z")) + .limit(1L) + .startTime(OffsetDateTime.parse("2026-01-01T00:00:00Z")) + .build() + ) + + val log = logFuture.get() + log.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun getSummary() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val logServiceAsync = client.logs() + + val responseFuture = + logServiceAsync.getSummary( + LogGetSummaryParams.builder() + .endTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .startTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + ) + + val response = responseFuture.get() + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncTest.kt new file mode 100644 index 0000000..de5bda4 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.nsdl.NsdlParseParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class NsdlServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parse() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val nsdlServiceAsync = client.nsdl() + + val unifiedResponseFuture = + nsdlServiceAsync.parse( + NsdlParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + val unifiedResponse = unifiedResponseFuture.get() + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncTest.kt new file mode 100644 index 0000000..db32247 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.smart.SmartParseCasPdfParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class SmartServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parseCasPdf() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val smartServiceAsync = client.smart() + + val unifiedResponseFuture = + smartServiceAsync.parseCasPdf( + SmartParseCasPdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + val unifiedResponse = unifiedResponseFuture.get() + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncTest.kt new file mode 100644 index 0000000..3d7800c --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncTest.kt @@ -0,0 +1,22 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class VerifyTokenServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun verify() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val verifyTokenServiceAsync = client.verifyToken() + + val responseFuture = verifyTokenServiceAsync.verify() + + val response = responseFuture.get() + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncTest.kt new file mode 100644 index 0000000..a7ac051 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncTest.kt @@ -0,0 +1,50 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.async.cdsl + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClientAsync +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class FetchServiceAsyncTest { + + @Disabled("Mock server tests are disabled") + @Test + fun requestOtp() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val fetchServiceAsync = client.cdsl().fetch() + + val responseFuture = + fetchServiceAsync.requestOtp( + FetchRequestOtpParams.builder() + .boId("1234567890123456") + .dob("1990-01-15") + .pan("ABCDE1234F") + .build() + ) + + val response = responseFuture.get() + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun verifyOtp() { + val client = CasParserOkHttpClientAsync.builder().apiKey("My API Key").build() + val fetchServiceAsync = client.cdsl().fetch() + + val responseFuture = + fetchServiceAsync.verifyOtp( + FetchVerifyOtpParams.builder() + .sessionId("session_id") + .otp("123456") + .numPeriods(6L) + .build() + ) + + val response = responseFuture.get() + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceTest.kt new file mode 100644 index 0000000..f7c9696 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceTest.kt @@ -0,0 +1,23 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.accesstoken.AccessTokenCreateParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class AccessTokenServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun create() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val accessTokenService = client.accessToken() + + val accessToken = + accessTokenService.create(AccessTokenCreateParams.builder().expiryMinutes(60L).build()) + + accessToken.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceTest.kt new file mode 100644 index 0000000..f23fd75 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.camskfintech.CamsKfintechParseParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class CamsKfintechServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parse() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val camsKfintechService = client.camsKfintech() + + val unifiedResponse = + camsKfintechService.parse( + CamsKfintechParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CdslServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CdslServiceTest.kt new file mode 100644 index 0000000..87dffc2 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CdslServiceTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.cdsl.CdslParsePdfParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class CdslServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parsePdf() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val cdslService = client.cdsl() + + val unifiedResponse = + cdslService.parsePdf( + CdslParsePdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceTest.kt new file mode 100644 index 0000000..effc5f4 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceTest.kt @@ -0,0 +1,30 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.contractnote.ContractNoteParseParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class ContractNoteServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parse() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val contractNoteService = client.contractNote() + + val response = + contractNoteService.parse( + ContractNoteParseParams.builder() + .brokerType(ContractNoteParseParams.BrokerType.ZERODHA) + .password("FAXAK2545F") + .pdfFile("JVBERi0xLjQKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwo...") + .pdfUrl("https://example.com/contract_note.pdf") + .build() + ) + + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CreditServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CreditServiceTest.kt new file mode 100644 index 0000000..94239a3 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/CreditServiceTest.kt @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class CreditServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun check() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val creditService = client.credits() + + val response = creditService.check() + + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceTest.kt new file mode 100644 index 0000000..8d826a9 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceTest.kt @@ -0,0 +1,79 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.core.JsonValue +import com.cas_parser.api.models.inboundemail.InboundEmailCreateParams +import com.cas_parser.api.models.inboundemail.InboundEmailListParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class InboundEmailServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun create() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboundEmailService = client.inboundEmail() + + val inboundEmail = + inboundEmailService.create( + InboundEmailCreateParams.builder() + .alias("john-portfolio") + .addAllowedSource(InboundEmailCreateParams.AllowedSource.CDSL) + .addAllowedSource(InboundEmailCreateParams.AllowedSource.NSDL) + .callbackUrl("https://api.yourapp.com/webhooks/cas-email") + .metadata( + InboundEmailCreateParams.Metadata.builder() + .putAdditionalProperty("plan", JsonValue.from("premium")) + .putAdditionalProperty("source", JsonValue.from("onboarding")) + .build() + ) + .reference("user_12345") + .build() + ) + + inboundEmail.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun retrieve() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboundEmailService = client.inboundEmail() + + val inboundEmail = inboundEmailService.retrieve("inbound_email_id") + + inboundEmail.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun list() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboundEmailService = client.inboundEmail() + + val inboundEmails = + inboundEmailService.list( + InboundEmailListParams.builder() + .limit(1L) + .offset(0L) + .status(InboundEmailListParams.Status.ACTIVE) + .build() + ) + + inboundEmails.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun delete() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboundEmailService = client.inboundEmail() + + val inboundEmail = inboundEmailService.delete("inbound_email_id") + + inboundEmail.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/InboxServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/InboxServiceTest.kt new file mode 100644 index 0000000..dc041fb --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/InboxServiceTest.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.inbox.InboxCheckConnectionStatusParams +import com.cas_parser.api.models.inbox.InboxConnectEmailParams +import com.cas_parser.api.models.inbox.InboxDisconnectEmailParams +import com.cas_parser.api.models.inbox.InboxListCasFilesParams +import java.time.LocalDate +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class InboxServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun checkConnectionStatus() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboxService = client.inbox() + + val response = + inboxService.checkConnectionStatus( + InboxCheckConnectionStatusParams.builder().xInboxToken("x-inbox-token").build() + ) + + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun connectEmail() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboxService = client.inbox() + + val response = + inboxService.connectEmail( + InboxConnectEmailParams.builder() + .redirectUri("https://yourapp.com/oauth-callback") + .state("abc123") + .build() + ) + + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun disconnectEmail() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboxService = client.inbox() + + val response = + inboxService.disconnectEmail( + InboxDisconnectEmailParams.builder().xInboxToken("x-inbox-token").build() + ) + + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun listCasFiles() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val inboxService = client.inbox() + + val response = + inboxService.listCasFiles( + InboxListCasFilesParams.builder() + .xInboxToken("x-inbox-token") + .addCasType(InboxListCasFilesParams.CasType.CDSL) + .addCasType(InboxListCasFilesParams.CasType.NSDL) + .endDate(LocalDate.parse("2025-12-31")) + .startDate(LocalDate.parse("2025-12-01")) + .build() + ) + + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/KfintechServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/KfintechServiceTest.kt new file mode 100644 index 0000000..ddc39f7 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/KfintechServiceTest.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.kfintech.KfintechGenerateCasParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class KfintechServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun generateCas() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val kfintechService = client.kfintech() + + val response = + kfintechService.generateCas( + KfintechGenerateCasParams.builder() + .email("user@example.com") + .fromDate("2023-01-01") + .password("Abcdefghi12\$") + .toDate("2023-12-31") + .panNo("ABCDE1234F") + .build() + ) + + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/LogServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/LogServiceTest.kt new file mode 100644 index 0000000..2ef5c48 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/LogServiceTest.kt @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.logs.LogCreateParams +import com.cas_parser.api.models.logs.LogGetSummaryParams +import java.time.OffsetDateTime +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class LogServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun create() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val logService = client.logs() + + val log = + logService.create( + LogCreateParams.builder() + .endTime(OffsetDateTime.parse("2026-01-31T23:59:59Z")) + .limit(1L) + .startTime(OffsetDateTime.parse("2026-01-01T00:00:00Z")) + .build() + ) + + log.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun getSummary() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val logService = client.logs() + + val response = + logService.getSummary( + LogGetSummaryParams.builder() + .endTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .startTime(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .build() + ) + + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/NsdlServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/NsdlServiceTest.kt new file mode 100644 index 0000000..9121446 --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/NsdlServiceTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.nsdl.NsdlParseParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class NsdlServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parse() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val nsdlService = client.nsdl() + + val unifiedResponse = + nsdlService.parse( + NsdlParseParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/SmartServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/SmartServiceTest.kt new file mode 100644 index 0000000..d28949d --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/SmartServiceTest.kt @@ -0,0 +1,29 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.smart.SmartParseCasPdfParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class SmartServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun parseCasPdf() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val smartService = client.smart() + + val unifiedResponse = + smartService.parseCasPdf( + SmartParseCasPdfParams.builder() + .password("password") + .pdfFile("pdf_file") + .pdfUrl("https://example.com") + .build() + ) + + unifiedResponse.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceTest.kt new file mode 100644 index 0000000..19af27d --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceTest.kt @@ -0,0 +1,21 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class VerifyTokenServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun verify() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val verifyTokenService = client.verifyToken() + + val response = verifyTokenService.verify() + + response.validate() + } +} diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceTest.kt new file mode 100644 index 0000000..8c93c2a --- /dev/null +++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceTest.kt @@ -0,0 +1,48 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.services.blocking.cdsl + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.models.cdsl.fetch.FetchRequestOtpParams +import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpParams +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +internal class FetchServiceTest { + + @Disabled("Mock server tests are disabled") + @Test + fun requestOtp() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val fetchService = client.cdsl().fetch() + + val response = + fetchService.requestOtp( + FetchRequestOtpParams.builder() + .boId("1234567890123456") + .dob("1990-01-15") + .pan("ABCDE1234F") + .build() + ) + + response.validate() + } + + @Disabled("Mock server tests are disabled") + @Test + fun verifyOtp() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + val fetchService = client.cdsl().fetch() + + val response = + fetchService.verifyOtp( + FetchVerifyOtpParams.builder() + .sessionId("session_id") + .otp("123456") + .numPeriods(6L) + .build() + ) + + response.validate() + } +} diff --git a/cas-parser-java-example/build.gradle.kts b/cas-parser-java-example/build.gradle.kts new file mode 100644 index 0000000..f13aa5f --- /dev/null +++ b/cas-parser-java-example/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("cas-parser.java") + application +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":cas-parser-java")) +} + +tasks.withType().configureEach { + // Allow using more modern APIs, like `List.of` and `Map.of`, in examples. + options.release.set(9) +} + +application { + // Use `./gradlew :cas-parser-java-example:run` to run `Main` + // Use `./gradlew :cas-parser-java-example:run -Pexample=Something` to run `SomethingExample` + mainClass = "com.cas_parser.api.example.${ + if (project.hasProperty("example")) + "${project.property("example")}Example" + else + "Main" + }" +} diff --git a/cas-parser-java-lib/.keep b/cas-parser-java-lib/.keep new file mode 100644 index 0000000..5e2c99f --- /dev/null +++ b/cas-parser-java-lib/.keep @@ -0,0 +1,4 @@ +File generated from our OpenAPI spec by Stainless. + +This directory can be used to store custom files to expand the SDK. +It is ignored by Stainless code generation and its content (other than this keep file) won't be touched. \ No newline at end of file diff --git a/cas-parser-java-proguard-test/build.gradle.kts b/cas-parser-java-proguard-test/build.gradle.kts new file mode 100644 index 0000000..8846b0b --- /dev/null +++ b/cas-parser-java-proguard-test/build.gradle.kts @@ -0,0 +1,101 @@ +plugins { + id("cas-parser.kotlin") + id("com.gradleup.shadow") version "8.3.8" +} + +buildscript { + repositories { + google() + } + + dependencies { + classpath("com.guardsquare:proguard-gradle:7.4.2") + classpath("com.android.tools:r8:8.3.37") + } +} + +dependencies { + testImplementation(project(":cas-parser-java")) + testImplementation(kotlin("test")) + testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") + testImplementation("org.assertj:assertj-core:3.27.7") + testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0") +} + +tasks.shadowJar { + from(sourceSets.test.get().output) + configurations = listOf(project.configurations.testRuntimeClasspath.get()) +} + +val proguardJarPath = "${layout.buildDirectory.get()}/libs/${project.name}-${project.version}-proguard.jar" +val proguardJar by tasks.registering(proguard.gradle.ProGuardTask::class) { + group = "verification" + dependsOn(tasks.shadowJar) + notCompatibleWithConfigurationCache("ProGuard") + + injars(tasks.shadowJar) + outjars(proguardJarPath) + printmapping("${layout.buildDirectory.get()}/proguard-mapping.txt") + + val javaHome = System.getProperty("java.home") + if (System.getProperty("java.version").startsWith("1.")) { + // Before Java 9, the runtime classes were packaged in a single jar file. + libraryjars("$javaHome/lib/rt.jar") + } else { + // As of Java 9, the runtime classes are packaged in modular jmod files. + libraryjars( + // Filters must be specified first, as a map. + mapOf("jarfilter" to "!**.jar", "filter" to "!module-info.class"), + "$javaHome/jmods/java.base.jmod" + ) + } + + configuration("./test.pro") + configuration("../cas-parser-java-core/src/main/resources/META-INF/proguard/cas-parser-java-core.pro") +} + +val testProGuard by tasks.registering(JavaExec::class) { + group = "verification" + dependsOn(proguardJar) + notCompatibleWithConfigurationCache("ProGuard") + + mainClass.set("com.cas_parser.api.proguard.ProGuardCompatibilityTest") + classpath = files(proguardJarPath) +} + +val r8JarPath = "${layout.buildDirectory.get()}/libs/${project.name}-${project.version}-r8.jar" +val r8Jar by tasks.registering(JavaExec::class) { + group = "verification" + dependsOn(tasks.shadowJar) + notCompatibleWithConfigurationCache("R8") + + mainClass.set("com.android.tools.r8.R8") + classpath = buildscript.configurations["classpath"] + + args = listOf( + "--release", + "--classfile", + "--output", r8JarPath, + "--lib", System.getProperty("java.home"), + "--pg-conf", "./test.pro", + "--pg-conf", "../cas-parser-java-core/src/main/resources/META-INF/proguard/cas-parser-java-core.pro", + "--pg-map-output", "${layout.buildDirectory.get()}/r8-mapping.txt", + tasks.shadowJar.get().archiveFile.get().asFile.absolutePath, + ) +} + +val testR8 by tasks.registering(JavaExec::class) { + group = "verification" + dependsOn(r8Jar) + notCompatibleWithConfigurationCache("R8") + + mainClass.set("com.cas_parser.api.proguard.ProGuardCompatibilityTest") + classpath = files(r8JarPath) +} + +tasks.test { + dependsOn(testProGuard) + dependsOn(testR8) + // We defer to the tests run via the ProGuard JAR. + enabled = false +} diff --git a/cas-parser-java-proguard-test/src/test/kotlin/com/cas_parser/api/proguard/ProGuardCompatibilityTest.kt b/cas-parser-java-proguard-test/src/test/kotlin/com/cas_parser/api/proguard/ProGuardCompatibilityTest.kt new file mode 100644 index 0000000..bc06f0e --- /dev/null +++ b/cas-parser-java-proguard-test/src/test/kotlin/com/cas_parser/api/proguard/ProGuardCompatibilityTest.kt @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.cas_parser.api.proguard + +import com.cas_parser.api.client.okhttp.CasParserOkHttpClient +import com.cas_parser.api.core.jsonMapper +import com.cas_parser.api.models.credits.CreditCheckResponse +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import java.time.OffsetDateTime +import kotlin.reflect.full.memberFunctions +import kotlin.reflect.jvm.javaMethod +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ProGuardCompatibilityTest { + + companion object { + + @JvmStatic + fun main(args: Array) { + // To debug that we're using the right JAR. + val jarPath = this::class.java.getProtectionDomain().codeSource.location + println("JAR being used: $jarPath") + + // We have to manually run the test methods instead of using the JUnit runner because it + // seems impossible to get working with R8. + val test = ProGuardCompatibilityTest() + test::class + .memberFunctions + .asSequence() + .filter { function -> + function.javaMethod?.isAnnotationPresent(Test::class.java) == true + } + .forEach { it.call(test) } + } + } + + @Test + fun proguardRules() { + val rulesFile = + javaClass.classLoader.getResourceAsStream("META-INF/proguard/cas-parser-java-core.pro") + + assertThat(rulesFile).isNotNull() + } + + @Test + fun client() { + val client = CasParserOkHttpClient.builder().apiKey("My API Key").build() + + assertThat(client).isNotNull() + assertThat(client.credits()).isNotNull() + assertThat(client.logs()).isNotNull() + assertThat(client.accessToken()).isNotNull() + assertThat(client.verifyToken()).isNotNull() + assertThat(client.camsKfintech()).isNotNull() + assertThat(client.cdsl()).isNotNull() + assertThat(client.contractNote()).isNotNull() + assertThat(client.inbox()).isNotNull() + assertThat(client.kfintech()).isNotNull() + assertThat(client.nsdl()).isNotNull() + assertThat(client.smart()).isNotNull() + assertThat(client.inboundEmail()).isNotNull() + } + + @Test + fun creditCheckResponseRoundtrip() { + val jsonMapper = jsonMapper() + val creditCheckResponse = + CreditCheckResponse.builder() + .enabledFeatures( + listOf("cams_kfintech_cas_parser", "cdsl_cas_parser", "nsdl_cas_parser") + ) + .isUnlimited(false) + .limit(50L) + .remaining(35.0) + .resetsAt(OffsetDateTime.parse("2026-02-15T00:00:00Z")) + .used(15.0) + .build() + + val roundtrippedCreditCheckResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(creditCheckResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedCreditCheckResponse).isEqualTo(creditCheckResponse) + } +} diff --git a/cas-parser-java-proguard-test/test.pro b/cas-parser-java-proguard-test/test.pro new file mode 100644 index 0000000..fcbe936 --- /dev/null +++ b/cas-parser-java-proguard-test/test.pro @@ -0,0 +1,9 @@ +# Specify the entrypoint where ProGuard starts to determine what's reachable. +-keep class com.cas_parser.api.proguard.** { *; } + +# For the testing framework. +-keep class org.junit.** { *; } + +# Many warnings don't apply for our testing purposes. +-dontnote +-dontwarn \ No newline at end of file diff --git a/cas-parser-java/build.gradle.kts b/cas-parser-java/build.gradle.kts new file mode 100644 index 0000000..a6aacff --- /dev/null +++ b/cas-parser-java/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id("cas-parser.kotlin") + id("cas-parser.publish") +} + +dependencies { + api(project(":cas-parser-java-client-okhttp")) +} + +// Redefine `dokkaJavadoc` to: +// - Depend on the root project's task for merging the docs of all the projects +// - Forward that task's output to this task's output +tasks.named("dokkaJavadoc").configure { + actions.clear() + + val dokkaJavadocCollector = rootProject.tasks["dokkaJavadocCollector"] + dependsOn(dokkaJavadocCollector) + + val outputDirectory = project.layout.buildDirectory.dir("dokka/javadoc") + doLast { + copy { + from(dokkaJavadocCollector.outputs.files) + into(outputDirectory) + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + } + + outputs.dir(outputDirectory) +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..6680f9c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,18 @@ +org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.parallel=true +org.gradle.daemon=false +# These options improve our compilation and test performance. They are inherited by the Kotlin daemon. +org.gradle.jvmargs=\ + -Xms2g \ + -Xmx8g \ + -XX:+UseParallelGC \ + -XX:InitialCodeCacheSize=256m \ + -XX:ReservedCodeCacheSize=1G \ + -XX:MetaspaceSize=512m \ + -XX:MaxMetaspaceSize=2G \ + -XX:TieredStopAtLevel=1 \ + -XX:GCTimeRatio=4 \ + -XX:CICompilerCount=4 \ + -XX:+OptimizeStringConcat \ + -XX:+UseStringDeduplication diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..cea7a79 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..f3b75f3 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..8f98719 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,67 @@ +{ + "packages": { + ".": {} + }, + "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", + "include-v-in-tag": true, + "include-component-in-tag": false, + "versioning": "prerelease", + "prerelease": true, + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "pull-request-header": "Automated Release PR", + "pull-request-title-pattern": "release: ${version}", + "changelog-sections": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "perf", + "section": "Performance Improvements" + }, + { + "type": "revert", + "section": "Reverts" + }, + { + "type": "chore", + "section": "Chores" + }, + { + "type": "docs", + "section": "Documentation" + }, + { + "type": "style", + "section": "Styles" + }, + { + "type": "refactor", + "section": "Refactors" + }, + { + "type": "test", + "section": "Tests", + "hidden": true + }, + { + "type": "build", + "section": "Build System" + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": true + } + ], + "release-type": "simple", + "extra-files": [ + "README.md", + "build.gradle.kts" + ] +} \ No newline at end of file diff --git a/scripts/build b/scripts/build new file mode 100755 index 0000000..16a2b00 --- /dev/null +++ b/scripts/build @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Building classes" +./gradlew build testClasses "$@" -x test diff --git a/scripts/fast-format b/scripts/fast-format new file mode 100755 index 0000000..35a1dee --- /dev/null +++ b/scripts/fast-format @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set -euo pipefail + +echo "Script started with $# arguments" +echo "Arguments: $*" +echo "Script location: $(dirname "$0")" + +cd "$(dirname "$0")/.." +echo "Changed to directory: $(pwd)" + +if [ $# -eq 0 ]; then + echo "Usage: $0 [additional-formatter-args...]" + echo "The file should contain one file path per line" + exit 1 +fi + +FILE_LIST="$1" + +echo "Looking for file: $FILE_LIST" + +if [ ! -f "$FILE_LIST" ]; then + echo "Error: File '$FILE_LIST' not found" + exit 1 +fi + +if ! command -v ktfmt &> /dev/null; then + echo "Error: ktfmt not found" + exit 1 +fi + +# Process Kotlin files +echo "==> Looking for Kotlin files" +kt_files=$(grep -E '\.kt$' "$FILE_LIST" | grep -v './buildSrc/build/' || true) +echo "==> Done looking for Kotlin files" + +if [[ -n "$kt_files" ]]; then + echo "==> will format Kotlin files" + echo "$kt_files" | tr '\n' '\0' | xargs -0 ktfmt --kotlinlang-style "$@" +else + echo "No Kotlin files to format -- expected outcome during incremental formatting" +fi + +# TODO(mbudayr): support palantir-java-format +# Process Java files +# grep -E '\.java$' "$FILE_LIST" | grep -v './buildSrc/build/' | tr '\n' '\0' | xargs -0 -r palantir-java-format --palantir --replace "$@" diff --git a/scripts/format b/scripts/format new file mode 100755 index 0000000..65db176 --- /dev/null +++ b/scripts/format @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +if command -v ktfmt &> /dev/null; then + echo "==> Running ktfmt" + ./scripts/kotlin-format +else + echo "==> Running gradlew formatKotlin" + ./gradlew formatKotlin +fi + +if command -v palantir-java-format &> /dev/null; then + echo "==> Running palantir-java-format" + ./scripts/java-format +else + echo "==> Running gradlew formatJava" + ./gradlew formatJava +fi diff --git a/scripts/java-format b/scripts/java-format new file mode 100755 index 0000000..ad5febc --- /dev/null +++ b/scripts/java-format @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +find . -name "*.java" -not -path "./buildSrc/build/*" -print0 | xargs -0 -r palantir-java-format --palantir --replace "$@" diff --git a/scripts/kotlin-format b/scripts/kotlin-format new file mode 100755 index 0000000..3b8be9e --- /dev/null +++ b/scripts/kotlin-format @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +find . -name "*.kt" -not -path "./buildSrc/build/*" -print0 | xargs -0 -r ktfmt --kotlinlang-style "$@" diff --git a/scripts/lint b/scripts/lint new file mode 100755 index 0000000..dbc8f77 --- /dev/null +++ b/scripts/lint @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +echo "==> Running lints" + +if command -v ktfmt &> /dev/null; then + echo "==> Checking ktfmt" + ./scripts/kotlin-format --dry-run --set-exit-if-changed +else + echo "==> Running gradlew lintKotlin" + ./gradlew lintKotlin +fi + +if command -v palantir-java-format &> /dev/null; then + echo "==> Checking palantir-java-format" + ./scripts/java-format --dry-run --set-exit-if-changed +else + echo "==> Running gradlew lintJava" + ./gradlew lintJava +fi diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..904aea6 --- /dev/null +++ b/scripts/test @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + + + +echo "==> Running tests" +./gradlew test "$@" diff --git a/scripts/upload-artifacts b/scripts/upload-artifacts new file mode 100755 index 0000000..10f3c70 --- /dev/null +++ b/scripts/upload-artifacts @@ -0,0 +1,193 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# ANSI Color Codes +GREEN='\033[32m' +RED='\033[31m' +NC='\033[0m' # No Color + +MAVEN_REPO_PATH="./build/local-maven-repo" + +log_error() { + local msg="$1" + local headers="$2" + local body="$3" + echo -e "${RED}${msg}${NC}" + [[ -f "$headers" ]] && echo -e "${RED}Headers:$(cat "$headers")${NC}" + echo -e "${RED}Body: ${body}${NC}" + exit 1 +} + +upload_file() { + local file_name="$1" + local tmp_headers + tmp_headers=$(mktemp) + + if [ -f "$file_name" ]; then + echo -e "${GREEN}Processing file: $file_name${NC}" + pkg_file_name="mvn${file_name#"${MAVEN_REPO_PATH}"}" + + # Get signed URL for uploading artifact file + signed_url_response=$(curl -X POST -G "$URL" \ + -sS --retry 5 \ + -D "$tmp_headers" \ + --data-urlencode "filename=$pkg_file_name" \ + -H "Authorization: Bearer $AUTH" \ + -H "Content-Type: application/json") + + # Validate JSON and extract URL + if ! signed_url=$(echo "$signed_url_response" | jq -e -r '.url' 2>/dev/null) || [[ "$signed_url" == "null" ]]; then + log_error "Failed to get valid signed URL" "$tmp_headers" "$signed_url_response" + fi + + # Set content-type based on file extension + local extension="${file_name##*.}" + local content_type + case "$extension" in + jar) content_type="application/java-archive" ;; + md5|sha1|sha256|sha512) content_type="text/plain" ;; + module) content_type="application/json" ;; + pom|xml) content_type="application/xml" ;; + html) content_type="text/html" ;; + *) content_type="application/octet-stream" ;; + esac + + # Upload file + upload_response=$(curl -v -X PUT \ + --retry 5 \ + --retry-all-errors \ + -D "$tmp_headers" \ + -H "Content-Type: $content_type" \ + --data-binary "@${file_name}" "$signed_url" 2>&1) + + if ! echo "$upload_response" | grep -q "HTTP/[0-9.]* 200"; then + log_error "Failed to upload artifact file" "$tmp_headers" "$upload_response" + fi + + # Insert small throttle to reduce rate limiting risk + sleep 0.1 + fi +} + +walk_tree() { + local current_dir="$1" + + for entry in "$current_dir"/*; do + # Check that entry is valid + [ -e "$entry" ] || [ -h "$entry" ] || continue + + if [ -d "$entry" ]; then + walk_tree "$entry" + else + upload_file "$entry" + fi + done +} + +generate_instructions() { + cat << EOF > "$MAVEN_REPO_PATH/index.html" + + + + Maven Repo + + +

Stainless SDK Maven Repository

+

This is the Maven repository for your Stainless Java SDK build.

+ +

Project configuration

+ +

The details depend on whether you're using Maven or Gradle as your build tool.

+ +

Maven

+ +

Add the following to your project's pom.xml:

+
<repositories>
+    <repository>
+        <id>stainless-sdk-repo</id>
+        <url>https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn</url>
+    </repository>
+</repositories>
+ +

Gradle

+

Add the following to your build.gradle file:

+
repositories {
+    maven {
+        url "https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn"
+    }
+}
+ +
+

Configuring authentication (if required)

+ +

Some accounts may require authentication to access the repository. If so, use the + following instructions, replacing YOUR_STAINLESS_API_TOKEN with your actual token.

+ +

Maven with authentication

+ +

First, ensure you have the following in your Maven settings.xml for repo authentication:

+
<servers>
+    <server>
+        <id>stainless-sdk-repo</id>
+        <configuration>
+            <httpHeaders>
+                <property>
+                    <name>Authorization</name>
+                    <value>Bearer YOUR_STAINLESS_API_TOKEN</value>
+                </property>
+            </httpHeaders>
+        </configuration>
+    </server>
+</servers>
+ +

Then, add the following to your project's pom.xml:

+
<repositories>
+    <repository>
+        <id>stainless-sdk-repo</id>
+        <url>https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn</url>
+    </repository>
+</repositories>
+ +

Gradle with authentication

+

Add the following to your build.gradle file:

+
repositories {
+    maven {
+        url "https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn"
+        credentials(HttpHeaderCredentials) {
+            name = "Authorization"
+            value = "Bearer YOUR_STAINLESS_API_TOKEN"
+        }
+        authentication {
+            header(HttpHeaderAuthentication)
+        }
+    }
+}
+
+ +

Using the repository

+

Once you've configured the repository, you can include dependencies from it as usual. See your + project README + for more details.

+ + +EOF + upload_file "${MAVEN_REPO_PATH}/index.html" + + echo "Configure maven or gradle to use the repo located at 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'" + echo "For more details, see the directions in https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn/index.html" +} + +cd "$(dirname "$0")/.." + +echo "::group::Creating local Maven content" +./gradlew publishMavenPublicationToLocalFileSystemRepository -PpublishLocal +echo "::endgroup::" + +echo "::group::Uploading to pkg.stainless.com" +walk_tree "$MAVEN_REPO_PATH" +echo "::endgroup::" + +echo "::group::Generating instructions" +generate_instructions +echo "::endgroup::" diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..03a6894 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,14 @@ +rootProject.name = "cas-parser-java-root" + +val projectNames = rootDir.listFiles() + ?.asSequence() + .orEmpty() + .filter { file -> + file.isDirectory && + file.name.startsWith("cas-parser-java") && + file.listFiles()?.asSequence().orEmpty().any { it.name == "build.gradle.kts" } + } + .map { it.name } + .toList() +println("projects: $projectNames") +projectNames.forEach { include(it) }