diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..bd8e2619 --- /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 00000000..d55fc4d6 --- /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 00000000..022b8414 --- /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.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 8bf18cab..00000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,39 +0,0 @@ -name: Java CI with Gradle - -on: - push: - branches: [ 'main' ] - pull_request: - branches: [ 'main' ] - -permissions: - contents: read - -jobs: - snapshot: - name: 'Publish Snapshot' - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 11 - uses: actions/setup-java@v3 - with: - java-version: '11' - distribution: 'temurin' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - - name: Test with Gradle - run: ./gradlew test - - name: Build with Gradle - run: ./gradlew build - - name: Publish Snapshot - run: | - export GPG_SIGNING_KEY=$(echo -n "$GPG_SIGNING_KEY_BASE64" | base64 -d) - ./gradlew -Psnapshot publishToSonatype - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - GPG_SIGNING_KEY_BASE64: ${{ secrets.GPG_SIGNING_KEY_BASE64 }} - GPG_SIGNING_PASSWORD: ${{ secrets.GPG_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..4c5f0ee6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,54 @@ +name: CI +on: + push: + branches-ignore: + - 'generated' + - 'codegen/**' + - 'integrated/**' + - 'stl-preview-head/**' + - 'stl-preview-base/**' + +jobs: + lint: + timeout-minutes: 10 + name: lint + runs-on: ${{ github.repository == 'stainless-sdks/knock-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + + steps: + - uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Run lints + run: ./scripts/lint + test: + timeout-minutes: 10 + name: test + runs-on: ${{ github.repository == 'stainless-sdks/knock-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + steps: + - uses: actions/checkout@v4 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: | + 8 + 21 + cache: gradle + + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - 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 00000000..bc7ecefd --- /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/knocklabs/knock-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@v4 + + - name: Set up Java + uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: | + 8 + 17 + cache: gradle + + - name: Set up Gradle + uses: gradle/gradle-build-action@v2 + + - 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 publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD" --no-configuration-cache + env: + SONATYPE_USERNAME: ${{ secrets.KNOCK_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.KNOCK_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.KNOCK_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.KNOCK_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 00000000..3f0b91ce --- /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 == 'knocklabs/knock-java' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment + env: + SONATYPE_USERNAME: ${{ secrets.KNOCK_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.KNOCK_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }} + GPG_SIGNING_KEY: ${{ secrets.KNOCK_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PASSWORD: ${{ secrets.KNOCK_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 1ed718f4..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Publish Package -on: - release: - types: [created] -jobs: - snapshot: - name: 'Publish Release' - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 11 - uses: actions/setup-java@v3 - with: - java-version: '11' - distribution: 'temurin' - - name: Setup Gradle - uses: gradle/gradle-build-action@v2 - - name: Set Version - id: set_version - uses: actions/github-script@v4 - with: - script: | - const noRef = context.ref.replace('refs/tags/', '') - const noPrefix = noRef.replace('v', '') - core.setOutput('version', noPrefix) - - name: Publish Release - run: | - export GPG_SIGNING_KEY=$(echo -n "$GPG_SIGNING_KEY_BASE64" | base64 -d) - ./gradlew -Pversion=${{steps.set_version.outputs.version}} publishToSonatype closeAndReleaseSonatypeStagingRepository - env: - MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} - MAVEN_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - GPG_SIGNING_KEY_BASE64: ${{ secrets.GPG_SIGNING_KEY_BASE64 }} - GPG_SIGNING_PASSWORD: ${{ secrets.GPG_SIGNING_PASSWORD }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a6a2054e..4e81838d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ +.prism.log .gradle .idea +.kotlin build -.env -*.pgp -bin/ +codegen.log +kls_database.db diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..37fcefaa --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "1.0.0" +} diff --git a/.stats.yml b/.stats.yml new file mode 100644 index 00000000..46b7dfc9 --- /dev/null +++ b/.stats.yml @@ -0,0 +1,4 @@ +configured_endpoints: 89 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/knock%2Fknock-5176d1bb3a88b127808b197c9ae1cf366fd56599fd8c7b7241ac829e72d69a42.yml +openapi_spec_hash: 92953a04021af2d0132fd9eebeb844b9 +config_hash: 7460c5bd6d1a7041faa274f677789407 diff --git a/.tool-versions b/.tool-versions deleted file mode 100644 index a6392983..00000000 --- a/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -java adoptopenjdk-11.0.18+10 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f3563ef..29a04da5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ -## v0.2.3 +# Changelog -* Add support for using the `Idempotency-Key` header on workflow trigger requests. \ No newline at end of file +## 1.0.0 (2025-05-09) + +Full Changelog: [v0.2.10...v1.0.0](https://github.com/knocklabs/knock-java/compare/v0.2.10...v1.0.0) + +### Features + +* **api:** api update ([690bacd](https://github.com/knocklabs/knock-java/commit/690bacd0ef95b8cf2f19f1863625960d40a96fcf)) +* **api:** change bearer to apiKey ([d551d83](https://github.com/knocklabs/knock-java/commit/d551d831c55388387d01f8893cf9296feb00ff44)) +* **api:** manual updates ([dc062fa](https://github.com/knocklabs/knock-java/commit/dc062fa71aedd3413d97fb898466d5e60121804f)) +* **api:** manual updates ([70c325e](https://github.com/knocklabs/knock-java/commit/70c325e24082e382fa28d173fd659536fd82b11e)) +* **api:** manual updates ([a63b672](https://github.com/knocklabs/knock-java/commit/a63b6721aa89122ed7aad6198fe41141ef5a6399)) +* **client:** allow providing some params positionally ([70694bc](https://github.com/knocklabs/knock-java/commit/70694bc27236ba551d855f16d261dd9d615c8868)) +* update java publishing ([9c7dcda](https://github.com/knocklabs/knock-java/commit/9c7dcda6abcbac6c2ed88e5c4c4be9fc02e7539f)) + + +### Bug Fixes + +* compilation errors ([df89e10](https://github.com/knocklabs/knock-java/commit/df89e10864b079c4140c1cfd310ab1611204585b)) + + +### Chores + +* **internal:** remove flaky `-Xbackend-threads=0` option ([51c85db](https://github.com/knocklabs/knock-java/commit/51c85db2e6726d5c5c466aa0b140722efd3ba788)) +* **internal:** update java toolchain ([a656f21](https://github.com/knocklabs/knock-java/commit/a656f210172b3b57736cb969a2d9a71e04827389)) +* sync repo ([320706d](https://github.com/knocklabs/knock-java/commit/320706da86be9bcb13493fdea2aeea93932062c1)) +* update SDK settings ([a768a6b](https://github.com/knocklabs/knock-java/commit/a768a6b6dd0ab6757bcb7d356f10d99096ccf792)) +* update SDK settings ([e4268c8](https://github.com/knocklabs/knock-java/commit/e4268c8f6a2965e1e99b0ee161ed55acb7d0085e)) +* update SDK settings ([7b950c7](https://github.com/knocklabs/knock-java/commit/7b950c7f4e1bea758c8e8c383648aa79d73063ae)) +* update SDK settings ([64a3291](https://github.com/knocklabs/knock-java/commit/64a32915952e528c1646cbf932e1b8b794e5d1c7)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 126e828d..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,29 +0,0 @@ -# Contributing - -## Getting Started - -1. Install [asdf](https://asdf-vm.com) -2. Install the asdf Java plugin - -```bash -asdf plugin add java https://github.com/halcyon/asdf-java.git # Visit that repository to see installation prerequisites -``` - -3. Run `asdf install` to install the version of Java specified in the [.tool-versions](.tool-versions) file - -## Running unit tests - -`./gradlew test` - -## Running integration tests - -This requires some initial setup. Go through the project and replace any API keys, channel IDs, etc. with your own. You will need to also create test workflows in your Knock account. - -```bash -# The optional `-t` flag will run the tests in watch mode -./gradlew integrationTest -t -``` - -## Building - -`./gradlew build` diff --git a/LICENSE b/LICENSE index 6a313ad7..07c8d389 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -MIT License - -Copyright (c) 2022 Knock Labs, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file + 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 2025 Knock + + 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 f936b0f1..7efca6b0 100644 --- a/README.md +++ b/README.md @@ -1,182 +1,660 @@ -# Knock +# Knock Java API Library -Knock API access for applications written in Java. + -## Documentation +[![Maven Central](https://img.shields.io/maven-central/v/app.knock.api/knock-java)](https://central.sonatype.com/artifact/app.knock.api/knock-java/1.0.0) +[![javadoc](https://javadoc.io/badge2/app.knock.api/knock-java/1.0.0/javadoc.svg)](https://javadoc.io/doc/app.knock.api/knock-java/1.0.0) + + + +The Knock Java SDK provides convenient access to the [Knock REST API](https://docs.knock.app) from applications written in Java. + +It is generated with [Stainless](https://www.stainless.com/). + + + +The REST API documentation can be found on [docs.knock.app](https://docs.knock.app). Javadocs are available on [javadoc.io](https://javadoc.io/doc/app.knock.api/knock-java/1.0.0). + + ## Installation -Add the dependency to your `build.grandle` file as follows: + -```groovy -dependencies { - implementation 'app.knock.api:knock-client:0.2.10' -} +### Gradle + +```kotlin +implementation("app.knock.api:knock-java:1.0.0") ``` -Or to your `maven.xml` file: +### Maven ```xml - - - - app.knock.api - knock-client - 0.2.10 - - - + + app.knock.api + knock-java + 1.0.0 + ``` -## Configuration + -Start by creating an instance of KnockClient. -To use the library you must provide a secret API key, provided in the Knock dashboard. +## Requirements -You can use the KnockClientBuilder to create a KnockClient that will pull from -environment variables. +This library requires Java 8 or later. + +## Usage ```java -KnockClient client = KnockClient.builder().build(); +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; +import app.knock.api.core.JsonValue; +import app.knock.api.models.workflows.WorkflowTriggerParams; +import app.knock.api.models.workflows.WorkflowTriggerResponse; + +// Configures using the `KNOCK_API_KEY` and `KNOCK_BASE_URL` environment variables +KnockClient client = KnockOkHttpClient.fromEnv(); + +WorkflowTriggerParams params = WorkflowTriggerParams.builder() + .key("dinosaurs-loose") + .addRecipient("dnedry") + .data(WorkflowTriggerParams.Data.builder() + .putAdditionalProperty("dinosaur", JsonValue.from("triceratops")) + .build()) + .build(); +WorkflowTriggerResponse response = client.workflows().trigger(params); ``` -You can set it as an environment variable: +## Client configuration -```bash -KNOCK_API_KEY="sk_12345" +Configure the client using environment variables: + +```java +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; + +// Configures using the `KNOCK_API_KEY` and `KNOCK_BASE_URL` environment variables +KnockClient client = KnockOkHttpClient.fromEnv(); ``` -You can also set the base API URL and API Key directly. +Or manually: ```java -KnockClient client = KnockClient.builder() - .baseUrl("https://mock-api.knock.app") - .apiKey("sk_12345") - .build(); +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; + +KnockClient client = KnockOkHttpClient.builder() + .apiKey("My API Key") + .build(); ``` -## Usage +Or using a combination of the two approaches: + +```java +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; + +KnockClient client = KnockOkHttpClient.builder() + // Configures using the `KNOCK_API_KEY` and `KNOCK_BASE_URL` environment variables + .fromEnv() + .apiKey("My API Key") + .build(); +``` + +See this table for the available options: + +| Setter | Environment variable | Required | Default value | +| --------- | -------------------- | -------- | ------------------------- | +| `apiKey` | `KNOCK_API_KEY` | true | - | +| `baseUrl` | `KNOCK_BASE_URL` | true | `"https://api.knock.app"` | + +> [!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. + +## Requests and responses + +To send a request to the Knock 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.workflows().trigger(...)` should be called with an instance of `WorkflowTriggerParams`, and it will return an instance of `WorkflowTriggerResponse`. + +## 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 app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; +import app.knock.api.core.JsonValue; +import app.knock.api.models.workflows.WorkflowTriggerParams; +import app.knock.api.models.workflows.WorkflowTriggerResponse; +import java.util.concurrent.CompletableFuture; + +// Configures using the `KNOCK_API_KEY` and `KNOCK_BASE_URL` environment variables +KnockClient client = KnockOkHttpClient.fromEnv(); + +WorkflowTriggerParams params = WorkflowTriggerParams.builder() + .key("dinosaurs-loose") + .addRecipient("dnedry") + .data(WorkflowTriggerParams.Data.builder() + .putAdditionalProperty("dinosaur", JsonValue.from("triceratops")) + .build()) + .build(); +CompletableFuture response = client.async().workflows().trigger(params); +``` + +Or create an asynchronous client from the beginning: + +```java +import app.knock.api.client.KnockClientAsync; +import app.knock.api.client.okhttp.KnockOkHttpClientAsync; +import app.knock.api.core.JsonValue; +import app.knock.api.models.workflows.WorkflowTriggerParams; +import app.knock.api.models.workflows.WorkflowTriggerResponse; +import java.util.concurrent.CompletableFuture; + +// Configures using the `KNOCK_API_KEY` and `KNOCK_BASE_URL` environment variables +KnockClientAsync client = KnockOkHttpClientAsync.fromEnv(); + +WorkflowTriggerParams params = WorkflowTriggerParams.builder() + .key("dinosaurs-loose") + .addRecipient("dnedry") + .data(WorkflowTriggerParams.Data.builder() + .putAdditionalProperty("dinosaur", JsonValue.from("triceratops")) + .build()) + .build(); +CompletableFuture response = client.workflows().trigger(params); +``` -### Identifying users +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 -UserIdentity userIdentity = UserIdentity.builder() - .id("jhammond") - .name("John Hammond") - .email("jhammond@ingen.com") - .property("expenses_spared", "none") - .build() +import app.knock.api.core.http.Headers; +import app.knock.api.core.http.HttpResponseFor; +import app.knock.api.models.users.User; +import app.knock.api.models.users.UserGetParams; -client.users().identify(userIdentity); +HttpResponseFor user = client.users().withRawResponse().get("dnedry"); + +int statusCode = user.statusCode(); +Headers headers = user.headers(); ``` -### Retrieving users +You can still deserialize the response into an instance of a Java class if needed: + +```java +import app.knock.api.models.users.User; -```elixir -UserIdentity userIdentity = client.users().get("jhammond") -// OR -Optional oUserIdentity = client.users().oGet("jhammond") +User parsedUser = user.parse(); ``` -### Sending notifies +## Error handling + +The SDK throws custom unchecked exception types: + +- [`KnockServiceException`](knock-java-core/src/main/kotlin/app/knock/api/errors/KnockServiceException.kt): Base class for HTTP errors. See this table for which exception subclass is thrown for each HTTP status code: + + | Status | Exception | + | ------ | ------------------------------------------------------------------------------------------------------------------------ | + | 400 | [`BadRequestException`](knock-java-core/src/main/kotlin/app/knock/api/errors/BadRequestException.kt) | + | 401 | [`UnauthorizedException`](knock-java-core/src/main/kotlin/app/knock/api/errors/UnauthorizedException.kt) | + | 403 | [`PermissionDeniedException`](knock-java-core/src/main/kotlin/app/knock/api/errors/PermissionDeniedException.kt) | + | 404 | [`NotFoundException`](knock-java-core/src/main/kotlin/app/knock/api/errors/NotFoundException.kt) | + | 422 | [`UnprocessableEntityException`](knock-java-core/src/main/kotlin/app/knock/api/errors/UnprocessableEntityException.kt) | + | 429 | [`RateLimitException`](knock-java-core/src/main/kotlin/app/knock/api/errors/RateLimitException.kt) | + | 5xx | [`InternalServerException`](knock-java-core/src/main/kotlin/app/knock/api/errors/InternalServerException.kt) | + | others | [`UnexpectedStatusCodeException`](knock-java-core/src/main/kotlin/app/knock/api/errors/UnexpectedStatusCodeException.kt) | + +- [`KnockIoException`](knock-java-core/src/main/kotlin/app/knock/api/errors/KnockIoException.kt): I/O networking errors. + +- [`KnockInvalidDataException`](knock-java-core/src/main/kotlin/app/knock/api/errors/KnockInvalidDataException.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. + +- [`KnockException`](knock-java-core/src/main/kotlin/app/knock/api/errors/KnockException.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. + +## Pagination + +For methods that return a paginated list of results, this library provides convenient ways access the results either one page at a time, or item-by-item across all pages. + +### Auto-pagination + +To iterate through all results across all pages, you can use `autoPager`, which automatically handles fetching more pages for you: + +### Synchronous ```java -WorkflowTrigger workflowTrigger = WorkflowTrigger.builder() - .key("dinosaurs-loose") - // user id of who performed the action - .actor("dnedry") - // list of user ids for who should receive the notification - .recipients(List.of(recipientId1, recipientId2)) - // data that can be used in notification templates - .data("fences_electrified", false) - .data("breeds", List.of("velociraptors", "trex")) - .build(); +import app.knock.api.models.users.User; +import app.knock.api.models.users.UserListPage; + +// As an Iterable: +UserListPage page = client.users().list(params); +for (User user : page.autoPager()) { + System.out.println(user); +}; + +// As a Stream: +client.users().list(params).autoPager().stream() + .limit(50) + .forEach(user -> System.out.println(user)); +``` + +### Asynchronous -WorkflowTriggerResult result = client.workflows().trigger(workflowTrigger); +```java +// Using forEach, which returns CompletableFuture: +asyncClient.users().list(params).autoPager() + .forEach(user -> System.out.println(user), executor); ``` -### User preferences +### Manual pagination + +If none of the above helpers meet your needs, you can also manually request pages one-by-one. A page of results has a `data()` method to fetch the list of objects, as well as top-level `response` and other methods to fetch top-level data about the page. It also has methods `hasNextPage`, `getNextPage`, and `getNextPageParams` methods to help with pagination. ```java -# Set preference set for user -PreferenceSetRequest request = PreferenceSetRequest.builder() - .channelTypes( - new PreferenceSetBuilder() - .email(true) - .buildChannelTypes()) - .build(); +import app.knock.api.models.users.User; +import app.knock.api.models.users.UserListPage; + +UserListPage page = client.users().list(params); +while (page != null) { + for (User user : page.entries()) { + System.out.println(user); + } + + page = page.getNextPage().orElse(null); +} +``` + +## Logging + +The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor). + +Enable logging by setting the `KNOCK_LOG` environment variable to `info`: + +```sh +$ export KNOCK_LOG=info +``` + +Or to `debug` for more verbose logging: + +```sh +$ export KNOCK_LOG=debug +``` + +## 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 [`KnockOkHttpClient`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClient.kt) or [`KnockOkHttpClientAsync`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClientAsync.kt). + +> [!CAUTION] +> We make no guarantee that the SDK works correctly when the Jackson version check is disabled. + +## Network options + +### Retries + +The SDK automatically retries 2 times by default, with a short exponential backoff. + +Only the following error types are retried: -client.users().setPreferences("jhammond", request); +- 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 response. -# Set granular workflow preferences -PreferenceSetRequest request = PreferenceSetRequest.builder() - .workflow("dinosaurs-loose", - new PreferenceSetBuilder() - .email(false) - .sms(true) - .condition("recipient.handles_dino_types", "contains", "data.dino_type") - .build()) +To set a custom number of retries, configure the client using the `maxRetries` method: + +```java +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; + +KnockClient client = KnockOkHttpClient.builder() + .fromEnv() + .maxRetries(4) .build(); +``` + +### Timeouts -client.users().setPreferences("jhammond", request); +Requests time out after 1 minute by default. -// NOTE: "default" preference set will be updated unless PreferenceSetRequest.id is provided. +To set a custom timeout, configure the method call using the `timeout` method: -# Retrieve preferences -PreferenceSet defaultPreferences = client.users().getDefaultPreferences("jhammond"); -PreferenceSet defaultPreferences = client.users().getPreferencesById("jhammond", "other-preference-set"); +```java +import app.knock.api.core.JsonValue; +import app.knock.api.models.workflows.WorkflowTriggerParams; +import app.knock.api.models.workflows.WorkflowTriggerResponse; + +WorkflowTriggerResponse response = client.workflows().trigger( + params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build() +); ``` -### Getting and setting channel data +Or configure the default for all method calls at the client level: ```java -# Set channel data for an APNS -String channelId = "114a928a-5b35-4e1b-9069-ac873ee972d3"; -ChannelData channelData = client.users().setChannelData("jhammond", channelId, Map.of("tokens", List.of("some-token"))); +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; +import java.time.Duration; + +KnockClient client = KnockOkHttpClient.builder() + .fromEnv() + .timeout(Duration.ofSeconds(30)) + .build(); +``` + +### Proxies -# Get channel data for the APNS channel -ChannelData retrievedChannelData = client.users().getUserChannelData("jhammond", channelId) +To route requests through a proxy, configure the client using the `proxy` method: -# Unset (delete) channel data -client.users().unsetUserChannelData(userId, channelId); +```java +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; +import java.net.InetSocketAddress; +import java.net.Proxy; + +KnockClient client = KnockOkHttpClient.builder() + .fromEnv() + .proxy(new Proxy( + Proxy.Type.HTTP, new InetSocketAddress( + "https://example.com", 8080 + ) + )) + .build(); ``` -### Canceling notifies +### Custom HTTP client + +The SDK consists of three artifacts: + +- `knock-java-core` + - Contains core SDK logic + - Does not depend on [OkHttp](https://square.github.io/okhttp) + - Exposes [`KnockClient`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClient.kt), [`KnockClientAsync`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsync.kt), [`KnockClientImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientImpl.kt), and [`KnockClientAsyncImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsyncImpl.kt), all of which can work with any HTTP client +- `knock-java-client-okhttp` + - Depends on [OkHttp](https://square.github.io/okhttp) + - Exposes [`KnockOkHttpClient`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClient.kt) and [`KnockOkHttpClientAsync`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClientAsync.kt), which provide a way to construct [`KnockClientImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientImpl.kt) and [`KnockClientAsyncImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsyncImpl.kt), respectively, using OkHttp +- `knock-java` + - Depends on and exposes the APIs of both `knock-java-core` and `knock-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 [`knock-java` dependency](#installation) with `knock-java-core` +2. Copy `knock-java-client-okhttp`'s [`OkHttpClient`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/OkHttpClient.kt) class into your code and customize it +3. Construct [`KnockClientImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientImpl.kt) or [`KnockClientAsyncImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsyncImpl.kt), similarly to [`KnockOkHttpClient`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClient.kt) or [`KnockOkHttpClientAsync`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClientAsync.kt), using your customized client + +### Completely custom HTTP client + +To use a completely custom HTTP client: + +1. Replace your [`knock-java` dependency](#installation) with `knock-java-core` +2. Write a class that implements the [`HttpClient`](knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpClient.kt) interface +3. Construct [`KnockClientImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientImpl.kt) or [`KnockClientAsyncImpl`](knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsyncImpl.kt), similarly to [`KnockOkHttpClient`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClient.kt) or [`KnockOkHttpClientAsync`](knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClientAsync.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 -String cancellationKey = UUID.randomUUID().toString(); +import app.knock.api.core.JsonValue; +import app.knock.api.models.workflows.WorkflowTriggerParams; -WorkflowTrigger workflowTrigger = WorkflowTrigger.builder() - .key("delayed-workflow") - .actor(actorId) - .cancellation_key(cancellationKey) - .recipients(List.of(recipientId1, recipientId2)) +WorkflowTriggerParams params = WorkflowTriggerParams.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 undocumented parameters on _nested_ headers, query params, or body classes, call the `putAdditionalProperty` method on the nested class: + +```java +import app.knock.api.core.JsonValue; +import app.knock.api.models.users.UserListMessagesParams; + +UserListMessagesParams params = UserListMessagesParams.builder() + .insertedAt(UserListMessagesParams.InsertedAt.builder() + .putAdditionalProperty("secretProperty", JsonValue.from("42")) + .build()) + .build(); +``` + +These properties can be accessed on the nested built object later using the `_additionalProperties()` method. + +To set a documented parameter or property to an undocumented or not yet supported _value_, pass a [`JsonValue`](knock-java-core/src/main/kotlin/app/knock/api/core/Values.kt) object to its setter: + +```java +import app.knock.api.core.JsonValue; +import app.knock.api.models.workflows.WorkflowTriggerParams; + +WorkflowTriggerParams params = WorkflowTriggerParams.builder() + .recipients(JsonValue.from(42)) + .data(WorkflowTriggerParams.Data.builder() + .putAdditionalProperty("dinosaur", JsonValue.from("triceratops")) + .build()) + .build(); +``` + +The most straightforward way to create a [`JsonValue`](knock-java-core/src/main/kotlin/app/knock/api/core/Values.kt) is using its `from(...)` method: + +```java +import app.knock.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. -client.workflows().trigger(workflowTrigger); +To forcibly omit a required parameter or property, pass [`JsonMissing`](knock-java-core/src/main/kotlin/app/knock/api/core/Values.kt): -client.workflows().cancel(workflowTrigger); +```java +import app.knock.api.core.JsonMissing; +import app.knock.api.models.recipients.RecipientRequest; +import app.knock.api.models.workflows.WorkflowTriggerParams; +import java.util.List; + +WorkflowTriggerParams params = WorkflowTriggerParams.builder() + .recipients(List.of( + RecipientRequest.ofUserRecipient("dr_grant"), + RecipientRequest.ofUserRecipient("dr_sattler"), + RecipientRequest.ofUserRecipient("dr_malcolm") + )) + .key(JsonMissing.of()) + .build(); ``` -### Handling Exceptions +### Response properties -Calls to resource methods will either succeed, or throw a KnockResourceException. A KnockResourceException -is returned if a response was received with a payload from Knock that is well defined. This is captured in the -exception, and can be used to determine the cause of the exception. +To access undocumented response properties, call the `_additionalProperties()` method: -See the following example code from UsersResourceTestsIT.getUser() +```java +import app.knock.api.core.JsonValue; +import java.util.Map; + +Map additionalProperties = client.workflows().trigger(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 -try { - client.users().get("askfjlsejfes"); - fail("there should be no user found"); -} catch (KnockClientResourceException e) { - assertEquals("resource_missing", e.knockErrorResponse.getCode()); - assertEquals("The resource you requested does not exist", e.knockErrorResponse.getMessage()); - assertEquals(404, e.knockErrorResponse.getStatus()); - assertEquals("api_error", e.knockErrorResponse.getType()); +import app.knock.api.core.JsonField; +import app.knock.api.models.recipients.RecipientRequest; +import java.util.Optional; + +JsonField> recipients = client.workflows().trigger(params)._recipients(); + +if (recipients.isMissing()) { + // The property is absent from the JSON response +} else if (recipients.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 = recipients.asString(); + + // Try to deserialize into a custom type + MyClass myObject = recipients.asUnknown().orElseThrow().convert(MyClass.class); } ``` -If the resource returns an Optional, KnockResourceExceptions are caught, and an empty Optional is returned. +### 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 [`KnockInvalidDataException`](knock-java-core/src/main/kotlin/app/knock/api/errors/KnockInvalidDataException.kt) only if you directly access the property. + +If you would prefer to check that the response is completely well-typed upfront, then either call `validate()`: + +```java +import app.knock.api.models.workflows.WorkflowTriggerResponse; + +WorkflowTriggerResponse response = client.workflows().trigger(params).validate(); +``` + +Or configure the method call to validate the response using the `responseValidation` method: + +```java +import app.knock.api.core.JsonValue; +import app.knock.api.models.workflows.WorkflowTriggerParams; +import app.knock.api.models.workflows.WorkflowTriggerResponse; + +WorkflowTriggerResponse response = client.workflows().trigger( + params, RequestOptions.builder().responseValidation(true).build() +); +``` + +Or configure the default for all method calls at the client level: + +```java +import app.knock.api.client.KnockClient; +import app.knock.api.client.okhttp.KnockOkHttpClient; + +KnockClient client = KnockOkHttpClient.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/knocklabs/knock-java/issues) with questions, bugs, or suggestions. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..a0469d9a --- /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 Knock please follow the respective company's security reporting guidelines. + +### Knock Terms and Policies + +Please contact security@knock.app for any questions or concerns regarding 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 00000000..a2cb4206 --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${SONATYPE_USERNAME}" ]; then + errors+=("The KNOCK_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 KNOCK_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 KNOCK_SONATYPE_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 KNOCK_SONATYPE_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 b/build.gradle deleted file mode 100644 index e96eeea2..00000000 --- a/build.gradle +++ /dev/null @@ -1,111 +0,0 @@ -plugins { - id 'java' - id 'java-library' - id 'io.freefair.lombok' version '6.5.0.2' - id 'maven-publish' - id 'signing' - id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' -} - -group 'app.knock.api' -version "${version}" - -repositories { - mavenCentral() -} - -nexusPublishing { - repositories { - sonatype { - nexusUrl.set(uri(project.property("repo.releases.url"))) - snapshotRepositoryUrl.set(uri(project.property("repo.snapshots.url"))) - username = System.getenv("MAVEN_USERNAME") - password = System.getenv("MAVEN_PASSWORD") - } - } -} - -java { - withJavadocJar() - withSourcesJar() -} - -compileJava { - sourceCompatibility '1.8' - targetCompatibility '1.8' -} - -dependencies { - api 'com.squareup.okhttp3:okhttp:4.10.0' - api 'com.fasterxml.jackson.core:jackson-databind:2.13.3' - api 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' - - testImplementation 'org.skyscreamer:jsonassert:1.5.0' - testImplementation 'org.mockito:mockito-core:4.6.1' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' -} - -task integrationTest(type: Test) { - include '**/*IT.class' - useJUnitPlatform() -} - -test { - exclude '**/*IT.class' - useJUnitPlatform() -} - -publishing { - publications { - maven(MavenPublication) { - from components.java - pom { - name = 'knock-client' - url = 'https://knock.app' - description = 'Knock API Java Client' - scm { - connection = 'scm:git:git://github.com/knocklabs/knock-java.git' - developerConnection = 'scm:git:ssh://git@github.com:knocklabs/knock-java.git' - url = 'https://knock.app/' - } - developers { - developer { - id = 'knock-support' - name = 'Knock Support' - email = 'support@knock.app' - } - } - licenses { - license { - name = 'MIT License' - url = 'https://opensource.org/licenses/MIT' - } - } - } - versionMapping { - usage('java-api') { - fromResolutionOf('runtimeClasspath') - } - } - } - } - repositories { - maven { - url = project.hasProperty('snapshot') ? project.property("repo.snapshots.url") : project.property("repo.releases.url") - credentials { - username = System.getenv("MAVEN_USERNAME") - password = System.getenv("MAVEN_PASSWORD") - } - } - } -} - -signing { -// def keyId = System.getenv("GPG_KEY_ID") - def signingKey = System.getenv("GPG_SIGNING_KEY") - def signingPassword = System.getenv("GPG_SIGNING_PASSWORD") -// useInMemoryPgpKeys(keyId, signingKey, signingPassword) - useInMemoryPgpKeys(signingKey, signingPassword) - sign publishing.publications.maven -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..b19b5516 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("org.jetbrains.dokka") version "2.0.0" +} + +repositories { + mavenCentral() +} + +allprojects { + group = "app.knock.api" + version = "1.0.0" // x-release-please-version +} + +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 != "knock-java" && it.name == "dokkaJavadocJar" } + .forEach { mustRunAfter(it) } +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..778c89de --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + `kotlin-dsl` + kotlin("jvm") version "1.9.20" + id("com.vanniktech.maven.publish") version "0.28.0" +} + +repositories { + gradlePluginPortal() + mavenCentral() +} + +dependencies { + implementation("com.diffplug.spotless:spotless-plugin-gradle:7.0.2") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20") + implementation("com.vanniktech:gradle-maven-publish-plugin:0.28.0") +} diff --git a/buildSrc/src/main/kotlin/knock.java.gradle.kts b/buildSrc/src/main/kotlin/knock.java.gradle.kts new file mode 100644 index 00000000..dfbacb86 --- /dev/null +++ b/buildSrc/src/main/kotlin/knock.java.gradle.kts @@ -0,0 +1,55 @@ +import com.diffplug.gradle.spotless.SpotlessExtension +import org.gradle.api.tasks.testing.logging.TestExceptionFormat + +plugins { + `java-library` + id("com.diffplug.spotless") +} + +repositories { + mavenCentral() +} + +configure { + java { + importOrder() + removeUnusedImports() + palantirJavaFormat() + toggleOffOn() + } +} + +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("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 + } +} diff --git a/buildSrc/src/main/kotlin/knock.kotlin.gradle.kts b/buildSrc/src/main/kotlin/knock.kotlin.gradle.kts new file mode 100644 index 00000000..3f079128 --- /dev/null +++ b/buildSrc/src/main/kotlin/knock.kotlin.gradle.kts @@ -0,0 +1,40 @@ +import com.diffplug.gradle.spotless.SpotlessExtension +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion + +plugins { + id("knock.java") + kotlin("jvm") +} + +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" + } +} + +configure { + kotlin { + ktfmt().kotlinlangStyle() + toggleOffOn() + } +} + +tasks.withType().configureEach { + systemProperty("junit.jupiter.execution.parallel.enabled", true) + systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent") +} diff --git a/buildSrc/src/main/kotlin/knock.publish.gradle.kts b/buildSrc/src/main/kotlin/knock.publish.gradle.kts new file mode 100644 index 00000000..d9b68306 --- /dev/null +++ b/buildSrc/src/main/kotlin/knock.publish.gradle.kts @@ -0,0 +1,55 @@ +import com.vanniktech.maven.publish.JavadocJar +import com.vanniktech.maven.publish.KotlinJvm +import com.vanniktech.maven.publish.MavenPublishBaseExtension +import com.vanniktech.maven.publish.SonatypeHost + +plugins { + id("com.vanniktech.maven.publish") +} + +repositories { + gradlePluginPortal() + mavenCentral() +} + +extra["signingInMemoryKey"] = System.getenv("GPG_SIGNING_KEY") +extra["signingInMemoryKeyId"] = System.getenv("GPG_SIGNING_KEY_ID") +extra["signingInMemoryKeyPassword"] = System.getenv("GPG_SIGNING_PASSWORD") + +configure { + signAllPublications() + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + + coordinates(project.group.toString(), project.name, project.version.toString()) + configure( + KotlinJvm( + javadocJar = JavadocJar.Dokka("dokkaJavadoc"), + sourcesJar = true, + ) + ) + + pom { + name.set("Knock API") + description.set("An SDK library for knock") + url.set("https://docs.knock.app") + + licenses { + license { + name.set("Apache-2.0") + } + } + + developers { + developer { + name.set("Knock") + email.set("support@knock.app") + } + } + + scm { + connection.set("scm:git:git://github.com/knocklabs/knock-java.git") + developerConnection.set("scm:git:git://github.com/knocklabs/knock-java.git") + url.set("https://github.com/knocklabs/knock-java") + } + } +} diff --git a/gradle.properties b/gradle.properties index 10532623..ff76593f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,17 @@ -version=0.2.10-SNAPSHOT -repo.releases.url=https://s01.oss.sonatype.org/service/local/ -repo.snapshots.url=https://s01.oss.sonatype.org/content/repositories/snapshots/ +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=\ + -Xms1g \ + -Xmx4g \ + -XX:+UseParallelGC \ + -XX:InitialCodeCacheSize=256m \ + -XX:ReservedCodeCacheSize=1G \ + -XX:MetaspaceSize=256m \ + -XX:TieredStopAtLevel=1 \ + -XX:GCTimeRatio=4 \ + -XX:CICompilerCount=4 \ + -XX:+OptimizeStringConcat \ + -XX:+UseStringDeduplication diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f..a4b76b95 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc3..cea7a793 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +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 index 744e882e..f3b75f3b 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# 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. @@ -15,69 +15,103 @@ # 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 UN*X -## +# +# 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 -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +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 -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# 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"' +# 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" +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 - ;; +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 @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 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" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + 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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +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 -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -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" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +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 - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + 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 - i=`expr $i + 1` + # 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 - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# 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 index 107acd32..9d21a218 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @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 +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +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 @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +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 @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +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! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +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 diff --git a/knock-java-client-okhttp/build.gradle.kts b/knock-java-client-okhttp/build.gradle.kts new file mode 100644 index 00000000..b9be04b8 --- /dev/null +++ b/knock-java-client-okhttp/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("knock.kotlin") + id("knock.publish") +} + +dependencies { + api(project(":knock-java-core")) + + implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") + + testImplementation(kotlin("test")) + testImplementation("org.assertj:assertj-core:3.25.3") +} diff --git a/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClient.kt b/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClient.kt new file mode 100644 index 00000000..02362b7c --- /dev/null +++ b/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClient.kt @@ -0,0 +1,174 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.client.okhttp + +import app.knock.api.client.KnockClient +import app.knock.api.client.KnockClientImpl +import app.knock.api.core.ClientOptions +import app.knock.api.core.Timeout +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import com.fasterxml.jackson.databind.json.JsonMapper +import java.net.Proxy +import java.time.Clock +import java.time.Duration + +class KnockOkHttpClient private constructor() { + + companion object { + + /** Returns a mutable builder for constructing an instance of [KnockOkHttpClient]. */ + @JvmStatic fun builder() = Builder() + + @JvmStatic fun fromEnv(): KnockClient = builder().fromEnv().build() + } + + /** A builder for [KnockOkHttpClient]. */ + class Builder internal constructor() { + + private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var timeout: Timeout = Timeout.default() + private var proxy: Proxy? = null + + fun baseUrl(baseUrl: String) = apply { clientOptions.baseUrl(baseUrl) } + + /** + * 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) + } + + fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) } + + fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + + 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) + } + + fun timeout(timeout: Timeout) = apply { + clientOptions.timeout(timeout) + 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()) + + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + fun proxy(proxy: Proxy) = apply { this.proxy = proxy } + + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } + + fun fromEnv() = apply { clientOptions.fromEnv() } + + /** + * Returns an immutable instance of [KnockClient]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): KnockClient = + KnockClientImpl( + clientOptions + .httpClient( + OkHttpClient.builder() + .baseUrl(clientOptions.baseUrl()) + .timeout(timeout) + .proxy(proxy) + .build() + ) + .build() + ) + } +} diff --git a/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClientAsync.kt b/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClientAsync.kt new file mode 100644 index 00000000..fda53d81 --- /dev/null +++ b/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/KnockOkHttpClientAsync.kt @@ -0,0 +1,174 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.client.okhttp + +import app.knock.api.client.KnockClientAsync +import app.knock.api.client.KnockClientAsyncImpl +import app.knock.api.core.ClientOptions +import app.knock.api.core.Timeout +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import com.fasterxml.jackson.databind.json.JsonMapper +import java.net.Proxy +import java.time.Clock +import java.time.Duration + +class KnockOkHttpClientAsync private constructor() { + + companion object { + + /** Returns a mutable builder for constructing an instance of [KnockOkHttpClientAsync]. */ + @JvmStatic fun builder() = Builder() + + @JvmStatic fun fromEnv(): KnockClientAsync = builder().fromEnv().build() + } + + /** A builder for [KnockOkHttpClientAsync]. */ + class Builder internal constructor() { + + private var clientOptions: ClientOptions.Builder = ClientOptions.builder() + private var timeout: Timeout = Timeout.default() + private var proxy: Proxy? = null + + fun baseUrl(baseUrl: String) = apply { clientOptions.baseUrl(baseUrl) } + + /** + * 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) + } + + fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) } + + fun clock(clock: Clock) = apply { clientOptions.clock(clock) } + + 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) + } + + fun timeout(timeout: Timeout) = apply { + clientOptions.timeout(timeout) + 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()) + + fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) } + + fun proxy(proxy: Proxy) = apply { this.proxy = proxy } + + fun responseValidation(responseValidation: Boolean) = apply { + clientOptions.responseValidation(responseValidation) + } + + fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) } + + fun fromEnv() = apply { clientOptions.fromEnv() } + + /** + * Returns an immutable instance of [KnockClientAsync]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): KnockClientAsync = + KnockClientAsyncImpl( + clientOptions + .httpClient( + OkHttpClient.builder() + .baseUrl(clientOptions.baseUrl()) + .timeout(timeout) + .proxy(proxy) + .build() + ) + .build() + ) + } +} diff --git a/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/OkHttpClient.kt b/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/OkHttpClient.kt new file mode 100644 index 00000000..83f8b4b2 --- /dev/null +++ b/knock-java-client-okhttp/src/main/kotlin/app/knock/api/client/okhttp/OkHttpClient.kt @@ -0,0 +1,221 @@ +package app.knock.api.client.okhttp + +import app.knock.api.core.RequestOptions +import app.knock.api.core.Timeout +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import app.knock.api.core.http.HttpClient +import app.knock.api.core.http.HttpMethod +import app.knock.api.core.http.HttpRequest +import app.knock.api.core.http.HttpRequestBody +import app.knock.api.core.http.HttpResponse +import app.knock.api.errors.KnockIoException +import java.io.IOException +import java.io.InputStream +import java.net.Proxy +import java.time.Duration +import java.util.concurrent.CompletableFuture +import okhttp3.Call +import okhttp3.Callback +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 okhttp3.logging.HttpLoggingInterceptor +import okio.BufferedSink + +class OkHttpClient +private constructor(private val okHttpClient: okhttp3.OkHttpClient, private val baseUrl: HttpUrl) : + HttpClient { + + override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse { + val call = newCall(request, requestOptions) + + return try { + call.execute().toResponse() + } catch (e: IOException) { + throw KnockIoException("Request failed", e) + } finally { + request.body?.close() + } + } + + override fun executeAsync( + request: HttpRequest, + requestOptions: RequestOptions, + ): CompletableFuture { + val future = CompletableFuture() + + request.body?.run { future.whenComplete { _, _ -> close() } } + + newCall(request, requestOptions) + .enqueue( + object : Callback { + override fun onResponse(call: Call, response: Response) { + future.complete(response.toResponse()) + } + + override fun onFailure(call: Call, e: IOException) { + future.completeExceptionally(KnockIoException("Request failed", e)) + } + } + ) + + 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() + + val logLevel = + when (System.getenv("KNOCK_LOG")?.lowercase()) { + "info" -> HttpLoggingInterceptor.Level.BASIC + "debug" -> HttpLoggingInterceptor.Level.BODY + else -> null + } + if (logLevel != null) { + clientBuilder.addNetworkInterceptor( + HttpLoggingInterceptor().setLevel(logLevel).apply { redactHeader("Authorization") } + ) + } + + 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)) + } + + 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.header(name, it) } + } + + if ( + !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0 + ) { + builder.header( + "X-Stainless-Read-Timeout", + Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(), + ) + } + if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) { + builder.header( + "X-Stainless-Timeout", + Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(), + ) + } + + return builder.build() + } + + /** `OkHttpClient` always requires a request body for some methods. */ + private fun requiresBody(method: HttpMethod): Boolean = + when (method) { + HttpMethod.POST, + HttpMethod.PUT, + HttpMethod.PATCH -> true + else -> false + } + + private fun HttpRequest.toUrl(): String { + url?.let { + return it + } + + val builder = baseUrl.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 Response.toResponse(): 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() + } + + companion object { + @JvmStatic fun builder() = Builder() + } + + class Builder internal constructor() { + + private var baseUrl: HttpUrl? = null + private var timeout: Timeout = Timeout.default() + private var proxy: Proxy? = null + + fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl.toHttpUrl() } + + 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 build(): OkHttpClient = + OkHttpClient( + okhttp3.OkHttpClient.Builder() + .connectTimeout(timeout.connect()) + .readTimeout(timeout.read()) + .writeTimeout(timeout.write()) + .callTimeout(timeout.request()) + .proxy(proxy) + .build(), + checkRequired("baseUrl", baseUrl), + ) + } +} diff --git a/knock-java-core/build.gradle.kts b/knock-java-core/build.gradle.kts new file mode 100644 index 00000000..b142bbfd --- /dev/null +++ b/knock-java-core/build.gradle.kts @@ -0,0 +1,41 @@ +plugins { + id("knock.kotlin") + id("knock.publish") +} + +configurations.all { + resolutionStrategy { + // Compile and test against a lower Jackson version to ensure we're compatible with it. + // We publish with a higher version (see below) to ensure users depend on a secure version by default. + force("com.fasterxml.jackson.core:jackson-core:2.13.4") + force("com.fasterxml.jackson.core:jackson-databind:2.13.4") + force("com.fasterxml.jackson.core:jackson-annotations:2.13.4") + force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.13.4") + force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.13.4") + force("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4") + } +} + +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") + implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4") + implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1") + + testImplementation(kotlin("test")) + testImplementation(project(":knock-java-client-okhttp")) + testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2") + testImplementation("org.assertj:assertj-core:3.25.3") + 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/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClient.kt b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClient.kt new file mode 100644 index 00000000..fa37c430 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClient.kt @@ -0,0 +1,116 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.client + +import app.knock.api.services.blocking.AudienceService +import app.knock.api.services.blocking.BulkOperationService +import app.knock.api.services.blocking.ChannelService +import app.knock.api.services.blocking.IntegrationService +import app.knock.api.services.blocking.MessageService +import app.knock.api.services.blocking.ObjectService +import app.knock.api.services.blocking.ProviderService +import app.knock.api.services.blocking.RecipientService +import app.knock.api.services.blocking.ScheduleService +import app.knock.api.services.blocking.SharedService +import app.knock.api.services.blocking.TenantService +import app.knock.api.services.blocking.UserService +import app.knock.api.services.blocking.WorkflowService + +/** + * A client for interacting with the Knock 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 KnockClient { + + /** + * 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(): KnockClientAsync + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun shared(): SharedService + + fun recipients(): RecipientService + + fun users(): UserService + + fun objects(): ObjectService + + fun tenants(): TenantService + + fun bulkOperations(): BulkOperationService + + fun messages(): MessageService + + fun providers(): ProviderService + + fun integrations(): IntegrationService + + fun workflows(): WorkflowService + + fun schedules(): ScheduleService + + fun channels(): ChannelService + + fun audiences(): AudienceService + + /** + * 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 [KnockClient] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun shared(): SharedService.WithRawResponse + + fun recipients(): RecipientService.WithRawResponse + + fun users(): UserService.WithRawResponse + + fun objects(): ObjectService.WithRawResponse + + fun tenants(): TenantService.WithRawResponse + + fun bulkOperations(): BulkOperationService.WithRawResponse + + fun messages(): MessageService.WithRawResponse + + fun providers(): ProviderService.WithRawResponse + + fun integrations(): IntegrationService.WithRawResponse + + fun workflows(): WorkflowService.WithRawResponse + + fun schedules(): ScheduleService.WithRawResponse + + fun channels(): ChannelService.WithRawResponse + + fun audiences(): AudienceService.WithRawResponse + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsync.kt b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsync.kt new file mode 100644 index 00000000..aeb75c3a --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsync.kt @@ -0,0 +1,116 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.client + +import app.knock.api.services.async.AudienceServiceAsync +import app.knock.api.services.async.BulkOperationServiceAsync +import app.knock.api.services.async.ChannelServiceAsync +import app.knock.api.services.async.IntegrationServiceAsync +import app.knock.api.services.async.MessageServiceAsync +import app.knock.api.services.async.ObjectServiceAsync +import app.knock.api.services.async.ProviderServiceAsync +import app.knock.api.services.async.RecipientServiceAsync +import app.knock.api.services.async.ScheduleServiceAsync +import app.knock.api.services.async.SharedServiceAsync +import app.knock.api.services.async.TenantServiceAsync +import app.knock.api.services.async.UserServiceAsync +import app.knock.api.services.async.WorkflowServiceAsync + +/** + * A client for interacting with the Knock 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 KnockClientAsync { + + /** + * 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(): KnockClient + + /** + * Returns a view of this service that provides access to raw HTTP responses for each method. + */ + fun withRawResponse(): WithRawResponse + + fun shared(): SharedServiceAsync + + fun recipients(): RecipientServiceAsync + + fun users(): UserServiceAsync + + fun objects(): ObjectServiceAsync + + fun tenants(): TenantServiceAsync + + fun bulkOperations(): BulkOperationServiceAsync + + fun messages(): MessageServiceAsync + + fun providers(): ProviderServiceAsync + + fun integrations(): IntegrationServiceAsync + + fun workflows(): WorkflowServiceAsync + + fun schedules(): ScheduleServiceAsync + + fun channels(): ChannelServiceAsync + + fun audiences(): AudienceServiceAsync + + /** + * 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 [KnockClientAsync] that provides access to raw HTTP responses for each method. */ + interface WithRawResponse { + + fun shared(): SharedServiceAsync.WithRawResponse + + fun recipients(): RecipientServiceAsync.WithRawResponse + + fun users(): UserServiceAsync.WithRawResponse + + fun objects(): ObjectServiceAsync.WithRawResponse + + fun tenants(): TenantServiceAsync.WithRawResponse + + fun bulkOperations(): BulkOperationServiceAsync.WithRawResponse + + fun messages(): MessageServiceAsync.WithRawResponse + + fun providers(): ProviderServiceAsync.WithRawResponse + + fun integrations(): IntegrationServiceAsync.WithRawResponse + + fun workflows(): WorkflowServiceAsync.WithRawResponse + + fun schedules(): ScheduleServiceAsync.WithRawResponse + + fun channels(): ChannelServiceAsync.WithRawResponse + + fun audiences(): AudienceServiceAsync.WithRawResponse + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsyncImpl.kt b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsyncImpl.kt new file mode 100644 index 00000000..c4fcfccc --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientAsyncImpl.kt @@ -0,0 +1,214 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.client + +import app.knock.api.core.ClientOptions +import app.knock.api.core.getPackageVersion +import app.knock.api.services.async.AudienceServiceAsync +import app.knock.api.services.async.AudienceServiceAsyncImpl +import app.knock.api.services.async.BulkOperationServiceAsync +import app.knock.api.services.async.BulkOperationServiceAsyncImpl +import app.knock.api.services.async.ChannelServiceAsync +import app.knock.api.services.async.ChannelServiceAsyncImpl +import app.knock.api.services.async.IntegrationServiceAsync +import app.knock.api.services.async.IntegrationServiceAsyncImpl +import app.knock.api.services.async.MessageServiceAsync +import app.knock.api.services.async.MessageServiceAsyncImpl +import app.knock.api.services.async.ObjectServiceAsync +import app.knock.api.services.async.ObjectServiceAsyncImpl +import app.knock.api.services.async.ProviderServiceAsync +import app.knock.api.services.async.ProviderServiceAsyncImpl +import app.knock.api.services.async.RecipientServiceAsync +import app.knock.api.services.async.RecipientServiceAsyncImpl +import app.knock.api.services.async.ScheduleServiceAsync +import app.knock.api.services.async.ScheduleServiceAsyncImpl +import app.knock.api.services.async.SharedServiceAsync +import app.knock.api.services.async.SharedServiceAsyncImpl +import app.knock.api.services.async.TenantServiceAsync +import app.knock.api.services.async.TenantServiceAsyncImpl +import app.knock.api.services.async.UserServiceAsync +import app.knock.api.services.async.UserServiceAsyncImpl +import app.knock.api.services.async.WorkflowServiceAsync +import app.knock.api.services.async.WorkflowServiceAsyncImpl + +class KnockClientAsyncImpl(private val clientOptions: ClientOptions) : KnockClientAsync { + + 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: KnockClient by lazy { KnockClientImpl(clientOptions) } + + private val withRawResponse: KnockClientAsync.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val shared: SharedServiceAsync by lazy { + SharedServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val recipients: RecipientServiceAsync by lazy { + RecipientServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val users: UserServiceAsync by lazy { UserServiceAsyncImpl(clientOptionsWithUserAgent) } + + private val objects: ObjectServiceAsync by lazy { + ObjectServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val tenants: TenantServiceAsync by lazy { + TenantServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val bulkOperations: BulkOperationServiceAsync by lazy { + BulkOperationServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val messages: MessageServiceAsync by lazy { + MessageServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val providers: ProviderServiceAsync by lazy { + ProviderServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val integrations: IntegrationServiceAsync by lazy { + IntegrationServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val workflows: WorkflowServiceAsync by lazy { + WorkflowServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val schedules: ScheduleServiceAsync by lazy { + ScheduleServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val channels: ChannelServiceAsync by lazy { + ChannelServiceAsyncImpl(clientOptionsWithUserAgent) + } + + private val audiences: AudienceServiceAsync by lazy { + AudienceServiceAsyncImpl(clientOptionsWithUserAgent) + } + + override fun sync(): KnockClient = sync + + override fun withRawResponse(): KnockClientAsync.WithRawResponse = withRawResponse + + override fun shared(): SharedServiceAsync = shared + + override fun recipients(): RecipientServiceAsync = recipients + + override fun users(): UserServiceAsync = users + + override fun objects(): ObjectServiceAsync = objects + + override fun tenants(): TenantServiceAsync = tenants + + override fun bulkOperations(): BulkOperationServiceAsync = bulkOperations + + override fun messages(): MessageServiceAsync = messages + + override fun providers(): ProviderServiceAsync = providers + + override fun integrations(): IntegrationServiceAsync = integrations + + override fun workflows(): WorkflowServiceAsync = workflows + + override fun schedules(): ScheduleServiceAsync = schedules + + override fun channels(): ChannelServiceAsync = channels + + override fun audiences(): AudienceServiceAsync = audiences + + override fun close() = clientOptions.httpClient.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + KnockClientAsync.WithRawResponse { + + private val shared: SharedServiceAsync.WithRawResponse by lazy { + SharedServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val recipients: RecipientServiceAsync.WithRawResponse by lazy { + RecipientServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val users: UserServiceAsync.WithRawResponse by lazy { + UserServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val objects: ObjectServiceAsync.WithRawResponse by lazy { + ObjectServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val tenants: TenantServiceAsync.WithRawResponse by lazy { + TenantServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val bulkOperations: BulkOperationServiceAsync.WithRawResponse by lazy { + BulkOperationServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val messages: MessageServiceAsync.WithRawResponse by lazy { + MessageServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val providers: ProviderServiceAsync.WithRawResponse by lazy { + ProviderServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val integrations: IntegrationServiceAsync.WithRawResponse by lazy { + IntegrationServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val workflows: WorkflowServiceAsync.WithRawResponse by lazy { + WorkflowServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val schedules: ScheduleServiceAsync.WithRawResponse by lazy { + ScheduleServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val channels: ChannelServiceAsync.WithRawResponse by lazy { + ChannelServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + private val audiences: AudienceServiceAsync.WithRawResponse by lazy { + AudienceServiceAsyncImpl.WithRawResponseImpl(clientOptions) + } + + override fun shared(): SharedServiceAsync.WithRawResponse = shared + + override fun recipients(): RecipientServiceAsync.WithRawResponse = recipients + + override fun users(): UserServiceAsync.WithRawResponse = users + + override fun objects(): ObjectServiceAsync.WithRawResponse = objects + + override fun tenants(): TenantServiceAsync.WithRawResponse = tenants + + override fun bulkOperations(): BulkOperationServiceAsync.WithRawResponse = bulkOperations + + override fun messages(): MessageServiceAsync.WithRawResponse = messages + + override fun providers(): ProviderServiceAsync.WithRawResponse = providers + + override fun integrations(): IntegrationServiceAsync.WithRawResponse = integrations + + override fun workflows(): WorkflowServiceAsync.WithRawResponse = workflows + + override fun schedules(): ScheduleServiceAsync.WithRawResponse = schedules + + override fun channels(): ChannelServiceAsync.WithRawResponse = channels + + override fun audiences(): AudienceServiceAsync.WithRawResponse = audiences + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientImpl.kt b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientImpl.kt new file mode 100644 index 00000000..ed7b6ce9 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/client/KnockClientImpl.kt @@ -0,0 +1,204 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.client + +import app.knock.api.core.ClientOptions +import app.knock.api.core.getPackageVersion +import app.knock.api.services.blocking.AudienceService +import app.knock.api.services.blocking.AudienceServiceImpl +import app.knock.api.services.blocking.BulkOperationService +import app.knock.api.services.blocking.BulkOperationServiceImpl +import app.knock.api.services.blocking.ChannelService +import app.knock.api.services.blocking.ChannelServiceImpl +import app.knock.api.services.blocking.IntegrationService +import app.knock.api.services.blocking.IntegrationServiceImpl +import app.knock.api.services.blocking.MessageService +import app.knock.api.services.blocking.MessageServiceImpl +import app.knock.api.services.blocking.ObjectService +import app.knock.api.services.blocking.ObjectServiceImpl +import app.knock.api.services.blocking.ProviderService +import app.knock.api.services.blocking.ProviderServiceImpl +import app.knock.api.services.blocking.RecipientService +import app.knock.api.services.blocking.RecipientServiceImpl +import app.knock.api.services.blocking.ScheduleService +import app.knock.api.services.blocking.ScheduleServiceImpl +import app.knock.api.services.blocking.SharedService +import app.knock.api.services.blocking.SharedServiceImpl +import app.knock.api.services.blocking.TenantService +import app.knock.api.services.blocking.TenantServiceImpl +import app.knock.api.services.blocking.UserService +import app.knock.api.services.blocking.UserServiceImpl +import app.knock.api.services.blocking.WorkflowService +import app.knock.api.services.blocking.WorkflowServiceImpl + +class KnockClientImpl(private val clientOptions: ClientOptions) : KnockClient { + + 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: KnockClientAsync by lazy { KnockClientAsyncImpl(clientOptions) } + + private val withRawResponse: KnockClient.WithRawResponse by lazy { + WithRawResponseImpl(clientOptions) + } + + private val shared: SharedService by lazy { SharedServiceImpl(clientOptionsWithUserAgent) } + + private val recipients: RecipientService by lazy { + RecipientServiceImpl(clientOptionsWithUserAgent) + } + + private val users: UserService by lazy { UserServiceImpl(clientOptionsWithUserAgent) } + + private val objects: ObjectService by lazy { ObjectServiceImpl(clientOptionsWithUserAgent) } + + private val tenants: TenantService by lazy { TenantServiceImpl(clientOptionsWithUserAgent) } + + private val bulkOperations: BulkOperationService by lazy { + BulkOperationServiceImpl(clientOptionsWithUserAgent) + } + + private val messages: MessageService by lazy { MessageServiceImpl(clientOptionsWithUserAgent) } + + private val providers: ProviderService by lazy { + ProviderServiceImpl(clientOptionsWithUserAgent) + } + + private val integrations: IntegrationService by lazy { + IntegrationServiceImpl(clientOptionsWithUserAgent) + } + + private val workflows: WorkflowService by lazy { + WorkflowServiceImpl(clientOptionsWithUserAgent) + } + + private val schedules: ScheduleService by lazy { + ScheduleServiceImpl(clientOptionsWithUserAgent) + } + + private val channels: ChannelService by lazy { ChannelServiceImpl(clientOptionsWithUserAgent) } + + private val audiences: AudienceService by lazy { + AudienceServiceImpl(clientOptionsWithUserAgent) + } + + override fun async(): KnockClientAsync = async + + override fun withRawResponse(): KnockClient.WithRawResponse = withRawResponse + + override fun shared(): SharedService = shared + + override fun recipients(): RecipientService = recipients + + override fun users(): UserService = users + + override fun objects(): ObjectService = objects + + override fun tenants(): TenantService = tenants + + override fun bulkOperations(): BulkOperationService = bulkOperations + + override fun messages(): MessageService = messages + + override fun providers(): ProviderService = providers + + override fun integrations(): IntegrationService = integrations + + override fun workflows(): WorkflowService = workflows + + override fun schedules(): ScheduleService = schedules + + override fun channels(): ChannelService = channels + + override fun audiences(): AudienceService = audiences + + override fun close() = clientOptions.httpClient.close() + + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : + KnockClient.WithRawResponse { + + private val shared: SharedService.WithRawResponse by lazy { + SharedServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val recipients: RecipientService.WithRawResponse by lazy { + RecipientServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val users: UserService.WithRawResponse by lazy { + UserServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val objects: ObjectService.WithRawResponse by lazy { + ObjectServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val tenants: TenantService.WithRawResponse by lazy { + TenantServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val bulkOperations: BulkOperationService.WithRawResponse by lazy { + BulkOperationServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val messages: MessageService.WithRawResponse by lazy { + MessageServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val providers: ProviderService.WithRawResponse by lazy { + ProviderServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val integrations: IntegrationService.WithRawResponse by lazy { + IntegrationServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val workflows: WorkflowService.WithRawResponse by lazy { + WorkflowServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val schedules: ScheduleService.WithRawResponse by lazy { + ScheduleServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val channels: ChannelService.WithRawResponse by lazy { + ChannelServiceImpl.WithRawResponseImpl(clientOptions) + } + + private val audiences: AudienceService.WithRawResponse by lazy { + AudienceServiceImpl.WithRawResponseImpl(clientOptions) + } + + override fun shared(): SharedService.WithRawResponse = shared + + override fun recipients(): RecipientService.WithRawResponse = recipients + + override fun users(): UserService.WithRawResponse = users + + override fun objects(): ObjectService.WithRawResponse = objects + + override fun tenants(): TenantService.WithRawResponse = tenants + + override fun bulkOperations(): BulkOperationService.WithRawResponse = bulkOperations + + override fun messages(): MessageService.WithRawResponse = messages + + override fun providers(): ProviderService.WithRawResponse = providers + + override fun integrations(): IntegrationService.WithRawResponse = integrations + + override fun workflows(): WorkflowService.WithRawResponse = workflows + + override fun schedules(): ScheduleService.WithRawResponse = schedules + + override fun channels(): ChannelService.WithRawResponse = channels + + override fun audiences(): AudienceService.WithRawResponse = audiences + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/BaseDeserializer.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/BaseDeserializer.kt new file mode 100644 index 00000000..b5e8934f --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/BaseDeserializer.kt @@ -0,0 +1,44 @@ +package app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/BaseSerializer.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/BaseSerializer.kt new file mode 100644 index 00000000..764e6b46 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/BaseSerializer.kt @@ -0,0 +1,6 @@ +package app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/Check.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/Check.kt new file mode 100644 index 00000000..d2ef6cf9 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/Check.kt @@ -0,0 +1,96 @@ +@file:JvmName("Check") + +package app.knock.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/knocklabs/knock-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/knock-java-core/src/main/kotlin/app/knock/api/core/ClientOptions.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/ClientOptions.kt new file mode 100644 index 00000000..b364164c --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/ClientOptions.kt @@ -0,0 +1,250 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.core + +import app.knock.api.core.http.Headers +import app.knock.api.core.http.HttpClient +import app.knock.api.core.http.PhantomReachableClosingHttpClient +import app.knock.api.core.http.QueryParams +import app.knock.api.core.http.RetryingHttpClient +import com.fasterxml.jackson.databind.json.JsonMapper +import java.time.Clock + +class ClientOptions +private constructor( + private val originalHttpClient: HttpClient, + @get:JvmName("httpClient") val httpClient: HttpClient, + @get:JvmName("checkJacksonVersionCompatibility") val checkJacksonVersionCompatibility: Boolean, + @get:JvmName("jsonMapper") val jsonMapper: JsonMapper, + @get:JvmName("clock") val clock: Clock, + @get:JvmName("baseUrl") val baseUrl: String, + @get:JvmName("headers") val headers: Headers, + @get:JvmName("queryParams") val queryParams: QueryParams, + @get:JvmName("responseValidation") val responseValidation: Boolean, + @get:JvmName("timeout") val timeout: Timeout, + @get:JvmName("maxRetries") val maxRetries: Int, + @get:JvmName("apiKey") val apiKey: String, +) { + + init { + if (checkJacksonVersionCompatibility) { + checkJacksonVersionCompatibility() + } + } + + fun toBuilder() = Builder().from(this) + + companion object { + + const val PRODUCTION_URL = "https://api.knock.app" + + /** + * Returns a mutable builder for constructing an instance of [ClientOptions]. + * + * The following fields are required: + * ```java + * .httpClient() + * .apiKey() + * ``` + */ + @JvmStatic fun builder() = Builder() + + @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 clock: Clock = Clock.systemUTC() + private var baseUrl: String = PRODUCTION_URL + 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 apiKey: String? = null + + @JvmSynthetic + internal fun from(clientOptions: ClientOptions) = apply { + httpClient = clientOptions.originalHttpClient + checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility + jsonMapper = clientOptions.jsonMapper + clock = clientOptions.clock + baseUrl = clientOptions.baseUrl + headers = clientOptions.headers.toBuilder() + queryParams = clientOptions.queryParams.toBuilder() + responseValidation = clientOptions.responseValidation + timeout = clientOptions.timeout + maxRetries = clientOptions.maxRetries + apiKey = clientOptions.apiKey + } + + fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient } + + fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply { + this.checkJacksonVersionCompatibility = checkJacksonVersionCompatibility + } + + fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper } + + fun clock(clock: Clock) = apply { this.clock = clock } + + fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl } + + fun responseValidation(responseValidation: Boolean) = apply { + this.responseValidation = responseValidation + } + + fun timeout(timeout: Timeout) = apply { this.timeout = timeout } + + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } + + 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 baseUrl(): String = baseUrl + + fun fromEnv() = apply { + System.getenv("KNOCK_BASE_URL")?.let { baseUrl(it) } + System.getenv("KNOCK_API_KEY")?.let { apiKey(it) } + } + + /** + * 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 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()) + apiKey.let { + if (!it.isEmpty()) { + headers.put("Authorization", "Bearer $it") + } + } + headers.replaceAll(this.headers.build()) + queryParams.replaceAll(this.queryParams.build()) + + return ClientOptions( + httpClient, + PhantomReachableClosingHttpClient( + RetryingHttpClient.builder() + .httpClient(httpClient) + .clock(clock) + .maxRetries(maxRetries) + .build() + ), + checkJacksonVersionCompatibility, + jsonMapper, + clock, + baseUrl, + headers.build(), + queryParams.build(), + responseValidation, + timeout, + maxRetries, + apiKey, + ) + } + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/ObjectMappers.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/ObjectMappers.kt new file mode 100644 index 00000000..dc414533 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/ObjectMappers.kt @@ -0,0 +1,167 @@ +@file:JvmName("ObjectMappers") + +package app.knock.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.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField + +fun jsonMapper(): JsonMapper = + JsonMapper.builder() + .addModule(kotlinModule()) + .addModule(Jdk8Module()) + .addModule(JavaTimeModule()) + .addModule( + SimpleModule() + .addSerializer(InputStreamSerializer) + .addDeserializer(LocalDateTime::class.java, LenientLocalDateTimeDeserializer()) + ) + .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.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.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 [LocalDateTime] from datetimes, dates, and zoned datetimes. + */ +private class LenientLocalDateTimeDeserializer : + StdDeserializer(LocalDateTime::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?): LocalDateTime { + 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() + !temporal.isSupported(ChronoField.OFFSET_SECONDS) -> + LocalDateTime.from(temporal) + else -> ZonedDateTime.from(temporal).toLocalDateTime() + } + } catch (e: DateTimeException) { + exceptions.add(e) + } + } + + throw JsonParseException(p, "Cannot parse `LocalDateTime` from value: ${p.text}").apply { + exceptions.forEach { addSuppressed(it) } + } + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/Params.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/Params.kt new file mode 100644 index 00000000..0c91f05a --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/Params.kt @@ -0,0 +1,16 @@ +package app.knock.api.core + +import app.knock.api.core.http.Headers +import app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/PhantomReachable.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/PhantomReachable.kt new file mode 100644 index 00000000..41bd8acc --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/PhantomReachable.kt @@ -0,0 +1,56 @@ +@file:JvmName("PhantomReachable") + +package app.knock.api.core + +import app.knock.api.errors.KnockException +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 KnockException("Unexpected reflective invocation failure", e) + } + } + } catch (e: ReflectiveOperationException) { + // We're running Java 8, which has no Cleaner. + null + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/PrepareRequest.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/PrepareRequest.kt new file mode 100644 index 00000000..ca4e8c97 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/PrepareRequest.kt @@ -0,0 +1,24 @@ +@file:JvmName("PrepareRequest") + +package app.knock.api.core + +import app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/Properties.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/Properties.kt new file mode 100644 index 00000000..5ab488db --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/Properties.kt @@ -0,0 +1,42 @@ +@file:JvmName("Properties") + +package app.knock.api.core + +import java.util.Properties + +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") + +fun getPackageVersion(): String = + Properties::class.java.`package`.implementationVersion ?: "unknown" + +fun getJavaVersion(): String = System.getProperty("java.version", "unknown") diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/RequestOptions.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/RequestOptions.kt new file mode 100644 index 00000000..544d1265 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/RequestOptions.kt @@ -0,0 +1,46 @@ +package app.knock.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 + + 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/knock-java-core/src/main/kotlin/app/knock/api/core/Timeout.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/Timeout.kt new file mode 100644 index 00000000..7f18212f --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/Timeout.kt @@ -0,0 +1,167 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.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 /* spotless:off */ other is Timeout && connect == other.connect && read == other.read && write == other.write && request == other.request /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(connect, read, write, request) /* spotless:on */ + + override fun toString() = + "Timeout{connect=$connect, read=$read, write=$write, request=$request}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/Utils.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/Utils.kt new file mode 100644 index 00000000..44ec19d9 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/Utils.kt @@ -0,0 +1,92 @@ +@file:JvmName("Utils") + +package app.knock.api.core + +import app.knock.api.errors.KnockInvalidDataException +import java.util.Collections +import java.util.SortedMap + +@JvmSynthetic +internal fun T?.getOrThrow(name: String): T = + this ?: throw KnockInvalidDataException("`${name}` is not present") + +@JvmSynthetic +internal fun List.toImmutable(): List = + if (isEmpty()) Collections.emptyList() else Collections.unmodifiableList(toList()) + +@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 diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/Values.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/Values.kt new file mode 100644 index 00000000..b42475eb --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/Values.kt @@ -0,0 +1,723 @@ +package app.knock.api.core + +import app.knock.api.errors.KnockInvalidDataException +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 { KnockInvalidDataException("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 KnockInvalidDataException("`$name` is not set") + is JsonNull -> throw KnockInvalidDataException("`$name` is null") + else -> throw KnockInvalidDataException("`$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 KnockInvalidDataException("`$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/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/ErrorHandler.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/ErrorHandler.kt new file mode 100644 index 00000000..d4d3df73 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/ErrorHandler.kt @@ -0,0 +1,84 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:JvmName("ErrorHandler") + +package app.knock.api.core.handlers + +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.http.HttpResponse +import app.knock.api.core.http.HttpResponse.Handler +import app.knock.api.errors.BadRequestException +import app.knock.api.errors.InternalServerException +import app.knock.api.errors.NotFoundException +import app.knock.api.errors.PermissionDeniedException +import app.knock.api.errors.RateLimitException +import app.knock.api.errors.UnauthorizedException +import app.knock.api.errors.UnexpectedStatusCodeException +import app.knock.api.errors.UnprocessableEntityException +import com.fasterxml.jackson.databind.json.JsonMapper + +@JvmSynthetic +internal fun errorHandler(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 Handler.withErrorHandler(errorHandler: Handler): Handler = + object : Handler { + override fun handle(response: HttpResponse): T = + when (val statusCode = response.statusCode()) { + in 200..299 -> this@withErrorHandler.handle(response) + 400 -> + throw BadRequestException.builder() + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + 401 -> + throw UnauthorizedException.builder() + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + 403 -> + throw PermissionDeniedException.builder() + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + 404 -> + throw NotFoundException.builder() + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + 422 -> + throw UnprocessableEntityException.builder() + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + 429 -> + throw RateLimitException.builder() + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + in 500..599 -> + throw InternalServerException.builder() + .statusCode(statusCode) + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + else -> + throw UnexpectedStatusCodeException.builder() + .statusCode(statusCode) + .headers(response.headers()) + .body(errorHandler.handle(response)) + .build() + } + } diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/JsonHandler.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/JsonHandler.kt new file mode 100644 index 00000000..eaa561b6 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/JsonHandler.kt @@ -0,0 +1,20 @@ +@file:JvmName("JsonHandler") + +package app.knock.api.core.handlers + +import app.knock.api.core.http.HttpResponse +import app.knock.api.core.http.HttpResponse.Handler +import app.knock.api.errors.KnockInvalidDataException +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 KnockInvalidDataException("Error reading response", e) + } + } diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/StringHandler.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/StringHandler.kt new file mode 100644 index 00000000..a72de0d8 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/handlers/StringHandler.kt @@ -0,0 +1,13 @@ +@file:JvmName("StringHandler") + +package app.knock.api.core.handlers + +import app.knock.api.core.http.HttpResponse +import app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/http/Headers.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/Headers.kt new file mode 100644 index 00000000..4b6f59f9 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/Headers.kt @@ -0,0 +1,92 @@ +package app.knock.api.core.http + +import app.knock.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: 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 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 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 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/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpClient.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpClient.kt new file mode 100644 index 00000000..da19390e --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpClient.kt @@ -0,0 +1,26 @@ +package app.knock.api.core.http + +import app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpMethod.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpMethod.kt new file mode 100644 index 00000000..5f292c26 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpMethod.kt @@ -0,0 +1,13 @@ +package app.knock.api.core.http + +enum class HttpMethod { + GET, + HEAD, + POST, + PUT, + DELETE, + CONNECT, + OPTIONS, + TRACE, + PATCH, +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequest.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequest.kt new file mode 100644 index 00000000..968e92ae --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequest.kt @@ -0,0 +1,146 @@ +package app.knock.api.core.http + +import app.knock.api.core.checkRequired +import app.knock.api.core.toImmutable + +class HttpRequest +private constructor( + @get:JvmName("method") val method: HttpMethod, + @get:JvmName("url") val url: 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 toBuilder(): Builder = Builder().from(this) + + override fun toString(): String = + "HttpRequest{method=$method, url=$url, 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 url: 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 + url = request.url + pathSegments = request.pathSegments.toMutableList() + headers = request.headers.toBuilder() + queryParams = request.queryParams.toBuilder() + body = request.body + } + + fun method(method: HttpMethod) = apply { this.method = method } + + fun url(url: String) = apply { this.url = url } + + 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), + url, + pathSegments.toImmutable(), + headers.build(), + queryParams.build(), + body, + ) + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequestBodies.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequestBodies.kt new file mode 100644 index 00000000..cadc2d13 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequestBodies.kt @@ -0,0 +1,106 @@ +// File generated from our OpenAPI spec by Stainless. + +@file:JvmName("HttpRequestBodies") + +package app.knock.api.core.http + +import app.knock.api.core.MultipartField +import app.knock.api.errors.KnockInvalidDataException +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.json.JsonMapper +import com.fasterxml.jackson.databind.node.JsonNodeType +import java.io.InputStream +import java.io.OutputStream +import kotlin.jvm.optionals.getOrNull +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder +import org.apache.hc.core5.http.ContentType +import org.apache.hc.core5.http.HttpEntity + +@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 = + object : HttpRequestBody { + private val entity: HttpEntity by lazy { + MultipartEntityBuilder.create() + .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) -> + addBinaryBody( + name, + bytes, + ContentType.parseLenient(field.contentType), + field.filename().getOrNull(), + ) + } + } + } + .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().inputStream()) + JsonNodeType.BOOLEAN -> + sequenceOf(name to node.booleanValue().toString().inputStream()) + JsonNodeType.NUMBER -> + sequenceOf(name to node.numberValue().toString().inputStream()) + JsonNodeType.ARRAY -> + node.elements().asSequence().flatMap { element -> + serializePart("$name[]", element) + } + JsonNodeType.OBJECT -> + node.fields().asSequence().flatMap { (key, value) -> + serializePart("$name[$key]", value) + } + JsonNodeType.POJO, + null -> + throw KnockInvalidDataException("Unexpected JsonNode type: ${node.nodeType}") + } + + private fun String.inputStream(): InputStream = toByteArray().inputStream() + + override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream) + + override fun contentType(): String = entity.contentType + + override fun contentLength(): Long = entity.contentLength + + override fun repeatable(): Boolean = entity.isRepeatable + + override fun close() = entity.close() + } diff --git a/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequestBody.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequestBody.kt new file mode 100644 index 00000000..f2989461 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpRequestBody.kt @@ -0,0 +1,25 @@ +package app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpResponse.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpResponse.kt new file mode 100644 index 00000000..2f9f4516 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpResponse.kt @@ -0,0 +1,22 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpResponseFor.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpResponseFor.kt new file mode 100644 index 00000000..aa5dc6eb --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/HttpResponseFor.kt @@ -0,0 +1,25 @@ +package app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/http/PhantomReachableClosingHttpClient.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/PhantomReachableClosingHttpClient.kt new file mode 100644 index 00000000..7f96eff9 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/PhantomReachableClosingHttpClient.kt @@ -0,0 +1,26 @@ +package app.knock.api.core.http + +import app.knock.api.core.RequestOptions +import app.knock.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/knock-java-core/src/main/kotlin/app/knock/api/core/http/QueryParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/QueryParams.kt new file mode 100644 index 00000000..d965feaa --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/QueryParams.kt @@ -0,0 +1,88 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.core.http + +import app.knock.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: 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/knock-java-core/src/main/kotlin/app/knock/api/core/http/RetryingHttpClient.kt b/knock-java-core/src/main/kotlin/app/knock/api/core/http/RetryingHttpClient.kt new file mode 100644 index 00000000..94e0f390 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/core/http/RetryingHttpClient.kt @@ -0,0 +1,286 @@ +package app.knock.api.core.http + +import app.knock.api.core.RequestOptions +import app.knock.api.core.checkRequired +import app.knock.api.errors.KnockIoException +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.Timer +import java.util.TimerTask +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 { + if (!isRetryable(request) || maxRetries <= 0) { + return httpClient.execute(request, requestOptions) + } + + var modifiedRequest = maybeAddIdempotencyHeader(request) + + // Don't send the current retry count in the headers if the caller set their own value. + val shouldSendRetryCount = + !modifiedRequest.headers.names().contains("X-Stainless-Retry-Count") + + var retries = 0 + + while (true) { + if (shouldSendRetryCount) { + modifiedRequest = setRetryCountHeader(modifiedRequest, retries) + } + + 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 { + if (!isRetryable(request) || maxRetries <= 0) { + return httpClient.executeAsync(request, requestOptions) + } + + val modifiedRequest = maybeAddIdempotencyHeader(request) + + // Don't send the current retry count in the headers if the caller set their own value. + 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 + + return httpClient + .executeAsync(requestWithRetryCount, requestOptions) + .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() + + 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 IOException and KnockIoException, other exceptions are not intended to be + // retried. + throwable is IOException || throwable is KnockIoException + + 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.MILLIS.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 (and it's a reasonable + // amount), just + // do what it says. + val retryAfter = Duration.ofNanos(retryAfterNanos.toLong()) + if (retryAfter in Duration.ofNanos(0)..Duration.ofMinutes(1)) { + return retryAfter + } + } + + // 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 = + object : Sleeper { + + private val timer = Timer("RetryingHttpClient", true) + + override fun sleep(duration: Duration) = Thread.sleep(duration.toMillis()) + + override fun sleepAsync(duration: Duration): CompletableFuture { + val future = CompletableFuture() + timer.schedule( + object : TimerTask() { + override fun run() { + future.complete(null) + } + }, + duration.toMillis(), + ) + return future + } + } + private var clock: Clock = Clock.systemUTC() + private var maxRetries: Int = 2 + private var idempotencyHeader: String? = null + + fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient } + + @JvmSynthetic internal fun sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper } + + fun clock(clock: Clock) = apply { this.clock = clock } + + fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries } + + fun idempotencyHeader(header: String) = apply { this.idempotencyHeader = header } + + fun build(): HttpClient = + RetryingHttpClient( + checkRequired("httpClient", httpClient), + sleeper, + clock, + maxRetries, + idempotencyHeader, + ) + } + + internal interface Sleeper { + + fun sleep(duration: Duration) + + fun sleepAsync(duration: Duration): CompletableFuture + } +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/errors/BadRequestException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/BadRequestException.kt new file mode 100644 index 00000000..0fd7c9f6 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/BadRequestException.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class BadRequestException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + KnockServiceException("400: $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/knock-java-core/src/main/kotlin/app/knock/api/errors/InternalServerException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/InternalServerException.kt new file mode 100644 index 00000000..3cf9c0cb --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/InternalServerException.kt @@ -0,0 +1,91 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +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?, +) : KnockServiceException("$statusCode: $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/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockException.kt new file mode 100644 index 00000000..af676986 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockException.kt @@ -0,0 +1,5 @@ +package app.knock.api.errors + +open class KnockException +@JvmOverloads +constructor(message: String? = null, cause: Throwable? = null) : RuntimeException(message, cause) diff --git a/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockInvalidDataException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockInvalidDataException.kt new file mode 100644 index 00000000..d41c9ed5 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockInvalidDataException.kt @@ -0,0 +1,5 @@ +package app.knock.api.errors + +class KnockInvalidDataException +@JvmOverloads +constructor(message: String? = null, cause: Throwable? = null) : KnockException(message, cause) diff --git a/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockIoException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockIoException.kt new file mode 100644 index 00000000..c87babcf --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockIoException.kt @@ -0,0 +1,5 @@ +package app.knock.api.errors + +class KnockIoException +@JvmOverloads +constructor(message: String? = null, cause: Throwable? = null) : KnockException(message, cause) diff --git a/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockServiceException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockServiceException.kt new file mode 100644 index 00000000..c97201ab --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/KnockServiceException.kt @@ -0,0 +1,16 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.http.Headers + +abstract class KnockServiceException +protected constructor(message: String, cause: Throwable? = null) : KnockException(message, cause) { + + abstract fun statusCode(): Int + + abstract fun headers(): Headers + + abstract fun body(): JsonValue +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/errors/NotFoundException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/NotFoundException.kt new file mode 100644 index 00000000..8919764b --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/NotFoundException.kt @@ -0,0 +1,76 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class NotFoundException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + KnockServiceException("404: $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/knock-java-core/src/main/kotlin/app/knock/api/errors/PermissionDeniedException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/PermissionDeniedException.kt new file mode 100644 index 00000000..2fd54201 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/PermissionDeniedException.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class PermissionDeniedException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + KnockServiceException("403: $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/knock-java-core/src/main/kotlin/app/knock/api/errors/RateLimitException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/RateLimitException.kt new file mode 100644 index 00000000..3bf674d4 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/RateLimitException.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class RateLimitException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + KnockServiceException("429: $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/knock-java-core/src/main/kotlin/app/knock/api/errors/UnauthorizedException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/UnauthorizedException.kt new file mode 100644 index 00000000..2ddf7f9d --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/UnauthorizedException.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class UnauthorizedException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + KnockServiceException("401: $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/knock-java-core/src/main/kotlin/app/knock/api/errors/UnexpectedStatusCodeException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/UnexpectedStatusCodeException.kt new file mode 100644 index 00000000..52157f0f --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/UnexpectedStatusCodeException.kt @@ -0,0 +1,92 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +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?, +) : KnockServiceException("$statusCode: $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/knock-java-core/src/main/kotlin/app/knock/api/errors/UnprocessableEntityException.kt b/knock-java-core/src/main/kotlin/app/knock/api/errors/UnprocessableEntityException.kt new file mode 100644 index 00000000..81d9151a --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/errors/UnprocessableEntityException.kt @@ -0,0 +1,80 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.errors + +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class UnprocessableEntityException +private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) : + KnockServiceException("422: $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/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceAddMembersParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceAddMembersParams.kt new file mode 100644 index 00000000..965918f9 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceAddMembersParams.kt @@ -0,0 +1,797 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.audiences + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.Params +import app.knock.api.core.checkKnown +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 + +/** Adds one or more members to the specified audience. */ +class AudienceAddMembersParams +private constructor( + private val key: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun key(): Optional = Optional.ofNullable(key) + + /** + * A list of audience members to add. + * + * @throws KnockInvalidDataException 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 members(): List = body.members() + + /** + * Returns the raw JSON value of [members]. + * + * Unlike [members], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _members(): JsonField> = body._members() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [AudienceAddMembersParams]. + * + * The following fields are required: + * ```java + * .members() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudienceAddMembersParams]. */ + class Builder internal constructor() { + + private var key: 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(audienceAddMembersParams: AudienceAddMembersParams) = apply { + key = audienceAddMembersParams.key + body = audienceAddMembersParams.body.toBuilder() + additionalHeaders = audienceAddMembersParams.additionalHeaders.toBuilder() + additionalQueryParams = audienceAddMembersParams.additionalQueryParams.toBuilder() + } + + fun key(key: String?) = apply { this.key = key } + + /** Alias for calling [Builder.key] with `key.orElse(null)`. */ + fun key(key: Optional) = key(key.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: + * - [members] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** A list of audience members to add. */ + fun members(members: List) = apply { body.members(members) } + + /** + * Sets [Builder.members] to an arbitrary JSON value. + * + * You should usually call [Builder.members] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun members(members: JsonField>) = apply { body.members(members) } + + /** + * Adds a single [Member] to [members]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addMember(member: Member) = apply { body.addMember(member) } + + 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 [AudienceAddMembersParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .members() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): AudienceAddMembersParams = + AudienceAddMembersParams( + key, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> key ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** A request to add a list of audience members. */ + class Body + private constructor( + private val members: JsonField>, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("members") + @ExcludeMissing + members: JsonField> = JsonMissing.of() + ) : this(members, mutableMapOf()) + + /** + * A list of audience members to add. + * + * @throws KnockInvalidDataException 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 members(): List = members.getRequired("members") + + /** + * Returns the raw JSON value of [members]. + * + * Unlike [members], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("members") @ExcludeMissing fun _members(): JsonField> = members + + @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 + * .members() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var members: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + members = body.members.map { it.toMutableList() } + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** A list of audience members to add. */ + fun members(members: List) = members(JsonField.of(members)) + + /** + * Sets [Builder.members] to an arbitrary JSON value. + * + * You should usually call [Builder.members] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun members(members: JsonField>) = apply { + this.members = members.map { it.toMutableList() } + } + + /** + * Adds a single [Member] to [members]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addMember(member: Member) = apply { + members = + (members ?: JsonField.of(mutableListOf())).also { + checkKnown("members", it).add(member) + } + } + + 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 + * .members() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("members", members).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + members().forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (members.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && members == other.members && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(members, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{members=$members, additionalProperties=$additionalProperties}" + } + + /** An audience member. */ + class Member + private constructor( + private val user: JsonField, + private val tenant: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("user") @ExcludeMissing user: JsonField = JsonMissing.of(), + @JsonProperty("tenant") @ExcludeMissing tenant: JsonField = JsonMissing.of(), + ) : this(user, tenant, mutableMapOf()) + + /** + * An object containing the user's ID. + * + * @throws KnockInvalidDataException 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 user(): User = user.getRequired("user") + + /** + * The unique identifier for the tenant. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tenant(): Optional = tenant.getOptional("tenant") + + /** + * Returns the raw JSON value of [user]. + * + * Unlike [user], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("user") @ExcludeMissing fun _user(): JsonField = user + + /** + * Returns the raw JSON value of [tenant]. + * + * Unlike [tenant], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("tenant") @ExcludeMissing fun _tenant(): JsonField = tenant + + @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 [Member]. + * + * The following fields are required: + * ```java + * .user() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Member]. */ + class Builder internal constructor() { + + private var user: JsonField? = null + private var tenant: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(member: Member) = apply { + user = member.user + tenant = member.tenant + additionalProperties = member.additionalProperties.toMutableMap() + } + + /** An object containing the user's ID. */ + fun user(user: User) = user(JsonField.of(user)) + + /** + * Sets [Builder.user] to an arbitrary JSON value. + * + * You should usually call [Builder.user] with a well-typed [User] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun user(user: JsonField) = apply { this.user = user } + + /** The unique identifier for the tenant. */ + fun tenant(tenant: String?) = tenant(JsonField.ofNullable(tenant)) + + /** Alias for calling [Builder.tenant] with `tenant.orElse(null)`. */ + fun tenant(tenant: Optional) = tenant(tenant.getOrNull()) + + /** + * Sets [Builder.tenant] to an arbitrary JSON value. + * + * You should usually call [Builder.tenant] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun tenant(tenant: JsonField) = apply { this.tenant = tenant } + + 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 [Member]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .user() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Member = + Member(checkRequired("user", user), tenant, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Member = apply { + if (validated) { + return@apply + } + + user().validate() + tenant() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (user.asKnown().getOrNull()?.validity() ?: 0) + + (if (tenant.asKnown().isPresent) 1 else 0) + + /** An object containing the user's ID. */ + class User + private constructor( + private val id: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of() + ) : this(id, mutableMapOf()) + + /** + * The ID for the user that you set when identifying them in Knock. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + @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 [User]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [User]. */ + class Builder internal constructor() { + + private var id: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(user: User) = apply { + id = user.id + additionalProperties = user.additionalProperties.toMutableMap() + } + + /** The ID for the user that you set when identifying them in Knock. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + 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 [User]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): User = User(id, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): User = apply { + if (validated) { + return@apply + } + + id() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is User && id == other.id && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "User{id=$id, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Member && user == other.user && tenant == other.tenant && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(user, tenant, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Member{user=$user, tenant=$tenant, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudienceAddMembersParams && key == other.key && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(key, body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "AudienceAddMembersParams{key=$key, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceListMembersParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceListMembersParams.kt new file mode 100644 index 00000000..5e517206 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceListMembersParams.kt @@ -0,0 +1,186 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.audiences + +import app.knock.api.core.Params +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Returns a paginated list of members for the specified audience. */ +class AudienceListMembersParams +private constructor( + private val key: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun key(): Optional = Optional.ofNullable(key) + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): AudienceListMembersParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of [AudienceListMembersParams]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudienceListMembersParams]. */ + class Builder internal constructor() { + + private var key: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(audienceListMembersParams: AudienceListMembersParams) = apply { + key = audienceListMembersParams.key + additionalHeaders = audienceListMembersParams.additionalHeaders.toBuilder() + additionalQueryParams = audienceListMembersParams.additionalQueryParams.toBuilder() + } + + fun key(key: String?) = apply { this.key = key } + + /** Alias for calling [Builder.key] with `key.orElse(null)`. */ + fun key(key: Optional) = key(key.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 [AudienceListMembersParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): AudienceListMembersParams = + AudienceListMembersParams(key, additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> key ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudienceListMembersParams && key == other.key && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(key, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "AudienceListMembersParams{key=$key, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceListMembersResponse.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceListMembersResponse.kt new file mode 100644 index 00000000..aa5bf946 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceListMembersResponse.kt @@ -0,0 +1,234 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.audiences + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.checkKnown +import app.knock.api.core.checkRequired +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +import app.knock.api.models.shared.PageInfo +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 kotlin.jvm.optionals.getOrNull + +/** A paginated list of audience members. */ +class AudienceListMembersResponse +private constructor( + private val entries: JsonField>, + private val pageInfo: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("entries") + @ExcludeMissing + entries: JsonField> = JsonMissing.of(), + @JsonProperty("page_info") @ExcludeMissing pageInfo: JsonField = JsonMissing.of(), + ) : this(entries, pageInfo, mutableMapOf()) + + /** + * A list of audience members. + * + * @throws KnockInvalidDataException 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 entries(): List = entries.getRequired("entries") + + /** + * Pagination information for a list of resources. + * + * @throws KnockInvalidDataException 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 pageInfo(): PageInfo = pageInfo.getRequired("page_info") + + /** + * Returns the raw JSON value of [entries]. + * + * Unlike [entries], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("entries") + @ExcludeMissing + fun _entries(): JsonField> = entries + + /** + * Returns the raw JSON value of [pageInfo]. + * + * Unlike [pageInfo], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("page_info") @ExcludeMissing fun _pageInfo(): JsonField = pageInfo + + @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 [AudienceListMembersResponse]. + * + * The following fields are required: + * ```java + * .entries() + * .pageInfo() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudienceListMembersResponse]. */ + class Builder internal constructor() { + + private var entries: JsonField>? = null + private var pageInfo: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(audienceListMembersResponse: AudienceListMembersResponse) = apply { + entries = audienceListMembersResponse.entries.map { it.toMutableList() } + pageInfo = audienceListMembersResponse.pageInfo + additionalProperties = audienceListMembersResponse.additionalProperties.toMutableMap() + } + + /** A list of audience members. */ + fun entries(entries: List) = entries(JsonField.of(entries)) + + /** + * Sets [Builder.entries] to an arbitrary JSON value. + * + * You should usually call [Builder.entries] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun entries(entries: JsonField>) = apply { + this.entries = entries.map { it.toMutableList() } + } + + /** + * Adds a single [AudienceMember] to [entries]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addEntry(entry: AudienceMember) = apply { + entries = + (entries ?: JsonField.of(mutableListOf())).also { + checkKnown("entries", it).add(entry) + } + } + + /** Pagination information for a list of resources. */ + fun pageInfo(pageInfo: PageInfo) = pageInfo(JsonField.of(pageInfo)) + + /** + * Sets [Builder.pageInfo] to an arbitrary JSON value. + * + * You should usually call [Builder.pageInfo] with a well-typed [PageInfo] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun pageInfo(pageInfo: JsonField) = apply { this.pageInfo = pageInfo } + + 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 [AudienceListMembersResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .entries() + * .pageInfo() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): AudienceListMembersResponse = + AudienceListMembersResponse( + checkRequired("entries", entries).map { it.toImmutable() }, + checkRequired("pageInfo", pageInfo), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): AudienceListMembersResponse = apply { + if (validated) { + return@apply + } + + entries().forEach { it.validate() } + pageInfo().validate() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (entries.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (pageInfo.asKnown().getOrNull()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudienceListMembersResponse && entries == other.entries && pageInfo == other.pageInfo && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(entries, pageInfo, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AudienceListMembersResponse{entries=$entries, pageInfo=$pageInfo, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceMember.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceMember.kt new file mode 100644 index 00000000..7a61fc99 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceMember.kt @@ -0,0 +1,330 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.audiences + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.errors.KnockInvalidDataException +import app.knock.api.models.users.User +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 audience member. */ +class AudienceMember +private constructor( + private val _typename: JsonField, + private val addedAt: JsonField, + private val user: JsonField, + private val userId: JsonField, + private val tenant: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("__typename") @ExcludeMissing _typename: JsonField = JsonMissing.of(), + @JsonProperty("added_at") + @ExcludeMissing + addedAt: JsonField = JsonMissing.of(), + @JsonProperty("user") @ExcludeMissing user: JsonField = JsonMissing.of(), + @JsonProperty("user_id") @ExcludeMissing userId: JsonField = JsonMissing.of(), + @JsonProperty("tenant") @ExcludeMissing tenant: JsonField = JsonMissing.of(), + ) : this(_typename, addedAt, user, userId, tenant, mutableMapOf()) + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * Timestamp when the resource was created. + * + * @throws KnockInvalidDataException 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 addedAt(): OffsetDateTime = addedAt.getRequired("added_at") + + /** + * A [User](/concepts/users) represents an individual in your system who can receive + * notifications through Knock. Users are the most common recipients of notifications and are + * always referenced by your internal identifier. + * + * @throws KnockInvalidDataException 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 user(): User = user.getRequired("user") + + /** + * The ID for the user that you set when identifying them in Knock. + * + * @throws KnockInvalidDataException 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 userId(): String = userId.getRequired("user_id") + + /** + * The unique identifier for the tenant. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tenant(): Optional = tenant.getOptional("tenant") + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [addedAt]. + * + * Unlike [addedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("added_at") @ExcludeMissing fun _addedAt(): JsonField = addedAt + + /** + * Returns the raw JSON value of [user]. + * + * Unlike [user], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("user") @ExcludeMissing fun _user(): JsonField = user + + /** + * Returns the raw JSON value of [userId]. + * + * Unlike [userId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("user_id") @ExcludeMissing fun _userId(): JsonField = userId + + /** + * Returns the raw JSON value of [tenant]. + * + * Unlike [tenant], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("tenant") @ExcludeMissing fun _tenant(): JsonField = tenant + + @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 [AudienceMember]. + * + * The following fields are required: + * ```java + * ._typename() + * .addedAt() + * .user() + * .userId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudienceMember]. */ + class Builder internal constructor() { + + private var _typename: JsonField? = null + private var addedAt: JsonField? = null + private var user: JsonField? = null + private var userId: JsonField? = null + private var tenant: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(audienceMember: AudienceMember) = apply { + _typename = audienceMember._typename + addedAt = audienceMember.addedAt + user = audienceMember.user + userId = audienceMember.userId + tenant = audienceMember.tenant + additionalProperties = audienceMember.additionalProperties.toMutableMap() + } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** Timestamp when the resource was created. */ + fun addedAt(addedAt: OffsetDateTime) = addedAt(JsonField.of(addedAt)) + + /** + * Sets [Builder.addedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.addedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun addedAt(addedAt: JsonField) = apply { this.addedAt = addedAt } + + /** + * A [User](/concepts/users) represents an individual in your system who can receive + * notifications through Knock. Users are the most common recipients of notifications and + * are always referenced by your internal identifier. + */ + fun user(user: User) = user(JsonField.of(user)) + + /** + * Sets [Builder.user] to an arbitrary JSON value. + * + * You should usually call [Builder.user] with a well-typed [User] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun user(user: JsonField) = apply { this.user = user } + + /** The ID for the user that you set when identifying them in Knock. */ + fun userId(userId: String) = userId(JsonField.of(userId)) + + /** + * Sets [Builder.userId] to an arbitrary JSON value. + * + * You should usually call [Builder.userId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun userId(userId: JsonField) = apply { this.userId = userId } + + /** The unique identifier for the tenant. */ + fun tenant(tenant: String?) = tenant(JsonField.ofNullable(tenant)) + + /** Alias for calling [Builder.tenant] with `tenant.orElse(null)`. */ + fun tenant(tenant: Optional) = tenant(tenant.getOrNull()) + + /** + * Sets [Builder.tenant] to an arbitrary JSON value. + * + * You should usually call [Builder.tenant] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun tenant(tenant: JsonField) = apply { this.tenant = tenant } + + 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 [AudienceMember]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * ._typename() + * .addedAt() + * .user() + * .userId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): AudienceMember = + AudienceMember( + checkRequired("_typename", _typename), + checkRequired("addedAt", addedAt), + checkRequired("user", user), + checkRequired("userId", userId), + tenant, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): AudienceMember = apply { + if (validated) { + return@apply + } + + _typename() + addedAt() + user().validate() + userId() + tenant() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (_typename.asKnown().isPresent) 1 else 0) + + (if (addedAt.asKnown().isPresent) 1 else 0) + + (user.asKnown().getOrNull()?.validity() ?: 0) + + (if (userId.asKnown().isPresent) 1 else 0) + + (if (tenant.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudienceMember && _typename == other._typename && addedAt == other.addedAt && user == other.user && userId == other.userId && tenant == other.tenant && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(_typename, addedAt, user, userId, tenant, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "AudienceMember{_typename=$_typename, addedAt=$addedAt, user=$user, userId=$userId, tenant=$tenant, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceRemoveMembersParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceRemoveMembersParams.kt new file mode 100644 index 00000000..942dfa2c --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/audiences/AudienceRemoveMembersParams.kt @@ -0,0 +1,797 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.audiences + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.Params +import app.knock.api.core.checkKnown +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 + +/** Removes one or more members from the specified audience. */ +class AudienceRemoveMembersParams +private constructor( + private val key: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun key(): Optional = Optional.ofNullable(key) + + /** + * A list of audience members to remove. + * + * @throws KnockInvalidDataException 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 members(): List = body.members() + + /** + * Returns the raw JSON value of [members]. + * + * Unlike [members], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _members(): JsonField> = body._members() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [AudienceRemoveMembersParams]. + * + * The following fields are required: + * ```java + * .members() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [AudienceRemoveMembersParams]. */ + class Builder internal constructor() { + + private var key: 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(audienceRemoveMembersParams: AudienceRemoveMembersParams) = apply { + key = audienceRemoveMembersParams.key + body = audienceRemoveMembersParams.body.toBuilder() + additionalHeaders = audienceRemoveMembersParams.additionalHeaders.toBuilder() + additionalQueryParams = audienceRemoveMembersParams.additionalQueryParams.toBuilder() + } + + fun key(key: String?) = apply { this.key = key } + + /** Alias for calling [Builder.key] with `key.orElse(null)`. */ + fun key(key: Optional) = key(key.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: + * - [members] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** A list of audience members to remove. */ + fun members(members: List) = apply { body.members(members) } + + /** + * Sets [Builder.members] to an arbitrary JSON value. + * + * You should usually call [Builder.members] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun members(members: JsonField>) = apply { body.members(members) } + + /** + * Adds a single [Member] to [members]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addMember(member: Member) = apply { body.addMember(member) } + + 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 [AudienceRemoveMembersParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .members() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): AudienceRemoveMembersParams = + AudienceRemoveMembersParams( + key, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> key ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** A request to remove a list of audience members. */ + class Body + private constructor( + private val members: JsonField>, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("members") + @ExcludeMissing + members: JsonField> = JsonMissing.of() + ) : this(members, mutableMapOf()) + + /** + * A list of audience members to remove. + * + * @throws KnockInvalidDataException 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 members(): List = members.getRequired("members") + + /** + * Returns the raw JSON value of [members]. + * + * Unlike [members], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("members") @ExcludeMissing fun _members(): JsonField> = members + + @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 + * .members() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var members: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + members = body.members.map { it.toMutableList() } + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** A list of audience members to remove. */ + fun members(members: List) = members(JsonField.of(members)) + + /** + * Sets [Builder.members] to an arbitrary JSON value. + * + * You should usually call [Builder.members] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun members(members: JsonField>) = apply { + this.members = members.map { it.toMutableList() } + } + + /** + * Adds a single [Member] to [members]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addMember(member: Member) = apply { + members = + (members ?: JsonField.of(mutableListOf())).also { + checkKnown("members", it).add(member) + } + } + + 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 + * .members() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("members", members).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + members().forEach { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (members.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && members == other.members && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(members, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{members=$members, additionalProperties=$additionalProperties}" + } + + /** An audience member. */ + class Member + private constructor( + private val user: JsonField, + private val tenant: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("user") @ExcludeMissing user: JsonField = JsonMissing.of(), + @JsonProperty("tenant") @ExcludeMissing tenant: JsonField = JsonMissing.of(), + ) : this(user, tenant, mutableMapOf()) + + /** + * An object containing the user's ID. + * + * @throws KnockInvalidDataException 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 user(): User = user.getRequired("user") + + /** + * The unique identifier for the tenant. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tenant(): Optional = tenant.getOptional("tenant") + + /** + * Returns the raw JSON value of [user]. + * + * Unlike [user], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("user") @ExcludeMissing fun _user(): JsonField = user + + /** + * Returns the raw JSON value of [tenant]. + * + * Unlike [tenant], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("tenant") @ExcludeMissing fun _tenant(): JsonField = tenant + + @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 [Member]. + * + * The following fields are required: + * ```java + * .user() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Member]. */ + class Builder internal constructor() { + + private var user: JsonField? = null + private var tenant: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(member: Member) = apply { + user = member.user + tenant = member.tenant + additionalProperties = member.additionalProperties.toMutableMap() + } + + /** An object containing the user's ID. */ + fun user(user: User) = user(JsonField.of(user)) + + /** + * Sets [Builder.user] to an arbitrary JSON value. + * + * You should usually call [Builder.user] with a well-typed [User] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun user(user: JsonField) = apply { this.user = user } + + /** The unique identifier for the tenant. */ + fun tenant(tenant: String?) = tenant(JsonField.ofNullable(tenant)) + + /** Alias for calling [Builder.tenant] with `tenant.orElse(null)`. */ + fun tenant(tenant: Optional) = tenant(tenant.getOrNull()) + + /** + * Sets [Builder.tenant] to an arbitrary JSON value. + * + * You should usually call [Builder.tenant] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun tenant(tenant: JsonField) = apply { this.tenant = tenant } + + 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 [Member]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .user() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Member = + Member(checkRequired("user", user), tenant, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Member = apply { + if (validated) { + return@apply + } + + user().validate() + tenant() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (user.asKnown().getOrNull()?.validity() ?: 0) + + (if (tenant.asKnown().isPresent) 1 else 0) + + /** An object containing the user's ID. */ + class User + private constructor( + private val id: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of() + ) : this(id, mutableMapOf()) + + /** + * The ID for the user that you set when identifying them in Knock. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + @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 [User]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [User]. */ + class Builder internal constructor() { + + private var id: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(user: User) = apply { + id = user.id + additionalProperties = user.additionalProperties.toMutableMap() + } + + /** The ID for the user that you set when identifying them in Knock. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + 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 [User]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): User = User(id, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): User = apply { + if (validated) { + return@apply + } + + id() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is User && id == other.id && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "User{id=$id, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Member && user == other.user && tenant == other.tenant && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(user, tenant, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Member{user=$user, tenant=$tenant, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is AudienceRemoveMembersParams && key == other.key && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(key, body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "AudienceRemoveMembersParams{key=$key, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/bulkoperations/BulkOperation.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/bulkoperations/BulkOperation.kt new file mode 100644 index 00000000..c4acd2c2 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/bulkoperations/BulkOperation.kt @@ -0,0 +1,1095 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.bulkoperations + +import app.knock.api.core.Enum +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.checkKnown +import app.knock.api.core.checkRequired +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 + +/** A bulk operation entity. */ +class BulkOperation +private constructor( + private val id: JsonField, + private val _typename: JsonField, + private val estimatedTotalRows: JsonField, + private val insertedAt: JsonField, + private val name: JsonField, + private val processedRows: JsonField, + private val status: JsonField, + private val successCount: JsonField, + private val updatedAt: JsonField, + private val completedAt: JsonField, + private val errorCount: JsonField, + private val errorItems: JsonField>, + private val failedAt: JsonField, + private val progressPath: JsonField, + private val startedAt: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("__typename") @ExcludeMissing _typename: JsonField = JsonMissing.of(), + @JsonProperty("estimated_total_rows") + @ExcludeMissing + estimatedTotalRows: JsonField = JsonMissing.of(), + @JsonProperty("inserted_at") + @ExcludeMissing + insertedAt: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("processed_rows") + @ExcludeMissing + processedRows: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("success_count") + @ExcludeMissing + successCount: JsonField = JsonMissing.of(), + @JsonProperty("updated_at") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("completed_at") + @ExcludeMissing + completedAt: JsonField = JsonMissing.of(), + @JsonProperty("error_count") @ExcludeMissing errorCount: JsonField = JsonMissing.of(), + @JsonProperty("error_items") + @ExcludeMissing + errorItems: JsonField> = JsonMissing.of(), + @JsonProperty("failed_at") + @ExcludeMissing + failedAt: JsonField = JsonMissing.of(), + @JsonProperty("progress_path") + @ExcludeMissing + progressPath: JsonField = JsonMissing.of(), + @JsonProperty("started_at") + @ExcludeMissing + startedAt: JsonField = JsonMissing.of(), + ) : this( + id, + _typename, + estimatedTotalRows, + insertedAt, + name, + processedRows, + status, + successCount, + updatedAt, + completedAt, + errorCount, + errorItems, + failedAt, + progressPath, + startedAt, + mutableMapOf(), + ) + + /** + * Unique identifier for the bulk operation. + * + * @throws KnockInvalidDataException 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 id(): String = id.getRequired("id") + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * The estimated total number of rows to process. + * + * @throws KnockInvalidDataException 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 estimatedTotalRows(): Long = estimatedTotalRows.getRequired("estimated_total_rows") + + /** + * Timestamp when the resource was created. + * + * @throws KnockInvalidDataException 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 insertedAt(): OffsetDateTime = insertedAt.getRequired("inserted_at") + + /** + * The name of the bulk operation. + * + * @throws KnockInvalidDataException 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 name(): String = name.getRequired("name") + + /** + * The number of rows processed so far. + * + * @throws KnockInvalidDataException 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 processedRows(): Long = processedRows.getRequired("processed_rows") + + /** + * The status of the bulk operation. + * + * @throws KnockInvalidDataException 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 status(): Status = status.getRequired("status") + + /** + * The number of successful operations. + * + * @throws KnockInvalidDataException 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 successCount(): Long = successCount.getRequired("success_count") + + /** + * The timestamp when the resource was last updated. + * + * @throws KnockInvalidDataException 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 updatedAt(): OffsetDateTime = updatedAt.getRequired("updated_at") + + /** + * Timestamp when the bulk operation was completed. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun completedAt(): Optional = completedAt.getOptional("completed_at") + + /** + * The number of failed operations. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun errorCount(): Optional = errorCount.getOptional("error_count") + + /** + * A list of items that failed to be processed. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun errorItems(): Optional> = errorItems.getOptional("error_items") + + /** + * Timestamp when the bulk operation failed. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun failedAt(): Optional = failedAt.getOptional("failed_at") + + /** + * The URI to the bulk operation's progress. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun progressPath(): Optional = progressPath.getOptional("progress_path") + + /** + * Timestamp when the bulk operation was started. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun startedAt(): Optional = startedAt.getOptional("started_at") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [estimatedTotalRows]. + * + * Unlike [estimatedTotalRows], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("estimated_total_rows") + @ExcludeMissing + fun _estimatedTotalRows(): JsonField = estimatedTotalRows + + /** + * Returns the raw JSON value of [insertedAt]. + * + * Unlike [insertedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inserted_at") + @ExcludeMissing + fun _insertedAt(): JsonField = insertedAt + + /** + * 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 [processedRows]. + * + * Unlike [processedRows], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("processed_rows") + @ExcludeMissing + fun _processedRows(): JsonField = processedRows + + /** + * 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 [successCount]. + * + * Unlike [successCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("success_count") + @ExcludeMissing + fun _successCount(): JsonField = successCount + + /** + * 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 + + /** + * Returns the raw JSON value of [completedAt]. + * + * Unlike [completedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("completed_at") + @ExcludeMissing + fun _completedAt(): JsonField = completedAt + + /** + * Returns the raw JSON value of [errorCount]. + * + * Unlike [errorCount], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("error_count") @ExcludeMissing fun _errorCount(): JsonField = errorCount + + /** + * Returns the raw JSON value of [errorItems]. + * + * Unlike [errorItems], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("error_items") + @ExcludeMissing + fun _errorItems(): JsonField> = errorItems + + /** + * Returns the raw JSON value of [failedAt]. + * + * Unlike [failedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("failed_at") @ExcludeMissing fun _failedAt(): JsonField = failedAt + + /** + * Returns the raw JSON value of [progressPath]. + * + * Unlike [progressPath], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("progress_path") + @ExcludeMissing + fun _progressPath(): JsonField = progressPath + + /** + * Returns the raw JSON value of [startedAt]. + * + * Unlike [startedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("started_at") + @ExcludeMissing + fun _startedAt(): JsonField = startedAt + + @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 [BulkOperation]. + * + * The following fields are required: + * ```java + * .id() + * ._typename() + * .estimatedTotalRows() + * .insertedAt() + * .name() + * .processedRows() + * .status() + * .successCount() + * .updatedAt() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BulkOperation]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var _typename: JsonField? = null + private var estimatedTotalRows: JsonField? = null + private var insertedAt: JsonField? = null + private var name: JsonField? = null + private var processedRows: JsonField? = null + private var status: JsonField? = null + private var successCount: JsonField? = null + private var updatedAt: JsonField? = null + private var completedAt: JsonField = JsonMissing.of() + private var errorCount: JsonField = JsonMissing.of() + private var errorItems: JsonField>? = null + private var failedAt: JsonField = JsonMissing.of() + private var progressPath: JsonField = JsonMissing.of() + private var startedAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(bulkOperation: BulkOperation) = apply { + id = bulkOperation.id + _typename = bulkOperation._typename + estimatedTotalRows = bulkOperation.estimatedTotalRows + insertedAt = bulkOperation.insertedAt + name = bulkOperation.name + processedRows = bulkOperation.processedRows + status = bulkOperation.status + successCount = bulkOperation.successCount + updatedAt = bulkOperation.updatedAt + completedAt = bulkOperation.completedAt + errorCount = bulkOperation.errorCount + errorItems = bulkOperation.errorItems.map { it.toMutableList() } + failedAt = bulkOperation.failedAt + progressPath = bulkOperation.progressPath + startedAt = bulkOperation.startedAt + additionalProperties = bulkOperation.additionalProperties.toMutableMap() + } + + /** Unique identifier for the bulk operation. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** The estimated total number of rows to process. */ + fun estimatedTotalRows(estimatedTotalRows: Long) = + estimatedTotalRows(JsonField.of(estimatedTotalRows)) + + /** + * Sets [Builder.estimatedTotalRows] to an arbitrary JSON value. + * + * You should usually call [Builder.estimatedTotalRows] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun estimatedTotalRows(estimatedTotalRows: JsonField) = apply { + this.estimatedTotalRows = estimatedTotalRows + } + + /** Timestamp when the resource was created. */ + fun insertedAt(insertedAt: OffsetDateTime) = insertedAt(JsonField.of(insertedAt)) + + /** + * Sets [Builder.insertedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.insertedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun insertedAt(insertedAt: JsonField) = apply { + this.insertedAt = insertedAt + } + + /** The name of the bulk operation. */ + 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 } + + /** The number of rows processed so far. */ + fun processedRows(processedRows: Long) = processedRows(JsonField.of(processedRows)) + + /** + * Sets [Builder.processedRows] to an arbitrary JSON value. + * + * You should usually call [Builder.processedRows] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun processedRows(processedRows: JsonField) = apply { + this.processedRows = processedRows + } + + /** The status of the bulk operation. */ + 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 } + + /** The number of successful operations. */ + fun successCount(successCount: Long) = successCount(JsonField.of(successCount)) + + /** + * Sets [Builder.successCount] to an arbitrary JSON value. + * + * You should usually call [Builder.successCount] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun successCount(successCount: JsonField) = apply { this.successCount = successCount } + + /** The timestamp when the resource 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 } + + /** Timestamp when the bulk operation was completed. */ + fun completedAt(completedAt: OffsetDateTime?) = + completedAt(JsonField.ofNullable(completedAt)) + + /** Alias for calling [Builder.completedAt] with `completedAt.orElse(null)`. */ + fun completedAt(completedAt: Optional) = + completedAt(completedAt.getOrNull()) + + /** + * Sets [Builder.completedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.completedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun completedAt(completedAt: JsonField) = apply { + this.completedAt = completedAt + } + + /** The number of failed operations. */ + fun errorCount(errorCount: Long) = errorCount(JsonField.of(errorCount)) + + /** + * Sets [Builder.errorCount] to an arbitrary JSON value. + * + * You should usually call [Builder.errorCount] with a well-typed [Long] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun errorCount(errorCount: JsonField) = apply { this.errorCount = errorCount } + + /** A list of items that failed to be processed. */ + fun errorItems(errorItems: List) = errorItems(JsonField.of(errorItems)) + + /** + * Sets [Builder.errorItems] to an arbitrary JSON value. + * + * You should usually call [Builder.errorItems] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun errorItems(errorItems: JsonField>) = apply { + this.errorItems = errorItems.map { it.toMutableList() } + } + + /** + * Adds a single [ErrorItem] to [errorItems]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addErrorItem(errorItem: ErrorItem) = apply { + errorItems = + (errorItems ?: JsonField.of(mutableListOf())).also { + checkKnown("errorItems", it).add(errorItem) + } + } + + /** Timestamp when the bulk operation failed. */ + fun failedAt(failedAt: OffsetDateTime?) = failedAt(JsonField.ofNullable(failedAt)) + + /** Alias for calling [Builder.failedAt] with `failedAt.orElse(null)`. */ + fun failedAt(failedAt: Optional) = failedAt(failedAt.getOrNull()) + + /** + * Sets [Builder.failedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.failedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun failedAt(failedAt: JsonField) = apply { this.failedAt = failedAt } + + /** The URI to the bulk operation's progress. */ + fun progressPath(progressPath: String) = progressPath(JsonField.of(progressPath)) + + /** + * Sets [Builder.progressPath] to an arbitrary JSON value. + * + * You should usually call [Builder.progressPath] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun progressPath(progressPath: JsonField) = apply { + this.progressPath = progressPath + } + + /** Timestamp when the bulk operation was started. */ + fun startedAt(startedAt: OffsetDateTime?) = startedAt(JsonField.ofNullable(startedAt)) + + /** Alias for calling [Builder.startedAt] with `startedAt.orElse(null)`. */ + fun startedAt(startedAt: Optional) = startedAt(startedAt.getOrNull()) + + /** + * Sets [Builder.startedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.startedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun startedAt(startedAt: JsonField) = apply { this.startedAt = startedAt } + + 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 [BulkOperation]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * ._typename() + * .estimatedTotalRows() + * .insertedAt() + * .name() + * .processedRows() + * .status() + * .successCount() + * .updatedAt() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BulkOperation = + BulkOperation( + checkRequired("id", id), + checkRequired("_typename", _typename), + checkRequired("estimatedTotalRows", estimatedTotalRows), + checkRequired("insertedAt", insertedAt), + checkRequired("name", name), + checkRequired("processedRows", processedRows), + checkRequired("status", status), + checkRequired("successCount", successCount), + checkRequired("updatedAt", updatedAt), + completedAt, + errorCount, + (errorItems ?: JsonMissing.of()).map { it.toImmutable() }, + failedAt, + progressPath, + startedAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BulkOperation = apply { + if (validated) { + return@apply + } + + id() + _typename() + estimatedTotalRows() + insertedAt() + name() + processedRows() + status().validate() + successCount() + updatedAt() + completedAt() + errorCount() + errorItems().ifPresent { it.forEach { it.validate() } } + failedAt() + progressPath() + startedAt() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + (if (_typename.asKnown().isPresent) 1 else 0) + + (if (estimatedTotalRows.asKnown().isPresent) 1 else 0) + + (if (insertedAt.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (processedRows.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (successCount.asKnown().isPresent) 1 else 0) + + (if (updatedAt.asKnown().isPresent) 1 else 0) + + (if (completedAt.asKnown().isPresent) 1 else 0) + + (if (errorCount.asKnown().isPresent) 1 else 0) + + (errorItems.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (failedAt.asKnown().isPresent) 1 else 0) + + (if (progressPath.asKnown().isPresent) 1 else 0) + + (if (startedAt.asKnown().isPresent) 1 else 0) + + /** The status of the bulk operation. */ + 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 QUEUED = of("queued") + + @JvmField val PROCESSING = of("processing") + + @JvmField val COMPLETED = of("completed") + + @JvmField val FAILED = of("failed") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + QUEUED, + PROCESSING, + COMPLETED, + FAILED, + } + + /** + * 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 { + QUEUED, + PROCESSING, + COMPLETED, + FAILED, + /** 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) { + QUEUED -> Value.QUEUED + PROCESSING -> Value.PROCESSING + COMPLETED -> Value.COMPLETED + FAILED -> Value.FAILED + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + QUEUED -> Known.QUEUED + PROCESSING -> Known.PROCESSING + COMPLETED -> Known.COMPLETED + FAILED -> Known.FAILED + else -> throw KnockInvalidDataException("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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Status && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class ErrorItem + private constructor( + private val id: JsonField, + private val collection: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("collection") + @ExcludeMissing + collection: JsonField = JsonMissing.of(), + ) : this(id, collection, mutableMapOf()) + + /** + * Unique identifier for the object. + * + * @throws KnockInvalidDataException 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 id(): String = id.getRequired("id") + + /** + * The collection this object belongs to. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun collection(): Optional = collection.getOptional("collection") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [collection]. + * + * Unlike [collection], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("collection") + @ExcludeMissing + fun _collection(): JsonField = collection + + @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 [ErrorItem]. + * + * The following fields are required: + * ```java + * .id() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ErrorItem]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var collection: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(errorItem: ErrorItem) = apply { + id = errorItem.id + collection = errorItem.collection + additionalProperties = errorItem.additionalProperties.toMutableMap() + } + + /** Unique identifier for the object. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The collection this object belongs to. */ + fun collection(collection: String?) = collection(JsonField.ofNullable(collection)) + + /** Alias for calling [Builder.collection] with `collection.orElse(null)`. */ + fun collection(collection: Optional) = collection(collection.getOrNull()) + + /** + * Sets [Builder.collection] to an arbitrary JSON value. + * + * You should usually call [Builder.collection] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun collection(collection: JsonField) = apply { this.collection = collection } + + 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 [ErrorItem]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ErrorItem = + ErrorItem(checkRequired("id", id), collection, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): ErrorItem = apply { + if (validated) { + return@apply + } + + id() + collection() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + (if (collection.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is ErrorItem && id == other.id && collection == other.collection && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, collection, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ErrorItem{id=$id, collection=$collection, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is BulkOperation && id == other.id && _typename == other._typename && estimatedTotalRows == other.estimatedTotalRows && insertedAt == other.insertedAt && name == other.name && processedRows == other.processedRows && status == other.status && successCount == other.successCount && updatedAt == other.updatedAt && completedAt == other.completedAt && errorCount == other.errorCount && errorItems == other.errorItems && failedAt == other.failedAt && progressPath == other.progressPath && startedAt == other.startedAt && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, _typename, estimatedTotalRows, insertedAt, name, processedRows, status, successCount, updatedAt, completedAt, errorCount, errorItems, failedAt, progressPath, startedAt, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BulkOperation{id=$id, _typename=$_typename, estimatedTotalRows=$estimatedTotalRows, insertedAt=$insertedAt, name=$name, processedRows=$processedRows, status=$status, successCount=$successCount, updatedAt=$updatedAt, completedAt=$completedAt, errorCount=$errorCount, errorItems=$errorItems, failedAt=$failedAt, progressPath=$progressPath, startedAt=$startedAt, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/bulkoperations/BulkOperationGetParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/bulkoperations/BulkOperationGetParams.kt new file mode 100644 index 00000000..119ddf7a --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/bulkoperations/BulkOperationGetParams.kt @@ -0,0 +1,184 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.bulkoperations + +import app.knock.api.core.Params +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** Retrieves a bulk operation (if it exists) and displays the current state of it. */ +class BulkOperationGetParams +private constructor( + private val id: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun id(): Optional = Optional.ofNullable(id) + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): BulkOperationGetParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [BulkOperationGetParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BulkOperationGetParams]. */ + class Builder internal constructor() { + + private var id: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(bulkOperationGetParams: BulkOperationGetParams) = apply { + id = bulkOperationGetParams.id + additionalHeaders = bulkOperationGetParams.additionalHeaders.toBuilder() + additionalQueryParams = bulkOperationGetParams.additionalQueryParams.toBuilder() + } + + fun id(id: String?) = apply { this.id = id } + + /** Alias for calling [Builder.id] with `id.orElse(null)`. */ + fun id(id: Optional) = id(id.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 [BulkOperationGetParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BulkOperationGetParams = + BulkOperationGetParams(id, additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> id ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is BulkOperationGetParams && id == other.id && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(id, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "BulkOperationGetParams{id=$id, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/channels/bulk/BulkUpdateMessageStatusParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/channels/bulk/BulkUpdateMessageStatusParams.kt new file mode 100644 index 00000000..c7eaee8b --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/channels/bulk/BulkUpdateMessageStatusParams.kt @@ -0,0 +1,1768 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.channels.bulk + +import app.knock.api.core.Enum +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.Params +import app.knock.api.core.checkKnown +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 + +/** + * Bulk update the status of messages for a specific channel. The channel is specified by the + * `channel_id` parameter. The action to perform is specified by the `action` parameter, where the + * action is a status change action (e.g. `archive`, `unarchive`). + */ +class BulkUpdateMessageStatusParams +private constructor( + private val channelId: String?, + private val action: Action?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun channelId(): Optional = Optional.ofNullable(channelId) + + fun action(): Optional = Optional.ofNullable(action) + + /** + * Limits the results to messages with the given archived status. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun archived(): Optional = body.archived() + + /** + * Limits the results to messages with the given delivery status. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun deliveryStatus(): Optional = body.deliveryStatus() + + /** + * Limits the results to messages with the given engagement status. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun engagementStatus(): Optional = body.engagementStatus() + + /** + * Limits the results to messages that have a tenant or not. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun hasTenant(): Optional = body.hasTenant() + + /** + * Limits the results to messages inserted after the given date. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun newerThan(): Optional = body.newerThan() + + /** + * Limits the results to messages inserted before the given date. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun olderThan(): Optional = body.olderThan() + + /** + * Limits the results to messages with the given recipient IDs. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun recipientIds(): Optional> = body.recipientIds() + + /** + * Limits the results to messages with the given tenant IDs. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tenants(): Optional> = body.tenants() + + /** + * Limits the results to only messages that were generated with the given data. See + * [trigger data filtering](/api-reference/overview/trigger-data-filtering) for more + * information. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun triggerData(): Optional = body.triggerData() + + /** + * Limits the results to messages with the given workflow keys. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun workflows(): Optional> = body.workflows() + + /** + * Returns the raw JSON value of [archived]. + * + * Unlike [archived], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _archived(): JsonField = body._archived() + + /** + * Returns the raw JSON value of [deliveryStatus]. + * + * Unlike [deliveryStatus], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _deliveryStatus(): JsonField = body._deliveryStatus() + + /** + * Returns the raw JSON value of [engagementStatus]. + * + * Unlike [engagementStatus], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _engagementStatus(): JsonField = body._engagementStatus() + + /** + * Returns the raw JSON value of [hasTenant]. + * + * Unlike [hasTenant], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _hasTenant(): JsonField = body._hasTenant() + + /** + * Returns the raw JSON value of [newerThan]. + * + * Unlike [newerThan], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _newerThan(): JsonField = body._newerThan() + + /** + * Returns the raw JSON value of [olderThan]. + * + * Unlike [olderThan], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _olderThan(): JsonField = body._olderThan() + + /** + * Returns the raw JSON value of [recipientIds]. + * + * Unlike [recipientIds], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _recipientIds(): JsonField> = body._recipientIds() + + /** + * Returns the raw JSON value of [tenants]. + * + * Unlike [tenants], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _tenants(): JsonField> = body._tenants() + + /** + * Returns the raw JSON value of [triggerData]. + * + * Unlike [triggerData], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _triggerData(): JsonField = body._triggerData() + + /** + * Returns the raw JSON value of [workflows]. + * + * Unlike [workflows], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _workflows(): JsonField> = body._workflows() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): BulkUpdateMessageStatusParams = builder().build() + + /** + * Returns a mutable builder for constructing an instance of + * [BulkUpdateMessageStatusParams]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BulkUpdateMessageStatusParams]. */ + class Builder internal constructor() { + + private var channelId: String? = null + private var action: Action? = 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(bulkUpdateMessageStatusParams: BulkUpdateMessageStatusParams) = apply { + channelId = bulkUpdateMessageStatusParams.channelId + action = bulkUpdateMessageStatusParams.action + body = bulkUpdateMessageStatusParams.body.toBuilder() + additionalHeaders = bulkUpdateMessageStatusParams.additionalHeaders.toBuilder() + additionalQueryParams = bulkUpdateMessageStatusParams.additionalQueryParams.toBuilder() + } + + fun channelId(channelId: String?) = apply { this.channelId = channelId } + + /** Alias for calling [Builder.channelId] with `channelId.orElse(null)`. */ + fun channelId(channelId: Optional) = channelId(channelId.getOrNull()) + + fun action(action: Action?) = apply { this.action = action } + + /** Alias for calling [Builder.action] with `action.orElse(null)`. */ + fun action(action: Optional) = action(action.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: + * - [archived] + * - [deliveryStatus] + * - [engagementStatus] + * - [hasTenant] + * - [newerThan] + * - etc. + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** Limits the results to messages with the given archived status. */ + fun archived(archived: Archived) = apply { body.archived(archived) } + + /** + * Sets [Builder.archived] to an arbitrary JSON value. + * + * You should usually call [Builder.archived] with a well-typed [Archived] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun archived(archived: JsonField) = apply { body.archived(archived) } + + /** Limits the results to messages with the given delivery status. */ + fun deliveryStatus(deliveryStatus: DeliveryStatus) = apply { + body.deliveryStatus(deliveryStatus) + } + + /** + * Sets [Builder.deliveryStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.deliveryStatus] with a well-typed [DeliveryStatus] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun deliveryStatus(deliveryStatus: JsonField) = apply { + body.deliveryStatus(deliveryStatus) + } + + /** Limits the results to messages with the given engagement status. */ + fun engagementStatus(engagementStatus: EngagementStatus) = apply { + body.engagementStatus(engagementStatus) + } + + /** + * Sets [Builder.engagementStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.engagementStatus] with a well-typed [EngagementStatus] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun engagementStatus(engagementStatus: JsonField) = apply { + body.engagementStatus(engagementStatus) + } + + /** Limits the results to messages that have a tenant or not. */ + fun hasTenant(hasTenant: Boolean) = apply { body.hasTenant(hasTenant) } + + /** + * Sets [Builder.hasTenant] to an arbitrary JSON value. + * + * You should usually call [Builder.hasTenant] with a well-typed [Boolean] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun hasTenant(hasTenant: JsonField) = apply { body.hasTenant(hasTenant) } + + /** Limits the results to messages inserted after the given date. */ + fun newerThan(newerThan: OffsetDateTime) = apply { body.newerThan(newerThan) } + + /** + * Sets [Builder.newerThan] to an arbitrary JSON value. + * + * You should usually call [Builder.newerThan] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun newerThan(newerThan: JsonField) = apply { body.newerThan(newerThan) } + + /** Limits the results to messages inserted before the given date. */ + fun olderThan(olderThan: OffsetDateTime) = apply { body.olderThan(olderThan) } + + /** + * Sets [Builder.olderThan] to an arbitrary JSON value. + * + * You should usually call [Builder.olderThan] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun olderThan(olderThan: JsonField) = apply { body.olderThan(olderThan) } + + /** Limits the results to messages with the given recipient IDs. */ + fun recipientIds(recipientIds: List) = apply { body.recipientIds(recipientIds) } + + /** + * Sets [Builder.recipientIds] to an arbitrary JSON value. + * + * You should usually call [Builder.recipientIds] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun recipientIds(recipientIds: JsonField>) = apply { + body.recipientIds(recipientIds) + } + + /** + * Adds a single [String] to [recipientIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRecipientId(recipientId: String) = apply { body.addRecipientId(recipientId) } + + /** Limits the results to messages with the given tenant IDs. */ + fun tenants(tenants: List) = apply { body.tenants(tenants) } + + /** + * Sets [Builder.tenants] to an arbitrary JSON value. + * + * You should usually call [Builder.tenants] with a well-typed `List` value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun tenants(tenants: JsonField>) = apply { body.tenants(tenants) } + + /** + * Adds a single [String] to [tenants]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTenant(tenant: String) = apply { body.addTenant(tenant) } + + /** + * Limits the results to only messages that were generated with the given data. See + * [trigger data filtering](/api-reference/overview/trigger-data-filtering) for more + * information. + */ + fun triggerData(triggerData: String) = apply { body.triggerData(triggerData) } + + /** + * Sets [Builder.triggerData] to an arbitrary JSON value. + * + * You should usually call [Builder.triggerData] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun triggerData(triggerData: JsonField) = apply { body.triggerData(triggerData) } + + /** Limits the results to messages with the given workflow keys. */ + fun workflows(workflows: List) = apply { body.workflows(workflows) } + + /** + * Sets [Builder.workflows] to an arbitrary JSON value. + * + * You should usually call [Builder.workflows] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun workflows(workflows: JsonField>) = apply { body.workflows(workflows) } + + /** + * Adds a single [String] to [workflows]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addWorkflow(workflow: String) = apply { body.addWorkflow(workflow) } + + 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 [BulkUpdateMessageStatusParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): BulkUpdateMessageStatusParams = + BulkUpdateMessageStatusParams( + channelId, + action, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> channelId ?: "" + 1 -> action?.toString() ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + /** + * Updates message statuses in a specified channel. Use the `channel_id` parameter to target the + * channel and the `status` parameter to define what the status should be changed to (e.g. + * `archive`, `unarchive`). Apply to all messages or use filters to target a subset. For in-app + * channels, messages can be updated indefinitely via this operation. For all other channel + * types, messages outside the account's retention window will not be updated as part of this + * operation. + */ + class Body + private constructor( + private val archived: JsonField, + private val deliveryStatus: JsonField, + private val engagementStatus: JsonField, + private val hasTenant: JsonField, + private val newerThan: JsonField, + private val olderThan: JsonField, + private val recipientIds: JsonField>, + private val tenants: JsonField>, + private val triggerData: JsonField, + private val workflows: JsonField>, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("archived") + @ExcludeMissing + archived: JsonField = JsonMissing.of(), + @JsonProperty("delivery_status") + @ExcludeMissing + deliveryStatus: JsonField = JsonMissing.of(), + @JsonProperty("engagement_status") + @ExcludeMissing + engagementStatus: JsonField = JsonMissing.of(), + @JsonProperty("has_tenant") + @ExcludeMissing + hasTenant: JsonField = JsonMissing.of(), + @JsonProperty("newer_than") + @ExcludeMissing + newerThan: JsonField = JsonMissing.of(), + @JsonProperty("older_than") + @ExcludeMissing + olderThan: JsonField = JsonMissing.of(), + @JsonProperty("recipient_ids") + @ExcludeMissing + recipientIds: JsonField> = JsonMissing.of(), + @JsonProperty("tenants") + @ExcludeMissing + tenants: JsonField> = JsonMissing.of(), + @JsonProperty("trigger_data") + @ExcludeMissing + triggerData: JsonField = JsonMissing.of(), + @JsonProperty("workflows") + @ExcludeMissing + workflows: JsonField> = JsonMissing.of(), + ) : this( + archived, + deliveryStatus, + engagementStatus, + hasTenant, + newerThan, + olderThan, + recipientIds, + tenants, + triggerData, + workflows, + mutableMapOf(), + ) + + /** + * Limits the results to messages with the given archived status. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun archived(): Optional = archived.getOptional("archived") + + /** + * Limits the results to messages with the given delivery status. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun deliveryStatus(): Optional = + deliveryStatus.getOptional("delivery_status") + + /** + * Limits the results to messages with the given engagement status. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun engagementStatus(): Optional = + engagementStatus.getOptional("engagement_status") + + /** + * Limits the results to messages that have a tenant or not. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun hasTenant(): Optional = hasTenant.getOptional("has_tenant") + + /** + * Limits the results to messages inserted after the given date. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun newerThan(): Optional = newerThan.getOptional("newer_than") + + /** + * Limits the results to messages inserted before the given date. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun olderThan(): Optional = olderThan.getOptional("older_than") + + /** + * Limits the results to messages with the given recipient IDs. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun recipientIds(): Optional> = recipientIds.getOptional("recipient_ids") + + /** + * Limits the results to messages with the given tenant IDs. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tenants(): Optional> = tenants.getOptional("tenants") + + /** + * Limits the results to only messages that were generated with the given data. See + * [trigger data filtering](/api-reference/overview/trigger-data-filtering) for more + * information. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun triggerData(): Optional = triggerData.getOptional("trigger_data") + + /** + * Limits the results to messages with the given workflow keys. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun workflows(): Optional> = workflows.getOptional("workflows") + + /** + * Returns the raw JSON value of [archived]. + * + * Unlike [archived], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("archived") @ExcludeMissing fun _archived(): JsonField = archived + + /** + * Returns the raw JSON value of [deliveryStatus]. + * + * Unlike [deliveryStatus], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("delivery_status") + @ExcludeMissing + fun _deliveryStatus(): JsonField = deliveryStatus + + /** + * Returns the raw JSON value of [engagementStatus]. + * + * Unlike [engagementStatus], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("engagement_status") + @ExcludeMissing + fun _engagementStatus(): JsonField = engagementStatus + + /** + * Returns the raw JSON value of [hasTenant]. + * + * Unlike [hasTenant], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("has_tenant") @ExcludeMissing fun _hasTenant(): JsonField = hasTenant + + /** + * Returns the raw JSON value of [newerThan]. + * + * Unlike [newerThan], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("newer_than") + @ExcludeMissing + fun _newerThan(): JsonField = newerThan + + /** + * Returns the raw JSON value of [olderThan]. + * + * Unlike [olderThan], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("older_than") + @ExcludeMissing + fun _olderThan(): JsonField = olderThan + + /** + * Returns the raw JSON value of [recipientIds]. + * + * Unlike [recipientIds], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("recipient_ids") + @ExcludeMissing + fun _recipientIds(): JsonField> = recipientIds + + /** + * Returns the raw JSON value of [tenants]. + * + * Unlike [tenants], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("tenants") @ExcludeMissing fun _tenants(): JsonField> = tenants + + /** + * Returns the raw JSON value of [triggerData]. + * + * Unlike [triggerData], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("trigger_data") + @ExcludeMissing + fun _triggerData(): JsonField = triggerData + + /** + * Returns the raw JSON value of [workflows]. + * + * Unlike [workflows], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("workflows") + @ExcludeMissing + fun _workflows(): JsonField> = workflows + + @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 archived: JsonField = JsonMissing.of() + private var deliveryStatus: JsonField = JsonMissing.of() + private var engagementStatus: JsonField = JsonMissing.of() + private var hasTenant: JsonField = JsonMissing.of() + private var newerThan: JsonField = JsonMissing.of() + private var olderThan: JsonField = JsonMissing.of() + private var recipientIds: JsonField>? = null + private var tenants: JsonField>? = null + private var triggerData: JsonField = JsonMissing.of() + private var workflows: JsonField>? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + archived = body.archived + deliveryStatus = body.deliveryStatus + engagementStatus = body.engagementStatus + hasTenant = body.hasTenant + newerThan = body.newerThan + olderThan = body.olderThan + recipientIds = body.recipientIds.map { it.toMutableList() } + tenants = body.tenants.map { it.toMutableList() } + triggerData = body.triggerData + workflows = body.workflows.map { it.toMutableList() } + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** Limits the results to messages with the given archived status. */ + fun archived(archived: Archived) = archived(JsonField.of(archived)) + + /** + * Sets [Builder.archived] to an arbitrary JSON value. + * + * You should usually call [Builder.archived] with a well-typed [Archived] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun archived(archived: JsonField) = apply { this.archived = archived } + + /** Limits the results to messages with the given delivery status. */ + fun deliveryStatus(deliveryStatus: DeliveryStatus) = + deliveryStatus(JsonField.of(deliveryStatus)) + + /** + * Sets [Builder.deliveryStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.deliveryStatus] with a well-typed [DeliveryStatus] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun deliveryStatus(deliveryStatus: JsonField) = apply { + this.deliveryStatus = deliveryStatus + } + + /** Limits the results to messages with the given engagement status. */ + fun engagementStatus(engagementStatus: EngagementStatus) = + engagementStatus(JsonField.of(engagementStatus)) + + /** + * Sets [Builder.engagementStatus] to an arbitrary JSON value. + * + * You should usually call [Builder.engagementStatus] with a well-typed + * [EngagementStatus] value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun engagementStatus(engagementStatus: JsonField) = apply { + this.engagementStatus = engagementStatus + } + + /** Limits the results to messages that have a tenant or not. */ + fun hasTenant(hasTenant: Boolean) = hasTenant(JsonField.of(hasTenant)) + + /** + * Sets [Builder.hasTenant] to an arbitrary JSON value. + * + * You should usually call [Builder.hasTenant] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun hasTenant(hasTenant: JsonField) = apply { this.hasTenant = hasTenant } + + /** Limits the results to messages inserted after the given date. */ + fun newerThan(newerThan: OffsetDateTime) = newerThan(JsonField.of(newerThan)) + + /** + * Sets [Builder.newerThan] to an arbitrary JSON value. + * + * You should usually call [Builder.newerThan] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun newerThan(newerThan: JsonField) = apply { + this.newerThan = newerThan + } + + /** Limits the results to messages inserted before the given date. */ + fun olderThan(olderThan: OffsetDateTime) = olderThan(JsonField.of(olderThan)) + + /** + * Sets [Builder.olderThan] to an arbitrary JSON value. + * + * You should usually call [Builder.olderThan] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun olderThan(olderThan: JsonField) = apply { + this.olderThan = olderThan + } + + /** Limits the results to messages with the given recipient IDs. */ + fun recipientIds(recipientIds: List) = recipientIds(JsonField.of(recipientIds)) + + /** + * Sets [Builder.recipientIds] to an arbitrary JSON value. + * + * You should usually call [Builder.recipientIds] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun recipientIds(recipientIds: JsonField>) = apply { + this.recipientIds = recipientIds.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [recipientIds]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addRecipientId(recipientId: String) = apply { + recipientIds = + (recipientIds ?: JsonField.of(mutableListOf())).also { + checkKnown("recipientIds", it).add(recipientId) + } + } + + /** Limits the results to messages with the given tenant IDs. */ + fun tenants(tenants: List) = tenants(JsonField.of(tenants)) + + /** + * Sets [Builder.tenants] to an arbitrary JSON value. + * + * You should usually call [Builder.tenants] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun tenants(tenants: JsonField>) = apply { + this.tenants = tenants.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [tenants]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addTenant(tenant: String) = apply { + tenants = + (tenants ?: JsonField.of(mutableListOf())).also { + checkKnown("tenants", it).add(tenant) + } + } + + /** + * Limits the results to only messages that were generated with the given data. See + * [trigger data filtering](/api-reference/overview/trigger-data-filtering) for more + * information. + */ + fun triggerData(triggerData: String) = triggerData(JsonField.of(triggerData)) + + /** + * Sets [Builder.triggerData] to an arbitrary JSON value. + * + * You should usually call [Builder.triggerData] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun triggerData(triggerData: JsonField) = apply { + this.triggerData = triggerData + } + + /** Limits the results to messages with the given workflow keys. */ + fun workflows(workflows: List) = workflows(JsonField.of(workflows)) + + /** + * Sets [Builder.workflows] to an arbitrary JSON value. + * + * You should usually call [Builder.workflows] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun workflows(workflows: JsonField>) = apply { + this.workflows = workflows.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [workflows]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addWorkflow(workflow: String) = apply { + workflows = + (workflows ?: JsonField.of(mutableListOf())).also { + checkKnown("workflows", it).add(workflow) + } + } + + 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( + archived, + deliveryStatus, + engagementStatus, + hasTenant, + newerThan, + olderThan, + (recipientIds ?: JsonMissing.of()).map { it.toImmutable() }, + (tenants ?: JsonMissing.of()).map { it.toImmutable() }, + triggerData, + (workflows ?: JsonMissing.of()).map { it.toImmutable() }, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + archived().ifPresent { it.validate() } + deliveryStatus().ifPresent { it.validate() } + engagementStatus().ifPresent { it.validate() } + hasTenant() + newerThan() + olderThan() + recipientIds() + tenants() + triggerData() + workflows() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (archived.asKnown().getOrNull()?.validity() ?: 0) + + (deliveryStatus.asKnown().getOrNull()?.validity() ?: 0) + + (engagementStatus.asKnown().getOrNull()?.validity() ?: 0) + + (if (hasTenant.asKnown().isPresent) 1 else 0) + + (if (newerThan.asKnown().isPresent) 1 else 0) + + (if (olderThan.asKnown().isPresent) 1 else 0) + + (recipientIds.asKnown().getOrNull()?.size ?: 0) + + (tenants.asKnown().getOrNull()?.size ?: 0) + + (if (triggerData.asKnown().isPresent) 1 else 0) + + (workflows.asKnown().getOrNull()?.size ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && archived == other.archived && deliveryStatus == other.deliveryStatus && engagementStatus == other.engagementStatus && hasTenant == other.hasTenant && newerThan == other.newerThan && olderThan == other.olderThan && recipientIds == other.recipientIds && tenants == other.tenants && triggerData == other.triggerData && workflows == other.workflows && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(archived, deliveryStatus, engagementStatus, hasTenant, newerThan, olderThan, recipientIds, tenants, triggerData, workflows, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{archived=$archived, deliveryStatus=$deliveryStatus, engagementStatus=$engagementStatus, hasTenant=$hasTenant, newerThan=$newerThan, olderThan=$olderThan, recipientIds=$recipientIds, tenants=$tenants, triggerData=$triggerData, workflows=$workflows, additionalProperties=$additionalProperties}" + } + + /** Limits the results to messages with the given archived status. */ + class Archived @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 EXCLUDE = of("exclude") + + @JvmField val INCLUDE = of("include") + + @JvmField val ONLY = of("only") + + @JvmStatic fun of(value: String) = Archived(JsonField.of(value)) + } + + /** An enum containing [Archived]'s known values. */ + enum class Known { + EXCLUDE, + INCLUDE, + ONLY, + } + + /** + * An enum containing [Archived]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Archived] 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 { + EXCLUDE, + INCLUDE, + ONLY, + /** An enum member indicating that [Archived] 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) { + EXCLUDE -> Value.EXCLUDE + INCLUDE -> Value.INCLUDE + ONLY -> Value.ONLY + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + EXCLUDE -> Known.EXCLUDE + INCLUDE -> Known.INCLUDE + ONLY -> Known.ONLY + else -> throw KnockInvalidDataException("Unknown Archived: $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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): Archived = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Archived && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Limits the results to messages with the given delivery status. */ + class DeliveryStatus @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 QUEUED = of("queued") + + @JvmField val SENT = of("sent") + + @JvmField val DELIVERED = of("delivered") + + @JvmField val DELIVERY_ATTEMPTED = of("delivery_attempted") + + @JvmField val UNDELIVERED = of("undelivered") + + @JvmField val NOT_SENT = of("not_sent") + + @JvmField val BOUNCED = of("bounced") + + @JvmStatic fun of(value: String) = DeliveryStatus(JsonField.of(value)) + } + + /** An enum containing [DeliveryStatus]'s known values. */ + enum class Known { + QUEUED, + SENT, + DELIVERED, + DELIVERY_ATTEMPTED, + UNDELIVERED, + NOT_SENT, + BOUNCED, + } + + /** + * An enum containing [DeliveryStatus]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [DeliveryStatus] 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 { + QUEUED, + SENT, + DELIVERED, + DELIVERY_ATTEMPTED, + UNDELIVERED, + NOT_SENT, + BOUNCED, + /** + * An enum member indicating that [DeliveryStatus] 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) { + QUEUED -> Value.QUEUED + SENT -> Value.SENT + DELIVERED -> Value.DELIVERED + DELIVERY_ATTEMPTED -> Value.DELIVERY_ATTEMPTED + UNDELIVERED -> Value.UNDELIVERED + NOT_SENT -> Value.NOT_SENT + BOUNCED -> Value.BOUNCED + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + QUEUED -> Known.QUEUED + SENT -> Known.SENT + DELIVERED -> Known.DELIVERED + DELIVERY_ATTEMPTED -> Known.DELIVERY_ATTEMPTED + UNDELIVERED -> Known.UNDELIVERED + NOT_SENT -> Known.NOT_SENT + BOUNCED -> Known.BOUNCED + else -> throw KnockInvalidDataException("Unknown DeliveryStatus: $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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): DeliveryStatus = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is DeliveryStatus && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** Limits the results to messages with the given engagement status. */ + class EngagementStatus @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 SEEN = of("seen") + + @JvmField val UNSEEN = of("unseen") + + @JvmField val READ = of("read") + + @JvmField val UNREAD = of("unread") + + @JvmField val ARCHIVED = of("archived") + + @JvmField val UNARCHIVED = of("unarchived") + + @JvmField val LINK_CLICKED = of("link_clicked") + + @JvmField val INTERACTED = of("interacted") + + @JvmStatic fun of(value: String) = EngagementStatus(JsonField.of(value)) + } + + /** An enum containing [EngagementStatus]'s known values. */ + enum class Known { + SEEN, + UNSEEN, + READ, + UNREAD, + ARCHIVED, + UNARCHIVED, + LINK_CLICKED, + INTERACTED, + } + + /** + * An enum containing [EngagementStatus]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [EngagementStatus] 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 { + SEEN, + UNSEEN, + READ, + UNREAD, + ARCHIVED, + UNARCHIVED, + LINK_CLICKED, + INTERACTED, + /** + * An enum member indicating that [EngagementStatus] 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) { + SEEN -> Value.SEEN + UNSEEN -> Value.UNSEEN + READ -> Value.READ + UNREAD -> Value.UNREAD + ARCHIVED -> Value.ARCHIVED + UNARCHIVED -> Value.UNARCHIVED + LINK_CLICKED -> Value.LINK_CLICKED + INTERACTED -> Value.INTERACTED + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + SEEN -> Known.SEEN + UNSEEN -> Known.UNSEEN + READ -> Known.READ + UNREAD -> Known.UNREAD + ARCHIVED -> Known.ARCHIVED + UNARCHIVED -> Known.UNARCHIVED + LINK_CLICKED -> Known.LINK_CLICKED + INTERACTED -> Known.INTERACTED + else -> throw KnockInvalidDataException("Unknown EngagementStatus: $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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): EngagementStatus = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is EngagementStatus && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + class Action @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 SEEN = of("seen") + + @JvmField val UNSEEN = of("unseen") + + @JvmField val READ = of("read") + + @JvmField val UNREAD = of("unread") + + @JvmField val ARCHIVED = of("archived") + + @JvmField val UNARCHIVED = of("unarchived") + + @JvmField val INTERACTED = of("interacted") + + @JvmField val ARCHIVE = of("archive") + + @JvmField val UNARCHIVE = of("unarchive") + + @JvmField val DELETE = of("delete") + + @JvmStatic fun of(value: String) = Action(JsonField.of(value)) + } + + /** An enum containing [Action]'s known values. */ + enum class Known { + SEEN, + UNSEEN, + READ, + UNREAD, + ARCHIVED, + UNARCHIVED, + INTERACTED, + ARCHIVE, + UNARCHIVE, + DELETE, + } + + /** + * An enum containing [Action]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Action] 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 { + SEEN, + UNSEEN, + READ, + UNREAD, + ARCHIVED, + UNARCHIVED, + INTERACTED, + ARCHIVE, + UNARCHIVE, + DELETE, + /** An enum member indicating that [Action] 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) { + SEEN -> Value.SEEN + UNSEEN -> Value.UNSEEN + READ -> Value.READ + UNREAD -> Value.UNREAD + ARCHIVED -> Value.ARCHIVED + UNARCHIVED -> Value.UNARCHIVED + INTERACTED -> Value.INTERACTED + ARCHIVE -> Value.ARCHIVE + UNARCHIVE -> Value.UNARCHIVE + DELETE -> Value.DELETE + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + SEEN -> Known.SEEN + UNSEEN -> Known.UNSEEN + READ -> Known.READ + UNREAD -> Known.UNREAD + ARCHIVED -> Known.ARCHIVED + UNARCHIVED -> Known.UNARCHIVED + INTERACTED -> Known.INTERACTED + ARCHIVE -> Known.ARCHIVE + UNARCHIVE -> Known.UNARCHIVE + DELETE -> Known.DELETE + else -> throw KnockInvalidDataException("Unknown Action: $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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): Action = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Action && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is BulkUpdateMessageStatusParams && channelId == other.channelId && action == other.action && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(channelId, action, body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "BulkUpdateMessageStatusParams{channelId=$channelId, action=$action, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/census/CensusCustomDestinationParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/census/CensusCustomDestinationParams.kt new file mode 100644 index 00000000..c682fb8e --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/census/CensusCustomDestinationParams.kt @@ -0,0 +1,711 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.integrations.census + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.Params +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 + +/** Processes a Census custom destination RPC request. */ +class CensusCustomDestinationParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The unique identifier for the RPC request. + * + * @throws KnockInvalidDataException 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 id(): String = body.id() + + /** + * The JSON-RPC version. + * + * @throws KnockInvalidDataException 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 jsonrpc(): String = body.jsonrpc() + + /** + * The method name to execute. + * + * @throws KnockInvalidDataException 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 method(): String = body.method() + + /** + * The parameters for the method. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun params(): Optional = body.params() + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _id(): JsonField = body._id() + + /** + * Returns the raw JSON value of [jsonrpc]. + * + * Unlike [jsonrpc], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _jsonrpc(): JsonField = body._jsonrpc() + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _method(): JsonField = body._method() + + /** + * Returns the raw JSON value of [params]. + * + * Unlike [params], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _params(): JsonField = body._params() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [CensusCustomDestinationParams]. + * + * The following fields are required: + * ```java + * .id() + * .jsonrpc() + * .method() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CensusCustomDestinationParams]. */ + 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(censusCustomDestinationParams: CensusCustomDestinationParams) = apply { + body = censusCustomDestinationParams.body.toBuilder() + additionalHeaders = censusCustomDestinationParams.additionalHeaders.toBuilder() + additionalQueryParams = censusCustomDestinationParams.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: + * - [id] + * - [jsonrpc] + * - [method] + * - [params] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The unique identifier for the RPC request. */ + fun id(id: String) = apply { body.id(id) } + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { body.id(id) } + + /** The JSON-RPC version. */ + fun jsonrpc(jsonrpc: String) = apply { body.jsonrpc(jsonrpc) } + + /** + * Sets [Builder.jsonrpc] to an arbitrary JSON value. + * + * You should usually call [Builder.jsonrpc] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun jsonrpc(jsonrpc: JsonField) = apply { body.jsonrpc(jsonrpc) } + + /** The method name to execute. */ + fun method(method: String) = apply { body.method(method) } + + /** + * Sets [Builder.method] to an arbitrary JSON value. + * + * You should usually call [Builder.method] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun method(method: JsonField) = apply { body.method(method) } + + /** The parameters for the method. */ + fun params(params: Params) = apply { body.params(params) } + + /** + * Sets [Builder.params] to an arbitrary JSON value. + * + * You should usually call [Builder.params] with a well-typed [Params] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun params(params: JsonField) = apply { body.params(params) } + + 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 [CensusCustomDestinationParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * .jsonrpc() + * .method() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): CensusCustomDestinationParams = + CensusCustomDestinationParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + private constructor( + private val id: JsonField, + private val jsonrpc: JsonField, + private val method: JsonField, + private val params: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("jsonrpc") @ExcludeMissing jsonrpc: JsonField = JsonMissing.of(), + @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(), + @JsonProperty("params") @ExcludeMissing params: JsonField = JsonMissing.of(), + ) : this(id, jsonrpc, method, params, mutableMapOf()) + + /** + * The unique identifier for the RPC request. + * + * @throws KnockInvalidDataException 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 id(): String = id.getRequired("id") + + /** + * The JSON-RPC version. + * + * @throws KnockInvalidDataException 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 jsonrpc(): String = jsonrpc.getRequired("jsonrpc") + + /** + * The method name to execute. + * + * @throws KnockInvalidDataException 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 method(): String = method.getRequired("method") + + /** + * The parameters for the method. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun params(): Optional = params.getOptional("params") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [jsonrpc]. + * + * Unlike [jsonrpc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("jsonrpc") @ExcludeMissing fun _jsonrpc(): JsonField = jsonrpc + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method + + /** + * Returns the raw JSON value of [params]. + * + * Unlike [params], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("params") @ExcludeMissing fun _params(): JsonField = params + + @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 + * .id() + * .jsonrpc() + * .method() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var jsonrpc: JsonField? = null + private var method: JsonField? = null + private var params: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + id = body.id + jsonrpc = body.jsonrpc + method = body.method + params = body.params + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The unique identifier for the RPC request. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The JSON-RPC version. */ + fun jsonrpc(jsonrpc: String) = jsonrpc(JsonField.of(jsonrpc)) + + /** + * Sets [Builder.jsonrpc] to an arbitrary JSON value. + * + * You should usually call [Builder.jsonrpc] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun jsonrpc(jsonrpc: JsonField) = apply { this.jsonrpc = jsonrpc } + + /** The method name to execute. */ + fun method(method: String) = method(JsonField.of(method)) + + /** + * Sets [Builder.method] to an arbitrary JSON value. + * + * You should usually call [Builder.method] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun method(method: JsonField) = apply { this.method = method } + + /** The parameters for the method. */ + fun params(params: Params) = params(JsonField.of(params)) + + /** + * Sets [Builder.params] to an arbitrary JSON value. + * + * You should usually call [Builder.params] with a well-typed [Params] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun params(params: JsonField) = apply { this.params = params } + + 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 + * .id() + * .jsonrpc() + * .method() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("id", id), + checkRequired("jsonrpc", jsonrpc), + checkRequired("method", method), + params, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + id() + jsonrpc() + method() + params().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + (if (jsonrpc.asKnown().isPresent) 1 else 0) + + (if (method.asKnown().isPresent) 1 else 0) + + (params.asKnown().getOrNull()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && id == other.id && jsonrpc == other.jsonrpc && method == other.method && params == other.params && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, jsonrpc, method, params, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{id=$id, jsonrpc=$jsonrpc, method=$method, params=$params, additionalProperties=$additionalProperties}" + } + + /** The parameters for the method. */ + class Params + @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 [Params]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Params]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(params: Params) = apply { + additionalProperties = params.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 [Params]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Params = Params(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Params = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Params && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Params{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is CensusCustomDestinationParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "CensusCustomDestinationParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/census/CensusCustomDestinationResponse.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/census/CensusCustomDestinationResponse.kt new file mode 100644 index 00000000..810082dd --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/census/CensusCustomDestinationResponse.kt @@ -0,0 +1,297 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.integrations.census + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 CensusCustomDestinationResponse +private constructor( + private val id: JsonField, + private val result: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("result") @ExcludeMissing result: JsonField = JsonMissing.of(), + ) : this(id, result, mutableMapOf()) + + /** + * The request ID. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * The result of the RPC call. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun result(): Optional = result.getOptional("result") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [result]. + * + * Unlike [result], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonField = result + + @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 + * [CensusCustomDestinationResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [CensusCustomDestinationResponse]. */ + class Builder internal constructor() { + + private var id: JsonField = JsonMissing.of() + private var result: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(censusCustomDestinationResponse: CensusCustomDestinationResponse) = + apply { + id = censusCustomDestinationResponse.id + result = censusCustomDestinationResponse.result + additionalProperties = + censusCustomDestinationResponse.additionalProperties.toMutableMap() + } + + /** The request ID. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The result of the RPC call. */ + fun result(result: Result) = result(JsonField.of(result)) + + /** + * Sets [Builder.result] to an arbitrary JSON value. + * + * You should usually call [Builder.result] with a well-typed [Result] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun result(result: JsonField) = apply { this.result = result } + + 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 [CensusCustomDestinationResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): CensusCustomDestinationResponse = + CensusCustomDestinationResponse(id, result, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): CensusCustomDestinationResponse = apply { + if (validated) { + return@apply + } + + id() + result().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + (result.asKnown().getOrNull()?.validity() ?: 0) + + /** The result of the RPC call. */ + class Result + @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 [Result]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Result]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(result: Result) = apply { + additionalProperties = result.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 [Result]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Result = Result(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Result = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Result && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Result{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is CensusCustomDestinationResponse && id == other.id && result == other.result && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, result, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "CensusCustomDestinationResponse{id=$id, result=$result, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/hightouch/HightouchEmbeddedDestinationParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/hightouch/HightouchEmbeddedDestinationParams.kt new file mode 100644 index 00000000..1f233a82 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/hightouch/HightouchEmbeddedDestinationParams.kt @@ -0,0 +1,713 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.integrations.hightouch + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.Params +import app.knock.api.core.checkRequired +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 + +/** Processes a Hightouch embedded destination RPC request. */ +class HightouchEmbeddedDestinationParams +private constructor( + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + /** + * The unique identifier for the RPC request. + * + * @throws KnockInvalidDataException 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 id(): String = body.id() + + /** + * The JSON-RPC version. + * + * @throws KnockInvalidDataException 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 jsonrpc(): String = body.jsonrpc() + + /** + * The method name to execute. + * + * @throws KnockInvalidDataException 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 method(): String = body.method() + + /** + * The parameters for the method. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun params(): Optional = body.params() + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _id(): JsonField = body._id() + + /** + * Returns the raw JSON value of [jsonrpc]. + * + * Unlike [jsonrpc], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _jsonrpc(): JsonField = body._jsonrpc() + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _method(): JsonField = body._method() + + /** + * Returns the raw JSON value of [params]. + * + * Unlike [params], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _params(): JsonField = body._params() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of + * [HightouchEmbeddedDestinationParams]. + * + * The following fields are required: + * ```java + * .id() + * .jsonrpc() + * .method() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [HightouchEmbeddedDestinationParams]. */ + 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(hightouchEmbeddedDestinationParams: HightouchEmbeddedDestinationParams) = + apply { + body = hightouchEmbeddedDestinationParams.body.toBuilder() + additionalHeaders = hightouchEmbeddedDestinationParams.additionalHeaders.toBuilder() + additionalQueryParams = + hightouchEmbeddedDestinationParams.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: + * - [id] + * - [jsonrpc] + * - [method] + * - [params] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The unique identifier for the RPC request. */ + fun id(id: String) = apply { body.id(id) } + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { body.id(id) } + + /** The JSON-RPC version. */ + fun jsonrpc(jsonrpc: String) = apply { body.jsonrpc(jsonrpc) } + + /** + * Sets [Builder.jsonrpc] to an arbitrary JSON value. + * + * You should usually call [Builder.jsonrpc] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun jsonrpc(jsonrpc: JsonField) = apply { body.jsonrpc(jsonrpc) } + + /** The method name to execute. */ + fun method(method: String) = apply { body.method(method) } + + /** + * Sets [Builder.method] to an arbitrary JSON value. + * + * You should usually call [Builder.method] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun method(method: JsonField) = apply { body.method(method) } + + /** The parameters for the method. */ + fun params(params: Params) = apply { body.params(params) } + + /** + * Sets [Builder.params] to an arbitrary JSON value. + * + * You should usually call [Builder.params] with a well-typed [Params] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun params(params: JsonField) = apply { body.params(params) } + + 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 [HightouchEmbeddedDestinationParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * .jsonrpc() + * .method() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): HightouchEmbeddedDestinationParams = + HightouchEmbeddedDestinationParams( + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + private constructor( + private val id: JsonField, + private val jsonrpc: JsonField, + private val method: JsonField, + private val params: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("jsonrpc") @ExcludeMissing jsonrpc: JsonField = JsonMissing.of(), + @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(), + @JsonProperty("params") @ExcludeMissing params: JsonField = JsonMissing.of(), + ) : this(id, jsonrpc, method, params, mutableMapOf()) + + /** + * The unique identifier for the RPC request. + * + * @throws KnockInvalidDataException 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 id(): String = id.getRequired("id") + + /** + * The JSON-RPC version. + * + * @throws KnockInvalidDataException 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 jsonrpc(): String = jsonrpc.getRequired("jsonrpc") + + /** + * The method name to execute. + * + * @throws KnockInvalidDataException 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 method(): String = method.getRequired("method") + + /** + * The parameters for the method. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun params(): Optional = params.getOptional("params") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [jsonrpc]. + * + * Unlike [jsonrpc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("jsonrpc") @ExcludeMissing fun _jsonrpc(): JsonField = jsonrpc + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method + + /** + * Returns the raw JSON value of [params]. + * + * Unlike [params], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("params") @ExcludeMissing fun _params(): JsonField = params + + @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 + * .id() + * .jsonrpc() + * .method() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var jsonrpc: JsonField? = null + private var method: JsonField? = null + private var params: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + id = body.id + jsonrpc = body.jsonrpc + method = body.method + params = body.params + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The unique identifier for the RPC request. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The JSON-RPC version. */ + fun jsonrpc(jsonrpc: String) = jsonrpc(JsonField.of(jsonrpc)) + + /** + * Sets [Builder.jsonrpc] to an arbitrary JSON value. + * + * You should usually call [Builder.jsonrpc] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun jsonrpc(jsonrpc: JsonField) = apply { this.jsonrpc = jsonrpc } + + /** The method name to execute. */ + fun method(method: String) = method(JsonField.of(method)) + + /** + * Sets [Builder.method] to an arbitrary JSON value. + * + * You should usually call [Builder.method] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun method(method: JsonField) = apply { this.method = method } + + /** The parameters for the method. */ + fun params(params: Params) = params(JsonField.of(params)) + + /** + * Sets [Builder.params] to an arbitrary JSON value. + * + * You should usually call [Builder.params] with a well-typed [Params] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun params(params: JsonField) = apply { this.params = params } + + 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 + * .id() + * .jsonrpc() + * .method() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Body = + Body( + checkRequired("id", id), + checkRequired("jsonrpc", jsonrpc), + checkRequired("method", method), + params, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + id() + jsonrpc() + method() + params().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + (if (jsonrpc.asKnown().isPresent) 1 else 0) + + (if (method.asKnown().isPresent) 1 else 0) + + (params.asKnown().getOrNull()?.validity() ?: 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && id == other.id && jsonrpc == other.jsonrpc && method == other.method && params == other.params && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, jsonrpc, method, params, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{id=$id, jsonrpc=$jsonrpc, method=$method, params=$params, additionalProperties=$additionalProperties}" + } + + /** The parameters for the method. */ + class Params + @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 [Params]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Params]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(params: Params) = apply { + additionalProperties = params.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 [Params]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Params = Params(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Params = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Params && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Params{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is HightouchEmbeddedDestinationParams && body == other.body && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(body, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "HightouchEmbeddedDestinationParams{body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/hightouch/HightouchEmbeddedDestinationResponse.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/hightouch/HightouchEmbeddedDestinationResponse.kt new file mode 100644 index 00000000..5294c16d --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/integrations/hightouch/HightouchEmbeddedDestinationResponse.kt @@ -0,0 +1,298 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.integrations.hightouch + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 HightouchEmbeddedDestinationResponse +private constructor( + private val id: JsonField, + private val result: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("result") @ExcludeMissing result: JsonField = JsonMissing.of(), + ) : this(id, result, mutableMapOf()) + + /** + * The request ID. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * The result of the RPC call. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun result(): Optional = result.getOptional("result") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [result]. + * + * Unlike [result], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("result") @ExcludeMissing fun _result(): JsonField = result + + @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 + * [HightouchEmbeddedDestinationResponse]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [HightouchEmbeddedDestinationResponse]. */ + class Builder internal constructor() { + + private var id: JsonField = JsonMissing.of() + private var result: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from( + hightouchEmbeddedDestinationResponse: HightouchEmbeddedDestinationResponse + ) = apply { + id = hightouchEmbeddedDestinationResponse.id + result = hightouchEmbeddedDestinationResponse.result + additionalProperties = + hightouchEmbeddedDestinationResponse.additionalProperties.toMutableMap() + } + + /** The request ID. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The result of the RPC call. */ + fun result(result: Result) = result(JsonField.of(result)) + + /** + * Sets [Builder.result] to an arbitrary JSON value. + * + * You should usually call [Builder.result] with a well-typed [Result] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun result(result: JsonField) = apply { this.result = result } + + 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 [HightouchEmbeddedDestinationResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): HightouchEmbeddedDestinationResponse = + HightouchEmbeddedDestinationResponse(id, result, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): HightouchEmbeddedDestinationResponse = apply { + if (validated) { + return@apply + } + + id() + result().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + (result.asKnown().getOrNull()?.validity() ?: 0) + + /** The result of the RPC call. */ + class Result + @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 [Result]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Result]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(result: Result) = apply { + additionalProperties = result.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 [Result]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Result = Result(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Result = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Result && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Result{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is HightouchEmbeddedDestinationResponse && id == other.id && result == other.result && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, result, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "HightouchEmbeddedDestinationResponse{id=$id, result=$result, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/messages/Activity.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/Activity.kt new file mode 100644 index 00000000..03716147 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/Activity.kt @@ -0,0 +1,505 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.messages + +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +import app.knock.api.models.objects.Object +import app.knock.api.models.recipients.Recipient +import app.knock.api.models.users.User +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 activity associated with a workflow trigger request. Messages produced after a + * [batch step](/designing-workflows/batch-function) can be associated with one or more activities. + * Non-batched messages will always be associated with a single activity. + */ +class Activity +private constructor( + private val id: JsonField, + private val _typename: JsonField, + private val actor: JsonField, + private val data: JsonField, + private val insertedAt: JsonField, + private val recipient: JsonField, + private val updatedAt: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("__typename") @ExcludeMissing _typename: JsonField = JsonMissing.of(), + @JsonProperty("actor") @ExcludeMissing actor: JsonField = JsonMissing.of(), + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("inserted_at") + @ExcludeMissing + insertedAt: JsonField = JsonMissing.of(), + @JsonProperty("recipient") + @ExcludeMissing + recipient: JsonField = JsonMissing.of(), + @JsonProperty("updated_at") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + ) : this(id, _typename, actor, data, insertedAt, recipient, updatedAt, mutableMapOf()) + + /** + * Unique identifier for the activity. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun _typename(): Optional = _typename.getOptional("__typename") + + /** + * A recipient of a notification, which is either a user or an object. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun actor(): Optional = actor.getOptional("actor") + + /** + * The workflow trigger `data` payload associated with the activity. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun data(): Optional = data.getOptional("data") + + /** + * Timestamp when the activity was created. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun insertedAt(): Optional = insertedAt.getOptional("inserted_at") + + /** + * A recipient of a notification, which is either a user or an object. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun recipient(): Optional = recipient.getOptional("recipient") + + /** + * Timestamp when the activity was last updated. + * + * @throws KnockInvalidDataException 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 [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [actor]. + * + * Unlike [actor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("actor") @ExcludeMissing fun _actor(): JsonField = actor + + /** + * 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 [insertedAt]. + * + * Unlike [insertedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inserted_at") + @ExcludeMissing + fun _insertedAt(): JsonField = insertedAt + + /** + * Returns the raw JSON value of [recipient]. + * + * Unlike [recipient], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("recipient") @ExcludeMissing fun _recipient(): JsonField = recipient + + /** + * 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 [Activity]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Activity]. */ + class Builder internal constructor() { + + private var id: JsonField = JsonMissing.of() + private var _typename: JsonField = JsonMissing.of() + private var actor: JsonField = JsonMissing.of() + private var data: JsonField = JsonMissing.of() + private var insertedAt: JsonField = JsonMissing.of() + private var recipient: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(activity: Activity) = apply { + id = activity.id + _typename = activity._typename + actor = activity.actor + data = activity.data + insertedAt = activity.insertedAt + recipient = activity.recipient + updatedAt = activity.updatedAt + additionalProperties = activity.additionalProperties.toMutableMap() + } + + /** Unique identifier for the activity. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** A recipient of a notification, which is either a user or an object. */ + fun actor(actor: Recipient?) = actor(JsonField.ofNullable(actor)) + + /** Alias for calling [Builder.actor] with `actor.orElse(null)`. */ + fun actor(actor: Optional) = actor(actor.getOrNull()) + + /** + * Sets [Builder.actor] to an arbitrary JSON value. + * + * You should usually call [Builder.actor] with a well-typed [Recipient] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun actor(actor: JsonField) = apply { this.actor = actor } + + /** Alias for calling [actor] with `Recipient.ofUser(user)`. */ + fun actor(user: User) = actor(Recipient.ofUser(user)) + + /** Alias for calling [actor] with `Recipient.ofObject(object_)`. */ + fun actor(object_: Object) = actor(Recipient.ofObject(object_)) + + /** The workflow trigger `data` payload associated with the activity. */ + fun data(data: Data?) = data(JsonField.ofNullable(data)) + + /** Alias for calling [Builder.data] with `data.orElse(null)`. */ + fun data(data: Optional) = data(data.getOrNull()) + + /** + * 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 } + + /** Timestamp when the activity was created. */ + fun insertedAt(insertedAt: OffsetDateTime) = insertedAt(JsonField.of(insertedAt)) + + /** + * Sets [Builder.insertedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.insertedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun insertedAt(insertedAt: JsonField) = apply { + this.insertedAt = insertedAt + } + + /** A recipient of a notification, which is either a user or an object. */ + fun recipient(recipient: Recipient) = recipient(JsonField.of(recipient)) + + /** + * Sets [Builder.recipient] to an arbitrary JSON value. + * + * You should usually call [Builder.recipient] with a well-typed [Recipient] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun recipient(recipient: JsonField) = apply { this.recipient = recipient } + + /** Alias for calling [recipient] with `Recipient.ofUser(user)`. */ + fun recipient(user: User) = recipient(Recipient.ofUser(user)) + + /** Alias for calling [recipient] with `Recipient.ofObject(object_)`. */ + fun recipient(object_: Object) = recipient(Recipient.ofObject(object_)) + + /** Timestamp when the activity 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 [Activity]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Activity = + Activity( + id, + _typename, + actor, + data, + insertedAt, + recipient, + updatedAt, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Activity = apply { + if (validated) { + return@apply + } + + id() + _typename() + actor().ifPresent { it.validate() } + data().ifPresent { it.validate() } + insertedAt() + recipient().ifPresent { it.validate() } + updatedAt() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + (if (_typename.asKnown().isPresent) 1 else 0) + + (actor.asKnown().getOrNull()?.validity() ?: 0) + + (data.asKnown().getOrNull()?.validity() ?: 0) + + (if (insertedAt.asKnown().isPresent) 1 else 0) + + (recipient.asKnown().getOrNull()?.validity() ?: 0) + + (if (updatedAt.asKnown().isPresent) 1 else 0) + + /** The workflow trigger `data` payload associated with the activity. */ + class Data + @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 [Data]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + additionalProperties = data.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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Data = Data(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Data && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Data{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Activity && id == other.id && _typename == other._typename && actor == other.actor && data == other.data && insertedAt == other.insertedAt && recipient == other.recipient && updatedAt == other.updatedAt && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, _typename, actor, data, insertedAt, recipient, updatedAt, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Activity{id=$id, _typename=$_typename, actor=$actor, data=$data, insertedAt=$insertedAt, recipient=$recipient, updatedAt=$updatedAt, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/messages/Message.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/Message.kt new file mode 100644 index 00000000..0f05b5f5 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/Message.kt @@ -0,0 +1,1845 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.messages + +import app.knock.api.core.Enum +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.checkKnown +import app.knock.api.core.checkRequired +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +import app.knock.api.models.recipients.RecipientReference +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 + +/** Represents a single message that was generated by a workflow for a given channel. */ +class Message +private constructor( + private val id: JsonField, + private val _typename: JsonField, + private val actors: JsonField>, + private val archivedAt: JsonField, + private val channelId: JsonField, + private val clickedAt: JsonField, + private val data: JsonField, + private val engagementStatuses: JsonField>, + private val insertedAt: JsonField, + private val interactedAt: JsonField, + private val linkClickedAt: JsonField, + private val metadata: JsonField, + private val readAt: JsonField, + private val recipient: JsonField, + private val scheduledAt: JsonField, + private val seenAt: JsonField, + private val source: JsonField, + private val status: JsonField, + private val tenant: JsonField, + private val updatedAt: JsonField, + private val workflow: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("__typename") @ExcludeMissing _typename: JsonField = JsonMissing.of(), + @JsonProperty("actors") + @ExcludeMissing + actors: JsonField> = JsonMissing.of(), + @JsonProperty("archived_at") + @ExcludeMissing + archivedAt: JsonField = JsonMissing.of(), + @JsonProperty("channel_id") @ExcludeMissing channelId: JsonField = JsonMissing.of(), + @JsonProperty("clicked_at") + @ExcludeMissing + clickedAt: JsonField = JsonMissing.of(), + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("engagement_statuses") + @ExcludeMissing + engagementStatuses: JsonField> = JsonMissing.of(), + @JsonProperty("inserted_at") + @ExcludeMissing + insertedAt: JsonField = JsonMissing.of(), + @JsonProperty("interacted_at") + @ExcludeMissing + interactedAt: JsonField = JsonMissing.of(), + @JsonProperty("link_clicked_at") + @ExcludeMissing + linkClickedAt: JsonField = JsonMissing.of(), + @JsonProperty("metadata") @ExcludeMissing metadata: JsonField = JsonMissing.of(), + @JsonProperty("read_at") + @ExcludeMissing + readAt: JsonField = JsonMissing.of(), + @JsonProperty("recipient") + @ExcludeMissing + recipient: JsonField = JsonMissing.of(), + @JsonProperty("scheduled_at") + @ExcludeMissing + scheduledAt: JsonField = JsonMissing.of(), + @JsonProperty("seen_at") + @ExcludeMissing + seenAt: JsonField = JsonMissing.of(), + @JsonProperty("source") @ExcludeMissing source: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("tenant") @ExcludeMissing tenant: JsonField = JsonMissing.of(), + @JsonProperty("updated_at") + @ExcludeMissing + updatedAt: JsonField = JsonMissing.of(), + @JsonProperty("workflow") @ExcludeMissing workflow: JsonField = JsonMissing.of(), + ) : this( + id, + _typename, + actors, + archivedAt, + channelId, + clickedAt, + data, + engagementStatuses, + insertedAt, + interactedAt, + linkClickedAt, + metadata, + readAt, + recipient, + scheduledAt, + seenAt, + source, + status, + tenant, + updatedAt, + workflow, + mutableMapOf(), + ) + + /** + * The unique identifier for the message. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun id(): Optional = id.getOptional("id") + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun _typename(): Optional = _typename.getOptional("__typename") + + /** + * One or more actors that are associated with this message. Note: this is a list that can + * contain up to 10 actors if the message is produced from a + * [batch](/designing-workflows/batch-function). + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun actors(): Optional> = actors.getOptional("actors") + + /** + * Timestamp when the message was archived. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun archivedAt(): Optional = archivedAt.getOptional("archived_at") + + /** + * The ID for the channel the message was sent through. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channelId(): Optional = channelId.getOptional("channel_id") + + /** + * Timestamp when the message was clicked. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun clickedAt(): Optional = clickedAt.getOptional("clicked_at") + + /** + * Data associated with the message’s workflow run. Includes the workflow trigger request’s + * `data` payload merged with any additional data returned by a + * [fetch function](/designing-workflows/fetch-function). For messages produced after a + * [batch step](/designing-workflows/batch-function), includes the payload `data` from the + * most-recent trigger request (the final `activity` in the batch). + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun data(): Optional = data.getOptional("data") + + /** + * A list of engagement statuses. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun engagementStatuses(): Optional> = + engagementStatuses.getOptional("engagement_statuses") + + /** + * Timestamp when the resource was created. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun insertedAt(): Optional = insertedAt.getOptional("inserted_at") + + /** + * Timestamp when the message was interacted with. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun interactedAt(): Optional = interactedAt.getOptional("interacted_at") + + /** + * Timestamp when a link in the message was clicked. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun linkClickedAt(): Optional = linkClickedAt.getOptional("link_clicked_at") + + /** + * The metadata associated with the message. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun metadata(): Optional = metadata.getOptional("metadata") + + /** + * Timestamp when the message was read. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun readAt(): Optional = readAt.getOptional("read_at") + + /** + * A reference to a recipient, either a user identifier (string) or an object reference (ID, + * collection). + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun recipient(): Optional = recipient.getOptional("recipient") + + /** + * Timestamp when the message was scheduled to be sent. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun scheduledAt(): Optional = scheduledAt.getOptional("scheduled_at") + + /** + * Timestamp when the message was seen. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun seenAt(): Optional = seenAt.getOptional("seen_at") + + /** + * The workflow that triggered the message. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun source(): Optional = source.getOptional("source") + + /** + * The message delivery status. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun status(): Optional = status.getOptional("status") + + /** + * The ID of the `tenant` associated with the message. Only present when a `tenant` is provided + * on a workflow trigger request. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun tenant(): Optional = tenant.getOptional("tenant") + + /** + * The timestamp when the resource was last updated. + * + * @throws KnockInvalidDataException 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") + + /** + * The key of the workflow that generated the message. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + @Deprecated("deprecated") fun workflow(): Optional = workflow.getOptional("workflow") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [actors]. + * + * Unlike [actors], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("actors") + @ExcludeMissing + fun _actors(): JsonField> = actors + + /** + * Returns the raw JSON value of [archivedAt]. + * + * Unlike [archivedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("archived_at") + @ExcludeMissing + fun _archivedAt(): JsonField = archivedAt + + /** + * Returns the raw JSON value of [channelId]. + * + * Unlike [channelId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channel_id") @ExcludeMissing fun _channelId(): JsonField = channelId + + /** + * Returns the raw JSON value of [clickedAt]. + * + * Unlike [clickedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("clicked_at") + @ExcludeMissing + fun _clickedAt(): JsonField = clickedAt + + /** + * 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 [engagementStatuses]. + * + * Unlike [engagementStatuses], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("engagement_statuses") + @ExcludeMissing + fun _engagementStatuses(): JsonField> = engagementStatuses + + /** + * Returns the raw JSON value of [insertedAt]. + * + * Unlike [insertedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inserted_at") + @ExcludeMissing + fun _insertedAt(): JsonField = insertedAt + + /** + * Returns the raw JSON value of [interactedAt]. + * + * Unlike [interactedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("interacted_at") + @ExcludeMissing + fun _interactedAt(): JsonField = interactedAt + + /** + * Returns the raw JSON value of [linkClickedAt]. + * + * Unlike [linkClickedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("link_clicked_at") + @ExcludeMissing + fun _linkClickedAt(): JsonField = linkClickedAt + + /** + * 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 [readAt]. + * + * Unlike [readAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("read_at") @ExcludeMissing fun _readAt(): JsonField = readAt + + /** + * Returns the raw JSON value of [recipient]. + * + * Unlike [recipient], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("recipient") + @ExcludeMissing + fun _recipient(): JsonField = recipient + + /** + * Returns the raw JSON value of [scheduledAt]. + * + * Unlike [scheduledAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("scheduled_at") + @ExcludeMissing + fun _scheduledAt(): JsonField = scheduledAt + + /** + * Returns the raw JSON value of [seenAt]. + * + * Unlike [seenAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("seen_at") @ExcludeMissing fun _seenAt(): JsonField = seenAt + + /** + * Returns the raw JSON value of [source]. + * + * Unlike [source], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("source") @ExcludeMissing fun _source(): JsonField = source + + /** + * 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 [tenant]. + * + * Unlike [tenant], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("tenant") @ExcludeMissing fun _tenant(): JsonField = tenant + + /** + * 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 + + /** + * Returns the raw JSON value of [workflow]. + * + * Unlike [workflow], this method doesn't throw if the JSON field has an unexpected type. + */ + @Deprecated("deprecated") + @JsonProperty("workflow") + @ExcludeMissing + fun _workflow(): JsonField = workflow + + @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 [Message]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Message]. */ + class Builder internal constructor() { + + private var id: JsonField = JsonMissing.of() + private var _typename: JsonField = JsonMissing.of() + private var actors: JsonField>? = null + private var archivedAt: JsonField = JsonMissing.of() + private var channelId: JsonField = JsonMissing.of() + private var clickedAt: JsonField = JsonMissing.of() + private var data: JsonField = JsonMissing.of() + private var engagementStatuses: JsonField>? = null + private var insertedAt: JsonField = JsonMissing.of() + private var interactedAt: JsonField = JsonMissing.of() + private var linkClickedAt: JsonField = JsonMissing.of() + private var metadata: JsonField = JsonMissing.of() + private var readAt: JsonField = JsonMissing.of() + private var recipient: JsonField = JsonMissing.of() + private var scheduledAt: JsonField = JsonMissing.of() + private var seenAt: JsonField = JsonMissing.of() + private var source: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var tenant: JsonField = JsonMissing.of() + private var updatedAt: JsonField = JsonMissing.of() + private var workflow: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(message: Message) = apply { + id = message.id + _typename = message._typename + actors = message.actors.map { it.toMutableList() } + archivedAt = message.archivedAt + channelId = message.channelId + clickedAt = message.clickedAt + data = message.data + engagementStatuses = message.engagementStatuses.map { it.toMutableList() } + insertedAt = message.insertedAt + interactedAt = message.interactedAt + linkClickedAt = message.linkClickedAt + metadata = message.metadata + readAt = message.readAt + recipient = message.recipient + scheduledAt = message.scheduledAt + seenAt = message.seenAt + source = message.source + status = message.status + tenant = message.tenant + updatedAt = message.updatedAt + workflow = message.workflow + additionalProperties = message.additionalProperties.toMutableMap() + } + + /** The unique identifier for the message. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** + * One or more actors that are associated with this message. Note: this is a list that can + * contain up to 10 actors if the message is produced from a + * [batch](/designing-workflows/batch-function). + */ + fun actors(actors: List) = actors(JsonField.of(actors)) + + /** + * Sets [Builder.actors] to an arbitrary JSON value. + * + * You should usually call [Builder.actors] with a well-typed `List` + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun actors(actors: JsonField>) = apply { + this.actors = actors.map { it.toMutableList() } + } + + /** + * Adds a single [RecipientReference] to [actors]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addActor(actor: RecipientReference) = apply { + actors = + (actors ?: JsonField.of(mutableListOf())).also { + checkKnown("actors", it).add(actor) + } + } + + /** Alias for calling [addActor] with `RecipientReference.ofUser(user)`. */ + fun addActor(user: String) = addActor(RecipientReference.ofUser(user)) + + /** + * Alias for calling [addActor] with + * `RecipientReference.ofObjectReference(objectReference)`. + */ + fun addActor(objectReference: RecipientReference.ObjectReference) = + addActor(RecipientReference.ofObjectReference(objectReference)) + + /** Timestamp when the message was archived. */ + fun archivedAt(archivedAt: OffsetDateTime?) = archivedAt(JsonField.ofNullable(archivedAt)) + + /** Alias for calling [Builder.archivedAt] with `archivedAt.orElse(null)`. */ + fun archivedAt(archivedAt: Optional) = archivedAt(archivedAt.getOrNull()) + + /** + * Sets [Builder.archivedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.archivedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun archivedAt(archivedAt: JsonField) = apply { + this.archivedAt = archivedAt + } + + /** The ID for the channel the message was sent through. */ + fun channelId(channelId: String) = channelId(JsonField.of(channelId)) + + /** + * Sets [Builder.channelId] to an arbitrary JSON value. + * + * You should usually call [Builder.channelId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun channelId(channelId: JsonField) = apply { this.channelId = channelId } + + /** Timestamp when the message was clicked. */ + fun clickedAt(clickedAt: OffsetDateTime?) = clickedAt(JsonField.ofNullable(clickedAt)) + + /** Alias for calling [Builder.clickedAt] with `clickedAt.orElse(null)`. */ + fun clickedAt(clickedAt: Optional) = clickedAt(clickedAt.getOrNull()) + + /** + * Sets [Builder.clickedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.clickedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun clickedAt(clickedAt: JsonField) = apply { this.clickedAt = clickedAt } + + /** + * Data associated with the message’s workflow run. Includes the workflow trigger request’s + * `data` payload merged with any additional data returned by a + * [fetch function](/designing-workflows/fetch-function). For messages produced after a + * [batch step](/designing-workflows/batch-function), includes the payload `data` from the + * most-recent trigger request (the final `activity` in the batch). + */ + fun data(data: Data?) = data(JsonField.ofNullable(data)) + + /** Alias for calling [Builder.data] with `data.orElse(null)`. */ + fun data(data: Optional) = data(data.getOrNull()) + + /** + * 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 } + + /** A list of engagement statuses. */ + fun engagementStatuses(engagementStatuses: List) = + engagementStatuses(JsonField.of(engagementStatuses)) + + /** + * Sets [Builder.engagementStatuses] to an arbitrary JSON value. + * + * You should usually call [Builder.engagementStatuses] with a well-typed + * `List` value instead. This method is primarily for setting the field to + * an undocumented or not yet supported value. + */ + fun engagementStatuses(engagementStatuses: JsonField>) = apply { + this.engagementStatuses = engagementStatuses.map { it.toMutableList() } + } + + /** + * Adds a single [EngagementStatus] to [engagementStatuses]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addEngagementStatus(engagementStatus: EngagementStatus) = apply { + engagementStatuses = + (engagementStatuses ?: JsonField.of(mutableListOf())).also { + checkKnown("engagementStatuses", it).add(engagementStatus) + } + } + + /** Timestamp when the resource was created. */ + fun insertedAt(insertedAt: OffsetDateTime) = insertedAt(JsonField.of(insertedAt)) + + /** + * Sets [Builder.insertedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.insertedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun insertedAt(insertedAt: JsonField) = apply { + this.insertedAt = insertedAt + } + + /** Timestamp when the message was interacted with. */ + fun interactedAt(interactedAt: OffsetDateTime?) = + interactedAt(JsonField.ofNullable(interactedAt)) + + /** Alias for calling [Builder.interactedAt] with `interactedAt.orElse(null)`. */ + fun interactedAt(interactedAt: Optional) = + interactedAt(interactedAt.getOrNull()) + + /** + * Sets [Builder.interactedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.interactedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun interactedAt(interactedAt: JsonField) = apply { + this.interactedAt = interactedAt + } + + /** Timestamp when a link in the message was clicked. */ + fun linkClickedAt(linkClickedAt: OffsetDateTime?) = + linkClickedAt(JsonField.ofNullable(linkClickedAt)) + + /** Alias for calling [Builder.linkClickedAt] with `linkClickedAt.orElse(null)`. */ + fun linkClickedAt(linkClickedAt: Optional) = + linkClickedAt(linkClickedAt.getOrNull()) + + /** + * Sets [Builder.linkClickedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.linkClickedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun linkClickedAt(linkClickedAt: JsonField) = apply { + this.linkClickedAt = linkClickedAt + } + + /** The metadata associated with the message. */ + fun metadata(metadata: Metadata?) = metadata(JsonField.ofNullable(metadata)) + + /** Alias for calling [Builder.metadata] with `metadata.orElse(null)`. */ + fun metadata(metadata: Optional) = metadata(metadata.getOrNull()) + + /** + * 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 } + + /** Timestamp when the message was read. */ + fun readAt(readAt: OffsetDateTime?) = readAt(JsonField.ofNullable(readAt)) + + /** Alias for calling [Builder.readAt] with `readAt.orElse(null)`. */ + fun readAt(readAt: Optional) = readAt(readAt.getOrNull()) + + /** + * Sets [Builder.readAt] to an arbitrary JSON value. + * + * You should usually call [Builder.readAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun readAt(readAt: JsonField) = apply { this.readAt = readAt } + + /** + * A reference to a recipient, either a user identifier (string) or an object reference (ID, + * collection). + */ + fun recipient(recipient: RecipientReference) = recipient(JsonField.of(recipient)) + + /** + * Sets [Builder.recipient] to an arbitrary JSON value. + * + * You should usually call [Builder.recipient] with a well-typed [RecipientReference] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun recipient(recipient: JsonField) = apply { + this.recipient = recipient + } + + /** Alias for calling [recipient] with `RecipientReference.ofUser(user)`. */ + fun recipient(user: String) = recipient(RecipientReference.ofUser(user)) + + /** + * Alias for calling [recipient] with + * `RecipientReference.ofObjectReference(objectReference)`. + */ + fun recipient(objectReference: RecipientReference.ObjectReference) = + recipient(RecipientReference.ofObjectReference(objectReference)) + + /** Timestamp when the message was scheduled to be sent. */ + fun scheduledAt(scheduledAt: OffsetDateTime?) = + scheduledAt(JsonField.ofNullable(scheduledAt)) + + /** Alias for calling [Builder.scheduledAt] with `scheduledAt.orElse(null)`. */ + fun scheduledAt(scheduledAt: Optional) = + scheduledAt(scheduledAt.getOrNull()) + + /** + * Sets [Builder.scheduledAt] to an arbitrary JSON value. + * + * You should usually call [Builder.scheduledAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun scheduledAt(scheduledAt: JsonField) = apply { + this.scheduledAt = scheduledAt + } + + /** Timestamp when the message was seen. */ + fun seenAt(seenAt: OffsetDateTime?) = seenAt(JsonField.ofNullable(seenAt)) + + /** Alias for calling [Builder.seenAt] with `seenAt.orElse(null)`. */ + fun seenAt(seenAt: Optional) = seenAt(seenAt.getOrNull()) + + /** + * Sets [Builder.seenAt] to an arbitrary JSON value. + * + * You should usually call [Builder.seenAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun seenAt(seenAt: JsonField) = apply { this.seenAt = seenAt } + + /** The workflow that triggered the message. */ + fun source(source: Source) = source(JsonField.of(source)) + + /** + * Sets [Builder.source] to an arbitrary JSON value. + * + * You should usually call [Builder.source] with a well-typed [Source] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun source(source: JsonField) = apply { this.source = source } + + /** The message delivery 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 } + + /** + * The ID of the `tenant` associated with the message. Only present when a `tenant` is + * provided on a workflow trigger request. + */ + fun tenant(tenant: String?) = tenant(JsonField.ofNullable(tenant)) + + /** Alias for calling [Builder.tenant] with `tenant.orElse(null)`. */ + fun tenant(tenant: Optional) = tenant(tenant.getOrNull()) + + /** + * Sets [Builder.tenant] to an arbitrary JSON value. + * + * You should usually call [Builder.tenant] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun tenant(tenant: JsonField) = apply { this.tenant = tenant } + + /** The timestamp when the resource 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 } + + /** The key of the workflow that generated the message. */ + @Deprecated("deprecated") + fun workflow(workflow: String?) = workflow(JsonField.ofNullable(workflow)) + + /** Alias for calling [Builder.workflow] with `workflow.orElse(null)`. */ + @Deprecated("deprecated") + fun workflow(workflow: Optional) = workflow(workflow.getOrNull()) + + /** + * Sets [Builder.workflow] to an arbitrary JSON value. + * + * You should usually call [Builder.workflow] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + @Deprecated("deprecated") + fun workflow(workflow: JsonField) = apply { this.workflow = workflow } + + 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 [Message]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Message = + Message( + id, + _typename, + (actors ?: JsonMissing.of()).map { it.toImmutable() }, + archivedAt, + channelId, + clickedAt, + data, + (engagementStatuses ?: JsonMissing.of()).map { it.toImmutable() }, + insertedAt, + interactedAt, + linkClickedAt, + metadata, + readAt, + recipient, + scheduledAt, + seenAt, + source, + status, + tenant, + updatedAt, + workflow, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Message = apply { + if (validated) { + return@apply + } + + id() + _typename() + actors().ifPresent { it.forEach { it.validate() } } + archivedAt() + channelId() + clickedAt() + data().ifPresent { it.validate() } + engagementStatuses().ifPresent { it.forEach { it.validate() } } + insertedAt() + interactedAt() + linkClickedAt() + metadata().ifPresent { it.validate() } + readAt() + recipient().ifPresent { it.validate() } + scheduledAt() + seenAt() + source().ifPresent { it.validate() } + status().ifPresent { it.validate() } + tenant() + updatedAt() + workflow() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + (if (_typename.asKnown().isPresent) 1 else 0) + + (actors.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (archivedAt.asKnown().isPresent) 1 else 0) + + (if (channelId.asKnown().isPresent) 1 else 0) + + (if (clickedAt.asKnown().isPresent) 1 else 0) + + (data.asKnown().getOrNull()?.validity() ?: 0) + + (engagementStatuses.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + + (if (insertedAt.asKnown().isPresent) 1 else 0) + + (if (interactedAt.asKnown().isPresent) 1 else 0) + + (if (linkClickedAt.asKnown().isPresent) 1 else 0) + + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (readAt.asKnown().isPresent) 1 else 0) + + (recipient.asKnown().getOrNull()?.validity() ?: 0) + + (if (scheduledAt.asKnown().isPresent) 1 else 0) + + (if (seenAt.asKnown().isPresent) 1 else 0) + + (source.asKnown().getOrNull()?.validity() ?: 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (tenant.asKnown().isPresent) 1 else 0) + + (if (updatedAt.asKnown().isPresent) 1 else 0) + + (if (workflow.asKnown().isPresent) 1 else 0) + + /** + * Data associated with the message’s workflow run. Includes the workflow trigger request’s + * `data` payload merged with any additional data returned by a + * [fetch function](/designing-workflows/fetch-function). For messages produced after a + * [batch step](/designing-workflows/batch-function), includes the payload `data` from the + * most-recent trigger request (the final `activity` in the batch). + */ + class Data + @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 [Data]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + additionalProperties = data.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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Data = Data(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Data && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Data{additionalProperties=$additionalProperties}" + } + + /** + * An engagement status for a message. Can be one of: read, seen, interacted, link_clicked, + * archived. + */ + class EngagementStatus @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 SEEN = of("seen") + + @JvmField val READ = of("read") + + @JvmField val INTERACTED = of("interacted") + + @JvmField val LINK_CLICKED = of("link_clicked") + + @JvmField val ARCHIVED = of("archived") + + @JvmStatic fun of(value: String) = EngagementStatus(JsonField.of(value)) + } + + /** An enum containing [EngagementStatus]'s known values. */ + enum class Known { + SEEN, + READ, + INTERACTED, + LINK_CLICKED, + ARCHIVED, + } + + /** + * An enum containing [EngagementStatus]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [EngagementStatus] 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 { + SEEN, + READ, + INTERACTED, + LINK_CLICKED, + ARCHIVED, + /** + * An enum member indicating that [EngagementStatus] 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) { + SEEN -> Value.SEEN + READ -> Value.READ + INTERACTED -> Value.INTERACTED + LINK_CLICKED -> Value.LINK_CLICKED + ARCHIVED -> Value.ARCHIVED + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + SEEN -> Known.SEEN + READ -> Known.READ + INTERACTED -> Known.INTERACTED + LINK_CLICKED -> Known.LINK_CLICKED + ARCHIVED -> Known.ARCHIVED + else -> throw KnockInvalidDataException("Unknown EngagementStatus: $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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): EngagementStatus = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is EngagementStatus && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The metadata associated with the message. */ + 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 + + fun validate(): Metadata = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Metadata && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Metadata{additionalProperties=$additionalProperties}" + } + + /** The workflow that triggered the message. */ + class Source + private constructor( + private val _typename: JsonField, + private val categories: JsonField>, + private val key: JsonField, + private val versionId: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("__typename") + @ExcludeMissing + _typename: JsonField = JsonMissing.of(), + @JsonProperty("categories") + @ExcludeMissing + categories: JsonField> = JsonMissing.of(), + @JsonProperty("key") @ExcludeMissing key: JsonField = JsonMissing.of(), + @JsonProperty("version_id") + @ExcludeMissing + versionId: JsonField = JsonMissing.of(), + ) : this(_typename, categories, key, versionId, mutableMapOf()) + + /** + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * The categories associated with the message. + * + * @throws KnockInvalidDataException 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 categories(): List = categories.getRequired("categories") + + /** + * The key of the workflow that triggered the message. + * + * @throws KnockInvalidDataException 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 key(): String = key.getRequired("key") + + /** + * The ID of the version of the workflow that triggered the message. + * + * @throws KnockInvalidDataException 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 versionId(): String = versionId.getRequired("version_id") + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [categories]. + * + * Unlike [categories], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("categories") + @ExcludeMissing + fun _categories(): JsonField> = categories + + /** + * Returns the raw JSON value of [key]. + * + * Unlike [key], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("key") @ExcludeMissing fun _key(): JsonField = key + + /** + * Returns the raw JSON value of [versionId]. + * + * Unlike [versionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("version_id") @ExcludeMissing fun _versionId(): JsonField = versionId + + @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 [Source]. + * + * The following fields are required: + * ```java + * ._typename() + * .categories() + * .key() + * .versionId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Source]. */ + class Builder internal constructor() { + + private var _typename: JsonField? = null + private var categories: JsonField>? = null + private var key: JsonField? = null + private var versionId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(source: Source) = apply { + _typename = source._typename + categories = source.categories.map { it.toMutableList() } + key = source.key + versionId = source.versionId + additionalProperties = source.additionalProperties.toMutableMap() + } + + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** The categories associated with the message. */ + fun categories(categories: List) = categories(JsonField.of(categories)) + + /** + * Sets [Builder.categories] to an arbitrary JSON value. + * + * You should usually call [Builder.categories] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun categories(categories: JsonField>) = apply { + this.categories = categories.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [categories]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addCategory(category: String) = apply { + categories = + (categories ?: JsonField.of(mutableListOf())).also { + checkKnown("categories", it).add(category) + } + } + + /** The key of the workflow that triggered the message. */ + fun key(key: String) = key(JsonField.of(key)) + + /** + * Sets [Builder.key] to an arbitrary JSON value. + * + * You should usually call [Builder.key] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun key(key: JsonField) = apply { this.key = key } + + /** The ID of the version of the workflow that triggered the message. */ + fun versionId(versionId: String) = versionId(JsonField.of(versionId)) + + /** + * Sets [Builder.versionId] to an arbitrary JSON value. + * + * You should usually call [Builder.versionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun versionId(versionId: JsonField) = apply { this.versionId = versionId } + + 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 [Source]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * ._typename() + * .categories() + * .key() + * .versionId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): Source = + Source( + checkRequired("_typename", _typename), + checkRequired("categories", categories).map { it.toImmutable() }, + checkRequired("key", key), + checkRequired("versionId", versionId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Source = apply { + if (validated) { + return@apply + } + + _typename() + categories() + key() + versionId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (_typename.asKnown().isPresent) 1 else 0) + + (categories.asKnown().getOrNull()?.size ?: 0) + + (if (key.asKnown().isPresent) 1 else 0) + + (if (versionId.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Source && _typename == other._typename && categories == other.categories && key == other.key && versionId == other.versionId && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(_typename, categories, key, versionId, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Source{_typename=$_typename, categories=$categories, key=$key, versionId=$versionId, additionalProperties=$additionalProperties}" + } + + /** The message delivery 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 QUEUED = of("queued") + + @JvmField val SENT = of("sent") + + @JvmField val DELIVERED = of("delivered") + + @JvmField val DELIVERY_ATTEMPTED = of("delivery_attempted") + + @JvmField val UNDELIVERED = of("undelivered") + + @JvmField val NOT_SENT = of("not_sent") + + @JvmField val BOUNCED = of("bounced") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + QUEUED, + SENT, + DELIVERED, + DELIVERY_ATTEMPTED, + UNDELIVERED, + NOT_SENT, + BOUNCED, + } + + /** + * 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 { + QUEUED, + SENT, + DELIVERED, + DELIVERY_ATTEMPTED, + UNDELIVERED, + NOT_SENT, + BOUNCED, + /** 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) { + QUEUED -> Value.QUEUED + SENT -> Value.SENT + DELIVERED -> Value.DELIVERED + DELIVERY_ATTEMPTED -> Value.DELIVERY_ATTEMPTED + UNDELIVERED -> Value.UNDELIVERED + NOT_SENT -> Value.NOT_SENT + BOUNCED -> Value.BOUNCED + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + QUEUED -> Known.QUEUED + SENT -> Known.SENT + DELIVERED -> Known.DELIVERED + DELIVERY_ATTEMPTED -> Known.DELIVERY_ATTEMPTED + UNDELIVERED -> Known.UNDELIVERED + NOT_SENT -> Known.NOT_SENT + BOUNCED -> Known.BOUNCED + else -> throw KnockInvalidDataException("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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Status && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Message && id == other.id && _typename == other._typename && actors == other.actors && archivedAt == other.archivedAt && channelId == other.channelId && clickedAt == other.clickedAt && data == other.data && engagementStatuses == other.engagementStatuses && insertedAt == other.insertedAt && interactedAt == other.interactedAt && linkClickedAt == other.linkClickedAt && metadata == other.metadata && readAt == other.readAt && recipient == other.recipient && scheduledAt == other.scheduledAt && seenAt == other.seenAt && source == other.source && status == other.status && tenant == other.tenant && updatedAt == other.updatedAt && workflow == other.workflow && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, _typename, actors, archivedAt, channelId, clickedAt, data, engagementStatuses, insertedAt, interactedAt, linkClickedAt, metadata, readAt, recipient, scheduledAt, seenAt, source, status, tenant, updatedAt, workflow, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Message{id=$id, _typename=$_typename, actors=$actors, archivedAt=$archivedAt, channelId=$channelId, clickedAt=$clickedAt, data=$data, engagementStatuses=$engagementStatuses, insertedAt=$insertedAt, interactedAt=$interactedAt, linkClickedAt=$linkClickedAt, metadata=$metadata, readAt=$readAt, recipient=$recipient, scheduledAt=$scheduledAt, seenAt=$seenAt, source=$source, status=$status, tenant=$tenant, updatedAt=$updatedAt, workflow=$workflow, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageArchiveParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageArchiveParams.kt new file mode 100644 index 00000000..91b41a96 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageArchiveParams.kt @@ -0,0 +1,224 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.messages + +import app.knock.api.core.JsonValue +import app.knock.api.core.Params +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import app.knock.api.core.toImmutable +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Archives a message for the user. Archived messages are hidden from the default message list in + * the feed but can still be accessed and unarchived later. + */ +class MessageArchiveParams +private constructor( + private val messageId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun messageId(): Optional = Optional.ofNullable(messageId) + + fun _additionalBodyProperties(): Map = additionalBodyProperties + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): MessageArchiveParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [MessageArchiveParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessageArchiveParams]. */ + class Builder internal constructor() { + + private var messageId: 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(messageArchiveParams: MessageArchiveParams) = apply { + messageId = messageArchiveParams.messageId + additionalHeaders = messageArchiveParams.additionalHeaders.toBuilder() + additionalQueryParams = messageArchiveParams.additionalQueryParams.toBuilder() + additionalBodyProperties = messageArchiveParams.additionalBodyProperties.toMutableMap() + } + + fun messageId(messageId: String?) = apply { this.messageId = messageId } + + /** Alias for calling [Builder.messageId] with `messageId.orElse(null)`. */ + fun messageId(messageId: Optional) = messageId(messageId.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 [MessageArchiveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): MessageArchiveParams = + MessageArchiveParams( + messageId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + fun _pathParam(index: Int): String = + when (index) { + 0 -> messageId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MessageArchiveParams && messageId == other.messageId && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams && additionalBodyProperties == other.additionalBodyProperties /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(messageId, additionalHeaders, additionalQueryParams, additionalBodyProperties) /* spotless:on */ + + override fun toString() = + "MessageArchiveParams{messageId=$messageId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageDeliveryLog.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageDeliveryLog.kt new file mode 100644 index 00000000..d8b2b9bf --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageDeliveryLog.kt @@ -0,0 +1,1895 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.messages + +import app.knock.api.core.BaseDeserializer +import app.knock.api.core.BaseSerializer +import app.knock.api.core.Enum +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.allMaxBy +import app.knock.api.core.checkRequired +import app.knock.api.core.getOrThrow +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +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.module.kotlin.jacksonTypeRef +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * A message delivery log contains a `request` from Knock to a downstream provider and the + * `response` that was returned. + */ +class MessageDeliveryLog +private constructor( + private val id: JsonField, + private val _typename: JsonField, + private val environmentId: JsonField, + private val insertedAt: JsonField, + private val request: JsonField, + private val response: JsonField, + private val serviceName: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("__typename") @ExcludeMissing _typename: JsonField = JsonMissing.of(), + @JsonProperty("environment_id") + @ExcludeMissing + environmentId: JsonField = JsonMissing.of(), + @JsonProperty("inserted_at") + @ExcludeMissing + insertedAt: JsonField = JsonMissing.of(), + @JsonProperty("request") @ExcludeMissing request: JsonField = JsonMissing.of(), + @JsonProperty("response") @ExcludeMissing response: JsonField = JsonMissing.of(), + @JsonProperty("service_name") + @ExcludeMissing + serviceName: JsonField = JsonMissing.of(), + ) : this( + id, + _typename, + environmentId, + insertedAt, + request, + response, + serviceName, + mutableMapOf(), + ) + + /** + * The unique identifier for the message delivery log. + * + * @throws KnockInvalidDataException 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 id(): String = id.getRequired("id") + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * The ID of the environment in which the message delivery occurred. + * + * @throws KnockInvalidDataException 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 environmentId(): String = environmentId.getRequired("environment_id") + + /** + * Timestamp when the message delivery log was created. + * + * @throws KnockInvalidDataException 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 insertedAt(): String = insertedAt.getRequired("inserted_at") + + /** + * A message delivery log request. + * + * @throws KnockInvalidDataException 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 request(): Request = request.getRequired("request") + + /** + * A message delivery log response. + * + * @throws KnockInvalidDataException 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 response(): Response = response.getRequired("response") + + /** + * The name of the service that processed the delivery. + * + * @throws KnockInvalidDataException 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 serviceName(): String = serviceName.getRequired("service_name") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [environmentId]. + * + * Unlike [environmentId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("environment_id") + @ExcludeMissing + fun _environmentId(): JsonField = environmentId + + /** + * Returns the raw JSON value of [insertedAt]. + * + * Unlike [insertedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inserted_at") @ExcludeMissing fun _insertedAt(): JsonField = insertedAt + + /** + * Returns the raw JSON value of [request]. + * + * Unlike [request], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("request") @ExcludeMissing fun _request(): JsonField = request + + /** + * Returns the raw JSON value of [response]. + * + * Unlike [response], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("response") @ExcludeMissing fun _response(): JsonField = response + + /** + * Returns the raw JSON value of [serviceName]. + * + * Unlike [serviceName], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("service_name") + @ExcludeMissing + fun _serviceName(): JsonField = serviceName + + @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 [MessageDeliveryLog]. + * + * The following fields are required: + * ```java + * .id() + * ._typename() + * .environmentId() + * .insertedAt() + * .request() + * .response() + * .serviceName() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessageDeliveryLog]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var _typename: JsonField? = null + private var environmentId: JsonField? = null + private var insertedAt: JsonField? = null + private var request: JsonField? = null + private var response: JsonField? = null + private var serviceName: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(messageDeliveryLog: MessageDeliveryLog) = apply { + id = messageDeliveryLog.id + _typename = messageDeliveryLog._typename + environmentId = messageDeliveryLog.environmentId + insertedAt = messageDeliveryLog.insertedAt + request = messageDeliveryLog.request + response = messageDeliveryLog.response + serviceName = messageDeliveryLog.serviceName + additionalProperties = messageDeliveryLog.additionalProperties.toMutableMap() + } + + /** The unique identifier for the message delivery log. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** The ID of the environment in which the message delivery occurred. */ + fun environmentId(environmentId: String) = environmentId(JsonField.of(environmentId)) + + /** + * Sets [Builder.environmentId] to an arbitrary JSON value. + * + * You should usually call [Builder.environmentId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun environmentId(environmentId: JsonField) = apply { + this.environmentId = environmentId + } + + /** Timestamp when the message delivery log was created. */ + fun insertedAt(insertedAt: String) = insertedAt(JsonField.of(insertedAt)) + + /** + * Sets [Builder.insertedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.insertedAt] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun insertedAt(insertedAt: JsonField) = apply { this.insertedAt = insertedAt } + + /** A message delivery log request. */ + fun request(request: Request) = request(JsonField.of(request)) + + /** + * Sets [Builder.request] to an arbitrary JSON value. + * + * You should usually call [Builder.request] with a well-typed [Request] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun request(request: JsonField) = apply { this.request = request } + + /** A message delivery log response. */ + fun response(response: Response) = response(JsonField.of(response)) + + /** + * Sets [Builder.response] to an arbitrary JSON value. + * + * You should usually call [Builder.response] with a well-typed [Response] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun response(response: JsonField) = apply { this.response = response } + + /** The name of the service that processed the delivery. */ + fun serviceName(serviceName: String) = serviceName(JsonField.of(serviceName)) + + /** + * Sets [Builder.serviceName] to an arbitrary JSON value. + * + * You should usually call [Builder.serviceName] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun serviceName(serviceName: JsonField) = apply { this.serviceName = serviceName } + + 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 [MessageDeliveryLog]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * ._typename() + * .environmentId() + * .insertedAt() + * .request() + * .response() + * .serviceName() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MessageDeliveryLog = + MessageDeliveryLog( + checkRequired("id", id), + checkRequired("_typename", _typename), + checkRequired("environmentId", environmentId), + checkRequired("insertedAt", insertedAt), + checkRequired("request", request), + checkRequired("response", response), + checkRequired("serviceName", serviceName), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): MessageDeliveryLog = apply { + if (validated) { + return@apply + } + + id() + _typename() + environmentId() + insertedAt() + request().validate() + response().validate() + serviceName() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + (if (_typename.asKnown().isPresent) 1 else 0) + + (if (environmentId.asKnown().isPresent) 1 else 0) + + (if (insertedAt.asKnown().isPresent) 1 else 0) + + (request.asKnown().getOrNull()?.validity() ?: 0) + + (response.asKnown().getOrNull()?.validity() ?: 0) + + (if (serviceName.asKnown().isPresent) 1 else 0) + + /** A message delivery log request. */ + class Request + private constructor( + private val body: JsonField, + private val headers: JsonField, + private val host: JsonField, + private val method: JsonField, + private val path: JsonField, + private val query: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("body") @ExcludeMissing body: JsonField = JsonMissing.of(), + @JsonProperty("headers") @ExcludeMissing headers: JsonField = JsonMissing.of(), + @JsonProperty("host") @ExcludeMissing host: JsonField = JsonMissing.of(), + @JsonProperty("method") @ExcludeMissing method: JsonField = JsonMissing.of(), + @JsonProperty("path") @ExcludeMissing path: JsonField = JsonMissing.of(), + @JsonProperty("query") @ExcludeMissing query: JsonField = JsonMissing.of(), + ) : this(body, headers, host, method, path, query, mutableMapOf()) + + /** + * The body content that was sent with the request. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun body(): Optional = body.getOptional("body") + + /** + * The headers that were sent with the request. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * The host to which the request was sent. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun host(): Optional = host.getOptional("host") + + /** + * The HTTP method used for the request. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun method(): Optional = method.getOptional("method") + + /** + * The path of the URL that was requested. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun path(): Optional = path.getOptional("path") + + /** + * The query string of the URL that was requested. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun query(): Optional = query.getOptional("query") + + /** + * Returns the raw JSON value of [body]. + * + * Unlike [body], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("body") @ExcludeMissing fun _body(): JsonField = body + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("headers") @ExcludeMissing fun _headers(): JsonField = headers + + /** + * Returns the raw JSON value of [host]. + * + * Unlike [host], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("host") @ExcludeMissing fun _host(): JsonField = host + + /** + * Returns the raw JSON value of [method]. + * + * Unlike [method], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("method") @ExcludeMissing fun _method(): JsonField = method + + /** + * 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 [query]. + * + * Unlike [query], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("query") @ExcludeMissing fun _query(): JsonField = query + + @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 [Request]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Request]. */ + class Builder internal constructor() { + + private var body: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var host: JsonField = JsonMissing.of() + private var method: JsonField = JsonMissing.of() + private var path: JsonField = JsonMissing.of() + private var query: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(request: Request) = apply { + body = request.body + headers = request.headers + host = request.host + method = request.method + path = request.path + query = request.query + additionalProperties = request.additionalProperties.toMutableMap() + } + + /** The body content that was sent with the request. */ + fun body(body: Body) = body(JsonField.of(body)) + + /** + * Sets [Builder.body] to an arbitrary JSON value. + * + * You should usually call [Builder.body] with a well-typed [Body] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun body(body: JsonField) = apply { this.body = body } + + /** Alias for calling [body] with `Body.ofString(string)`. */ + fun body(string: String) = body(Body.ofString(string)) + + /** Alias for calling [body] with `Body.ofUnionMember1(unionMember1)`. */ + fun body(unionMember1: Body.UnionMember1) = body(Body.ofUnionMember1(unionMember1)) + + /** The headers that were sent with the request. */ + fun headers(headers: Headers?) = headers(JsonField.ofNullable(headers)) + + /** Alias for calling [Builder.headers] with `headers.orElse(null)`. */ + fun headers(headers: Optional) = headers(headers.getOrNull()) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** The host to which the request was sent. */ + fun host(host: String) = host(JsonField.of(host)) + + /** + * Sets [Builder.host] to an arbitrary JSON value. + * + * You should usually call [Builder.host] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun host(host: JsonField) = apply { this.host = host } + + /** The HTTP method used for the request. */ + fun method(method: Method) = method(JsonField.of(method)) + + /** + * Sets [Builder.method] to an arbitrary JSON value. + * + * You should usually call [Builder.method] with a well-typed [Method] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun method(method: JsonField) = apply { this.method = method } + + /** The path of the URL that was requested. */ + 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 } + + /** The query string of the URL that was requested. */ + fun query(query: String?) = query(JsonField.ofNullable(query)) + + /** Alias for calling [Builder.query] with `query.orElse(null)`. */ + fun query(query: Optional) = query(query.getOrNull()) + + /** + * Sets [Builder.query] to an arbitrary JSON value. + * + * You should usually call [Builder.query] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun query(query: JsonField) = apply { this.query = query } + + 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 [Request]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Request = + Request( + body, + headers, + host, + method, + path, + query, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): Request = apply { + if (validated) { + return@apply + } + + body().ifPresent { it.validate() } + headers().ifPresent { it.validate() } + host() + method().ifPresent { it.validate() } + path() + query() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (body.asKnown().getOrNull()?.validity() ?: 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (if (host.asKnown().isPresent) 1 else 0) + + (method.asKnown().getOrNull()?.validity() ?: 0) + + (if (path.asKnown().isPresent) 1 else 0) + + (if (query.asKnown().isPresent) 1 else 0) + + /** The body content that was sent with the request. */ + @JsonDeserialize(using = Body.Deserializer::class) + @JsonSerialize(using = Body.Serializer::class) + class Body + private constructor( + private val string: String? = null, + private val unionMember1: UnionMember1? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun unionMember1(): Optional = Optional.ofNullable(unionMember1) + + fun isString(): Boolean = string != null + + fun isUnionMember1(): Boolean = unionMember1 != null + + fun asString(): String = string.getOrThrow("string") + + fun asUnionMember1(): UnionMember1 = unionMember1.getOrThrow("unionMember1") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + unionMember1 != null -> visitor.visitUnionMember1(unionMember1) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitUnionMember1(unionMember1: UnionMember1) { + unionMember1.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitUnionMember1(unionMember1: UnionMember1) = + unionMember1.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && string == other.string && unionMember1 == other.unionMember1 /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(string, unionMember1) /* spotless:on */ + + override fun toString(): String = + when { + string != null -> "Body{string=$string}" + unionMember1 != null -> "Body{unionMember1=$unionMember1}" + _json != null -> "Body{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Body") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Body(string = string) + + @JvmStatic + fun ofUnionMember1(unionMember1: UnionMember1) = Body(unionMember1 = unionMember1) + } + + /** + * An interface that defines how to map each variant of [Body] to a value of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitUnionMember1(unionMember1: UnionMember1): T + + /** + * Maps an unknown variant of [Body] to a value of type [T]. + * + * An instance of [Body] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws KnockInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw KnockInvalidDataException("Unknown Body: $json") + } + } + + internal class Deserializer : BaseDeserializer(Body::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Body { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Body(unionMember1 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Body(string = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible + // with all the possible variants (e.g. deserializing from array). + 0 -> Body(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the + // first completely valid match, or simply the first match if none are + // completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Body::class) { + + override fun serialize( + value: Body, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.unionMember1 != null -> generator.writeObject(value.unionMember1) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Body") + } + } + } + + class UnionMember1 + @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 [UnionMember1]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember1]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember1: UnionMember1) = apply { + additionalProperties = unionMember1.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 [UnionMember1]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UnionMember1 = UnionMember1(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): UnionMember1 = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is UnionMember1 && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "UnionMember1{additionalProperties=$additionalProperties}" + } + } + + /** The headers that were sent with the request. */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Headers && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + /** The HTTP method used for the request. */ + class Method @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 GET = of("GET") + + @JvmField val POST = of("POST") + + @JvmField val PUT = of("PUT") + + @JvmField val DELETE = of("DELETE") + + @JvmField val PATCH = of("PATCH") + + @JvmStatic fun of(value: String) = Method(JsonField.of(value)) + } + + /** An enum containing [Method]'s known values. */ + enum class Known { + GET, + POST, + PUT, + DELETE, + PATCH, + } + + /** + * An enum containing [Method]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Method] 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 { + GET, + POST, + PUT, + DELETE, + PATCH, + /** + * An enum member indicating that [Method] 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) { + GET -> Value.GET + POST -> Value.POST + PUT -> Value.PUT + DELETE -> Value.DELETE + PATCH -> Value.PATCH + 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 KnockInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + GET -> Known.GET + POST -> Known.POST + PUT -> Known.PUT + DELETE -> Known.DELETE + PATCH -> Known.PATCH + else -> throw KnockInvalidDataException("Unknown Method: $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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + KnockInvalidDataException("Value is not a String") + } + + private var validated: Boolean = false + + fun validate(): Method = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Method && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Request && body == other.body && headers == other.headers && host == other.host && method == other.method && path == other.path && query == other.query && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(body, headers, host, method, path, query, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Request{body=$body, headers=$headers, host=$host, method=$method, path=$path, query=$query, additionalProperties=$additionalProperties}" + } + + /** A message delivery log response. */ + class Response + private constructor( + private val body: JsonField, + private val headers: JsonField, + private val status: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("body") @ExcludeMissing body: JsonField = JsonMissing.of(), + @JsonProperty("headers") @ExcludeMissing headers: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + ) : this(body, headers, status, mutableMapOf()) + + /** + * The body content that was received with the response. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun body(): Optional = body.getOptional("body") + + /** + * The headers that were received with the response. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun headers(): Optional = headers.getOptional("headers") + + /** + * The HTTP status code of the response. + * + * @throws KnockInvalidDataException 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 [body]. + * + * Unlike [body], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("body") @ExcludeMissing fun _body(): JsonField = body + + /** + * Returns the raw JSON value of [headers]. + * + * Unlike [headers], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("headers") @ExcludeMissing fun _headers(): JsonField = headers + + /** + * 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 [Response]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Response]. */ + class Builder internal constructor() { + + private var body: JsonField = JsonMissing.of() + private var headers: JsonField = JsonMissing.of() + private var status: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(response: Response) = apply { + body = response.body + headers = response.headers + status = response.status + additionalProperties = response.additionalProperties.toMutableMap() + } + + /** The body content that was received with the response. */ + fun body(body: Body) = body(JsonField.of(body)) + + /** + * Sets [Builder.body] to an arbitrary JSON value. + * + * You should usually call [Builder.body] with a well-typed [Body] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun body(body: JsonField) = apply { this.body = body } + + /** Alias for calling [body] with `Body.ofString(string)`. */ + fun body(string: String) = body(Body.ofString(string)) + + /** Alias for calling [body] with `Body.ofUnionMember1(unionMember1)`. */ + fun body(unionMember1: Body.UnionMember1) = body(Body.ofUnionMember1(unionMember1)) + + /** The headers that were received with the response. */ + fun headers(headers: Headers?) = headers(JsonField.ofNullable(headers)) + + /** Alias for calling [Builder.headers] with `headers.orElse(null)`. */ + fun headers(headers: Optional) = headers(headers.getOrNull()) + + /** + * Sets [Builder.headers] to an arbitrary JSON value. + * + * You should usually call [Builder.headers] with a well-typed [Headers] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun headers(headers: JsonField) = apply { this.headers = headers } + + /** The HTTP status code of the response. */ + fun status(status: Long) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Long] 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 [Response]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Response = + Response(body, headers, status, additionalProperties.toMutableMap()) + } + + private var validated: Boolean = false + + fun validate(): Response = apply { + if (validated) { + return@apply + } + + body().ifPresent { it.validate() } + headers().ifPresent { it.validate() } + status() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + (body.asKnown().getOrNull()?.validity() ?: 0) + + (headers.asKnown().getOrNull()?.validity() ?: 0) + + (if (status.asKnown().isPresent) 1 else 0) + + /** The body content that was received with the response. */ + @JsonDeserialize(using = Body.Deserializer::class) + @JsonSerialize(using = Body.Serializer::class) + class Body + private constructor( + private val string: String? = null, + private val unionMember1: UnionMember1? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun unionMember1(): Optional = Optional.ofNullable(unionMember1) + + fun isString(): Boolean = string != null + + fun isUnionMember1(): Boolean = unionMember1 != null + + fun asString(): String = string.getOrThrow("string") + + fun asUnionMember1(): UnionMember1 = unionMember1.getOrThrow("unionMember1") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + unionMember1 != null -> visitor.visitUnionMember1(unionMember1) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Body = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitUnionMember1(unionMember1: UnionMember1) { + unionMember1.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + accept( + object : Visitor { + override fun visitString(string: String) = 1 + + override fun visitUnionMember1(unionMember1: UnionMember1) = + unionMember1.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Body && string == other.string && unionMember1 == other.unionMember1 /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(string, unionMember1) /* spotless:on */ + + override fun toString(): String = + when { + string != null -> "Body{string=$string}" + unionMember1 != null -> "Body{unionMember1=$unionMember1}" + _json != null -> "Body{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Body") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Body(string = string) + + @JvmStatic + fun ofUnionMember1(unionMember1: UnionMember1) = Body(unionMember1 = unionMember1) + } + + /** + * An interface that defines how to map each variant of [Body] to a value of type [T]. + */ + interface Visitor { + + fun visitString(string: String): T + + fun visitUnionMember1(unionMember1: UnionMember1): T + + /** + * Maps an unknown variant of [Body] to a value of type [T]. + * + * An instance of [Body] can contain an unknown variant if it was deserialized from + * data that doesn't match any known variant. For example, if the SDK is on an older + * version than the API, then the API may respond with new variants that the SDK is + * unaware of. + * + * @throws KnockInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw KnockInvalidDataException("Unknown Body: $json") + } + } + + internal class Deserializer : BaseDeserializer(Body::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Body { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Body(unionMember1 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Body(string = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible + // with all the possible variants (e.g. deserializing from array). + 0 -> Body(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the + // first completely valid match, or simply the first match if none are + // completely valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Body::class) { + + override fun serialize( + value: Body, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.unionMember1 != null -> generator.writeObject(value.unionMember1) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Body") + } + } + } + + class UnionMember1 + @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 [UnionMember1]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember1]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember1: UnionMember1) = apply { + additionalProperties = unionMember1.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 [UnionMember1]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UnionMember1 = UnionMember1(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): UnionMember1 = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is UnionMember1 && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "UnionMember1{additionalProperties=$additionalProperties}" + } + } + + /** The headers that were received with the response. */ + class Headers + @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 [Headers]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Headers]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(headers: Headers) = apply { + additionalProperties = headers.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 [Headers]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Headers = Headers(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Headers = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Headers && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Headers{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Response && body == other.body && headers == other.headers && status == other.status && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(body, headers, status, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Response{body=$body, headers=$headers, status=$status, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MessageDeliveryLog && id == other.id && _typename == other._typename && environmentId == other.environmentId && insertedAt == other.insertedAt && request == other.request && response == other.response && serviceName == other.serviceName && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, _typename, environmentId, insertedAt, request, response, serviceName, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MessageDeliveryLog{id=$id, _typename=$_typename, environmentId=$environmentId, insertedAt=$insertedAt, request=$request, response=$response, serviceName=$serviceName, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageEvent.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageEvent.kt new file mode 100644 index 00000000..c0cfe0ce --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageEvent.kt @@ -0,0 +1,694 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.messages + +import app.knock.api.core.Enum +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.checkRequired +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +import app.knock.api.models.recipients.RecipientReference +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 + +/** + * A message event. Occurs when a message + * [delivery or engagement status](/send-notifications/message-statuses) changes. + */ +class MessageEvent +private constructor( + private val id: JsonField, + private val _typename: JsonField, + private val insertedAt: JsonField, + private val recipient: JsonField, + private val type: JsonField, + private val data: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("__typename") @ExcludeMissing _typename: JsonField = JsonMissing.of(), + @JsonProperty("inserted_at") + @ExcludeMissing + insertedAt: JsonField = JsonMissing.of(), + @JsonProperty("recipient") + @ExcludeMissing + recipient: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + ) : this(id, _typename, insertedAt, recipient, type, data, mutableMapOf()) + + /** + * The unique identifier for the message event. + * + * @throws KnockInvalidDataException 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 id(): String = id.getRequired("id") + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * Timestamp when the event was created. + * + * @throws KnockInvalidDataException 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 insertedAt(): OffsetDateTime = insertedAt.getRequired("inserted_at") + + /** + * A reference to a recipient, either a user identifier (string) or an object reference (ID, + * collection). + * + * @throws KnockInvalidDataException 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 recipient(): RecipientReference = recipient.getRequired("recipient") + + /** + * The type of event that occurred. + * + * @throws KnockInvalidDataException 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 type(): Type = type.getRequired("type") + + /** + * The data associated with the message event. Only present for some event types. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun data(): Optional = data.getOptional("data") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [insertedAt]. + * + * Unlike [insertedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inserted_at") + @ExcludeMissing + fun _insertedAt(): JsonField = insertedAt + + /** + * Returns the raw JSON value of [recipient]. + * + * Unlike [recipient], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("recipient") + @ExcludeMissing + fun _recipient(): JsonField = recipient + + /** + * 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 [data]. + * + * Unlike [data], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data + + @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 [MessageEvent]. + * + * The following fields are required: + * ```java + * .id() + * ._typename() + * .insertedAt() + * .recipient() + * .type() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessageEvent]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var _typename: JsonField? = null + private var insertedAt: JsonField? = null + private var recipient: JsonField? = null + private var type: JsonField? = null + private var data: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(messageEvent: MessageEvent) = apply { + id = messageEvent.id + _typename = messageEvent._typename + insertedAt = messageEvent.insertedAt + recipient = messageEvent.recipient + type = messageEvent.type + data = messageEvent.data + additionalProperties = messageEvent.additionalProperties.toMutableMap() + } + + /** The unique identifier for the message event. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** Timestamp when the event was created. */ + fun insertedAt(insertedAt: OffsetDateTime) = insertedAt(JsonField.of(insertedAt)) + + /** + * Sets [Builder.insertedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.insertedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun insertedAt(insertedAt: JsonField) = apply { + this.insertedAt = insertedAt + } + + /** + * A reference to a recipient, either a user identifier (string) or an object reference (ID, + * collection). + */ + fun recipient(recipient: RecipientReference) = recipient(JsonField.of(recipient)) + + /** + * Sets [Builder.recipient] to an arbitrary JSON value. + * + * You should usually call [Builder.recipient] with a well-typed [RecipientReference] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun recipient(recipient: JsonField) = apply { + this.recipient = recipient + } + + /** Alias for calling [recipient] with `RecipientReference.ofUser(user)`. */ + fun recipient(user: String) = recipient(RecipientReference.ofUser(user)) + + /** + * Alias for calling [recipient] with + * `RecipientReference.ofObjectReference(objectReference)`. + */ + fun recipient(objectReference: RecipientReference.ObjectReference) = + recipient(RecipientReference.ofObjectReference(objectReference)) + + /** The type of event that occurred. */ + 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 } + + /** The data associated with the message event. Only present for some event types. */ + fun data(data: Data?) = data(JsonField.ofNullable(data)) + + /** Alias for calling [Builder.data] with `data.orElse(null)`. */ + fun data(data: Optional) = data(data.getOrNull()) + + /** + * 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 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 [MessageEvent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * ._typename() + * .insertedAt() + * .recipient() + * .type() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MessageEvent = + MessageEvent( + checkRequired("id", id), + checkRequired("_typename", _typename), + checkRequired("insertedAt", insertedAt), + checkRequired("recipient", recipient), + checkRequired("type", type), + data, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): MessageEvent = apply { + if (validated) { + return@apply + } + + id() + _typename() + insertedAt() + recipient().validate() + type().validate() + data().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (id.asKnown().isPresent) 1 else 0) + + (if (_typename.asKnown().isPresent) 1 else 0) + + (if (insertedAt.asKnown().isPresent) 1 else 0) + + (recipient.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + (data.asKnown().getOrNull()?.validity() ?: 0) + + /** The type of event that occurred. */ + 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 MESSAGE_ARCHIVED = of("message.archived") + + @JvmField val MESSAGE_BOUNCED = of("message.bounced") + + @JvmField val MESSAGE_DELIVERED = of("message.delivered") + + @JvmField val MESSAGE_DELIVERY_ATTEMPTED = of("message.delivery_attempted") + + @JvmField val MESSAGE_INTERACTED = of("message.interacted") + + @JvmField val MESSAGE_LINK_CLICKED = of("message.link_clicked") + + @JvmField val MESSAGE_NOT_SENT = of("message.not_sent") + + @JvmField val MESSAGE_QUEUED = of("message.queued") + + @JvmField val MESSAGE_READ = of("message.read") + + @JvmField val MESSAGE_SEEN = of("message.seen") + + @JvmField val MESSAGE_SENT = of("message.sent") + + @JvmField val MESSAGE_UNARCHIVED = of("message.unarchived") + + @JvmField val MESSAGE_UNDELIVERED = of("message.undelivered") + + @JvmField val MESSAGE_UNREAD = of("message.unread") + + @JvmField val MESSAGE_UNSEEN = of("message.unseen") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + MESSAGE_ARCHIVED, + MESSAGE_BOUNCED, + MESSAGE_DELIVERED, + MESSAGE_DELIVERY_ATTEMPTED, + MESSAGE_INTERACTED, + MESSAGE_LINK_CLICKED, + MESSAGE_NOT_SENT, + MESSAGE_QUEUED, + MESSAGE_READ, + MESSAGE_SEEN, + MESSAGE_SENT, + MESSAGE_UNARCHIVED, + MESSAGE_UNDELIVERED, + MESSAGE_UNREAD, + MESSAGE_UNSEEN, + } + + /** + * 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 { + MESSAGE_ARCHIVED, + MESSAGE_BOUNCED, + MESSAGE_DELIVERED, + MESSAGE_DELIVERY_ATTEMPTED, + MESSAGE_INTERACTED, + MESSAGE_LINK_CLICKED, + MESSAGE_NOT_SENT, + MESSAGE_QUEUED, + MESSAGE_READ, + MESSAGE_SEEN, + MESSAGE_SENT, + MESSAGE_UNARCHIVED, + MESSAGE_UNDELIVERED, + MESSAGE_UNREAD, + MESSAGE_UNSEEN, + /** 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) { + MESSAGE_ARCHIVED -> Value.MESSAGE_ARCHIVED + MESSAGE_BOUNCED -> Value.MESSAGE_BOUNCED + MESSAGE_DELIVERED -> Value.MESSAGE_DELIVERED + MESSAGE_DELIVERY_ATTEMPTED -> Value.MESSAGE_DELIVERY_ATTEMPTED + MESSAGE_INTERACTED -> Value.MESSAGE_INTERACTED + MESSAGE_LINK_CLICKED -> Value.MESSAGE_LINK_CLICKED + MESSAGE_NOT_SENT -> Value.MESSAGE_NOT_SENT + MESSAGE_QUEUED -> Value.MESSAGE_QUEUED + MESSAGE_READ -> Value.MESSAGE_READ + MESSAGE_SEEN -> Value.MESSAGE_SEEN + MESSAGE_SENT -> Value.MESSAGE_SENT + MESSAGE_UNARCHIVED -> Value.MESSAGE_UNARCHIVED + MESSAGE_UNDELIVERED -> Value.MESSAGE_UNDELIVERED + MESSAGE_UNREAD -> Value.MESSAGE_UNREAD + MESSAGE_UNSEEN -> Value.MESSAGE_UNSEEN + 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 KnockInvalidDataException if this class instance's value is a not a known member. + */ + fun known(): Known = + when (this) { + MESSAGE_ARCHIVED -> Known.MESSAGE_ARCHIVED + MESSAGE_BOUNCED -> Known.MESSAGE_BOUNCED + MESSAGE_DELIVERED -> Known.MESSAGE_DELIVERED + MESSAGE_DELIVERY_ATTEMPTED -> Known.MESSAGE_DELIVERY_ATTEMPTED + MESSAGE_INTERACTED -> Known.MESSAGE_INTERACTED + MESSAGE_LINK_CLICKED -> Known.MESSAGE_LINK_CLICKED + MESSAGE_NOT_SENT -> Known.MESSAGE_NOT_SENT + MESSAGE_QUEUED -> Known.MESSAGE_QUEUED + MESSAGE_READ -> Known.MESSAGE_READ + MESSAGE_SEEN -> Known.MESSAGE_SEEN + MESSAGE_SENT -> Known.MESSAGE_SENT + MESSAGE_UNARCHIVED -> Known.MESSAGE_UNARCHIVED + MESSAGE_UNDELIVERED -> Known.MESSAGE_UNDELIVERED + MESSAGE_UNREAD -> Known.MESSAGE_UNREAD + MESSAGE_UNSEEN -> Known.MESSAGE_UNSEEN + else -> throw KnockInvalidDataException("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 KnockInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { KnockInvalidDataException("Value is not a String") } + + private var validated: Boolean = false + + fun validate(): Type = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Type && value == other.value /* spotless:on */ + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } + + /** The data associated with the message event. Only present for some event types. */ + class Data + @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 [Data]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Data]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(data: Data) = apply { + additionalProperties = data.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 [Data]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Data = Data(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is Data && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "Data{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MessageEvent && id == other.id && _typename == other._typename && insertedAt == other.insertedAt && recipient == other.recipient && type == other.type && data == other.data && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(id, _typename, insertedAt, recipient, type, data, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MessageEvent{id=$id, _typename=$_typename, insertedAt=$insertedAt, recipient=$recipient, type=$type, data=$data, additionalProperties=$additionalProperties}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageGetContentParams.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageGetContentParams.kt new file mode 100644 index 00000000..38720306 --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageGetContentParams.kt @@ -0,0 +1,191 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.messages + +import app.knock.api.core.Params +import app.knock.api.core.http.Headers +import app.knock.api.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Returns the fully rendered contents of a message, where the response depends on which channel the + * message was sent through. + */ +class MessageGetContentParams +private constructor( + private val messageId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun messageId(): Optional = Optional.ofNullable(messageId) + + fun _additionalHeaders(): Headers = additionalHeaders + + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): MessageGetContentParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [MessageGetContentParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessageGetContentParams]. */ + class Builder internal constructor() { + + private var messageId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(messageGetContentParams: MessageGetContentParams) = apply { + messageId = messageGetContentParams.messageId + additionalHeaders = messageGetContentParams.additionalHeaders.toBuilder() + additionalQueryParams = messageGetContentParams.additionalQueryParams.toBuilder() + } + + fun messageId(messageId: String?) = apply { this.messageId = messageId } + + /** Alias for calling [Builder.messageId] with `messageId.orElse(null)`. */ + fun messageId(messageId: Optional) = messageId(messageId.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 [MessageGetContentParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): MessageGetContentParams = + MessageGetContentParams( + messageId, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> messageId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MessageGetContentParams && messageId == other.messageId && additionalHeaders == other.additionalHeaders && additionalQueryParams == other.additionalQueryParams /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(messageId, additionalHeaders, additionalQueryParams) /* spotless:on */ + + override fun toString() = + "MessageGetContentParams{messageId=$messageId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageGetContentResponse.kt b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageGetContentResponse.kt new file mode 100644 index 00000000..34bd2d0d --- /dev/null +++ b/knock-java-core/src/main/kotlin/app/knock/api/models/messages/MessageGetContentResponse.kt @@ -0,0 +1,4602 @@ +// File generated from our OpenAPI spec by Stainless. + +package app.knock.api.models.messages + +import app.knock.api.core.BaseDeserializer +import app.knock.api.core.BaseSerializer +import app.knock.api.core.Enum +import app.knock.api.core.ExcludeMissing +import app.knock.api.core.JsonField +import app.knock.api.core.JsonMissing +import app.knock.api.core.JsonValue +import app.knock.api.core.allMaxBy +import app.knock.api.core.checkKnown +import app.knock.api.core.checkRequired +import app.knock.api.core.getOrThrow +import app.knock.api.core.toImmutable +import app.knock.api.errors.KnockInvalidDataException +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 com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +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.module.kotlin.jacksonTypeRef +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** The content of a message. */ +class MessageGetContentResponse +private constructor( + private val _typename: JsonField, + private val data: JsonField, + private val insertedAt: JsonField, + private val messageId: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("__typename") @ExcludeMissing _typename: JsonField = JsonMissing.of(), + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + @JsonProperty("inserted_at") + @ExcludeMissing + insertedAt: JsonField = JsonMissing.of(), + @JsonProperty("message_id") @ExcludeMissing messageId: JsonField = JsonMissing.of(), + ) : this(_typename, data, insertedAt, messageId, mutableMapOf()) + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * Content data specific to the channel type. + * + * @throws KnockInvalidDataException 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 data(): Data = data.getRequired("data") + + /** + * Timestamp when the message content was created. + * + * @throws KnockInvalidDataException 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 insertedAt(): OffsetDateTime = insertedAt.getRequired("inserted_at") + + /** + * The unique identifier for the message content. + * + * @throws KnockInvalidDataException 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 messageId(): String = messageId.getRequired("message_id") + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("__typename") @ExcludeMissing fun __typename(): JsonField = _typename + + /** + * 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 [insertedAt]. + * + * Unlike [insertedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("inserted_at") + @ExcludeMissing + fun _insertedAt(): JsonField = insertedAt + + /** + * 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 + + @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 [MessageGetContentResponse]. + * + * The following fields are required: + * ```java + * ._typename() + * .data() + * .insertedAt() + * .messageId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessageGetContentResponse]. */ + class Builder internal constructor() { + + private var _typename: JsonField? = null + private var data: JsonField? = null + private var insertedAt: JsonField? = null + private var messageId: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(messageGetContentResponse: MessageGetContentResponse) = apply { + _typename = messageGetContentResponse._typename + data = messageGetContentResponse.data + insertedAt = messageGetContentResponse.insertedAt + messageId = messageGetContentResponse.messageId + additionalProperties = messageGetContentResponse.additionalProperties.toMutableMap() + } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** Content data specific to the channel type. */ + 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 } + + /** Alias for calling [data] with `Data.ofMessageEmailContent(messageEmailContent)`. */ + fun data(messageEmailContent: Data.MessageEmailContent) = + data(Data.ofMessageEmailContent(messageEmailContent)) + + /** Alias for calling [data] with `Data.ofMessageSmsContent(messageSmsContent)`. */ + fun data(messageSmsContent: Data.MessageSmsContent) = + data(Data.ofMessageSmsContent(messageSmsContent)) + + /** Alias for calling [data] with `Data.ofMessagePushContent(messagePushContent)`. */ + fun data(messagePushContent: Data.MessagePushContent) = + data(Data.ofMessagePushContent(messagePushContent)) + + /** Alias for calling [data] with `Data.ofMessageChatContent(messageChatContent)`. */ + fun data(messageChatContent: Data.MessageChatContent) = + data(Data.ofMessageChatContent(messageChatContent)) + + /** + * Alias for calling [data] with `Data.ofMessageInAppFeedContent(messageInAppFeedContent)`. + */ + fun data(messageInAppFeedContent: Data.MessageInAppFeedContent) = + data(Data.ofMessageInAppFeedContent(messageInAppFeedContent)) + + /** Timestamp when the message content was created. */ + fun insertedAt(insertedAt: OffsetDateTime) = insertedAt(JsonField.of(insertedAt)) + + /** + * Sets [Builder.insertedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.insertedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun insertedAt(insertedAt: JsonField) = apply { + this.insertedAt = insertedAt + } + + /** The unique identifier for the message content. */ + 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 } + + 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 [MessageGetContentResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * ._typename() + * .data() + * .insertedAt() + * .messageId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MessageGetContentResponse = + MessageGetContentResponse( + checkRequired("_typename", _typename), + checkRequired("data", data), + checkRequired("insertedAt", insertedAt), + checkRequired("messageId", messageId), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): MessageGetContentResponse = apply { + if (validated) { + return@apply + } + + _typename() + data().validate() + insertedAt() + messageId() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (_typename.asKnown().isPresent) 1 else 0) + + (data.asKnown().getOrNull()?.validity() ?: 0) + + (if (insertedAt.asKnown().isPresent) 1 else 0) + + (if (messageId.asKnown().isPresent) 1 else 0) + + /** Content data specific to the channel type. */ + @JsonDeserialize(using = Data.Deserializer::class) + @JsonSerialize(using = Data.Serializer::class) + class Data + private constructor( + private val messageEmailContent: MessageEmailContent? = null, + private val messageSmsContent: MessageSmsContent? = null, + private val messagePushContent: MessagePushContent? = null, + private val messageChatContent: MessageChatContent? = null, + private val messageInAppFeedContent: MessageInAppFeedContent? = null, + private val _json: JsonValue? = null, + ) { + + /** The content of an email message. */ + fun messageEmailContent(): Optional = + Optional.ofNullable(messageEmailContent) + + /** The content of an SMS message. */ + fun messageSmsContent(): Optional = + Optional.ofNullable(messageSmsContent) + + /** The content of a push notification. */ + fun messagePushContent(): Optional = + Optional.ofNullable(messagePushContent) + + /** The content of a chat message. */ + fun messageChatContent(): Optional = + Optional.ofNullable(messageChatContent) + + /** The content of an in-app feed message. */ + fun messageInAppFeedContent(): Optional = + Optional.ofNullable(messageInAppFeedContent) + + fun isMessageEmailContent(): Boolean = messageEmailContent != null + + fun isMessageSmsContent(): Boolean = messageSmsContent != null + + fun isMessagePushContent(): Boolean = messagePushContent != null + + fun isMessageChatContent(): Boolean = messageChatContent != null + + fun isMessageInAppFeedContent(): Boolean = messageInAppFeedContent != null + + /** The content of an email message. */ + fun asMessageEmailContent(): MessageEmailContent = + messageEmailContent.getOrThrow("messageEmailContent") + + /** The content of an SMS message. */ + fun asMessageSmsContent(): MessageSmsContent = + messageSmsContent.getOrThrow("messageSmsContent") + + /** The content of a push notification. */ + fun asMessagePushContent(): MessagePushContent = + messagePushContent.getOrThrow("messagePushContent") + + /** The content of a chat message. */ + fun asMessageChatContent(): MessageChatContent = + messageChatContent.getOrThrow("messageChatContent") + + /** The content of an in-app feed message. */ + fun asMessageInAppFeedContent(): MessageInAppFeedContent = + messageInAppFeedContent.getOrThrow("messageInAppFeedContent") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + messageEmailContent != null -> visitor.visitMessageEmailContent(messageEmailContent) + messageSmsContent != null -> visitor.visitMessageSmsContent(messageSmsContent) + messagePushContent != null -> visitor.visitMessagePushContent(messagePushContent) + messageChatContent != null -> visitor.visitMessageChatContent(messageChatContent) + messageInAppFeedContent != null -> + visitor.visitMessageInAppFeedContent(messageInAppFeedContent) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Data = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitMessageEmailContent( + messageEmailContent: MessageEmailContent + ) { + messageEmailContent.validate() + } + + override fun visitMessageSmsContent(messageSmsContent: MessageSmsContent) { + messageSmsContent.validate() + } + + override fun visitMessagePushContent(messagePushContent: MessagePushContent) { + messagePushContent.validate() + } + + override fun visitMessageChatContent(messageChatContent: MessageChatContent) { + messageChatContent.validate() + } + + override fun visitMessageInAppFeedContent( + messageInAppFeedContent: MessageInAppFeedContent + ) { + messageInAppFeedContent.validate() + } + } + ) + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 = + accept( + object : Visitor { + override fun visitMessageEmailContent( + messageEmailContent: MessageEmailContent + ) = messageEmailContent.validity() + + override fun visitMessageSmsContent(messageSmsContent: MessageSmsContent) = + messageSmsContent.validity() + + override fun visitMessagePushContent(messagePushContent: MessagePushContent) = + messagePushContent.validity() + + override fun visitMessageChatContent(messageChatContent: MessageChatContent) = + messageChatContent.validity() + + override fun visitMessageInAppFeedContent( + messageInAppFeedContent: MessageInAppFeedContent + ) = messageInAppFeedContent.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is Data && messageEmailContent == other.messageEmailContent && messageSmsContent == other.messageSmsContent && messagePushContent == other.messagePushContent && messageChatContent == other.messageChatContent && messageInAppFeedContent == other.messageInAppFeedContent /* spotless:on */ + } + + override fun hashCode(): Int = /* spotless:off */ Objects.hash(messageEmailContent, messageSmsContent, messagePushContent, messageChatContent, messageInAppFeedContent) /* spotless:on */ + + override fun toString(): String = + when { + messageEmailContent != null -> "Data{messageEmailContent=$messageEmailContent}" + messageSmsContent != null -> "Data{messageSmsContent=$messageSmsContent}" + messagePushContent != null -> "Data{messagePushContent=$messagePushContent}" + messageChatContent != null -> "Data{messageChatContent=$messageChatContent}" + messageInAppFeedContent != null -> + "Data{messageInAppFeedContent=$messageInAppFeedContent}" + _json != null -> "Data{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Data") + } + + companion object { + + /** The content of an email message. */ + @JvmStatic + fun ofMessageEmailContent(messageEmailContent: MessageEmailContent) = + Data(messageEmailContent = messageEmailContent) + + /** The content of an SMS message. */ + @JvmStatic + fun ofMessageSmsContent(messageSmsContent: MessageSmsContent) = + Data(messageSmsContent = messageSmsContent) + + /** The content of a push notification. */ + @JvmStatic + fun ofMessagePushContent(messagePushContent: MessagePushContent) = + Data(messagePushContent = messagePushContent) + + /** The content of a chat message. */ + @JvmStatic + fun ofMessageChatContent(messageChatContent: MessageChatContent) = + Data(messageChatContent = messageChatContent) + + /** The content of an in-app feed message. */ + @JvmStatic + fun ofMessageInAppFeedContent(messageInAppFeedContent: MessageInAppFeedContent) = + Data(messageInAppFeedContent = messageInAppFeedContent) + } + + /** An interface that defines how to map each variant of [Data] to a value of type [T]. */ + interface Visitor { + + /** The content of an email message. */ + fun visitMessageEmailContent(messageEmailContent: MessageEmailContent): T + + /** The content of an SMS message. */ + fun visitMessageSmsContent(messageSmsContent: MessageSmsContent): T + + /** The content of a push notification. */ + fun visitMessagePushContent(messagePushContent: MessagePushContent): T + + /** The content of a chat message. */ + fun visitMessageChatContent(messageChatContent: MessageChatContent): T + + /** The content of an in-app feed message. */ + fun visitMessageInAppFeedContent(messageInAppFeedContent: MessageInAppFeedContent): T + + /** + * Maps an unknown variant of [Data] to a value of type [T]. + * + * An instance of [Data] can contain an unknown variant if it was deserialized from data + * that doesn't match any known variant. For example, if the SDK is on an older version + * than the API, then the API may respond with new variants that the SDK is unaware of. + * + * @throws KnockInvalidDataException in the default implementation. + */ + fun unknown(json: JsonValue?): T { + throw KnockInvalidDataException("Unknown Data: $json") + } + } + + internal class Deserializer : BaseDeserializer(Data::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Data { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Data(messageEmailContent = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Data(messageSmsContent = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Data(messagePushContent = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Data(messageChatContent = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Data(messageInAppFeedContent = it, _json = json) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from boolean). + 0 -> Data(_json = json) + 1 -> bestMatches.single() + // If there's more than one match with the highest validity, then use the first + // completely valid match, or simply the first match if none are completely + // valid. + else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first() + } + } + } + + internal class Serializer : BaseSerializer(Data::class) { + + override fun serialize( + value: Data, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.messageEmailContent != null -> + generator.writeObject(value.messageEmailContent) + value.messageSmsContent != null -> + generator.writeObject(value.messageSmsContent) + value.messagePushContent != null -> + generator.writeObject(value.messagePushContent) + value.messageChatContent != null -> + generator.writeObject(value.messageChatContent) + value.messageInAppFeedContent != null -> + generator.writeObject(value.messageInAppFeedContent) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Data") + } + } + } + + /** The content of an email message. */ + class MessageEmailContent + private constructor( + private val _typename: JsonField, + private val from: JsonField, + private val htmlBody: JsonField, + private val subjectLine: JsonField, + private val textBody: JsonField, + private val to: JsonField, + private val bcc: JsonField, + private val cc: JsonField, + private val replyTo: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("__typename") + @ExcludeMissing + _typename: JsonField = JsonMissing.of(), + @JsonProperty("from") @ExcludeMissing from: JsonField = JsonMissing.of(), + @JsonProperty("html_body") + @ExcludeMissing + htmlBody: JsonField = JsonMissing.of(), + @JsonProperty("subject_line") + @ExcludeMissing + subjectLine: JsonField = JsonMissing.of(), + @JsonProperty("text_body") + @ExcludeMissing + textBody: JsonField = JsonMissing.of(), + @JsonProperty("to") @ExcludeMissing to: JsonField = JsonMissing.of(), + @JsonProperty("bcc") @ExcludeMissing bcc: JsonField = JsonMissing.of(), + @JsonProperty("cc") @ExcludeMissing cc: JsonField = JsonMissing.of(), + @JsonProperty("reply_to") + @ExcludeMissing + replyTo: JsonField = JsonMissing.of(), + ) : this( + _typename, + from, + htmlBody, + subjectLine, + textBody, + to, + bcc, + cc, + replyTo, + mutableMapOf(), + ) + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * The sender's email address. + * + * @throws KnockInvalidDataException 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 from(): String = from.getRequired("from") + + /** + * The HTML body of the email message. + * + * @throws KnockInvalidDataException 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 htmlBody(): String = htmlBody.getRequired("html_body") + + /** + * The subject line of the email message. + * + * @throws KnockInvalidDataException 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 subjectLine(): String = subjectLine.getRequired("subject_line") + + /** + * The text body of the email message. + * + * @throws KnockInvalidDataException 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 textBody(): String = textBody.getRequired("text_body") + + /** + * The recipient's email address. + * + * @throws KnockInvalidDataException 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 to(): String = to.getRequired("to") + + /** + * The BCC email addresses. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun bcc(): Optional = bcc.getOptional("bcc") + + /** + * The CC email addresses. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun cc(): Optional = cc.getOptional("cc") + + /** + * The reply-to email address. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun replyTo(): Optional = replyTo.getOptional("reply_to") + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("__typename") + @ExcludeMissing + fun __typename(): JsonField = _typename + + /** + * 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 [htmlBody]. + * + * Unlike [htmlBody], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("html_body") @ExcludeMissing fun _htmlBody(): JsonField = htmlBody + + /** + * Returns the raw JSON value of [subjectLine]. + * + * Unlike [subjectLine], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("subject_line") + @ExcludeMissing + fun _subjectLine(): JsonField = subjectLine + + /** + * Returns the raw JSON value of [textBody]. + * + * Unlike [textBody], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("text_body") @ExcludeMissing fun _textBody(): JsonField = textBody + + /** + * 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 + + /** + * Returns the raw JSON value of [bcc]. + * + * Unlike [bcc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bcc") @ExcludeMissing fun _bcc(): JsonField = bcc + + /** + * Returns the raw JSON value of [cc]. + * + * Unlike [cc], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("cc") @ExcludeMissing fun _cc(): JsonField = cc + + /** + * Returns the raw JSON value of [replyTo]. + * + * Unlike [replyTo], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("reply_to") @ExcludeMissing fun _replyTo(): JsonField = replyTo + + @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 [MessageEmailContent]. + * + * The following fields are required: + * ```java + * ._typename() + * .from() + * .htmlBody() + * .subjectLine() + * .textBody() + * .to() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessageEmailContent]. */ + class Builder internal constructor() { + + private var _typename: JsonField? = null + private var from: JsonField? = null + private var htmlBody: JsonField? = null + private var subjectLine: JsonField? = null + private var textBody: JsonField? = null + private var to: JsonField? = null + private var bcc: JsonField = JsonMissing.of() + private var cc: JsonField = JsonMissing.of() + private var replyTo: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(messageEmailContent: MessageEmailContent) = apply { + _typename = messageEmailContent._typename + from = messageEmailContent.from + htmlBody = messageEmailContent.htmlBody + subjectLine = messageEmailContent.subjectLine + textBody = messageEmailContent.textBody + to = messageEmailContent.to + bcc = messageEmailContent.bcc + cc = messageEmailContent.cc + replyTo = messageEmailContent.replyTo + additionalProperties = messageEmailContent.additionalProperties.toMutableMap() + } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** The sender's email address. */ + fun from(from: String) = from(JsonField.of(from)) + + /** + * Sets [Builder.from] to an arbitrary JSON value. + * + * You should usually call [Builder.from] with a well-typed [String] 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 } + + /** The HTML body of the email message. */ + fun htmlBody(htmlBody: String) = htmlBody(JsonField.of(htmlBody)) + + /** + * Sets [Builder.htmlBody] to an arbitrary JSON value. + * + * You should usually call [Builder.htmlBody] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun htmlBody(htmlBody: JsonField) = apply { this.htmlBody = htmlBody } + + /** The subject line of the email message. */ + fun subjectLine(subjectLine: String) = subjectLine(JsonField.of(subjectLine)) + + /** + * Sets [Builder.subjectLine] to an arbitrary JSON value. + * + * You should usually call [Builder.subjectLine] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun subjectLine(subjectLine: JsonField) = apply { + this.subjectLine = subjectLine + } + + /** The text body of the email message. */ + fun textBody(textBody: String) = textBody(JsonField.of(textBody)) + + /** + * Sets [Builder.textBody] to an arbitrary JSON value. + * + * You should usually call [Builder.textBody] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun textBody(textBody: JsonField) = apply { this.textBody = textBody } + + /** The recipient's email address. */ + fun to(to: String) = to(JsonField.of(to)) + + /** + * Sets [Builder.to] to an arbitrary JSON value. + * + * You should usually call [Builder.to] with a well-typed [String] 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 } + + /** The BCC email addresses. */ + fun bcc(bcc: String?) = bcc(JsonField.ofNullable(bcc)) + + /** Alias for calling [Builder.bcc] with `bcc.orElse(null)`. */ + fun bcc(bcc: Optional) = bcc(bcc.getOrNull()) + + /** + * Sets [Builder.bcc] to an arbitrary JSON value. + * + * You should usually call [Builder.bcc] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun bcc(bcc: JsonField) = apply { this.bcc = bcc } + + /** The CC email addresses. */ + fun cc(cc: String?) = cc(JsonField.ofNullable(cc)) + + /** Alias for calling [Builder.cc] with `cc.orElse(null)`. */ + fun cc(cc: Optional) = cc(cc.getOrNull()) + + /** + * Sets [Builder.cc] to an arbitrary JSON value. + * + * You should usually call [Builder.cc] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cc(cc: JsonField) = apply { this.cc = cc } + + /** The reply-to email address. */ + fun replyTo(replyTo: String?) = replyTo(JsonField.ofNullable(replyTo)) + + /** Alias for calling [Builder.replyTo] with `replyTo.orElse(null)`. */ + fun replyTo(replyTo: Optional) = replyTo(replyTo.getOrNull()) + + /** + * Sets [Builder.replyTo] to an arbitrary JSON value. + * + * You should usually call [Builder.replyTo] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun replyTo(replyTo: JsonField) = apply { this.replyTo = replyTo } + + 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 [MessageEmailContent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * ._typename() + * .from() + * .htmlBody() + * .subjectLine() + * .textBody() + * .to() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MessageEmailContent = + MessageEmailContent( + checkRequired("_typename", _typename), + checkRequired("from", from), + checkRequired("htmlBody", htmlBody), + checkRequired("subjectLine", subjectLine), + checkRequired("textBody", textBody), + checkRequired("to", to), + bcc, + cc, + replyTo, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): MessageEmailContent = apply { + if (validated) { + return@apply + } + + _typename() + from() + htmlBody() + subjectLine() + textBody() + to() + bcc() + cc() + replyTo() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (_typename.asKnown().isPresent) 1 else 0) + + (if (from.asKnown().isPresent) 1 else 0) + + (if (htmlBody.asKnown().isPresent) 1 else 0) + + (if (subjectLine.asKnown().isPresent) 1 else 0) + + (if (textBody.asKnown().isPresent) 1 else 0) + + (if (to.asKnown().isPresent) 1 else 0) + + (if (bcc.asKnown().isPresent) 1 else 0) + + (if (cc.asKnown().isPresent) 1 else 0) + + (if (replyTo.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MessageEmailContent && _typename == other._typename && from == other.from && htmlBody == other.htmlBody && subjectLine == other.subjectLine && textBody == other.textBody && to == other.to && bcc == other.bcc && cc == other.cc && replyTo == other.replyTo && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(_typename, from, htmlBody, subjectLine, textBody, to, bcc, cc, replyTo, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MessageEmailContent{_typename=$_typename, from=$from, htmlBody=$htmlBody, subjectLine=$subjectLine, textBody=$textBody, to=$to, bcc=$bcc, cc=$cc, replyTo=$replyTo, additionalProperties=$additionalProperties}" + } + + /** The content of an SMS message. */ + class MessageSmsContent + private constructor( + private val _typename: JsonField, + private val body: JsonField, + private val to: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("__typename") + @ExcludeMissing + _typename: JsonField = JsonMissing.of(), + @JsonProperty("body") @ExcludeMissing body: JsonField = JsonMissing.of(), + @JsonProperty("to") @ExcludeMissing to: JsonField = JsonMissing.of(), + ) : this(_typename, body, to, mutableMapOf()) + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * The content body of the SMS message. + * + * @throws KnockInvalidDataException 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 body(): String = body.getRequired("body") + + /** + * The phone number the SMS was sent to. + * + * @throws KnockInvalidDataException 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 to(): String = to.getRequired("to") + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("__typename") + @ExcludeMissing + fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [body]. + * + * Unlike [body], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("body") @ExcludeMissing fun _body(): JsonField = body + + /** + * 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 [MessageSmsContent]. + * + * The following fields are required: + * ```java + * ._typename() + * .body() + * .to() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessageSmsContent]. */ + class Builder internal constructor() { + + private var _typename: JsonField? = null + private var body: JsonField? = null + private var to: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(messageSmsContent: MessageSmsContent) = apply { + _typename = messageSmsContent._typename + body = messageSmsContent.body + to = messageSmsContent.to + additionalProperties = messageSmsContent.additionalProperties.toMutableMap() + } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** The content body of the SMS message. */ + fun body(body: String) = body(JsonField.of(body)) + + /** + * Sets [Builder.body] to an arbitrary JSON value. + * + * You should usually call [Builder.body] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun body(body: JsonField) = apply { this.body = body } + + /** The phone number the SMS was sent to. */ + fun to(to: String) = to(JsonField.of(to)) + + /** + * Sets [Builder.to] to an arbitrary JSON value. + * + * You should usually call [Builder.to] with a well-typed [String] 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 [MessageSmsContent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * ._typename() + * .body() + * .to() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MessageSmsContent = + MessageSmsContent( + checkRequired("_typename", _typename), + checkRequired("body", body), + checkRequired("to", to), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): MessageSmsContent = apply { + if (validated) { + return@apply + } + + _typename() + body() + to() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (_typename.asKnown().isPresent) 1 else 0) + + (if (body.asKnown().isPresent) 1 else 0) + + (if (to.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MessageSmsContent && _typename == other._typename && body == other.body && to == other.to && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(_typename, body, to, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MessageSmsContent{_typename=$_typename, body=$body, to=$to, additionalProperties=$additionalProperties}" + } + + /** The content of a push notification. */ + class MessagePushContent + private constructor( + private val token: JsonField, + private val _typename: JsonField, + private val body: JsonField, + private val title: JsonField, + private val data: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("token") @ExcludeMissing token: JsonField = JsonMissing.of(), + @JsonProperty("__typename") + @ExcludeMissing + _typename: JsonField = JsonMissing.of(), + @JsonProperty("body") @ExcludeMissing body: JsonField = JsonMissing.of(), + @JsonProperty("title") @ExcludeMissing title: JsonField = JsonMissing.of(), + @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(), + ) : this(token, _typename, body, title, data, mutableMapOf()) + + /** + * The device token to send the push notification to. + * + * @throws KnockInvalidDataException 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 token(): String = token.getRequired("token") + + /** + * The typename of the schema. + * + * @throws KnockInvalidDataException 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 _typename(): String = _typename.getRequired("__typename") + + /** + * The content body of the push notification. + * + * @throws KnockInvalidDataException 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 body(): String = body.getRequired("body") + + /** + * The title of the push notification. + * + * @throws KnockInvalidDataException 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 title(): String = title.getRequired("title") + + /** + * Additional data payload for the push notification. + * + * @throws KnockInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun data(): Optional = data.getOptional("data") + + /** + * Returns the raw JSON value of [token]. + * + * Unlike [token], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("token") @ExcludeMissing fun _token(): JsonField = token + + /** + * Returns the raw JSON value of [_typename]. + * + * Unlike [_typename], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("__typename") + @ExcludeMissing + fun __typename(): JsonField = _typename + + /** + * Returns the raw JSON value of [body]. + * + * Unlike [body], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("body") @ExcludeMissing fun _body(): JsonField = body + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + /** + * 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 + + @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 [MessagePushContent]. + * + * The following fields are required: + * ```java + * .token() + * ._typename() + * .body() + * .title() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [MessagePushContent]. */ + class Builder internal constructor() { + + private var token: JsonField? = null + private var _typename: JsonField? = null + private var body: JsonField? = null + private var title: JsonField? = null + private var data: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(messagePushContent: MessagePushContent) = apply { + token = messagePushContent.token + _typename = messagePushContent._typename + body = messagePushContent.body + title = messagePushContent.title + data = messagePushContent.data + additionalProperties = messagePushContent.additionalProperties.toMutableMap() + } + + /** The device token to send the push notification to. */ + fun token(token: String) = token(JsonField.of(token)) + + /** + * Sets [Builder.token] to an arbitrary JSON value. + * + * You should usually call [Builder.token] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun token(token: JsonField) = apply { this.token = token } + + /** The typename of the schema. */ + fun _typename(_typename: String) = _typename(JsonField.of(_typename)) + + /** + * Sets [Builder._typename] to an arbitrary JSON value. + * + * You should usually call [Builder._typename] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun _typename(_typename: JsonField) = apply { this._typename = _typename } + + /** The content body of the push notification. */ + fun body(body: String) = body(JsonField.of(body)) + + /** + * Sets [Builder.body] to an arbitrary JSON value. + * + * You should usually call [Builder.body] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun body(body: JsonField) = apply { this.body = body } + + /** The title of the push notification. */ + fun title(title: String) = title(JsonField.of(title)) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + /** Additional data payload for the push notification. */ + fun data(data: InnerData?) = data(JsonField.ofNullable(data)) + + /** Alias for calling [Builder.data] with `data.orElse(null)`. */ + fun data(data: Optional) = data(data.getOrNull()) + + /** + * Sets [Builder.data] to an arbitrary JSON value. + * + * You should usually call [Builder.data] with a well-typed [InnerData] 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 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 [MessagePushContent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .token() + * ._typename() + * .body() + * .title() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): MessagePushContent = + MessagePushContent( + checkRequired("token", token), + checkRequired("_typename", _typename), + checkRequired("body", body), + checkRequired("title", title), + data, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): MessagePushContent = apply { + if (validated) { + return@apply + } + + token() + _typename() + body() + title() + data().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 (token.asKnown().isPresent) 1 else 0) + + (if (_typename.asKnown().isPresent) 1 else 0) + + (if (body.asKnown().isPresent) 1 else 0) + + (if (title.asKnown().isPresent) 1 else 0) + + (data.asKnown().getOrNull()?.validity() ?: 0) + + /** Additional data payload for the push notification. */ + class InnerData + @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 [InnerData]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InnerData]. */ + class Builder internal constructor() { + + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(innerData: InnerData) = apply { + additionalProperties = innerData.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 [InnerData]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): InnerData = InnerData(additionalProperties.toImmutable()) + } + + private var validated: Boolean = false + + fun validate(): InnerData = apply { + if (validated) { + return@apply + } + + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: KnockInvalidDataException) { + 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 /* spotless:off */ other is InnerData && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = "InnerData{additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return /* spotless:off */ other is MessagePushContent && token == other.token && _typename == other._typename && body == other.body && title == other.title && data == other.data && additionalProperties == other.additionalProperties /* spotless:on */ + } + + /* spotless:off */ + private val hashCode: Int by lazy { Objects.hash(token, _typename, body, title, data, additionalProperties) } + /* spotless:on */ + + override fun hashCode(): Int = hashCode + + override fun toString() = + "MessagePushContent{token=$token, _typename=$_typename, body=$body, title=$title, data=$data, additionalProperties=$additionalProperties}" + } + + /** The content of a chat message. */ + class MessageChatContent + private constructor( + private val _typename: JsonField, + private val connection: JsonField, + private val template: JsonField