diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..bd8e261
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,23 @@
+# syntax=docker/dockerfile:1
+FROM debian:bookworm-slim
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ libxkbcommon0 \
+ ca-certificates \
+ ca-certificates-java \
+ make \
+ curl \
+ git \
+ openjdk-17-jdk-headless \
+ unzip \
+ libc++1 \
+ vim \
+ && apt-get clean autoclean
+
+# Ensure UTF-8 encoding
+ENV LANG=C.UTF-8
+ENV LC_ALL=C.UTF-8
+
+WORKDIR /workspace
+
+COPY . /workspace
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..d55fc4d
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,20 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/debian
+{
+ "name": "Debian",
+ "build": {
+ "dockerfile": "Dockerfile"
+ }
+
+ // Features to add to the dev container. More info: https://containers.dev/features.
+ // "features": {},
+
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ // "forwardPorts": [],
+
+ // Configure tool-specific properties.
+ // "customizations": {},
+
+ // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
+ // "remoteUser": "root"
+}
diff --git a/.github/workflows/azure-functions-app-nodejs.yml b/.github/workflows/azure-functions-app-nodejs.yml
new file mode 100644
index 0000000..cb158eb
--- /dev/null
+++ b/.github/workflows/azure-functions-app-nodejs.yml
@@ -0,0 +1,66 @@
+# This workflow will build a Node.js project and deploy it to an Azure Functions App on Windows or Linux when a commit is pushed to your default branch.
+#
+# This workflow assumes you have already created the target Azure Functions app.
+# For instructions see:
+# - https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-node
+# - https://learn.microsoft.com/en-us/azure/azure-functions/create-first-function-vs-code-typescript
+#
+# To configure this workflow:
+# 1. Set up the following secrets in your repository:
+# - AZURE_FUNCTIONAPP_PUBLISH_PROFILE
+# 2. Change env variables for your configuration.
+#
+# For more information on:
+# - GitHub Actions for Azure: https://github.com/Azure/Actions
+# - Azure Functions Action: https://github.com/Azure/functions-action
+# - Publish Profile: https://github.com/Azure/functions-action#using-publish-profile-as-deployment-credential-recommended
+# - Azure Service Principal for RBAC: https://github.com/Azure/functions-action#using-azure-service-principal-for-rbac-as-deployment-credential
+#
+# For more samples to get started with GitHub Action workflows to deploy to Azure: https://github.com/Azure/actions-workflow-samples/tree/master/FunctionApp
+
+name: Deploy Node.js project to Azure Function App
+
+on:
+ push:
+ branches: ["main"]
+
+env:
+ AZURE_FUNCTIONAPP_NAME: 'your-app-name' # set this to your function app name on Azure
+ AZURE_FUNCTIONAPP_PACKAGE_PATH: '.' # set this to the path to your function app project, defaults to the repository root
+ NODE_VERSION: '20.x' # set this to the node version to use (e.g. '8.x', '10.x', '12.x')
+
+jobs:
+ build-and-deploy:
+ runs-on: windows-latest # For Linux, use ubuntu-latest
+ environment: dev
+ steps:
+ - name: 'Checkout GitHub Action'
+ uses: actions/checkout@v4
+
+ # If you want to use Azure RBAC instead of Publish Profile, then uncomment the task below
+ # - name: 'Login via Azure CLI'
+ # uses: azure/login@v1
+ # with:
+ # creds: ${{ secrets.AZURE_RBAC_CREDENTIALS }} # set up AZURE_RBAC_CREDENTIALS secrets in your repository
+
+ - name: Setup Node ${{ env.NODE_VERSION }} Environment
+ uses: actions/setup-node@v4
+ with:
+ node-version: ${{ env.NODE_VERSION }}
+
+ - name: 'Resolve Project Dependencies Using Npm'
+ shell: pwsh # For Linux, use bash
+ run: |
+ pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
+ npm install
+ npm run build --if-present
+ npm run test --if-present
+ popd
+
+ - name: 'Run Azure Functions Action'
+ uses: Azure/functions-action@v1
+ id: fa
+ with:
+ app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
+ package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
+ publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }} # Remove publish-profile to use Azure RBAC
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..e46d9eb
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,85 @@
+name: CI
+on:
+ push:
+ branches-ignore:
+ - 'generated'
+ - 'codegen/**'
+ - 'integrated/**'
+ - 'stl-preview-head/**'
+ - 'stl-preview-base/**'
+ pull_request:
+ branches-ignore:
+ - 'stl-preview-head/**'
+ - 'stl-preview-base/**'
+
+jobs:
+ lint:
+ timeout-minutes: 15
+ name: lint
+ runs-on: ${{ github.repository == 'stainless-sdks/pioneer-intergration-app-kotlin' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Set up Java
+ uses: actions/setup-java@v5
+ 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
+
+ build:
+ timeout-minutes: 15
+ name: build
+ runs-on: ${{ github.repository == 'stainless-sdks/pioneer-intergration-app-kotlin' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Set up Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: temurin
+ java-version: |
+ 8
+ 21
+ cache: gradle
+
+ - name: Set up Gradle
+ uses: gradle/actions/setup-gradle@v4
+
+ - name: Build SDK
+ run: ./scripts/build
+
+ test:
+ timeout-minutes: 15
+ name: test
+ runs-on: ${{ github.repository == 'stainless-sdks/pioneer-intergration-app-kotlin' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Set up Java
+ uses: actions/setup-java@v5
+ 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 0000000..c507f6d
--- /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/ummitsmerogers-droid/GetTrustedInstallerShell/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@v6
+
+ - name: Set up Java
+ uses: actions/setup-java@v5
+ with:
+ distribution: temurin
+ java-version: |
+ 8
+ 21
+ 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 publish --no-configuration-cache
+ env:
+ SONATYPE_USERNAME: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }}
+ SONATYPE_PASSWORD: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }}
+ GPG_SIGNING_KEY: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }}
+ GPG_SIGNING_PASSWORD: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }}
\ No newline at end of file
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
new file mode 100644
index 0000000..e748494
--- /dev/null
+++ b/.github/workflows/release-doctor.yml
@@ -0,0 +1,24 @@
+name: Release Doctor
+on:
+ pull_request:
+ branches:
+ - master
+ workflow_dispatch:
+
+jobs:
+ release_doctor:
+ name: release doctor
+ runs-on: ubuntu-latest
+ if: github.repository == 'ummitsmerogers-droid/GetTrustedInstallerShell' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Check release environment
+ run: |
+ bash ./bin/check-release-environment
+ env:
+ SONATYPE_USERNAME: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }}
+ SONATYPE_PASSWORD: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }}
+ GPG_SIGNING_KEY: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_GPG_SIGNING_KEY || secrets.GPG_SIGNING_KEY }}
+ GPG_SIGNING_PASSWORD: ${{ secrets.PIONEER_INTERGRATION_APP_SONATYPE_GPG_SIGNING_PASSWORD || secrets.GPG_SIGNING_PASSWORD }}
diff --git a/.gitignore b/.gitignore
index dfcfd56..b1346e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,350 +1,7 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
-
-# User-specific files
-*.rsuser
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Mono auto generated files
-mono_crash.*
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-[Aa][Rr][Mm]/
-[Aa][Rr][Mm]64/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-[Ll]ogs/
-
-# Visual Studio 2015/2017 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# Visual Studio 2017 auto generated files
-Generated\ Files/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUnit
-*.VisualState.xml
-TestResult.xml
-nunit-*.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# Benchmark Results
-BenchmarkDotNet.Artifacts/
-
-# .NET Core
-project.lock.json
-project.fragment.lock.json
-artifacts/
-
-# StyleCop
-StyleCopReport.xml
-
-# Files built by Visual Studio
-*_i.c
-*_p.c
-*_h.h
-*.ilk
-*.meta
-*.obj
-*.iobj
-*.pch
-*.pdb
-*.ipdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*_wpftmp.csproj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# Visual Studio Trace Files
-*.e2e
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# AxoCover is a Code Coverage Tool
-.axoCover/*
-!.axoCover/settings.json
-
-# Visual Studio code coverage results
-*.coverage
-*.coveragexml
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# Note: Comment the next line if you want to checkin your web deploy settings,
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# NuGet Symbol Packages
-*.snupkg
-# The packages folder can be ignored because of Package Restore
-**/[Pp]ackages/*
-# except build/, which is used as an MSBuild target.
-!**/[Pp]ackages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/[Pp]ackages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-*.appx
-*.appxbundle
-*.appxupload
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!?*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.jfm
-*.pfx
-*.publishsettings
-orleans.codegen.cs
-
-# Including strong name files can present a security risk
-# (https://github.com/github/gitignore/pull/2483#issue-259490424)
-#*.snk
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-ServiceFabricBackup/
-*.rptproj.bak
-
-# SQL Server files
-*.mdf
-*.ldf
-*.ndf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-*.rptproj.rsuser
-*- [Bb]ackup.rdl
-*- [Bb]ackup ([0-9]).rdl
-*- [Bb]ackup ([0-9][0-9]).rdl
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-node_modules/
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
-*.vbw
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# CodeRush personal settings
-.cr/personal
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
-
-# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
-
-# Tabs Studio
-*.tss
-
-# Telerik's JustMock configuration file
-*.jmconfig
-
-# BizTalk build output
-*.btp.cs
-*.btm.cs
-*.odx.cs
-*.xsd.cs
-
-# OpenCover UI analysis results
-OpenCover/
-
-# Azure Stream Analytics local run output
-ASALocalRun/
-
-# MSBuild Binary and Structured Log
-*.binlog
-
-# NVidia Nsight GPU debugger configuration file
-*.nvuser
-
-# MFractors (Xamarin productivity tool) working folder
-.mfractor/
-
-# Local History for Visual Studio
-.localhistory/
-
-# BeatPulse healthcheck temp database
-healthchecksdb
-
-# Backup folder for Package Reference Convert tool in Visual Studio 2017
-MigrationBackup/
-
-# Ionide (cross platform F# VS Code tools) working folder
-.ionide/
+.prism.log
+.gradle
+.idea
+.kotlin
+build/
+codegen.log
+kls_database.db
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
new file mode 100644
index 0000000..3d2ac0b
--- /dev/null
+++ b/.release-please-manifest.json
@@ -0,0 +1,3 @@
+{
+ ".": "0.1.0"
+}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
new file mode 100644
index 0000000..a56446c
--- /dev/null
+++ b/.stats.yml
@@ -0,0 +1,4 @@
+configured_endpoints: 19
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/ummitsmerogersllc%2Fpioneer-intergration-app-21652c2d2e6a5aab678ca435ba09ef698ff34270021f349660b84d5d98bf3923.yml
+openapi_spec_hash: 356a7298b2abac3fdf72ad5ee046b952
+config_hash: 8cc5fee2612d381d6dc5fa26a170b5e6
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..0e0ff44
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,36 @@
+# Changelog
+
+## 0.1.0 (2026-02-07)
+
+Full Changelog: [v0.0.1...v0.1.0](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/compare/v0.0.1...v0.1.0)
+
+### Features
+
+* **client:** allow configuring dispatcher executor service ([ee5ba12](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/ee5ba122373a91853a02f648e2d79f3fa5c4ad27))
+* **client:** send `X-Stainless-Kotlin-Version` header ([afbcfed](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/afbcfed2ef4db4eb879b538e4acde5549b72b13c))
+
+
+### Bug Fixes
+
+* **client:** disallow coercion from float to int ([a5b0b70](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/a5b0b70ed6c64a7e964b788429d445fb466fd94f))
+* **client:** fully respect max retries ([99fb79e](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/99fb79e69355202787789110d15615b40a927ce6))
+* **client:** preserve time zone in lenient date-time parsing ([94546f0](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/94546f0e320cad92c856cb45fcf205875f495c20))
+* **client:** send retry count header for max retries 0 ([99fb79e](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/99fb79e69355202787789110d15615b40a927ce6))
+* date time deserialization leniency ([e450559](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/e450559d6041ea67c018f05678a12cab3c710253))
+* **tests:** add missing query/header params ([bf42f71](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/bf42f71c4925ffb27211a4f9572ed10a980f977d))
+
+
+### Chores
+
+* **ci:** upgrade `actions/setup-java` ([ce82eb8](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/ce82eb8f438a1de87ddba7c944eca968f2b38c9d))
+* **internal:** allow passing args to `./scripts/test` ([5ad9b39](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/5ad9b393f08b27c6ec87292f7b3bc7ea1c261f94))
+* **internal:** codegen related update ([a06486f](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/a06486f32db603002cdc9ef891311a621df0d710))
+* **internal:** codegen related update ([07af80a](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/07af80a3aea53084d0291e9e0d8e09f13b5b27e9))
+* **internal:** correct cache invalidation for `SKIP_MOCK_TESTS` ([eea3a33](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/eea3a332b8fd86ffd263491c62599e033e0b6d7e))
+* **internal:** depend on packages directly in example ([99fb79e](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/99fb79e69355202787789110d15615b40a927ce6))
+* **internal:** refactor build files to support future stainless package uploads ([6024a4f](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/6024a4f7fb7e5b3a48b3067531cc6882bae2c911))
+* **internal:** update `actions/checkout` version ([cd70de1](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/cd70de10007c9dda537b1d71cbf1537954f2e0b2))
+* **internal:** upgrade AssertJ ([d0f62ae](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/d0f62ae004a2f302717f688649877cf5bd0a0ed3))
+* sync repo ([db34c4a](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/db34c4af675d9c7245525201b1da9b6c84a50433))
+* test on Jackson 2.14.0 to avoid encountering FasterXML/jackson-databind[#3240](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/issues/3240) in tests ([e450559](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/e450559d6041ea67c018f05678a12cab3c710253))
+* update SDK settings ([d64728f](https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell/commit/d64728faddd00d52ea2a33faae5453966524c9cb))
diff --git a/Demo/GetTrustedInstallerShell.mp4 b/Demo/GetTrustedInstallerShell.mp4
deleted file mode 100644
index ed9b3b2..0000000
Binary files a/Demo/GetTrustedInstallerShell.mp4 and /dev/null differ
diff --git a/GetTrustedInstallerShell.sln b/GetTrustedInstallerShell.sln
deleted file mode 100644
index f34dcd6..0000000
--- a/GetTrustedInstallerShell.sln
+++ /dev/null
@@ -1,31 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.31005.135
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GetTrustedInstallerShell", "GetTrustedInstallerShell\GetTrustedInstallerShell.vcxproj", "{2446785E-E78E-45F0-A89E-FEA778C1E51B}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Debug|x64.ActiveCfg = Debug|x64
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Debug|x64.Build.0 = Debug|x64
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Debug|x86.ActiveCfg = Debug|Win32
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Debug|x86.Build.0 = Debug|Win32
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Release|x64.ActiveCfg = Release|x64
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Release|x64.Build.0 = Release|x64
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Release|x86.ActiveCfg = Release|Win32
- {2446785E-E78E-45F0-A89E-FEA778C1E51B}.Release|x86.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {BAB527F6-C17C-4BF4-9446-F118A022E8D1}
- EndGlobalSection
-EndGlobal
diff --git a/GetTrustedInstallerShell/GetTrustedInstallerShell.vcxproj b/GetTrustedInstallerShell/GetTrustedInstallerShell.vcxproj
deleted file mode 100644
index 53035e2..0000000
--- a/GetTrustedInstallerShell/GetTrustedInstallerShell.vcxproj
+++ /dev/null
@@ -1,148 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Release
- Win32
-
-
- Debug
- x64
-
-
- Release
- x64
-
-
-
- 16.0
- Win32Proj
- {2446785e-e78e-45f0-a89e-fea778c1e51b}
- GetTrustedInstallerShell
- 10.0
-
-
-
- Application
- true
- v142
- Unicode
-
-
- Application
- false
- v142
- true
- Unicode
-
-
- Application
- true
- v142
- Unicode
-
-
- Application
- false
- v142
- true
- Unicode
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
- false
-
-
- true
-
-
- false
-
-
-
- Level3
- true
- WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
-
-
- Console
- true
-
-
-
-
- Level3
- true
- true
- true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- MultiThreaded
-
-
- Console
- true
- true
- true
-
-
-
-
- Level3
- true
- _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
-
-
- Console
- true
-
-
-
-
- Level3
- true
- true
- true
- NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
-
-
- Console
- true
- true
- true
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/GetTrustedInstallerShell/GetTrustedInstallerShell.vcxproj.filters b/GetTrustedInstallerShell/GetTrustedInstallerShell.vcxproj.filters
deleted file mode 100644
index a12df58..0000000
--- a/GetTrustedInstallerShell/GetTrustedInstallerShell.vcxproj.filters
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
- cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
-
-
- {93995380-89BD-4b04-88EB-625FBE52EBFB}
- h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
-
-
- {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
- rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
-
-
-
-
- Source Files
-
-
-
\ No newline at end of file
diff --git a/GetTrustedInstallerShell/src/main.cpp b/GetTrustedInstallerShell/src/main.cpp
deleted file mode 100644
index 9fad99b..0000000
--- a/GetTrustedInstallerShell/src/main.cpp
+++ /dev/null
@@ -1,304 +0,0 @@
-#define WIN32_LEAN_AND_MEAN
-#include
-#include
-#include
-#include
-#include
-
-///
-/// Displays a message, followed by the descriptor for a win32 error code
-///
-/// : message to display, precedes error description
-/// : win32 error code. optional.
-void PrintError(const char* msg, DWORD err = -1);
-///
-/// Get a handle to the primary token of a process
-///
-/// : target process id. if value is 0, gets token for current process
-/// handle to the process' token
-HANDLE GetProcessToken(DWORD pid);
-///
-/// Duplicate a token with the specified type
-///
-/// : target process id
-/// : type of token (impersonation or primary)
-/// handle to the duplicate token
-HANDLE DuplicateProcessToken(DWORD pid, TOKEN_TYPE type);
-///
-/// enable or disable the specified privilege
-///
-/// : handle to process token
-/// : privilege name
-/// : TRUE to enable privilege, FALSE to disable
-/// win32 error code (0 on success)
-DWORD SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege);
-///
-/// enable debug privilege for current process
-///
-/// true on success
-bool GetDebugPrivilege();
-///
-/// Get pid of process with specified name.
-///
-/// : name of executable file for process
-/// process id
-DWORD GetPidByName(const wchar_t* procName);
-///
-/// calls TerminateProcess
-///
-/// process id of target
-/// success
-bool TerminateProcess(DWORD pid);
-
-///
-/// get a shell with trustedinstaller privileges
-///
-int main(int argc, char** argv) {
-#pragma region get debug privilege
- // get debug privilege
- if (!GetDebugPrivilege()) {
- printf("[-] could not enable debug privilege. please run as a local administrator.");
- return -1;
- }
- printf("[+] enabled debug privilege\n");
-#pragma endregion
-
-#pragma region get target pid
- // pid
- DWORD pid;
- if (argc == 2)
- /*
- * use 1st argument as target pid.
- * target should be a system process, otherwise impersonating its token will not
- * give sufficient permissions to allow accessing the trustedinstaller token
- */
- pid = atoi(argv[1]);
- else
- pid = GetPidByName(L"winlogon.exe");
- if (pid == 0) return -1; // unable to find process (or first argument was not an integer)
-#pragma endregion
-
-#pragma region impersonate system
- // duplicate system token as an impersonation token
- HANDLE hImpToken = DuplicateProcessToken(pid, TOKEN_TYPE::TokenImpersonation);
- if (hImpToken != INVALID_HANDLE_VALUE) printf("[+] process token duplicated\n");
- else { printf("[-] failed to duplicate token\n"); return -1; }
-
- // use the impersonation token
- HANDLE hThread = GetCurrentThread();
- if (!SetThreadToken(&hThread, hImpToken)) {
- PrintError("SetThreadToken()", GetLastError());
- return -1;
- }
- printf("[+] successfully impersonated\n");
-
- // don't need these handles anymore
- CloseHandle(hThread);
- CloseHandle(hImpToken);
-#pragma endregion
-
-#pragma region start trustedinstaller
- // get handle to trustedinstaller service
- SC_HANDLE hService = OpenServiceW(OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS), L"trustedinstaller", MAXIMUM_ALLOWED);
- if (!hService) {
- PrintError("OpenServiceW()", GetLastError());
- return -1;
- }
- // check if service is already running
- SERVICE_STATUS_PROCESS ssp = {}; DWORD bytesNeeded;
- if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (BYTE*)&ssp, sizeof(ssp), &bytesNeeded)) {
- PrintError("QueryServiceStatusEx()", GetLastError());
- return -1;
- }
- // if running do nothing, otherwise start service and query again
- if (ssp.dwCurrentState == SERVICE_RUNNING) {
- printf("[+] trustedinstaller service already running\n");
- }
- else {
- // start
- if (!StartServiceW(hService, 0, NULL)) {
- PrintError("StartServiceW()", GetLastError());
- return -1;
- }
- printf("[+] started trustedinstaller service\n");
-
- // update ssp (interested in the pid)
- if (!QueryServiceStatusEx(hService, SC_STATUS_PROCESS_INFO, (BYTE*)&ssp, sizeof(ssp), &bytesNeeded)) {
- PrintError("QueryServiceStatusEx()", GetLastError());
- return -1;
- }
- }
- CloseServiceHandle(hService);
-#pragma endregion
-
-#pragma region duplicate trustedinstaller token
- // get pid from service status query
- printf("[+] pid of trustedinstaller service: %d\n", ssp.dwProcessId);
- // duplicate token
- HANDLE hTrustedInstallerToken = DuplicateProcessToken(ssp.dwProcessId, TOKEN_TYPE::TokenPrimary);
- if (hTrustedInstallerToken != INVALID_HANDLE_VALUE) printf("[+] process token duplicated\n");
- else { printf("[-] failed to duplicate token\n"); return -1; }
-#pragma endregion
-
-#pragma region stop trustedinstaller service
- // stop service by killing process, as it does not accept SERVICE_CONTROL_STOP
- if (TerminateProcess(ssp.dwProcessId))
- {
- printf("[+] stopped trustedinstaller service\n");
- }
-#pragma endregion
-
-#pragma region stop trustedinstaller process
- if (TerminateProcess(GetPidByName(L"TrustedInstaller.exe")))
- {
- printf("[+] killed trustedinstaller process\n");
- }
-#pragma endregion
-
-#pragma region create process with trustedinstaller token
- // start new process with token
- STARTUPINFO si = {};
- PROCESS_INFORMATION pi = {};
- BOOL success = CreateProcessWithTokenW(hTrustedInstallerToken, LOGON_NETCREDENTIALS_ONLY, L"C:\\Windows\\System32\\cmd.exe", NULL, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
- if (!success) {
- PrintError("CreateProcessWithTokenW()", GetLastError());
- return -1;
- }
- printf("[+] created cmd process with trustedinstaller token\n");
- CloseHandle(hTrustedInstallerToken);
-#pragma endregion
-
- return 0;
-}
-
-HANDLE GetProcessToken(DWORD pid) {
- HANDLE hCurrentProcess = {};
- HANDLE hToken = {};
- // get handle to process
- if (pid == 0)
- {
- hCurrentProcess = GetCurrentProcess();
- }
- else
- {
- hCurrentProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, TRUE, pid);
- if (!hCurrentProcess)
- {
- PrintError("OpenProcess()", GetLastError());
- return INVALID_HANDLE_VALUE;
- }
- }
- // get handle to token
- if (!OpenProcessToken(hCurrentProcess, TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY, &hToken))
- {
- PrintError("OpenProcessToken()", GetLastError());
- CloseHandle(hCurrentProcess);
- return INVALID_HANDLE_VALUE;
- }
- CloseHandle(hCurrentProcess);
- return hToken;
-}
-
-HANDLE DuplicateProcessToken(DWORD pid, TOKEN_TYPE tokenType) {
- // retrieve token
- HANDLE hToken = GetProcessToken(pid);
- if (hToken == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
-
- // args for DuplicateTokenEx
- SECURITY_IMPERSONATION_LEVEL seImpersonateLevel = SecurityImpersonation;
- HANDLE hNewToken = {};
- // duplicate the token
- if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, seImpersonateLevel, tokenType, &hNewToken)) {
- PrintError("DuplicateTokenEx()", GetLastError());
- CloseHandle(hToken);
- return INVALID_HANDLE_VALUE;
- }
- CloseHandle(hToken);
- return hNewToken;
-}
-
-void PrintError(const char* msg, DWORD err) {
- if (err == -1) {
- // only print message
- printf(" [-] %s.", msg);
- return;
- }
- // use winapi formatmessage to retrieve descriptor for error code
- wchar_t* msgBuf = nullptr;
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (wchar_t*)&msgBuf, 0, NULL);
- _bstr_t b(msgBuf); const char* c = b;
- // print
- printf("[-] %s. err: %d %s", msg, err, c);
- LocalFree(msgBuf);
-}
-
-bool GetDebugPrivilege() {
- // pretty self explanatory
- HANDLE hProcess = GetCurrentProcess();
- HANDLE hToken;
- if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
- {
- DWORD errCode = SetPrivilege(hToken, SE_DEBUG_NAME, TRUE);
- // if errcode is 0 then operation was successful
- return errCode == 0;
- }
- return false;
-}
-
-DWORD SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) {
- LUID luid;
- // get current values for privilege
- if (LookupPrivilegeValueW(NULL, lpszPrivilege, &luid))
- {
- TOKEN_PRIVILEGES tp;
- memset(&tp, 0, sizeof(tp));
- tp.PrivilegeCount = 1;
- tp.Privileges[0].Luid = luid;
- // update this field
- tp.Privileges[0].Attributes = (bEnablePrivilege) ? SE_PRIVILEGE_ENABLED : 0;
- // adjust
- AdjustTokenPrivileges(hToken, FALSE, &tp, NULL, (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL);
- }
- // return error code. 0 on success
- return GetLastError();
-}
-
-DWORD GetPidByName(const wchar_t* procName) {
- DWORD procId = 0;
- HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
-
- if (hSnap != INVALID_HANDLE_VALUE) {
- PROCESSENTRY32 procEntry;
- memset(&procEntry, 0, sizeof(procEntry));
- procEntry.dwSize = sizeof(procEntry);
- // iterate through every process, checking if process name matches target
- if (Process32First(hSnap, &procEntry)) {
- do {
- if (!lstrcmpW(procEntry.szExeFile, procName)) {
- procId = procEntry.th32ProcessID;
- wprintf(L"[+] found process '%s'. pid: %d\n", procName, procId);
- break;
- }
- } while (Process32Next(hSnap, &procEntry));
- }
- }
- CloseHandle(hSnap);
- return procId;
-}
-
-bool TerminateProcess(DWORD pid)
-{
- if (pid == 0) return false;
- HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
- if (hProc == INVALID_HANDLE_VALUE) {
- PrintError("OpenProcess()", GetLastError());
- return false;
- }
- bool flag = true;
- if (!TerminateProcess(hProc, 1)) {
- PrintError("TerminateProcess()", GetLastError());
- flag = false;
- }
- CloseHandle(hProc);
- return flag;
-}
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index b764521..73417ff 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,201 @@
-MIT License
-
-Copyright (c) 2021 wilszdev
-
-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.
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2026 Pioneer Intergration App
+
+ 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 aec49ae..a7d4303 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,589 @@
-# GetTrustedInstallerShell
+# Pioneer Intergration App Kotlin API Library
-run as local administrator.
-impersonates the trusted installer
+
+
+[](https://central.sonatype.com/artifact/com.pioneer_intergration_app.api/pioneer-intergration-app-kotlin/0.1.0)
+[](https://javadoc.io/doc/com.pioneer_intergration_app.api/pioneer-intergration-app-kotlin/0.1.0)
+
+
+
+The Pioneer Intergration App Kotlin SDK provides convenient access to the Pioneer Intergration App REST API from applications written in Kotlin.
+
+It is generated with [Stainless](https://www.stainless.com/).
+
+
+
+KDocs are available on [javadoc.io](https://javadoc.io/doc/com.pioneer_intergration_app.api/pioneer-intergration-app-kotlin/0.1.0).
+
+
+
+## Installation
+
+
+
+### Gradle
+
+```kotlin
+implementation("com.pioneer_intergration_app.api:pioneer-intergration-app-kotlin:0.1.0")
+```
+
+### Maven
+
+```xml
+
+ com.pioneer_intergration_app.api
+ pioneer-intergration-app-kotlin
+ 0.1.0
+
+```
+
+
+
+## Requirements
+
+This library requires Java 8 or later.
+
+## Usage
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+import com.pioneer_intergration_app.api.models.Order
+import com.pioneer_intergration_app.api.models.store.orders.OrderCreateParams
+
+// Configures using the `pioneerintergrationapp.petstoreApiKey` and `pioneerintergrationapp.baseUrl` system properties
+// Or configures using the `PETSTORE_API_KEY` and `PIONEER_INTERGRATION_APP_BASE_URL` environment variables
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.fromEnv()
+
+val order: Order = client.store().orders().create()
+```
+
+## Client configuration
+
+Configure the client using system properties or environment variables:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+
+// Configures using the `pioneerintergrationapp.petstoreApiKey` and `pioneerintergrationapp.baseUrl` system properties
+// Or configures using the `PETSTORE_API_KEY` and `PIONEER_INTERGRATION_APP_BASE_URL` environment variables
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.fromEnv()
+```
+
+Or manually:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.builder()
+ .apiKey("My API Key")
+ .build()
+```
+
+Or using a combination of the two approaches:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.builder()
+ // Configures using the `pioneerintergrationapp.petstoreApiKey` and `pioneerintergrationapp.baseUrl` system properties
+ // Or configures using the `PETSTORE_API_KEY` and `PIONEER_INTERGRATION_APP_BASE_URL` environment variables
+ .fromEnv()
+ .apiKey("My API Key")
+ .build()
+```
+
+See this table for the available options:
+
+| Setter | System property | Environment variable | Required | Default value |
+| --------- | --------------------------------------- | ----------------------------------- | -------- | --------------------------------------- |
+| `apiKey` | `pioneerintergrationapp.petstoreApiKey` | `PETSTORE_API_KEY` | true | - |
+| `baseUrl` | `pioneerintergrationapp.baseUrl` | `PIONEER_INTERGRATION_APP_BASE_URL` | true | `"https://petstore3.swagger.io/api/v3"` |
+
+System properties take precedence over environment variables.
+
+> [!TIP]
+> Don't create more than one client in the same application. Each client has a connection pool and
+> thread pools, which are more efficient to share between requests.
+
+### Modifying configuration
+
+To temporarily use a modified client configuration, while reusing the same connection and thread pools, call `withOptions()` on any client or service:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+
+val clientWithOptions: PioneerIntergrationAppClient = client.withOptions {
+ it.baseUrl("https://example.com")
+ it.maxRetries(42)
+}
+```
+
+The `withOptions()` method does not affect the original client or service.
+
+## Requests and responses
+
+To send a request to the Pioneer Intergration App 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 Kotlin class.
+
+For example, `client.store().orders().create(...)` should be called with an instance of `OrderCreateParams`, and it will return an instance of `Order`.
+
+## 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:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+import com.pioneer_intergration_app.api.models.Order
+import com.pioneer_intergration_app.api.models.store.orders.OrderCreateParams
+
+// Configures using the `pioneerintergrationapp.petstoreApiKey` and `pioneerintergrationapp.baseUrl` system properties
+// Or configures using the `PETSTORE_API_KEY` and `PIONEER_INTERGRATION_APP_BASE_URL` environment variables
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.fromEnv()
+
+val order: Order = client.async().store().orders().create()
+```
+
+Or create an asynchronous client from the beginning:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClientAsync
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClientAsync
+import com.pioneer_intergration_app.api.models.Order
+import com.pioneer_intergration_app.api.models.store.orders.OrderCreateParams
+
+// Configures using the `pioneerintergrationapp.petstoreApiKey` and `pioneerintergrationapp.baseUrl` system properties
+// Or configures using the `PETSTORE_API_KEY` and `PIONEER_INTERGRATION_APP_BASE_URL` environment variables
+val client: PioneerIntergrationAppClientAsync = PioneerIntergrationAppOkHttpClientAsync.fromEnv()
+
+val order: Order = client.store().orders().create()
+```
+
+The asynchronous client supports the same options as the synchronous one, except most methods are [suspending](https://kotlinlang.org/docs/coroutines-guide.html).
+
+## Raw responses
+
+The SDK defines methods that deserialize responses into instances of Kotlin 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()`:
+
+```kotlin
+import com.pioneer_intergration_app.api.core.http.Headers
+import com.pioneer_intergration_app.api.core.http.HttpResponseFor
+import com.pioneer_intergration_app.api.models.store.StoreListInventoryParams
+import com.pioneer_intergration_app.api.models.store.StoreListInventoryResponse
+
+val response: HttpResponseFor = client.store().withRawResponse().listInventory()
+
+val statusCode: Int = response.statusCode()
+val headers: Headers = response.headers()
+```
+
+You can still deserialize the response into an instance of a Kotlin class if needed:
+
+```kotlin
+import com.pioneer_intergration_app.api.models.store.StoreListInventoryResponse
+
+val parsedResponse: StoreListInventoryResponse = response.parse()
+```
+
+## Error handling
+
+The SDK throws custom unchecked exception types:
+
+- [`PioneerIntergrationAppServiceException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppServiceException.kt): Base class for HTTP errors. See this table for which exception subclass is thrown for each HTTP status code:
+
+ | Status | Exception |
+ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | 400 | [`BadRequestException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/BadRequestException.kt) |
+ | 401 | [`UnauthorizedException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnauthorizedException.kt) |
+ | 403 | [`PermissionDeniedException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PermissionDeniedException.kt) |
+ | 404 | [`NotFoundException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/NotFoundException.kt) |
+ | 422 | [`UnprocessableEntityException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnprocessableEntityException.kt) |
+ | 429 | [`RateLimitException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/RateLimitException.kt) |
+ | 5xx | [`InternalServerException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/InternalServerException.kt) |
+ | others | [`UnexpectedStatusCodeException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnexpectedStatusCodeException.kt) |
+
+- [`PioneerIntergrationAppIoException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppIoException.kt): I/O networking errors.
+
+- [`PioneerIntergrationAppRetryableException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppRetryableException.kt): Generic error indicating a failure that could be retried by the client.
+
+- [`PioneerIntergrationAppInvalidDataException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppInvalidDataException.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.
+
+- [`PioneerIntergrationAppException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppException.kt): Base class for all exceptions. Most errors will result in one of the previously mentioned ones, but completely generic errors may be thrown using the base class.
+
+## Logging
+
+The SDK uses the standard [OkHttp logging interceptor](https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor).
+
+Enable logging by setting the `PIONEER_INTERGRATION_APP_LOG` environment variable to `info`:
+
+```sh
+export PIONEER_INTERGRATION_APP_LOG=info
+```
+
+Or to `debug` for more verbose logging:
+
+```sh
+export PIONEER_INTERGRATION_APP_LOG=debug
+```
+
+## ProGuard and R8
+
+Although the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `pioneer-intergration-app-kotlin-core` is published with a [configuration file](pioneer-intergration-app-kotlin-core/src/main/resources/META-INF/proguard/pioneer-intergration-app-kotlin-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage).
+
+ProGuard and R8 should automatically detect and use the published rules, but you can also manually copy the keep rules if necessary.
+
+## Jackson
+
+The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default.
+
+The SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config).
+
+If the SDK threw an exception, but you're _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`PioneerIntergrationAppOkHttpClient`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClient.kt) or [`PioneerIntergrationAppOkHttpClientAsync`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClientAsync.kt).
+
+> [!CAUTION]
+> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.
+
+Also note that there are bugs in older Jackson versions that can affect the SDK. We don't work around all Jackson bugs ([example](https://github.com/FasterXML/jackson-databind/issues/3240)) and expect users to upgrade Jackson for those instead.
+
+## Network options
+
+### Retries
+
+The SDK automatically retries 2 times by default, with a short exponential backoff between requests.
+
+Only the following error types are retried:
+
+- Connection errors (for example, due to a network connectivity problem)
+- 408 Request Timeout
+- 409 Conflict
+- 429 Rate Limit
+- 5xx Internal
+
+The API may also explicitly instruct the SDK to retry or not retry a request.
+
+To set a custom number of retries, configure the client using the `maxRetries` method:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.builder()
+ .fromEnv()
+ .maxRetries(4)
+ .build()
+```
+
+### Timeouts
+
+Requests time out after 1 minute by default.
+
+To set a custom timeout, configure the method call using the `timeout` method:
+
+```kotlin
+import com.pioneer_intergration_app.api.models.store.StoreListInventoryResponse
+
+val response: StoreListInventoryResponse = client.store().listInventory(RequestOptions.builder().timeout(Duration.ofSeconds(30)).build())
+```
+
+Or configure the default for all method calls at the client level:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+import java.time.Duration
+
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.builder()
+ .fromEnv()
+ .timeout(Duration.ofSeconds(30))
+ .build()
+```
+
+### Proxies
+
+To route requests through a proxy, configure the client using the `proxy` method:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+import java.net.InetSocketAddress
+import java.net.Proxy
+
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.builder()
+ .fromEnv()
+ .proxy(Proxy(
+ Proxy.Type.HTTP, InetSocketAddress(
+ "https://example.com", 8080
+ )
+ ))
+ .build()
+```
+
+### HTTPS
+
+> [!NOTE]
+> Most applications should not call these methods, and instead use the system defaults. The defaults include
+> special optimizations that can be lost if the implementations are modified.
+
+To configure how HTTPS connections are secured, configure the client using the `sslSocketFactory`, `trustManager`, and `hostnameVerifier` methods:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.builder()
+ .fromEnv()
+ // If `sslSocketFactory` is set, then `trustManager` must be set, and vice versa.
+ .sslSocketFactory(yourSSLSocketFactory)
+ .trustManager(yourTrustManager)
+ .hostnameVerifier(yourHostnameVerifier)
+ .build()
+```
+
+### Custom HTTP client
+
+The SDK consists of three artifacts:
+
+- `pioneer-intergration-app-kotlin-core`
+ - Contains core SDK logic
+ - Does not depend on [OkHttp](https://square.github.io/okhttp)
+ - Exposes [`PioneerIntergrationAppClient`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClient.kt), [`PioneerIntergrationAppClientAsync`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsync.kt), [`PioneerIntergrationAppClientImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientImpl.kt), and [`PioneerIntergrationAppClientAsyncImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsyncImpl.kt), all of which can work with any HTTP client
+- `pioneer-intergration-app-kotlin-client-okhttp`
+ - Depends on [OkHttp](https://square.github.io/okhttp)
+ - Exposes [`PioneerIntergrationAppOkHttpClient`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClient.kt) and [`PioneerIntergrationAppOkHttpClientAsync`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClientAsync.kt), which provide a way to construct [`PioneerIntergrationAppClientImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientImpl.kt) and [`PioneerIntergrationAppClientAsyncImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsyncImpl.kt), respectively, using OkHttp
+- `pioneer-intergration-app-kotlin`
+ - Depends on and exposes the APIs of both `pioneer-intergration-app-kotlin-core` and `pioneer-intergration-app-kotlin-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 [`pioneer-intergration-app-kotlin` dependency](#installation) with `pioneer-intergration-app-kotlin-core`
+2. Copy `pioneer-intergration-app-kotlin-client-okhttp`'s [`OkHttpClient`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/OkHttpClient.kt) class into your code and customize it
+3. Construct [`PioneerIntergrationAppClientImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientImpl.kt) or [`PioneerIntergrationAppClientAsyncImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsyncImpl.kt), similarly to [`PioneerIntergrationAppOkHttpClient`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClient.kt) or [`PioneerIntergrationAppOkHttpClientAsync`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClientAsync.kt), using your customized client
+
+### Completely custom HTTP client
+
+To use a completely custom HTTP client:
+
+1. Replace your [`pioneer-intergration-app-kotlin` dependency](#installation) with `pioneer-intergration-app-kotlin-core`
+2. Write a class that implements the [`HttpClient`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpClient.kt) interface
+3. Construct [`PioneerIntergrationAppClientImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientImpl.kt) or [`PioneerIntergrationAppClientAsyncImpl`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsyncImpl.kt), similarly to [`PioneerIntergrationAppOkHttpClient`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClient.kt) or [`PioneerIntergrationAppOkHttpClientAsync`](pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClientAsync.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:
+
+```kotlin
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.models.store.orders.OrderCreateParams
+
+val params: OrderCreateParams = OrderCreateParams.builder()
+ .putAdditionalHeader("Secret-Header", "42")
+ .putAdditionalQueryParam("secret_query_param", "42")
+ .putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
+ .build()
+```
+
+These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.
+
+To set a documented parameter or property to an undocumented or not yet supported _value_, pass a [`JsonValue`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Values.kt) object to its setter:
+
+```kotlin
+import com.pioneer_intergration_app.api.models.store.orders.OrderCreateParams
+
+val params: OrderCreateParams = OrderCreateParams.builder().build()
+```
+
+The most straightforward way to create a [`JsonValue`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Values.kt) is using its `from(...)` method:
+
+```kotlin
+import com.pioneer_intergration_app.api.core.JsonValue
+
+// Create primitive JSON values
+val nullValue: JsonValue = JsonValue.from(null)
+val booleanValue: JsonValue = JsonValue.from(true)
+val numberValue: JsonValue = JsonValue.from(42)
+val stringValue: JsonValue = JsonValue.from("Hello World!")
+
+// Create a JSON array value equivalent to `["Hello", "World"]`
+val arrayValue: JsonValue = JsonValue.from(listOf(
+ "Hello", "World"
+))
+
+// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`
+val objectValue: JsonValue = JsonValue.from(mapOf(
+ "a" to 1, "b" to 2
+))
+
+// Create an arbitrarily nested JSON equivalent to:
+// {
+// "a": [1, 2],
+// "b": [3, 4]
+// }
+val complexValue: JsonValue = JsonValue.from(mapOf(
+ "a" to listOf(
+ 1, 2
+ ), "b" to listOf(
+ 3, 4
+ )
+))
+```
+
+Normally a `Builder` class's `build` method will throw [`IllegalStateException`](https://docs.oracle.com/javase/8/docs/api/java/lang/IllegalStateException.html) if any required parameter or property is unset.
+
+To forcibly omit a required parameter or property, pass [`JsonMissing`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Values.kt):
+
+```kotlin
+import com.pioneer_intergration_app.api.core.JsonMissing
+import com.pioneer_intergration_app.api.models.pets.Pet
+import com.pioneer_intergration_app.api.models.pets.PetCreateParams
+import com.pioneer_intergration_app.api.models.store.orders.OrderCreateParams
+
+val params: OrderCreateParams = PetCreateParams.builder()
+ .pet(Pet.builder()
+ .name("doggie")
+ .addPhotoUrl("string")
+ .build())
+ .name(JsonMissing.of())
+ .build()
+```
+
+### Response properties
+
+To access undocumented response properties, call the `_additionalProperties()` method:
+
+```kotlin
+import com.pioneer_intergration_app.api.core.JsonBoolean
+import com.pioneer_intergration_app.api.core.JsonNull
+import com.pioneer_intergration_app.api.core.JsonNumber
+import com.pioneer_intergration_app.api.core.JsonValue
+
+val additionalProperties: Map = client.store().orders().create(params)._additionalProperties()
+val secretPropertyValue: JsonValue = additionalProperties.get("secretProperty")
+
+val result = when (secretPropertyValue) {
+ is JsonNull -> "It's null!"
+ is JsonBoolean -> "It's a boolean!"
+ is JsonNumber -> "It's a number!"
+ // Other types include `JsonMissing`, `JsonString`, `JsonArray`, and `JsonObject`
+ else -> "It's something else!"
+}
+```
+
+To access a property's raw JSON value, which may be undocumented, call its `_` prefixed method:
+
+```kotlin
+import com.pioneer_intergration_app.api.core.JsonField
+
+val field: JsonField = client.store().orders().create(params)._field()
+
+if (field.isMissing()) {
+ // The property is absent from the JSON response
+} else if (field.isNull()) {
+ // The property was set to literal null
+} else {
+ // Check if value was provided as a string
+ // Other methods include `asNumber()`, `asBoolean()`, etc.
+ val jsonString: String? = field.asString();
+
+ // Try to deserialize into a custom type
+ val myObject: MyClass = field.asUnknown()!!.convert(MyClass::class.java)
+}
+```
+
+### 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 [`PioneerIntergrationAppInvalidDataException`](pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppInvalidDataException.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()`:
+
+```kotlin
+import com.pioneer_intergration_app.api.models.Order
+
+val order: Order = client.store().orders().create(params).validate()
+```
+
+Or configure the method call to validate the response using the `responseValidation` method:
+
+```kotlin
+import com.pioneer_intergration_app.api.models.Order
+
+val order: Order = client.store().orders().create(RequestOptions.builder().responseValidation(true).build())
+```
+
+Or configure the default for all method calls at the client level:
+
+```kotlin
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.okhttp.PioneerIntergrationAppOkHttpClient
+
+val client: PioneerIntergrationAppClient = PioneerIntergrationAppOkHttpClient.builder()
+ .fromEnv()
+ .responseValidation(true)
+ .build()
+```
+
+## FAQ
+
+### Why don't you use plain `enum` classes?
+
+Kotlin `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/ummitsmerogers-droid/GetTrustedInstallerShell/issues) with questions, bugs, or suggestions.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..21f51dc
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,23 @@
+# 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 Pioneer Intergration App, please follow the respective company's security reporting guidelines.
+
+---
+
+Thank you for helping us keep the SDKs and systems they interact with secure.
diff --git a/bin/check-release-environment b/bin/check-release-environment
new file mode 100644
index 0000000..3a6a7b4
--- /dev/null
+++ b/bin/check-release-environment
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+errors=()
+
+if [ -z "${SONATYPE_USERNAME}" ]; then
+ errors+=("The SONATYPE_USERNAME secret has not been set. Please set it in either this repository's secrets or your organization secrets")
+fi
+
+if [ -z "${SONATYPE_PASSWORD}" ]; then
+ errors+=("The SONATYPE_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets")
+fi
+
+if [ -z "${GPG_SIGNING_KEY}" ]; then
+ errors+=("The GPG_SIGNING_KEY secret has not been set. Please set it in either this repository's secrets or your organization secrets")
+fi
+
+if [ -z "${GPG_SIGNING_PASSWORD}" ]; then
+ errors+=("The GPG_SIGNING_PASSWORD secret has not been set. Please set it in either this repository's secrets or your organization secrets")
+fi
+
+lenErrors=${#errors[@]}
+
+if [[ lenErrors -gt 0 ]]; then
+ echo -e "Found the following errors in the release environment:\n"
+
+ for error in "${errors[@]}"; do
+ echo -e "- $error\n"
+ done
+
+ exit 1
+fi
+
+echo "The environment is ready to push releases!"
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..649caa7
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,49 @@
+plugins {
+ id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
+ id("org.jetbrains.dokka") version "2.0.0"
+}
+
+repositories {
+ mavenCentral()
+}
+
+allprojects {
+ group = "com.pioneer_intergration_app.api"
+ version = "0.1.0" // x-release-please-version
+}
+
+subprojects {
+ // These are populated with dependencies by `buildSrc` scripts.
+ tasks.register("format") {
+ group = "Verification"
+ description = "Formats all source files."
+ }
+ tasks.register("lint") {
+ group = "Verification"
+ description = "Verifies all source files are formatted."
+ }
+ apply(plugin = "org.jetbrains.dokka")
+}
+
+subprojects {
+ apply(plugin = "org.jetbrains.dokka")
+}
+
+// Avoid race conditions between `dokkaHtmlCollector` and `dokkaJavadocJar` tasks
+tasks.named("dokkaHtmlCollector").configure {
+ subprojects.flatMap { it.tasks }
+ .filter { it.project.name != "pioneer-intergration-app-kotlin" && it.name == "dokkaJavadocJar" }
+ .forEach { mustRunAfter(it) }
+}
+
+nexusPublishing {
+ repositories {
+ sonatype {
+ nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
+ snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
+
+ username.set(System.getenv("SONATYPE_USERNAME"))
+ password.set(System.getenv("SONATYPE_PASSWORD"))
+ }
+ }
+}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
new file mode 100644
index 0000000..0b14135
--- /dev/null
+++ b/buildSrc/build.gradle.kts
@@ -0,0 +1,12 @@
+plugins {
+ `kotlin-dsl`
+ kotlin("jvm") version "1.9.20"
+}
+
+repositories {
+ gradlePluginPortal()
+}
+
+dependencies {
+ implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20")
+}
diff --git a/buildSrc/src/main/kotlin/pioneer-intergration-app.java.gradle.kts b/buildSrc/src/main/kotlin/pioneer-intergration-app.java.gradle.kts
new file mode 100644
index 0000000..81d5d32
--- /dev/null
+++ b/buildSrc/src/main/kotlin/pioneer-intergration-app.java.gradle.kts
@@ -0,0 +1,136 @@
+import org.gradle.api.tasks.testing.logging.TestExceptionFormat
+
+plugins {
+ `java-library`
+}
+
+repositories {
+ mavenCentral()
+}
+
+configure {
+ withJavadocJar()
+ withSourcesJar()
+}
+
+java {
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(21))
+ }
+
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+tasks.withType().configureEach {
+ options.compilerArgs.add("-Werror")
+ options.release.set(8)
+}
+
+tasks.named("javadocJar") {
+ setZip64(true)
+}
+
+tasks.named("jar") {
+ manifest {
+ attributes(mapOf(
+ "Implementation-Title" to project.name,
+ "Implementation-Version" to project.version
+ ))
+ }
+}
+
+tasks.withType().configureEach {
+ useJUnitPlatform()
+
+ // Run tests in parallel to some degree.
+ maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1)
+ forkEvery = 100
+
+ testLogging {
+ exceptionFormat = TestExceptionFormat.FULL
+ }
+}
+
+val palantir by configurations.creating
+dependencies {
+ palantir("com.palantir.javaformat:palantir-java-format:2.73.0")
+}
+
+fun registerPalantir(
+ name: String,
+ description: String,
+) {
+ val javaName = "${name}Java"
+ tasks.register(javaName) {
+ group = "Verification"
+ this.description = description
+
+ classpath = palantir
+ mainClass = "com.palantir.javaformat.java.Main"
+
+ // Avoid an `IllegalAccessError` on Java 9+.
+ jvmArgs(
+ "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
+ "--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
+ "--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
+ "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ )
+
+ // Use paths relative to the current module.
+ val argumentFile =
+ project.layout.buildDirectory.file("palantir-$name-args.txt").get().asFile
+ val lastRunTimeFile =
+ project.layout.buildDirectory.file("palantir-$name-last-run.txt").get().asFile
+
+ // Read the time when this task was last executed for this module (if ever).
+ val lastRunTime = lastRunTimeFile.takeIf { it.exists() }?.readText()?.toLongOrNull() ?: 0L
+
+ // Use a `fileTree` relative to the module's source directory.
+ val javaFiles = project.fileTree("src") { include("**/*.java") }
+
+ // Determine if any files need to be formatted or linted and continue only if there is at least
+ // one file.
+ onlyIf { javaFiles.any { it.lastModified() > lastRunTime } }
+
+ inputs.files(javaFiles)
+
+ doFirst {
+ // Create the argument file and set the preferred formatting style.
+ argumentFile.parentFile.mkdirs()
+ argumentFile.writeText("--palantir\n")
+
+ if (name == "lint") {
+ // For lint, do a dry run, so no files are modified. Set the exit code to 1 (instead of
+ // the default 0) if any files need to be formatted, indicating that linting has failed.
+ argumentFile.appendText("--dry-run\n")
+ argumentFile.appendText("--set-exit-if-changed\n")
+ } else {
+ // `--dry-run` and `--replace` (for in-place formatting) are mutually exclusive.
+ argumentFile.appendText("--replace\n")
+ }
+
+ // Write the modified files to the argument file.
+ javaFiles.filter { it.lastModified() > lastRunTime }
+ .forEach { argumentFile.appendText("${it.absolutePath}\n") }
+ }
+
+ doLast {
+ // Record the last execution time for later up-to-date checking.
+ lastRunTimeFile.writeText(System.currentTimeMillis().toString())
+ }
+
+ // Pass the argument file using the @ symbol
+ args = listOf("@${argumentFile.absolutePath}")
+
+ outputs.upToDateWhen { javaFiles.none { it.lastModified() > lastRunTime } }
+ }
+
+ tasks.named(name) {
+ dependsOn(tasks.named(javaName))
+ }
+}
+
+registerPalantir(name = "format", description = "Formats all Java source files.")
+registerPalantir(name = "lint", description = "Verifies all Java source files are formatted.")
diff --git a/buildSrc/src/main/kotlin/pioneer-intergration-app.kotlin.gradle.kts b/buildSrc/src/main/kotlin/pioneer-intergration-app.kotlin.gradle.kts
new file mode 100644
index 0000000..1eda9c9
--- /dev/null
+++ b/buildSrc/src/main/kotlin/pioneer-intergration-app.kotlin.gradle.kts
@@ -0,0 +1,109 @@
+import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
+
+plugins {
+ id("pioneer-intergration-app.java")
+ kotlin("jvm")
+}
+
+repositories {
+ mavenCentral()
+}
+
+kotlin {
+ jvmToolchain {
+ languageVersion.set(JavaLanguageVersion.of(21))
+ }
+
+ compilerOptions {
+ freeCompilerArgs = listOf(
+ "-Xjvm-default=all",
+ "-Xjdk-release=1.8",
+ // Suppress deprecation warnings because we may still reference and test deprecated members.
+ // TODO: Replace with `-Xsuppress-warning=DEPRECATION` once we use Kotlin compiler 2.1.0+.
+ "-nowarn",
+ )
+ jvmTarget.set(JvmTarget.JVM_1_8)
+ languageVersion.set(KotlinVersion.KOTLIN_1_8)
+ apiVersion.set(KotlinVersion.KOTLIN_1_8)
+ coreLibrariesVersion = "1.8.0"
+ }
+}
+
+tasks.withType().configureEach {
+ systemProperty("junit.jupiter.execution.parallel.enabled", true)
+ systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent")
+
+ // `SKIP_MOCK_TESTS` affects which tests run so it must be added as input for proper cache invalidation.
+ inputs.property("skipMockTests", System.getenv("SKIP_MOCK_TESTS")).optional(true)
+}
+
+val ktfmt by configurations.creating
+dependencies {
+ ktfmt("com.facebook:ktfmt:0.56")
+}
+
+fun registerKtfmt(
+ name: String,
+ description: String,
+) {
+ val kotlinName = "${name}Kotlin"
+ tasks.register(kotlinName) {
+ group = "Verification"
+ this.description = description
+
+ classpath = ktfmt
+ mainClass = "com.facebook.ktfmt.cli.Main"
+
+ // Use paths relative to the current module.
+ val argumentFile = project.layout.buildDirectory.file("ktfmt-$name-args.txt").get().asFile
+ val lastRunTimeFile =
+ project.layout.buildDirectory.file("ktfmt-$name-last-run.txt").get().asFile
+
+ // Read the time when this task was last executed for this module (if ever).
+ val lastRunTime = lastRunTimeFile.takeIf { it.exists() }?.readText()?.toLongOrNull() ?: 0L
+
+ // Use a `fileTree` relative to the module's source directory.
+ val kotlinFiles = project.fileTree("src") { include("**/*.kt") }
+
+ // Determine if any files need to be formatted or linted and continue only if there is at least
+ // one file (otherwise Ktfmt will fail).
+ onlyIf { kotlinFiles.any { it.lastModified() > lastRunTime } }
+
+ inputs.files(kotlinFiles)
+
+ doFirst {
+ // Create the argument file and set the preferred formatting style.
+ argumentFile.parentFile.mkdirs()
+ argumentFile.writeText("--kotlinlang-style\n")
+
+ if (name == "lint") {
+ // For lint, do a dry run, so no files are modified. Set the exit code to 1 (instead of
+ // the default 0) if any files need to be formatted, indicating that linting has failed.
+ argumentFile.appendText("--dry-run\n")
+ argumentFile.appendText("--set-exit-if-changed\n")
+ }
+
+ // Write the modified files to the argument file.
+ kotlinFiles.filter { it.lastModified() > lastRunTime }
+ .forEach { argumentFile.appendText("${it.absolutePath}\n") }
+ }
+
+ doLast {
+ // Record the last execution time for later up-to-date checking.
+ lastRunTimeFile.writeText(System.currentTimeMillis().toString())
+ }
+
+ // Pass the argument file using the @ symbol
+ args = listOf("@${argumentFile.absolutePath}")
+
+ outputs.upToDateWhen { kotlinFiles.none { it.lastModified() > lastRunTime } }
+ }
+
+ tasks.named(name) {
+ dependsOn(tasks.named(kotlinName))
+ }
+}
+
+registerKtfmt(name = "format", description = "Formats all Kotlin source files.")
+registerKtfmt(name = "lint", description = "Verifies all Kotlin source files are formatted.")
diff --git a/buildSrc/src/main/kotlin/pioneer-intergration-app.publish.gradle.kts b/buildSrc/src/main/kotlin/pioneer-intergration-app.publish.gradle.kts
new file mode 100644
index 0000000..895938c
--- /dev/null
+++ b/buildSrc/src/main/kotlin/pioneer-intergration-app.publish.gradle.kts
@@ -0,0 +1,68 @@
+plugins {
+ `maven-publish`
+ signing
+}
+
+configure {
+ publications {
+ register("maven") {
+ from(components["java"])
+
+ pom {
+ name.set("OpenAPI 3.0 Pet Store")
+ description.set("This is a sample Pet Store Server based on the OpenAPI 3.0 specification.")
+ url.set("https://www.github.com/ummitsmerogers-droid/GetTrustedInstallerShell")
+
+ licenses {
+ license {
+ name.set("Apache-2.0")
+ }
+ }
+
+ developers {
+ developer {
+ name.set("Pioneer Intergration App")
+ }
+ }
+
+ scm {
+ connection.set("scm:git:git://github.com/ummitsmerogers-droid/GetTrustedInstallerShell.git")
+ developerConnection.set("scm:git:git://github.com/ummitsmerogers-droid/GetTrustedInstallerShell.git")
+ url.set("https://github.com/ummitsmerogers-droid/GetTrustedInstallerShell")
+ }
+
+ versionMapping {
+ allVariants {
+ fromResolutionResult()
+ }
+ }
+ }
+ }
+ }
+ repositories {
+ if (project.hasProperty("publishLocal")) {
+ maven {
+ name = "LocalFileSystem"
+ url = uri("${rootProject.layout.buildDirectory.get()}/local-maven-repo")
+ }
+ }
+ }
+}
+
+signing {
+ val signingKeyId = System.getenv("GPG_SIGNING_KEY_ID")?.ifBlank { null }
+ val signingKey = System.getenv("GPG_SIGNING_KEY")?.ifBlank { null }
+ val signingPassword = System.getenv("GPG_SIGNING_PASSWORD")?.ifBlank { null }
+ if (signingKey != null && signingPassword != null) {
+ useInMemoryPgpKeys(
+ signingKeyId,
+ signingKey,
+ signingPassword,
+ )
+ sign(publishing.publications["maven"])
+ }
+}
+
+tasks.named("publish") {
+ dependsOn(":closeAndReleaseSonatypeStagingRepository")
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..6680f9c
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+org.gradle.caching=true
+org.gradle.configuration-cache=true
+org.gradle.parallel=true
+org.gradle.daemon=false
+# These options improve our compilation and test performance. They are inherited by the Kotlin daemon.
+org.gradle.jvmargs=\
+ -Xms2g \
+ -Xmx8g \
+ -XX:+UseParallelGC \
+ -XX:InitialCodeCacheSize=256m \
+ -XX:ReservedCodeCacheSize=1G \
+ -XX:MetaspaceSize=512m \
+ -XX:MaxMetaspaceSize=2G \
+ -XX:TieredStopAtLevel=1 \
+ -XX:GCTimeRatio=4 \
+ -XX:CICompilerCount=4 \
+ -XX:+OptimizeStringConcat \
+ -XX:+UseStringDeduplication
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..a4b76b9
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..cea7a79
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..f3b75f3
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD=$JAVA_HOME/jre/sh/java
+ else
+ JAVACMD=$JAVA_HOME/bin/java
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..9b42019
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,94 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/pioneer-intergration-app-kotlin-client-okhttp/build.gradle.kts b/pioneer-intergration-app-kotlin-client-okhttp/build.gradle.kts
new file mode 100644
index 0000000..232bf64
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-client-okhttp/build.gradle.kts
@@ -0,0 +1,15 @@
+plugins {
+ id("pioneer-intergration-app.kotlin")
+ id("pioneer-intergration-app.publish")
+}
+
+dependencies {
+ api(project(":pioneer-intergration-app-kotlin-core"))
+
+ implementation("com.squareup.okhttp3:okhttp:4.12.0")
+ implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
+
+ testImplementation(kotlin("test"))
+ testImplementation("org.assertj:assertj-core:3.27.7")
+}
diff --git a/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/OkHttpClient.kt b/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/OkHttpClient.kt
new file mode 100644
index 0000000..6052c9e
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/OkHttpClient.kt
@@ -0,0 +1,265 @@
+package com.pioneer_intergration_app.api.client.okhttp
+
+import com.pioneer_intergration_app.api.core.RequestOptions
+import com.pioneer_intergration_app.api.core.Timeout
+import com.pioneer_intergration_app.api.core.http.Headers
+import com.pioneer_intergration_app.api.core.http.HttpClient
+import com.pioneer_intergration_app.api.core.http.HttpMethod
+import com.pioneer_intergration_app.api.core.http.HttpRequest
+import com.pioneer_intergration_app.api.core.http.HttpRequestBody
+import com.pioneer_intergration_app.api.core.http.HttpResponse
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppIoException
+import java.io.IOException
+import java.io.InputStream
+import java.net.Proxy
+import java.time.Duration
+import java.util.concurrent.ExecutorService
+import javax.net.ssl.HostnameVerifier
+import javax.net.ssl.SSLSocketFactory
+import javax.net.ssl.X509TrustManager
+import kotlinx.coroutines.suspendCancellableCoroutine
+import okhttp3.Call
+import okhttp3.Callback
+import okhttp3.Dispatcher
+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) :
+ HttpClient {
+
+ override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
+ val call = newCall(request, requestOptions)
+
+ return try {
+ call.execute().toResponse()
+ } catch (e: IOException) {
+ throw PioneerIntergrationAppIoException("Request failed", e)
+ } finally {
+ request.body?.close()
+ }
+ }
+
+ override suspend fun executeAsync(
+ request: HttpRequest,
+ requestOptions: RequestOptions,
+ ): HttpResponse {
+ val call = newCall(request, requestOptions)
+
+ return try {
+ call.executeAsync().toResponse()
+ } catch (e: IOException) {
+ throw PioneerIntergrationAppIoException("Request failed", e)
+ } finally {
+ request.body?.close()
+ }
+ }
+
+ 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("PIONEER_INTERGRATION_APP_LOG")?.lowercase()) {
+ "info" -> HttpLoggingInterceptor.Level.BASIC
+ "debug" -> HttpLoggingInterceptor.Level.BODY
+ else -> null
+ }
+ if (logLevel != null) {
+ clientBuilder.addNetworkInterceptor(
+ HttpLoggingInterceptor().setLevel(logLevel).apply { redactHeader("api_key") }
+ )
+ }
+
+ 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 suspend fun Call.executeAsync(): Response =
+ suspendCancellableCoroutine { continuation ->
+ continuation.invokeOnCancellation { this.cancel() }
+
+ enqueue(
+ object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ continuation.resumeWith(Result.failure(e))
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ continuation.resumeWith(Result.success(response))
+ }
+ }
+ )
+ }
+
+ private fun HttpRequest.toRequest(client: okhttp3.OkHttpClient): Request {
+ var body: RequestBody? = body?.toRequestBody()
+ if (body == null && requiresBody(method)) {
+ body = "".toRequestBody()
+ }
+
+ val builder = Request.Builder().url(toUrl()).method(method.name, body)
+ headers.names().forEach { name ->
+ headers.values(name).forEach { builder.addHeader(name, it) }
+ }
+
+ if (
+ !headers.names().contains("X-Stainless-Read-Timeout") && client.readTimeoutMillis != 0
+ ) {
+ builder.addHeader(
+ "X-Stainless-Read-Timeout",
+ Duration.ofMillis(client.readTimeoutMillis.toLong()).seconds.toString(),
+ )
+ }
+ if (!headers.names().contains("X-Stainless-Timeout") && client.callTimeoutMillis != 0) {
+ builder.addHeader(
+ "X-Stainless-Timeout",
+ Duration.ofMillis(client.callTimeoutMillis.toLong()).seconds.toString(),
+ )
+ }
+
+ return builder.build()
+ }
+
+ /** `OkHttpClient` always requires a request body for some methods. */
+ private fun requiresBody(method: HttpMethod): Boolean =
+ when (method) {
+ HttpMethod.POST,
+ HttpMethod.PUT,
+ HttpMethod.PATCH -> true
+ else -> false
+ }
+
+ private fun HttpRequest.toUrl(): String {
+ val builder = baseUrl.toHttpUrl().newBuilder()
+ pathSegments.forEach(builder::addPathSegment)
+ queryParams.keys().forEach { key ->
+ queryParams.values(key).forEach { builder.addQueryParameter(key, it) }
+ }
+
+ return builder.toString()
+ }
+
+ private fun HttpRequestBody.toRequestBody(): RequestBody {
+ val mediaType = contentType()?.toMediaType()
+ val length = contentLength()
+
+ return object : RequestBody() {
+ override fun contentType(): MediaType? = mediaType
+
+ override fun contentLength(): Long = length
+
+ override fun isOneShot(): Boolean = !repeatable()
+
+ override fun writeTo(sink: BufferedSink) = writeTo(sink.outputStream())
+ }
+ }
+
+ private fun 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 {
+ fun builder() = Builder()
+ }
+
+ class Builder internal constructor() {
+
+ private var timeout: Timeout = Timeout.default()
+ private var proxy: Proxy? = null
+ private var dispatcherExecutorService: ExecutorService? = null
+ private var sslSocketFactory: SSLSocketFactory? = null
+ private var trustManager: X509TrustManager? = null
+ private var hostnameVerifier: HostnameVerifier? = null
+
+ 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 dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
+ this.dispatcherExecutorService = dispatcherExecutorService
+ }
+
+ fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
+ this.sslSocketFactory = sslSocketFactory
+ }
+
+ fun trustManager(trustManager: X509TrustManager?) = apply {
+ this.trustManager = trustManager
+ }
+
+ fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply {
+ this.hostnameVerifier = hostnameVerifier
+ }
+
+ fun build(): OkHttpClient =
+ OkHttpClient(
+ okhttp3.OkHttpClient.Builder()
+ // `RetryingHttpClient` handles retries if the user enabled them.
+ .retryOnConnectionFailure(false)
+ .connectTimeout(timeout.connect())
+ .readTimeout(timeout.read())
+ .writeTimeout(timeout.write())
+ .callTimeout(timeout.request())
+ .proxy(proxy)
+ .apply {
+ dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
+
+ val sslSocketFactory = sslSocketFactory
+ val trustManager = trustManager
+ if (sslSocketFactory != null && trustManager != null) {
+ sslSocketFactory(sslSocketFactory, trustManager)
+ } else {
+ check((sslSocketFactory != null) == (trustManager != null)) {
+ "Both or none of `sslSocketFactory` and `trustManager` must be set, but only one was set"
+ }
+ }
+
+ hostnameVerifier?.let(::hostnameVerifier)
+ }
+ .build()
+ .apply {
+ // We usually make all our requests to the same host so it makes sense to
+ // raise the per-host limit to the overall limit.
+ dispatcher.maxRequestsPerHost = dispatcher.maxRequests
+ }
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClient.kt b/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClient.kt
new file mode 100644
index 0000000..8bc4fa6
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClient.kt
@@ -0,0 +1,304 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.client.okhttp
+
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClientImpl
+import com.pioneer_intergration_app.api.core.ClientOptions
+import com.pioneer_intergration_app.api.core.Sleeper
+import com.pioneer_intergration_app.api.core.Timeout
+import com.pioneer_intergration_app.api.core.http.Headers
+import com.pioneer_intergration_app.api.core.http.HttpClient
+import com.pioneer_intergration_app.api.core.http.QueryParams
+import com.pioneer_intergration_app.api.core.jsonMapper
+import java.net.Proxy
+import java.time.Clock
+import java.time.Duration
+import java.util.concurrent.ExecutorService
+import javax.net.ssl.HostnameVerifier
+import javax.net.ssl.SSLSocketFactory
+import javax.net.ssl.X509TrustManager
+
+/**
+ * A class that allows building an instance of [PioneerIntergrationAppClient] with [OkHttpClient] as
+ * the underlying [HttpClient].
+ */
+class PioneerIntergrationAppOkHttpClient private constructor() {
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [PioneerIntergrationAppClient].
+ */
+ fun builder() = Builder()
+
+ /**
+ * Returns a client configured using system properties and environment variables.
+ *
+ * @see ClientOptions.Builder.fromEnv
+ */
+ fun fromEnv(): PioneerIntergrationAppClient = builder().fromEnv().build()
+ }
+
+ /** A builder for [PioneerIntergrationAppOkHttpClient]. */
+ class Builder internal constructor() {
+
+ private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
+ private var dispatcherExecutorService: ExecutorService? = null
+ private var proxy: Proxy? = null
+ private var sslSocketFactory: SSLSocketFactory? = null
+ private var trustManager: X509TrustManager? = null
+ private var hostnameVerifier: HostnameVerifier? = null
+
+ /**
+ * The executor service to use for running HTTP requests.
+ *
+ * Defaults to OkHttp's
+ * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
+ *
+ * This class takes ownership of the executor service and shuts it down when closed.
+ */
+ fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
+ this.dispatcherExecutorService = dispatcherExecutorService
+ }
+
+ fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
+
+ /**
+ * The socket factory used to secure HTTPS connections.
+ *
+ * If this is set, then [trustManager] must also be set.
+ *
+ * If unset, then the system default is used. Most applications should not call this method,
+ * and instead use the system default. The default include special optimizations that can be
+ * lost if the implementation is modified.
+ */
+ fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
+ this.sslSocketFactory = sslSocketFactory
+ }
+
+ /**
+ * The trust manager used to secure HTTPS connections.
+ *
+ * If this is set, then [sslSocketFactory] must also be set.
+ *
+ * If unset, then the system default is used. Most applications should not call this method,
+ * and instead use the system default. The default include special optimizations that can be
+ * lost if the implementation is modified.
+ */
+ fun trustManager(trustManager: X509TrustManager?) = apply {
+ this.trustManager = trustManager
+ }
+
+ /**
+ * The verifier used to confirm that response certificates apply to requested hostnames for
+ * HTTPS connections.
+ *
+ * If unset, then a default hostname verifier is used.
+ */
+ fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply {
+ this.hostnameVerifier = hostnameVerifier
+ }
+
+ /**
+ * Whether to throw an exception if any of the Jackson versions detected at runtime are
+ * incompatible with the SDK's minimum supported Jackson version (2.13.4).
+ *
+ * Defaults to true. Use extreme caution when disabling this option. There is no guarantee
+ * that the SDK will work correctly when using an incompatible Jackson version.
+ */
+ fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply {
+ clientOptions.checkJacksonVersionCompatibility(checkJacksonVersionCompatibility)
+ }
+
+ /**
+ * The Jackson JSON mapper to use for serializing and deserializing JSON.
+ *
+ * Defaults to [com.pioneer_intergration_app.api.core.jsonMapper]. The default is usually
+ * sufficient and rarely needs to be overridden.
+ */
+ fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
+
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) }
+
+ /**
+ * The clock to use for operations that require timing, like retries.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ fun clock(clock: Clock) = apply { clientOptions.clock(clock) }
+
+ /**
+ * The base URL to use for every request.
+ *
+ * Defaults to the production environment: `https://petstore3.swagger.io/api/v3`.
+ */
+ fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) }
+
+ /**
+ * Whether to call `validate` on every response before returning it.
+ *
+ * Defaults to false, which means the shape of the response will not be validated upfront.
+ * Instead, validation will only occur for the parts of the response that are accessed.
+ */
+ fun responseValidation(responseValidation: Boolean) = apply {
+ clientOptions.responseValidation(responseValidation)
+ }
+
+ /**
+ * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding
+ * retries.
+ *
+ * Defaults to [Timeout.default].
+ */
+ fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) }
+
+ /**
+ * Sets the maximum time allowed for a complete HTTP call, not including retries.
+ *
+ * See [Timeout.request] for more details.
+ *
+ * For fine-grained control, pass a [Timeout] object.
+ */
+ fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) }
+
+ /**
+ * The maximum number of times to retry failed requests, with a short exponential backoff
+ * between requests.
+ *
+ * Only the following error types are retried:
+ * - Connection errors (for example, due to a network connectivity problem)
+ * - 408 Request Timeout
+ * - 409 Conflict
+ * - 429 Rate Limit
+ * - 5xx Internal
+ *
+ * The API may also explicitly instruct the SDK to retry or not retry a request.
+ *
+ * Defaults to 2.
+ */
+ fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) }
+
+ fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }
+
+ fun headers(headers: Headers) = apply { clientOptions.headers(headers) }
+
+ fun headers(headers: Map>) = apply {
+ clientOptions.headers(headers)
+ }
+
+ fun putHeader(name: String, value: String) = apply { clientOptions.putHeader(name, value) }
+
+ fun putHeaders(name: String, values: Iterable) = apply {
+ clientOptions.putHeaders(name, values)
+ }
+
+ fun putAllHeaders(headers: Headers) = apply { clientOptions.putAllHeaders(headers) }
+
+ fun putAllHeaders(headers: Map>) = apply {
+ clientOptions.putAllHeaders(headers)
+ }
+
+ fun replaceHeaders(name: String, value: String) = apply {
+ clientOptions.replaceHeaders(name, value)
+ }
+
+ fun replaceHeaders(name: String, values: Iterable) = apply {
+ clientOptions.replaceHeaders(name, values)
+ }
+
+ fun replaceAllHeaders(headers: Headers) = apply { clientOptions.replaceAllHeaders(headers) }
+
+ fun replaceAllHeaders(headers: Map>) = apply {
+ clientOptions.replaceAllHeaders(headers)
+ }
+
+ fun removeHeaders(name: String) = apply { clientOptions.removeHeaders(name) }
+
+ fun removeAllHeaders(names: Set) = apply { clientOptions.removeAllHeaders(names) }
+
+ fun queryParams(queryParams: QueryParams) = apply { clientOptions.queryParams(queryParams) }
+
+ fun queryParams(queryParams: Map>) = apply {
+ clientOptions.queryParams(queryParams)
+ }
+
+ fun putQueryParam(key: String, value: String) = apply {
+ clientOptions.putQueryParam(key, value)
+ }
+
+ fun putQueryParams(key: String, values: Iterable) = apply {
+ clientOptions.putQueryParams(key, values)
+ }
+
+ fun putAllQueryParams(queryParams: QueryParams) = apply {
+ clientOptions.putAllQueryParams(queryParams)
+ }
+
+ fun putAllQueryParams(queryParams: Map>) = apply {
+ clientOptions.putAllQueryParams(queryParams)
+ }
+
+ fun replaceQueryParams(key: String, value: String) = apply {
+ clientOptions.replaceQueryParams(key, value)
+ }
+
+ fun replaceQueryParams(key: String, values: Iterable) = apply {
+ clientOptions.replaceQueryParams(key, values)
+ }
+
+ fun replaceAllQueryParams(queryParams: QueryParams) = apply {
+ clientOptions.replaceAllQueryParams(queryParams)
+ }
+
+ fun replaceAllQueryParams(queryParams: Map>) = apply {
+ clientOptions.replaceAllQueryParams(queryParams)
+ }
+
+ fun removeQueryParams(key: String) = apply { clientOptions.removeQueryParams(key) }
+
+ fun removeAllQueryParams(keys: Set) = apply {
+ clientOptions.removeAllQueryParams(keys)
+ }
+
+ /**
+ * Updates configuration using system properties and environment variables.
+ *
+ * @see ClientOptions.Builder.fromEnv
+ */
+ fun fromEnv() = apply { clientOptions.fromEnv() }
+
+ /**
+ * Returns an immutable instance of [PioneerIntergrationAppClient].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): PioneerIntergrationAppClient =
+ PioneerIntergrationAppClientImpl(
+ clientOptions
+ .httpClient(
+ OkHttpClient.builder()
+ .timeout(clientOptions.timeout())
+ .proxy(proxy)
+ .dispatcherExecutorService(dispatcherExecutorService)
+ .sslSocketFactory(sslSocketFactory)
+ .trustManager(trustManager)
+ .hostnameVerifier(hostnameVerifier)
+ .build()
+ )
+ .build()
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClientAsync.kt b/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClientAsync.kt
new file mode 100644
index 0000000..77fdad5
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-client-okhttp/src/main/kotlin/com/pioneer_intergration_app/api/client/okhttp/PioneerIntergrationAppOkHttpClientAsync.kt
@@ -0,0 +1,305 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.client.okhttp
+
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClientAsync
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClientAsyncImpl
+import com.pioneer_intergration_app.api.core.ClientOptions
+import com.pioneer_intergration_app.api.core.Sleeper
+import com.pioneer_intergration_app.api.core.Timeout
+import com.pioneer_intergration_app.api.core.http.Headers
+import com.pioneer_intergration_app.api.core.http.HttpClient
+import com.pioneer_intergration_app.api.core.http.QueryParams
+import com.pioneer_intergration_app.api.core.jsonMapper
+import java.net.Proxy
+import java.time.Clock
+import java.time.Duration
+import java.util.concurrent.ExecutorService
+import javax.net.ssl.HostnameVerifier
+import javax.net.ssl.SSLSocketFactory
+import javax.net.ssl.X509TrustManager
+
+/**
+ * A class that allows building an instance of [PioneerIntergrationAppClientAsync] with
+ * [OkHttpClient] as the underlying [HttpClient].
+ */
+class PioneerIntergrationAppOkHttpClientAsync private constructor() {
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of
+ * [PioneerIntergrationAppClientAsync].
+ */
+ fun builder() = Builder()
+
+ /**
+ * Returns a client configured using system properties and environment variables.
+ *
+ * @see ClientOptions.Builder.fromEnv
+ */
+ fun fromEnv(): PioneerIntergrationAppClientAsync = builder().fromEnv().build()
+ }
+
+ /** A builder for [PioneerIntergrationAppOkHttpClientAsync]. */
+ class Builder internal constructor() {
+
+ private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
+ private var dispatcherExecutorService: ExecutorService? = null
+ private var proxy: Proxy? = null
+ private var sslSocketFactory: SSLSocketFactory? = null
+ private var trustManager: X509TrustManager? = null
+ private var hostnameVerifier: HostnameVerifier? = null
+
+ /**
+ * The executor service to use for running HTTP requests.
+ *
+ * Defaults to OkHttp's
+ * [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
+ *
+ * This class takes ownership of the executor service and shuts it down when closed.
+ */
+ fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
+ this.dispatcherExecutorService = dispatcherExecutorService
+ }
+
+ fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
+
+ /**
+ * The socket factory used to secure HTTPS connections.
+ *
+ * If this is set, then [trustManager] must also be set.
+ *
+ * If unset, then the system default is used. Most applications should not call this method,
+ * and instead use the system default. The default include special optimizations that can be
+ * lost if the implementation is modified.
+ */
+ fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
+ this.sslSocketFactory = sslSocketFactory
+ }
+
+ /**
+ * The trust manager used to secure HTTPS connections.
+ *
+ * If this is set, then [sslSocketFactory] must also be set.
+ *
+ * If unset, then the system default is used. Most applications should not call this method,
+ * and instead use the system default. The default include special optimizations that can be
+ * lost if the implementation is modified.
+ */
+ fun trustManager(trustManager: X509TrustManager?) = apply {
+ this.trustManager = trustManager
+ }
+
+ /**
+ * The verifier used to confirm that response certificates apply to requested hostnames for
+ * HTTPS connections.
+ *
+ * If unset, then a default hostname verifier is used.
+ */
+ fun hostnameVerifier(hostnameVerifier: HostnameVerifier?) = apply {
+ this.hostnameVerifier = hostnameVerifier
+ }
+
+ /**
+ * Whether to throw an exception if any of the Jackson versions detected at runtime are
+ * incompatible with the SDK's minimum supported Jackson version (2.13.4).
+ *
+ * Defaults to true. Use extreme caution when disabling this option. There is no guarantee
+ * that the SDK will work correctly when using an incompatible Jackson version.
+ */
+ fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply {
+ clientOptions.checkJacksonVersionCompatibility(checkJacksonVersionCompatibility)
+ }
+
+ /**
+ * The Jackson JSON mapper to use for serializing and deserializing JSON.
+ *
+ * Defaults to [com.pioneer_intergration_app.api.core.jsonMapper]. The default is usually
+ * sufficient and rarely needs to be overridden.
+ */
+ fun jsonMapper(jsonMapper: JsonMapper) = apply { clientOptions.jsonMapper(jsonMapper) }
+
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) }
+
+ /**
+ * The clock to use for operations that require timing, like retries.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ fun clock(clock: Clock) = apply { clientOptions.clock(clock) }
+
+ /**
+ * The base URL to use for every request.
+ *
+ * Defaults to the production environment: `https://petstore3.swagger.io/api/v3`.
+ */
+ fun baseUrl(baseUrl: String?) = apply { clientOptions.baseUrl(baseUrl) }
+
+ /**
+ * Whether to call `validate` on every response before returning it.
+ *
+ * Defaults to false, which means the shape of the response will not be validated upfront.
+ * Instead, validation will only occur for the parts of the response that are accessed.
+ */
+ fun responseValidation(responseValidation: Boolean) = apply {
+ clientOptions.responseValidation(responseValidation)
+ }
+
+ /**
+ * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding
+ * retries.
+ *
+ * Defaults to [Timeout.default].
+ */
+ fun timeout(timeout: Timeout) = apply { clientOptions.timeout(timeout) }
+
+ /**
+ * Sets the maximum time allowed for a complete HTTP call, not including retries.
+ *
+ * See [Timeout.request] for more details.
+ *
+ * For fine-grained control, pass a [Timeout] object.
+ */
+ fun timeout(timeout: Duration) = apply { clientOptions.timeout(timeout) }
+
+ /**
+ * The maximum number of times to retry failed requests, with a short exponential backoff
+ * between requests.
+ *
+ * Only the following error types are retried:
+ * - Connection errors (for example, due to a network connectivity problem)
+ * - 408 Request Timeout
+ * - 409 Conflict
+ * - 429 Rate Limit
+ * - 5xx Internal
+ *
+ * The API may also explicitly instruct the SDK to retry or not retry a request.
+ *
+ * Defaults to 2.
+ */
+ fun maxRetries(maxRetries: Int) = apply { clientOptions.maxRetries(maxRetries) }
+
+ fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }
+
+ fun headers(headers: Headers) = apply { clientOptions.headers(headers) }
+
+ fun headers(headers: Map>) = apply {
+ clientOptions.headers(headers)
+ }
+
+ fun putHeader(name: String, value: String) = apply { clientOptions.putHeader(name, value) }
+
+ fun putHeaders(name: String, values: Iterable) = apply {
+ clientOptions.putHeaders(name, values)
+ }
+
+ fun putAllHeaders(headers: Headers) = apply { clientOptions.putAllHeaders(headers) }
+
+ fun putAllHeaders(headers: Map>) = apply {
+ clientOptions.putAllHeaders(headers)
+ }
+
+ fun replaceHeaders(name: String, value: String) = apply {
+ clientOptions.replaceHeaders(name, value)
+ }
+
+ fun replaceHeaders(name: String, values: Iterable) = apply {
+ clientOptions.replaceHeaders(name, values)
+ }
+
+ fun replaceAllHeaders(headers: Headers) = apply { clientOptions.replaceAllHeaders(headers) }
+
+ fun replaceAllHeaders(headers: Map>) = apply {
+ clientOptions.replaceAllHeaders(headers)
+ }
+
+ fun removeHeaders(name: String) = apply { clientOptions.removeHeaders(name) }
+
+ fun removeAllHeaders(names: Set) = apply { clientOptions.removeAllHeaders(names) }
+
+ fun queryParams(queryParams: QueryParams) = apply { clientOptions.queryParams(queryParams) }
+
+ fun queryParams(queryParams: Map>) = apply {
+ clientOptions.queryParams(queryParams)
+ }
+
+ fun putQueryParam(key: String, value: String) = apply {
+ clientOptions.putQueryParam(key, value)
+ }
+
+ fun putQueryParams(key: String, values: Iterable) = apply {
+ clientOptions.putQueryParams(key, values)
+ }
+
+ fun putAllQueryParams(queryParams: QueryParams) = apply {
+ clientOptions.putAllQueryParams(queryParams)
+ }
+
+ fun putAllQueryParams(queryParams: Map>) = apply {
+ clientOptions.putAllQueryParams(queryParams)
+ }
+
+ fun replaceQueryParams(key: String, value: String) = apply {
+ clientOptions.replaceQueryParams(key, value)
+ }
+
+ fun replaceQueryParams(key: String, values: Iterable) = apply {
+ clientOptions.replaceQueryParams(key, values)
+ }
+
+ fun replaceAllQueryParams(queryParams: QueryParams) = apply {
+ clientOptions.replaceAllQueryParams(queryParams)
+ }
+
+ fun replaceAllQueryParams(queryParams: Map>) = apply {
+ clientOptions.replaceAllQueryParams(queryParams)
+ }
+
+ fun removeQueryParams(key: String) = apply { clientOptions.removeQueryParams(key) }
+
+ fun removeAllQueryParams(keys: Set) = apply {
+ clientOptions.removeAllQueryParams(keys)
+ }
+
+ /**
+ * Updates configuration using system properties and environment variables.
+ *
+ * @see ClientOptions.Builder.fromEnv
+ */
+ fun fromEnv() = apply { clientOptions.fromEnv() }
+
+ /**
+ * Returns an immutable instance of [PioneerIntergrationAppClientAsync].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): PioneerIntergrationAppClientAsync =
+ PioneerIntergrationAppClientAsyncImpl(
+ clientOptions
+ .httpClient(
+ OkHttpClient.builder()
+ .timeout(clientOptions.timeout())
+ .proxy(proxy)
+ .dispatcherExecutorService(dispatcherExecutorService)
+ .sslSocketFactory(sslSocketFactory)
+ .trustManager(trustManager)
+ .hostnameVerifier(hostnameVerifier)
+ .build()
+ )
+ .build()
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/build.gradle.kts b/pioneer-intergration-app-kotlin-core/build.gradle.kts
new file mode 100644
index 0000000..34f158e
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/build.gradle.kts
@@ -0,0 +1,44 @@
+plugins {
+ id("pioneer-intergration-app.kotlin")
+ id("pioneer-intergration-app.publish")
+}
+
+configurations.all {
+ resolutionStrategy {
+ // Compile and test against a lower Jackson version to ensure we're compatible with it. Note that
+ // we generally support 2.13.4, but test against 2.14.0 because 2.13.4 has some annoying (but
+ // niche) bugs (users should upgrade if they encounter them). We publish with a higher version
+ // (see below) to ensure users depend on a secure version by default.
+ force("com.fasterxml.jackson.core:jackson-core:2.14.0")
+ force("com.fasterxml.jackson.core:jackson-databind:2.14.0")
+ force("com.fasterxml.jackson.core:jackson-annotations:2.14.0")
+ force("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.14.0")
+ force("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0")
+ force("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0")
+ }
+}
+
+dependencies {
+ api("com.fasterxml.jackson.core:jackson-core:2.18.2")
+ api("com.fasterxml.jackson.core:jackson-databind:2.18.2")
+ api("com.google.errorprone:error_prone_annotations:2.33.0")
+
+ implementation("com.fasterxml.jackson.core:jackson-annotations:2.18.2")
+ implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2")
+ implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2")
+ implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
+ implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4")
+ implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
+
+ testImplementation(kotlin("test"))
+ testImplementation(project(":pioneer-intergration-app-kotlin-client-okhttp"))
+ testImplementation("com.github.tomakehurst:wiremock-jre8:2.35.2")
+ testImplementation("org.assertj:assertj-core:3.27.7")
+ testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
+ testImplementation("org.junit.jupiter:junit-jupiter-params:5.9.3")
+ testImplementation("org.junit-pioneer:junit-pioneer:1.9.1")
+ testImplementation("org.mockito:mockito-core:5.14.2")
+ testImplementation("org.mockito:mockito-junit-jupiter:5.14.2")
+ testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClient.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClient.kt
new file mode 100644
index 0000000..2f4ee60
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClient.kt
@@ -0,0 +1,86 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.client
+
+import com.pioneer_intergration_app.api.core.ClientOptions
+import com.pioneer_intergration_app.api.services.blocking.PetService
+import com.pioneer_intergration_app.api.services.blocking.StoreService
+import com.pioneer_intergration_app.api.services.blocking.UserService
+
+/**
+ * A client for interacting with the Pioneer Intergration App 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 PioneerIntergrationAppClient {
+
+ /**
+ * 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(): PioneerIntergrationAppClientAsync
+
+ /**
+ * Returns a view of this service that provides access to raw HTTP responses for each method.
+ */
+ fun withRawResponse(): WithRawResponse
+
+ /**
+ * Returns a view of this service with the given option modifications applied.
+ *
+ * The original service is not modified.
+ */
+ fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PioneerIntergrationAppClient
+
+ fun pets(): PetService
+
+ fun store(): StoreService
+
+ fun users(): UserService
+
+ /**
+ * 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 [PioneerIntergrationAppClient] that provides access to raw HTTP responses for each
+ * method.
+ */
+ interface WithRawResponse {
+
+ /**
+ * Returns a view of this service with the given option modifications applied.
+ *
+ * The original service is not modified.
+ */
+ fun withOptions(
+ modifier: (ClientOptions.Builder) -> Unit
+ ): PioneerIntergrationAppClient.WithRawResponse
+
+ fun pets(): PetService.WithRawResponse
+
+ fun store(): StoreService.WithRawResponse
+
+ fun users(): UserService.WithRawResponse
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsync.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsync.kt
new file mode 100644
index 0000000..5777d3f
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsync.kt
@@ -0,0 +1,86 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.client
+
+import com.pioneer_intergration_app.api.core.ClientOptions
+import com.pioneer_intergration_app.api.services.async.PetServiceAsync
+import com.pioneer_intergration_app.api.services.async.StoreServiceAsync
+import com.pioneer_intergration_app.api.services.async.UserServiceAsync
+
+/**
+ * A client for interacting with the Pioneer Intergration App 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 PioneerIntergrationAppClientAsync {
+
+ /**
+ * 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(): PioneerIntergrationAppClient
+
+ /**
+ * Returns a view of this service that provides access to raw HTTP responses for each method.
+ */
+ fun withRawResponse(): WithRawResponse
+
+ /**
+ * Returns a view of this service with the given option modifications applied.
+ *
+ * The original service is not modified.
+ */
+ fun withOptions(modifier: (ClientOptions.Builder) -> Unit): PioneerIntergrationAppClientAsync
+
+ fun pets(): PetServiceAsync
+
+ fun store(): StoreServiceAsync
+
+ fun users(): UserServiceAsync
+
+ /**
+ * 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 [PioneerIntergrationAppClientAsync] that provides access to raw HTTP responses for
+ * each method.
+ */
+ interface WithRawResponse {
+
+ /**
+ * Returns a view of this service with the given option modifications applied.
+ *
+ * The original service is not modified.
+ */
+ fun withOptions(
+ modifier: (ClientOptions.Builder) -> Unit
+ ): PioneerIntergrationAppClientAsync.WithRawResponse
+
+ fun pets(): PetServiceAsync.WithRawResponse
+
+ fun store(): StoreServiceAsync.WithRawResponse
+
+ fun users(): UserServiceAsync.WithRawResponse
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsyncImpl.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsyncImpl.kt
new file mode 100644
index 0000000..ceb3c8f
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientAsyncImpl.kt
@@ -0,0 +1,88 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.client
+
+import com.pioneer_intergration_app.api.core.ClientOptions
+import com.pioneer_intergration_app.api.core.getPackageVersion
+import com.pioneer_intergration_app.api.services.async.PetServiceAsync
+import com.pioneer_intergration_app.api.services.async.PetServiceAsyncImpl
+import com.pioneer_intergration_app.api.services.async.StoreServiceAsync
+import com.pioneer_intergration_app.api.services.async.StoreServiceAsyncImpl
+import com.pioneer_intergration_app.api.services.async.UserServiceAsync
+import com.pioneer_intergration_app.api.services.async.UserServiceAsyncImpl
+
+class PioneerIntergrationAppClientAsyncImpl(private val clientOptions: ClientOptions) :
+ PioneerIntergrationAppClientAsync {
+
+ private val clientOptionsWithUserAgent =
+ if (clientOptions.headers.names().contains("User-Agent")) clientOptions
+ else
+ clientOptions
+ .toBuilder()
+ .putHeader("User-Agent", "${javaClass.simpleName}/Kotlin ${getPackageVersion()}")
+ .build()
+
+ // Pass the original clientOptions so that this client sets its own User-Agent.
+ private val sync: PioneerIntergrationAppClient by lazy {
+ PioneerIntergrationAppClientImpl(clientOptions)
+ }
+
+ private val withRawResponse: PioneerIntergrationAppClientAsync.WithRawResponse by lazy {
+ WithRawResponseImpl(clientOptions)
+ }
+
+ private val pets: PetServiceAsync by lazy { PetServiceAsyncImpl(clientOptionsWithUserAgent) }
+
+ private val store: StoreServiceAsync by lazy {
+ StoreServiceAsyncImpl(clientOptionsWithUserAgent)
+ }
+
+ private val users: UserServiceAsync by lazy { UserServiceAsyncImpl(clientOptionsWithUserAgent) }
+
+ override fun sync(): PioneerIntergrationAppClient = sync
+
+ override fun withRawResponse(): PioneerIntergrationAppClientAsync.WithRawResponse =
+ withRawResponse
+
+ override fun withOptions(
+ modifier: (ClientOptions.Builder) -> Unit
+ ): PioneerIntergrationAppClientAsync =
+ PioneerIntergrationAppClientAsyncImpl(clientOptions.toBuilder().apply(modifier).build())
+
+ override fun pets(): PetServiceAsync = pets
+
+ override fun store(): StoreServiceAsync = store
+
+ override fun users(): UserServiceAsync = users
+
+ override fun close() = clientOptions.close()
+
+ class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) :
+ PioneerIntergrationAppClientAsync.WithRawResponse {
+
+ private val pets: PetServiceAsync.WithRawResponse by lazy {
+ PetServiceAsyncImpl.WithRawResponseImpl(clientOptions)
+ }
+
+ private val store: StoreServiceAsync.WithRawResponse by lazy {
+ StoreServiceAsyncImpl.WithRawResponseImpl(clientOptions)
+ }
+
+ private val users: UserServiceAsync.WithRawResponse by lazy {
+ UserServiceAsyncImpl.WithRawResponseImpl(clientOptions)
+ }
+
+ override fun withOptions(
+ modifier: (ClientOptions.Builder) -> Unit
+ ): PioneerIntergrationAppClientAsync.WithRawResponse =
+ PioneerIntergrationAppClientAsyncImpl.WithRawResponseImpl(
+ clientOptions.toBuilder().apply(modifier).build()
+ )
+
+ override fun pets(): PetServiceAsync.WithRawResponse = pets
+
+ override fun store(): StoreServiceAsync.WithRawResponse = store
+
+ override fun users(): UserServiceAsync.WithRawResponse = users
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientImpl.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientImpl.kt
new file mode 100644
index 0000000..f32aaaa
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/client/PioneerIntergrationAppClientImpl.kt
@@ -0,0 +1,85 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.client
+
+import com.pioneer_intergration_app.api.core.ClientOptions
+import com.pioneer_intergration_app.api.core.getPackageVersion
+import com.pioneer_intergration_app.api.services.blocking.PetService
+import com.pioneer_intergration_app.api.services.blocking.PetServiceImpl
+import com.pioneer_intergration_app.api.services.blocking.StoreService
+import com.pioneer_intergration_app.api.services.blocking.StoreServiceImpl
+import com.pioneer_intergration_app.api.services.blocking.UserService
+import com.pioneer_intergration_app.api.services.blocking.UserServiceImpl
+
+class PioneerIntergrationAppClientImpl(private val clientOptions: ClientOptions) :
+ PioneerIntergrationAppClient {
+
+ private val clientOptionsWithUserAgent =
+ if (clientOptions.headers.names().contains("User-Agent")) clientOptions
+ else
+ clientOptions
+ .toBuilder()
+ .putHeader("User-Agent", "${javaClass.simpleName}/Kotlin ${getPackageVersion()}")
+ .build()
+
+ // Pass the original clientOptions so that this client sets its own User-Agent.
+ private val async: PioneerIntergrationAppClientAsync by lazy {
+ PioneerIntergrationAppClientAsyncImpl(clientOptions)
+ }
+
+ private val withRawResponse: PioneerIntergrationAppClient.WithRawResponse by lazy {
+ WithRawResponseImpl(clientOptions)
+ }
+
+ private val pets: PetService by lazy { PetServiceImpl(clientOptionsWithUserAgent) }
+
+ private val store: StoreService by lazy { StoreServiceImpl(clientOptionsWithUserAgent) }
+
+ private val users: UserService by lazy { UserServiceImpl(clientOptionsWithUserAgent) }
+
+ override fun async(): PioneerIntergrationAppClientAsync = async
+
+ override fun withRawResponse(): PioneerIntergrationAppClient.WithRawResponse = withRawResponse
+
+ override fun withOptions(
+ modifier: (ClientOptions.Builder) -> Unit
+ ): PioneerIntergrationAppClient =
+ PioneerIntergrationAppClientImpl(clientOptions.toBuilder().apply(modifier).build())
+
+ override fun pets(): PetService = pets
+
+ override fun store(): StoreService = store
+
+ override fun users(): UserService = users
+
+ override fun close() = clientOptions.close()
+
+ class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) :
+ PioneerIntergrationAppClient.WithRawResponse {
+
+ private val pets: PetService.WithRawResponse by lazy {
+ PetServiceImpl.WithRawResponseImpl(clientOptions)
+ }
+
+ private val store: StoreService.WithRawResponse by lazy {
+ StoreServiceImpl.WithRawResponseImpl(clientOptions)
+ }
+
+ private val users: UserService.WithRawResponse by lazy {
+ UserServiceImpl.WithRawResponseImpl(clientOptions)
+ }
+
+ override fun withOptions(
+ modifier: (ClientOptions.Builder) -> Unit
+ ): PioneerIntergrationAppClient.WithRawResponse =
+ PioneerIntergrationAppClientImpl.WithRawResponseImpl(
+ clientOptions.toBuilder().apply(modifier).build()
+ )
+
+ override fun pets(): PetService.WithRawResponse = pets
+
+ override fun store(): StoreService.WithRawResponse = store
+
+ override fun users(): UserService.WithRawResponse = users
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/BaseDeserializer.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/BaseDeserializer.kt
new file mode 100644
index 0000000..794e0bb
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/BaseDeserializer.kt
@@ -0,0 +1,44 @@
+package com.pioneer_intergration_app.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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/BaseSerializer.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/BaseSerializer.kt
new file mode 100644
index 0000000..7d0b921
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/BaseSerializer.kt
@@ -0,0 +1,6 @@
+package com.pioneer_intergration_app.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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Check.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Check.kt
new file mode 100644
index 0000000..b848fb4
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Check.kt
@@ -0,0 +1,86 @@
+package com.pioneer_intergration_app.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" }
+
+internal fun checkKnown(name: String, value: JsonField): T =
+ value.asKnown()
+ ?: throw IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}")
+
+internal fun checkKnown(name: String, value: MultipartField): T =
+ value.value.asKnown()
+ ?: throw IllegalStateException("`$name` is not a known type: ${value.javaClass.simpleName}")
+
+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}" }
+ }
+
+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}"
+ }
+ }
+
+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}"
+ }
+ }
+
+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/ummitsmerogers-droid/GetTrustedInstallerShell#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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/ClientOptions.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/ClientOptions.kt
new file mode 100644
index 0000000..4b76840
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/ClientOptions.kt
@@ -0,0 +1,447 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.core
+
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.pioneer_intergration_app.api.core.http.Headers
+import com.pioneer_intergration_app.api.core.http.HttpClient
+import com.pioneer_intergration_app.api.core.http.PhantomReachableClosingHttpClient
+import com.pioneer_intergration_app.api.core.http.QueryParams
+import com.pioneer_intergration_app.api.core.http.RetryingHttpClient
+import java.time.Clock
+import java.time.Duration
+
+/** A class representing the SDK client configuration. */
+class ClientOptions
+private constructor(
+ private val originalHttpClient: HttpClient,
+ /**
+ * The HTTP client to use in the SDK.
+ *
+ * Use the one published in `pioneer-intergration-app-kotlin-client-okhttp` or implement your
+ * own.
+ *
+ * This class takes ownership of the client and closes it when closed.
+ */
+ val httpClient: HttpClient,
+ /**
+ * Whether to throw an exception if any of the Jackson versions detected at runtime are
+ * incompatible with the SDK's minimum supported Jackson version (2.13.4).
+ *
+ * Defaults to true. Use extreme caution when disabling this option. There is no guarantee that
+ * the SDK will work correctly when using an incompatible Jackson version.
+ */
+ val checkJacksonVersionCompatibility: Boolean,
+ /**
+ * The Jackson JSON mapper to use for serializing and deserializing JSON.
+ *
+ * Defaults to [com.pioneer_intergration_app.api.core.jsonMapper]. The default is usually
+ * sufficient and rarely needs to be overridden.
+ */
+ val jsonMapper: JsonMapper,
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ val sleeper: Sleeper,
+ /**
+ * The clock to use for operations that require timing, like retries.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ val clock: Clock,
+ private val baseUrl: String?,
+ /** Headers to send with the request. */
+ val headers: Headers,
+ /** Query params to send with the request. */
+ val queryParams: QueryParams,
+ /**
+ * Whether to call `validate` on every response before returning it.
+ *
+ * Defaults to false, which means the shape of the response will not be validated upfront.
+ * Instead, validation will only occur for the parts of the response that are accessed.
+ */
+ val responseValidation: Boolean,
+ /**
+ * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding
+ * retries.
+ *
+ * Defaults to [Timeout.default].
+ */
+ val timeout: Timeout,
+ /**
+ * The maximum number of times to retry failed requests, with a short exponential backoff
+ * between requests.
+ *
+ * Only the following error types are retried:
+ * - Connection errors (for example, due to a network connectivity problem)
+ * - 408 Request Timeout
+ * - 409 Conflict
+ * - 429 Rate Limit
+ * - 5xx Internal
+ *
+ * The API may also explicitly instruct the SDK to retry or not retry a request.
+ *
+ * Defaults to 2.
+ */
+ val maxRetries: Int,
+ val apiKey: String,
+) {
+
+ init {
+ if (checkJacksonVersionCompatibility) {
+ checkJacksonVersionCompatibility()
+ }
+ }
+
+ /**
+ * The base URL to use for every request.
+ *
+ * Defaults to the production environment: `https://petstore3.swagger.io/api/v3`.
+ */
+ fun baseUrl(): String = baseUrl ?: PRODUCTION_URL
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ const val PRODUCTION_URL = "https://petstore3.swagger.io/api/v3"
+
+ /**
+ * Returns a mutable builder for constructing an instance of [ClientOptions].
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .httpClient()
+ * .apiKey()
+ * ```
+ */
+ fun builder() = Builder()
+
+ /**
+ * Returns options configured using system properties and environment variables.
+ *
+ * @see Builder.fromEnv
+ */
+ fun fromEnv(): ClientOptions = builder().fromEnv().build()
+ }
+
+ /** A builder for [ClientOptions]. */
+ class Builder internal constructor() {
+
+ private var httpClient: HttpClient? = null
+ private var checkJacksonVersionCompatibility: Boolean = true
+ private var jsonMapper: JsonMapper = jsonMapper()
+ private var sleeper: Sleeper? = null
+ private var clock: Clock = Clock.systemUTC()
+ private var baseUrl: String? = null
+ private var headers: Headers.Builder = Headers.builder()
+ private var queryParams: QueryParams.Builder = QueryParams.builder()
+ private var responseValidation: Boolean = false
+ private var timeout: Timeout = Timeout.default()
+ private var maxRetries: Int = 2
+ private var apiKey: String? = null
+
+ internal fun from(clientOptions: ClientOptions) = apply {
+ httpClient = clientOptions.originalHttpClient
+ checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility
+ jsonMapper = clientOptions.jsonMapper
+ sleeper = clientOptions.sleeper
+ clock = clientOptions.clock
+ baseUrl = clientOptions.baseUrl
+ headers = clientOptions.headers.toBuilder()
+ queryParams = clientOptions.queryParams.toBuilder()
+ responseValidation = clientOptions.responseValidation
+ timeout = clientOptions.timeout
+ maxRetries = clientOptions.maxRetries
+ apiKey = clientOptions.apiKey
+ }
+
+ /**
+ * The HTTP client to use in the SDK.
+ *
+ * Use the one published in `pioneer-intergration-app-kotlin-client-okhttp` or implement
+ * your own.
+ *
+ * This class takes ownership of the client and closes it when closed.
+ */
+ fun httpClient(httpClient: HttpClient) = apply {
+ this.httpClient = PhantomReachableClosingHttpClient(httpClient)
+ }
+
+ /**
+ * Whether to throw an exception if any of the Jackson versions detected at runtime are
+ * incompatible with the SDK's minimum supported Jackson version (2.13.4).
+ *
+ * Defaults to true. Use extreme caution when disabling this option. There is no guarantee
+ * that the SDK will work correctly when using an incompatible Jackson version.
+ */
+ fun checkJacksonVersionCompatibility(checkJacksonVersionCompatibility: Boolean) = apply {
+ this.checkJacksonVersionCompatibility = checkJacksonVersionCompatibility
+ }
+
+ /**
+ * The Jackson JSON mapper to use for serializing and deserializing JSON.
+ *
+ * Defaults to [com.pioneer_intergration_app.api.core.jsonMapper]. The default is usually
+ * sufficient and rarely needs to be overridden.
+ */
+ fun jsonMapper(jsonMapper: JsonMapper) = apply { this.jsonMapper = jsonMapper }
+
+ /**
+ * The interface to use for delaying execution, like during retries.
+ *
+ * This is primarily useful for using fake delays in tests.
+ *
+ * Defaults to real execution delays.
+ *
+ * This class takes ownership of the sleeper and closes it when closed.
+ */
+ fun sleeper(sleeper: Sleeper) = apply { this.sleeper = PhantomReachableSleeper(sleeper) }
+
+ /**
+ * The clock to use for operations that require timing, like retries.
+ *
+ * This is primarily useful for using a fake clock in tests.
+ *
+ * Defaults to [Clock.systemUTC].
+ */
+ fun clock(clock: Clock) = apply { this.clock = clock }
+
+ /**
+ * The base URL to use for every request.
+ *
+ * Defaults to the production environment: `https://petstore3.swagger.io/api/v3`.
+ */
+ fun baseUrl(baseUrl: String?) = apply { this.baseUrl = baseUrl }
+
+ /**
+ * Whether to call `validate` on every response before returning it.
+ *
+ * Defaults to false, which means the shape of the response will not be validated upfront.
+ * Instead, validation will only occur for the parts of the response that are accessed.
+ */
+ fun responseValidation(responseValidation: Boolean) = apply {
+ this.responseValidation = responseValidation
+ }
+
+ /**
+ * Sets the maximum time allowed for various parts of an HTTP call's lifecycle, excluding
+ * retries.
+ *
+ * Defaults to [Timeout.default].
+ */
+ fun timeout(timeout: Timeout) = apply { this.timeout = timeout }
+
+ /**
+ * Sets the maximum time allowed for a complete HTTP call, not including retries.
+ *
+ * See [Timeout.request] for more details.
+ *
+ * For fine-grained control, pass a [Timeout] object.
+ */
+ fun timeout(timeout: Duration) = timeout(Timeout.builder().request(timeout).build())
+
+ /**
+ * The maximum number of times to retry failed requests, with a short exponential backoff
+ * between requests.
+ *
+ * Only the following error types are retried:
+ * - Connection errors (for example, due to a network connectivity problem)
+ * - 408 Request Timeout
+ * - 409 Conflict
+ * - 429 Rate Limit
+ * - 5xx Internal
+ *
+ * The API may also explicitly instruct the SDK to retry or not retry a request.
+ *
+ * Defaults to 2.
+ */
+ fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries }
+
+ fun apiKey(apiKey: String) = apply { this.apiKey = apiKey }
+
+ fun headers(headers: Headers) = apply {
+ this.headers.clear()
+ putAllHeaders(headers)
+ }
+
+ fun headers(headers: Map>) = apply {
+ this.headers.clear()
+ putAllHeaders(headers)
+ }
+
+ fun putHeader(name: String, value: String) = apply { headers.put(name, value) }
+
+ fun putHeaders(name: String, values: Iterable) = apply { headers.put(name, values) }
+
+ fun putAllHeaders(headers: Headers) = apply { this.headers.putAll(headers) }
+
+ fun putAllHeaders(headers: Map>) = apply {
+ this.headers.putAll(headers)
+ }
+
+ fun replaceHeaders(name: String, value: String) = apply { headers.replace(name, value) }
+
+ fun replaceHeaders(name: String, values: Iterable) = apply {
+ headers.replace(name, values)
+ }
+
+ fun replaceAllHeaders(headers: Headers) = apply { this.headers.replaceAll(headers) }
+
+ fun replaceAllHeaders(headers: Map>) = apply {
+ this.headers.replaceAll(headers)
+ }
+
+ fun removeHeaders(name: String) = apply { headers.remove(name) }
+
+ fun removeAllHeaders(names: Set) = apply { headers.removeAll(names) }
+
+ fun queryParams(queryParams: QueryParams) = apply {
+ this.queryParams.clear()
+ putAllQueryParams(queryParams)
+ }
+
+ fun queryParams(queryParams: Map>) = apply {
+ this.queryParams.clear()
+ putAllQueryParams(queryParams)
+ }
+
+ fun putQueryParam(key: String, value: String) = apply { queryParams.put(key, value) }
+
+ fun putQueryParams(key: String, values: Iterable) = apply {
+ queryParams.put(key, values)
+ }
+
+ fun putAllQueryParams(queryParams: QueryParams) = apply {
+ this.queryParams.putAll(queryParams)
+ }
+
+ fun putAllQueryParams(queryParams: Map>) = apply {
+ this.queryParams.putAll(queryParams)
+ }
+
+ fun replaceQueryParams(key: String, value: String) = apply {
+ queryParams.replace(key, value)
+ }
+
+ fun replaceQueryParams(key: String, values: Iterable) = apply {
+ queryParams.replace(key, values)
+ }
+
+ fun replaceAllQueryParams(queryParams: QueryParams) = apply {
+ this.queryParams.replaceAll(queryParams)
+ }
+
+ fun replaceAllQueryParams(queryParams: Map>) = apply {
+ this.queryParams.replaceAll(queryParams)
+ }
+
+ fun removeQueryParams(key: String) = apply { queryParams.remove(key) }
+
+ fun removeAllQueryParams(keys: Set) = apply { queryParams.removeAll(keys) }
+
+ fun timeout(): Timeout = timeout
+
+ /**
+ * Updates configuration using system properties and environment variables.
+ *
+ * See this table for the available options:
+ *
+ * |Setter |System property |Environment variable |Required|Default value |
+ * |---------|---------------------------------------|-----------------------------------|--------|---------------------------------------|
+ * |`apiKey` |`pioneerintergrationapp.petstoreApiKey`|`PETSTORE_API_KEY` |true |- |
+ * |`baseUrl`|`pioneerintergrationapp.baseUrl` |`PIONEER_INTERGRATION_APP_BASE_URL`|true |`"https://petstore3.swagger.io/api/v3"`|
+ *
+ * System properties take precedence over environment variables.
+ */
+ fun fromEnv() = apply {
+ (System.getProperty("pioneerintergrationapp.baseUrl")
+ ?: System.getenv("PIONEER_INTERGRATION_APP_BASE_URL"))
+ ?.let { baseUrl(it) }
+ (System.getProperty("pioneerintergrationapp.petstoreApiKey")
+ ?: System.getenv("PETSTORE_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:
+ * ```kotlin
+ * .httpClient()
+ * .apiKey()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): ClientOptions {
+ val httpClient = checkRequired("httpClient", httpClient)
+ val sleeper = sleeper ?: PhantomReachableSleeper(DefaultSleeper())
+ val apiKey = checkRequired("apiKey", apiKey)
+
+ val headers = Headers.builder()
+ val queryParams = QueryParams.builder()
+ headers.put("X-Stainless-Lang", "kotlin")
+ headers.put("X-Stainless-Arch", getOsArch())
+ headers.put("X-Stainless-OS", getOsName())
+ headers.put("X-Stainless-OS-Version", getOsVersion())
+ headers.put("X-Stainless-Package-Version", getPackageVersion())
+ headers.put("X-Stainless-Runtime", "JRE")
+ headers.put("X-Stainless-Runtime-Version", getJavaVersion())
+ headers.put("X-Stainless-Kotlin-Version", KotlinVersion.CURRENT.toString())
+ apiKey.let {
+ if (!it.isEmpty()) {
+ headers.put("api_key", it)
+ }
+ }
+ headers.replaceAll(this.headers.build())
+ queryParams.replaceAll(this.queryParams.build())
+
+ return ClientOptions(
+ httpClient,
+ RetryingHttpClient.builder()
+ .httpClient(httpClient)
+ .sleeper(sleeper)
+ .clock(clock)
+ .maxRetries(maxRetries)
+ .build(),
+ checkJacksonVersionCompatibility,
+ jsonMapper,
+ sleeper,
+ clock,
+ baseUrl,
+ headers.build(),
+ queryParams.build(),
+ responseValidation,
+ timeout,
+ maxRetries,
+ apiKey,
+ )
+ }
+ }
+
+ /**
+ * Closes these client options, relinquishing any underlying resources.
+ *
+ * This is purposefully not inherited from [AutoCloseable] because the client options are
+ * long-lived and usually should not be synchronously closed via try-with-resources.
+ *
+ * It's also usually not necessary to call this method at all. the default client automatically
+ * releases threads and connections if they remain idle, but if you are writing an application
+ * that needs to aggressively release unused resources, then you may call this method.
+ */
+ fun close() {
+ httpClient.close()
+ sleeper.close()
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/DefaultSleeper.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/DefaultSleeper.kt
new file mode 100644
index 0000000..20ed4e4
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/DefaultSleeper.kt
@@ -0,0 +1,14 @@
+package com.pioneer_intergration_app.api.core
+
+import java.time.Duration
+import kotlin.time.toKotlinDuration
+import kotlinx.coroutines.delay
+
+class DefaultSleeper : Sleeper {
+
+ override fun sleep(duration: Duration) = Thread.sleep(duration.toMillis())
+
+ override suspend fun sleepAsync(duration: Duration) = delay(duration.toKotlinDuration())
+
+ override fun close() {}
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/ObjectMappers.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/ObjectMappers.kt
new file mode 100644
index 0000000..c9496b4
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/ObjectMappers.kt
@@ -0,0 +1,178 @@
+@file:JvmName("ObjectMappers")
+
+package com.pioneer_intergration_app.api.core
+
+import com.fasterxml.jackson.annotation.JsonInclude
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.JsonParseException
+import com.fasterxml.jackson.core.JsonParser
+import com.fasterxml.jackson.databind.DeserializationContext
+import com.fasterxml.jackson.databind.DeserializationFeature
+import com.fasterxml.jackson.databind.MapperFeature
+import com.fasterxml.jackson.databind.SerializationFeature
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.cfg.CoercionAction
+import com.fasterxml.jackson.databind.cfg.CoercionInputShape
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.fasterxml.jackson.databind.module.SimpleModule
+import com.fasterxml.jackson.databind.type.LogicalType
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
+import com.fasterxml.jackson.module.kotlin.kotlinModule
+import java.io.InputStream
+import java.time.DateTimeException
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.OffsetDateTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.time.temporal.ChronoField
+
+fun jsonMapper(): JsonMapper =
+ JsonMapper.builder()
+ .addModule(kotlinModule())
+ .addModule(Jdk8Module())
+ .addModule(JavaTimeModule())
+ .addModule(
+ SimpleModule()
+ .addSerializer(InputStreamSerializer)
+ .addDeserializer(OffsetDateTime::class.java, LenientOffsetDateTimeDeserializer())
+ )
+ .withCoercionConfig(LogicalType.Boolean) {
+ it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.String, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.Integer) {
+ it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.String, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.Float) {
+ it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.String, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.Textual) {
+ it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.DateTime) {
+ it.setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.Array) {
+ it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.String, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.Collection) {
+ it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.String, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.Map) {
+ it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.String, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Object, CoercionAction.Fail)
+ }
+ .withCoercionConfig(LogicalType.POJO) {
+ it.setCoercion(CoercionInputShape.Boolean, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Integer, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Float, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.String, CoercionAction.Fail)
+ .setCoercion(CoercionInputShape.Array, CoercionAction.Fail)
+ }
+ .serializationInclusion(JsonInclude.Include.NON_ABSENT)
+ .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
+ .disable(SerializationFeature.FLUSH_AFTER_WRITE_VALUE)
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .disable(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
+ .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS)
+ .disable(MapperFeature.AUTO_DETECT_CREATORS)
+ .disable(MapperFeature.AUTO_DETECT_FIELDS)
+ .disable(MapperFeature.AUTO_DETECT_GETTERS)
+ .disable(MapperFeature.AUTO_DETECT_IS_GETTERS)
+ .disable(MapperFeature.AUTO_DETECT_SETTERS)
+ .build()
+
+/** A serializer that serializes [InputStream] to bytes. */
+private object InputStreamSerializer : BaseSerializer(InputStream::class) {
+
+ private fun readResolve(): Any = InputStreamSerializer
+
+ override fun serialize(
+ value: InputStream?,
+ gen: JsonGenerator?,
+ serializers: SerializerProvider?,
+ ) {
+ if (value == null) {
+ gen?.writeNull()
+ } else {
+ value.use { gen?.writeBinary(it.readBytes()) }
+ }
+ }
+}
+
+/**
+ * A deserializer that can deserialize [OffsetDateTime] from datetimes, dates, and zoned datetimes.
+ */
+private class LenientOffsetDateTimeDeserializer :
+ StdDeserializer(OffsetDateTime::class.java) {
+
+ companion object {
+
+ private val DATE_TIME_FORMATTERS =
+ listOf(
+ DateTimeFormatter.ISO_LOCAL_DATE_TIME,
+ DateTimeFormatter.ISO_LOCAL_DATE,
+ DateTimeFormatter.ISO_ZONED_DATE_TIME,
+ )
+ }
+
+ override fun logicalType(): LogicalType = LogicalType.DateTime
+
+ override fun deserialize(p: JsonParser, context: DeserializationContext): OffsetDateTime {
+ val exceptions = mutableListOf()
+
+ for (formatter in DATE_TIME_FORMATTERS) {
+ try {
+ val temporal = formatter.parse(p.text)
+
+ return when {
+ !temporal.isSupported(ChronoField.HOUR_OF_DAY) ->
+ LocalDate.from(temporal)
+ .atStartOfDay()
+ .atZone(ZoneId.of("UTC"))
+ .toOffsetDateTime()
+ !temporal.isSupported(ChronoField.OFFSET_SECONDS) ->
+ LocalDateTime.from(temporal).atZone(ZoneId.of("UTC")).toOffsetDateTime()
+ else -> OffsetDateTime.from(temporal)
+ }
+ } catch (e: DateTimeException) {
+ exceptions.add(e)
+ }
+ }
+
+ throw JsonParseException(p, "Cannot parse `OffsetDateTime` from value: ${p.text}").apply {
+ exceptions.forEach { addSuppressed(it) }
+ }
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Params.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Params.kt
new file mode 100644
index 0000000..f9e735b
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Params.kt
@@ -0,0 +1,16 @@
+package com.pioneer_intergration_app.api.core
+
+import com.pioneer_intergration_app.api.core.http.Headers
+import com.pioneer_intergration_app.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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachable.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachable.kt
new file mode 100644
index 0000000..1fbd797
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachable.kt
@@ -0,0 +1,54 @@
+@file:JvmName("PhantomReachable")
+
+package com.pioneer_intergration_app.api.core
+
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppException
+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.
+ */
+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.
+ */
+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 PioneerIntergrationAppException("Unexpected reflective invocation failure", e)
+ }
+ }
+ } catch (e: ReflectiveOperationException) {
+ // We're running Java 8, which has no Cleaner.
+ null
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachableExecutorService.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachableExecutorService.kt
new file mode 100644
index 0000000..a94e75e
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachableExecutorService.kt
@@ -0,0 +1,58 @@
+package com.pioneer_intergration_app.api.core
+
+import java.util.concurrent.Callable
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Future
+import java.util.concurrent.TimeUnit
+
+/**
+ * A delegating wrapper around an [ExecutorService] that shuts it down once it's only phantom
+ * reachable.
+ *
+ * This class ensures the [ExecutorService] is shut down even if the user forgets to do it.
+ */
+internal class PhantomReachableExecutorService(private val executorService: ExecutorService) :
+ ExecutorService {
+ init {
+ closeWhenPhantomReachable(this) { executorService.shutdown() }
+ }
+
+ override fun execute(command: Runnable) = executorService.execute(command)
+
+ override fun shutdown() = executorService.shutdown()
+
+ override fun shutdownNow(): MutableList = executorService.shutdownNow()
+
+ override fun isShutdown(): Boolean = executorService.isShutdown
+
+ override fun isTerminated(): Boolean = executorService.isTerminated
+
+ override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean =
+ executorService.awaitTermination(timeout, unit)
+
+ override fun submit(task: Callable): Future = executorService.submit(task)
+
+ override fun submit(task: Runnable, result: T): Future =
+ executorService.submit(task, result)
+
+ override fun submit(task: Runnable): Future<*> = executorService.submit(task)
+
+ override fun invokeAll(
+ tasks: MutableCollection>
+ ): MutableList> = executorService.invokeAll(tasks)
+
+ override fun invokeAll(
+ tasks: MutableCollection>,
+ timeout: Long,
+ unit: TimeUnit,
+ ): MutableList> = executorService.invokeAll(tasks, timeout, unit)
+
+ override fun invokeAny(tasks: MutableCollection>): T =
+ executorService.invokeAny(tasks)
+
+ override fun invokeAny(
+ tasks: MutableCollection>,
+ timeout: Long,
+ unit: TimeUnit,
+ ): T = executorService.invokeAny(tasks, timeout, unit)
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachableSleeper.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachableSleeper.kt
new file mode 100644
index 0000000..9702741
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PhantomReachableSleeper.kt
@@ -0,0 +1,21 @@
+package com.pioneer_intergration_app.api.core
+
+import java.time.Duration
+
+/**
+ * A delegating wrapper around a [Sleeper] that closes it once it's only phantom reachable.
+ *
+ * This class ensures the [Sleeper] is closed even if the user forgets to do it.
+ */
+internal class PhantomReachableSleeper(private val sleeper: Sleeper) : Sleeper {
+
+ init {
+ closeWhenPhantomReachable(this, sleeper)
+ }
+
+ override fun sleep(duration: Duration) = sleeper.sleep(duration)
+
+ override suspend fun sleepAsync(duration: Duration) = sleeper.sleepAsync(duration)
+
+ override fun close() = sleeper.close()
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PrepareRequest.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PrepareRequest.kt
new file mode 100644
index 0000000..1e4d2bf
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/PrepareRequest.kt
@@ -0,0 +1,16 @@
+package com.pioneer_intergration_app.api.core
+
+import com.pioneer_intergration_app.api.core.http.HttpRequest
+
+internal fun HttpRequest.prepare(clientOptions: ClientOptions, params: Params): HttpRequest =
+ toBuilder()
+ .putAllQueryParams(clientOptions.queryParams)
+ .replaceAllQueryParams(params._queryParams())
+ .putAllHeaders(clientOptions.headers)
+ .replaceAllHeaders(params._headers())
+ .build()
+
+internal suspend fun HttpRequest.prepareAsync(clientOptions: ClientOptions, params: Params) =
+ // This async version exists to make it easier to add async specific preparation logic in the
+ // future.
+ prepare(clientOptions, params)
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Properties.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Properties.kt
new file mode 100644
index 0000000..b30f35c
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Properties.kt
@@ -0,0 +1,42 @@
+@file:JvmName("Properties")
+
+package com.pioneer_intergration_app.api.core
+
+import com.pioneer_intergration_app.api.client.PioneerIntergrationAppClient
+
+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 =
+ PioneerIntergrationAppClient::class.java.`package`.implementationVersion ?: "unknown"
+
+fun getJavaVersion(): String = System.getProperty("java.version", "unknown")
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/RequestOptions.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/RequestOptions.kt
new file mode 100644
index 0000000..93205b0
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/RequestOptions.kt
@@ -0,0 +1,45 @@
+package com.pioneer_intergration_app.api.core
+
+import java.time.Duration
+
+class RequestOptions private constructor(val responseValidation: Boolean?, val timeout: Timeout?) {
+
+ companion object {
+
+ private val NONE = builder().build()
+
+ fun none() = NONE
+
+ internal fun from(clientOptions: ClientOptions): RequestOptions =
+ builder()
+ .responseValidation(clientOptions.responseValidation)
+ .timeout(clientOptions.timeout)
+ .build()
+
+ 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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Sleeper.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Sleeper.kt
new file mode 100644
index 0000000..412b016
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Sleeper.kt
@@ -0,0 +1,17 @@
+package com.pioneer_intergration_app.api.core
+
+import java.time.Duration
+
+/**
+ * An interface for delaying execution for a specified amount of time.
+ *
+ * Useful for testing and cleaning up resources.
+ */
+interface Sleeper : AutoCloseable {
+
+ /** Synchronously pauses execution for the given [duration]. */
+ fun sleep(duration: Duration)
+
+ /** Asynchronously pauses execution for the given [duration]. */
+ suspend fun sleepAsync(duration: Duration)
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Timeout.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Timeout.kt
new file mode 100644
index 0000000..328e47b
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Timeout.kt
@@ -0,0 +1,155 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.core
+
+import java.time.Duration
+import java.util.Objects
+
+/** 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 {
+
+ fun default() = builder().build()
+
+ /** Returns a mutable builder for constructing an instance of [Timeout]. */
+ 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
+
+ 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 }
+
+ /**
+ * 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 }
+
+ /**
+ * 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 }
+
+ /**
+ * 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 }
+
+ /**
+ * 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)
+ }
+
+ internal fun assign(target: Timeout): Timeout =
+ target
+ .toBuilder()
+ .apply {
+ connect?.let(this::connect)
+ read?.let(this::read)
+ write?.let(this::write)
+ request?.let(this::request)
+ }
+ .build()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Timeout &&
+ connect == other.connect &&
+ read == other.read &&
+ write == other.write &&
+ request == other.request
+ }
+
+ override fun hashCode(): Int = Objects.hash(connect, read, write, request)
+
+ override fun toString() =
+ "Timeout{connect=$connect, read=$read, write=$write, request=$request}"
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Utils.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Utils.kt
new file mode 100644
index 0000000..2f1fed0
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Utils.kt
@@ -0,0 +1,102 @@
+@file:JvmName("Utils")
+
+package com.pioneer_intergration_app.api.core
+
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppInvalidDataException
+import java.util.Collections
+import java.util.SortedMap
+import java.util.concurrent.locks.Lock
+
+internal fun T?.getOrThrow(name: String): T =
+ this ?: throw PioneerIntergrationAppInvalidDataException("`${name}` is not present")
+
+internal fun List.toImmutable(): List =
+ if (isEmpty()) Collections.emptyList() else Collections.unmodifiableList(toList())
+
+internal fun Map.toImmutable(): Map =
+ if (isEmpty()) immutableEmptyMap() else Collections.unmodifiableMap(toMap())
+
+internal fun immutableEmptyMap(): Map = Collections.emptyMap()
+
+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.
+ */
+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.
+ */
+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.
+ */
+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.
+ */
+internal fun Any?.contentToString(): String {
+ var string = arrayOf(this).contentDeepToString()
+ if (string.startsWith('[')) {
+ string = string.substring(1)
+ }
+ if (string.endsWith(']')) {
+ string = string.substring(0, string.length - 1)
+ }
+ return string
+}
+
+internal interface Enum
+
+/**
+ * Executes a suspending block of code while holding this lock.
+ *
+ * @param T the return type of the action
+ * @param action the suspending function to execute while holding the lock
+ * @return the result of executing the action
+ */
+internal suspend fun Lock.withLockAsync(action: suspend () -> T): T {
+ lock()
+ return try {
+ action()
+ } finally {
+ unlock()
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Values.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Values.kt
new file mode 100644
index 0000000..01f6f79
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/Values.kt
@@ -0,0 +1,700 @@
+package com.pioneer_intergration_app.api.core
+
+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 com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppInvalidDataException
+import java.io.InputStream
+import java.util.*
+import kotlin.reflect.KClass
+
+/**
+ * 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 this field's "known" value, meaning it matches the type the SDK expects, or null if
+ * this field contains an arbitrary [JsonValue].
+ *
+ * This is the opposite of [asUnknown].
+ */
+ fun asKnown(): T? = (this as? KnownValue)?.value
+
+ /**
+ * Returns this field's arbitrary [JsonValue], meaning it mismatches the type the SDK expects,
+ * or null if this field contains a "known" value.
+ *
+ * This is the opposite of [asKnown].
+ */
+ fun asUnknown(): JsonValue? = this as? JsonValue
+
+ /**
+ * Returns this field's boolean value, or null if it doesn't contain a boolean.
+ *
+ * This method checks for both a [KnownValue] containing a boolean and for [JsonBoolean].
+ */
+ fun asBoolean(): Boolean? =
+ when (this) {
+ is JsonBoolean -> value
+ is KnownValue -> value as? Boolean
+ else -> null
+ }
+
+ /**
+ * Returns this field's numerical value, or null if it doesn't contain a number.
+ *
+ * This method checks for both a [KnownValue] containing a number and for [JsonNumber].
+ */
+ fun asNumber(): Number? =
+ when (this) {
+ is JsonNumber -> value
+ is KnownValue -> value as? Number
+ else -> null
+ }
+
+ /**
+ * Returns this field's string value, or null if it doesn't contain a string.
+ *
+ * This method checks for both a [KnownValue] containing a string and for [JsonString].
+ */
+ fun asString(): String? =
+ when (this) {
+ is JsonString -> value
+ is KnownValue -> value as? String
+ else -> null
+ }
+
+ fun asStringOrThrow(): String =
+ asString() ?: throw PioneerIntergrationAppInvalidDataException("Value is not a string")
+
+ /**
+ * Returns this field's list value, or null if it doesn't contain a list.
+ *
+ * This method checks for both a [KnownValue] containing a list and for [JsonArray].
+ */
+ fun asArray(): List? =
+ when (this) {
+ is JsonArray -> values
+ is KnownValue ->
+ (value as? List<*>)?.map {
+ try {
+ JsonValue.from(it)
+ } catch (e: IllegalArgumentException) {
+ // The known value is a list, but not all items are convertible to
+ // `JsonValue`.
+ return null
+ }
+ }
+ else -> null
+ }
+
+ /**
+ * Returns this field's map value, or null if it doesn't contain a map.
+ *
+ * This method checks for both a [KnownValue] containing a map and for [JsonObject].
+ */
+ fun asObject(): Map? =
+ when (this) {
+ is JsonObject -> values
+ is KnownValue ->
+ (value as? Map<*, *>)
+ ?.map { (key, value) ->
+ if (key !is String) {
+ return null
+ }
+
+ val jsonValue =
+ try {
+ JsonValue.from(value)
+ } catch (e: IllegalArgumentException) {
+ // The known value is a map, but not all values are convertible to
+ // `JsonValue`.
+ return null
+ }
+
+ key to jsonValue
+ }
+ ?.toMap()
+ else -> null
+ }
+
+ internal fun getRequired(name: String): T =
+ when (this) {
+ is KnownValue -> value
+ is JsonMissing -> throw PioneerIntergrationAppInvalidDataException("`$name` is not set")
+ is JsonNull -> throw PioneerIntergrationAppInvalidDataException("`$name` is null")
+ else ->
+ throw PioneerIntergrationAppInvalidDataException(
+ "`$name` is invalid, received $this"
+ )
+ }
+
+ internal fun getNullable(name: String): T? =
+ when (this) {
+ is KnownValue -> value
+ is JsonMissing -> null
+ is JsonNull -> null
+ else ->
+ throw PioneerIntergrationAppInvalidDataException(
+ "`$name` is invalid, received $this"
+ )
+ }
+
+ internal fun map(transform: (T) -> R): JsonField =
+ when (this) {
+ is KnownValue -> KnownValue.of(transform(value))
+ is JsonValue -> this
+ }
+
+ internal fun accept(consume: (T) -> Unit) {
+ asKnown()?.let(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]. */
+ fun of(value: T): JsonField = KnownValue.of(value)
+
+ /**
+ * Returns a [JsonField] containing the given "known" [value], or [JsonNull] if [value] is
+ * null.
+ */
+ 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() {
+
+ inline fun convert(): R? = convert(jacksonTypeRef())
+
+ fun convert(type: TypeReference): R? = JSON_MAPPER.convertValue(this, type)
+
+ fun convert(type: KClass): R? = JSON_MAPPER.convertValue(this, type.java)
+
+ /** 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:
+ * ```kotlin
+ * // Create primitive JSON values
+ * val nullValue: JsonValue = JsonValue.from(null)
+ * val booleanValue: JsonValue = JsonValue.from(true)
+ * val numberValue: JsonValue = JsonValue.from(42)
+ * val stringValue: JsonValue = JsonValue.from("Hello World!")
+ *
+ * // Create a JSON array value equivalent to `["Hello", "World"]`
+ * val arrayValue: JsonValue = JsonValue.from(listOf("Hello", "World"))
+ *
+ * // Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`
+ * val objectValue: JsonValue = JsonValue.from(mapOf(
+ * "a" to 1,
+ * "b" to 2,
+ * ))
+ *
+ * // Create an arbitrarily nested JSON equivalent to:
+ * // {
+ * // "a": [1, 2],
+ * // "b": [3, 4]
+ * // }
+ * val complexValue: JsonValue = JsonValue.from(mapOf(
+ * "a" to listOf(1, 2),
+ * "b" to listOf(3, 4),
+ * ))
+ * ```
+ *
+ * @throws IllegalArgumentException if [value] is not JSON serializable.
+ */
+ 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.
+ */
+ 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 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]. */
+ 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 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 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 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 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 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 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 val value: JsonField,
+ /** A content type for the serialized parts. */
+ val contentType: String,
+ /** Returns the filename directive that will be included in the serialized field. */
+ 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.
+ */
+ 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.
+ */
+ fun of(value: JsonField) = builder().value(value).build()
+
+ /**
+ * Returns a mutable builder for constructing an instance of [MultipartField].
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .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.
+ */
+ fun builder() = Builder()
+ }
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [MultipartField].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/EmptyHandler.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/EmptyHandler.kt
new file mode 100644
index 0000000..10211dd
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/EmptyHandler.kt
@@ -0,0 +1,12 @@
+@file:JvmName("EmptyHandler")
+
+package com.pioneer_intergration_app.api.core.handlers
+
+import com.pioneer_intergration_app.api.core.http.HttpResponse
+import com.pioneer_intergration_app.api.core.http.HttpResponse.Handler
+
+internal fun emptyHandler(): Handler = EmptyHandlerInternal
+
+private object EmptyHandlerInternal : Handler {
+ override fun handle(response: HttpResponse): Void? = null
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/ErrorHandler.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/ErrorHandler.kt
new file mode 100644
index 0000000..aed4f8e
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/ErrorHandler.kt
@@ -0,0 +1,80 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.core.handlers
+
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.pioneer_intergration_app.api.core.JsonMissing
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.http.HttpResponse
+import com.pioneer_intergration_app.api.core.http.HttpResponse.Handler
+import com.pioneer_intergration_app.api.errors.BadRequestException
+import com.pioneer_intergration_app.api.errors.InternalServerException
+import com.pioneer_intergration_app.api.errors.NotFoundException
+import com.pioneer_intergration_app.api.errors.PermissionDeniedException
+import com.pioneer_intergration_app.api.errors.RateLimitException
+import com.pioneer_intergration_app.api.errors.UnauthorizedException
+import com.pioneer_intergration_app.api.errors.UnexpectedStatusCodeException
+import com.pioneer_intergration_app.api.errors.UnprocessableEntityException
+
+internal fun errorBodyHandler(jsonMapper: JsonMapper): Handler {
+ val handler = jsonHandler(jsonMapper)
+
+ return object : Handler {
+ override fun handle(response: HttpResponse): JsonValue =
+ try {
+ handler.handle(response)
+ } catch (e: Exception) {
+ JsonMissing.of()
+ }
+ }
+}
+
+internal fun errorHandler(errorBodyHandler: Handler): Handler =
+ object : Handler {
+ override fun handle(response: HttpResponse): HttpResponse =
+ when (val statusCode = response.statusCode()) {
+ in 200..299 -> response
+ 400 ->
+ throw BadRequestException.builder()
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ 401 ->
+ throw UnauthorizedException.builder()
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ 403 ->
+ throw PermissionDeniedException.builder()
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ 404 ->
+ throw NotFoundException.builder()
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ 422 ->
+ throw UnprocessableEntityException.builder()
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ 429 ->
+ throw RateLimitException.builder()
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ in 500..599 ->
+ throw InternalServerException.builder()
+ .statusCode(statusCode)
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ else ->
+ throw UnexpectedStatusCodeException.builder()
+ .statusCode(statusCode)
+ .headers(response.headers())
+ .body(errorBodyHandler.handle(response))
+ .build()
+ }
+ }
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/JsonHandler.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/JsonHandler.kt
new file mode 100644
index 0000000..0a30484
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/JsonHandler.kt
@@ -0,0 +1,20 @@
+@file:JvmName("JsonHandler")
+
+package com.pioneer_intergration_app.api.core.handlers
+
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import com.pioneer_intergration_app.api.core.http.HttpResponse
+import com.pioneer_intergration_app.api.core.http.HttpResponse.Handler
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppInvalidDataException
+
+internal inline fun jsonHandler(jsonMapper: JsonMapper): Handler =
+ object : Handler {
+ override fun handle(response: HttpResponse): T {
+ try {
+ return jsonMapper.readValue(response.body(), jacksonTypeRef())
+ } catch (e: Exception) {
+ throw PioneerIntergrationAppInvalidDataException("Error reading response", e)
+ }
+ }
+ }
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/StringHandler.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/StringHandler.kt
new file mode 100644
index 0000000..e93a77f
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/handlers/StringHandler.kt
@@ -0,0 +1,13 @@
+@file:JvmName("StringHandler")
+
+package com.pioneer_intergration_app.api.core.handlers
+
+import com.pioneer_intergration_app.api.core.http.HttpResponse
+import com.pioneer_intergration_app.api.core.http.HttpResponse.Handler
+
+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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/Headers.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/Headers.kt
new file mode 100644
index 0000000..7dc82b3
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/Headers.kt
@@ -0,0 +1,111 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.core.http
+
+import com.pioneer_intergration_app.api.core.JsonArray
+import com.pioneer_intergration_app.api.core.JsonBoolean
+import com.pioneer_intergration_app.api.core.JsonMissing
+import com.pioneer_intergration_app.api.core.JsonNull
+import com.pioneer_intergration_app.api.core.JsonNumber
+import com.pioneer_intergration_app.api.core.JsonObject
+import com.pioneer_intergration_app.api.core.JsonString
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.toImmutable
+import java.util.TreeMap
+
+class Headers private constructor(private val map: Map>, 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 {
+
+ fun builder() = Builder()
+ }
+
+ class Builder internal constructor() {
+
+ private val map: MutableMap> =
+ TreeMap(String.CASE_INSENSITIVE_ORDER)
+ private var size: Int = 0
+
+ fun put(name: String, value: JsonValue): Builder = apply {
+ when (value) {
+ is JsonMissing,
+ is JsonNull -> {}
+ is JsonBoolean -> put(name, value.value.toString())
+ is JsonNumber -> put(name, value.value.toString())
+ is JsonString -> put(name, value.value)
+ is JsonArray -> value.values.forEach { put(name, it) }
+ is JsonObject ->
+ value.values.forEach { (nestedName, value) -> put("$name.$nestedName", value) }
+ }
+ }
+
+ fun put(name: String, value: String) = apply {
+ map.getOrPut(name) { mutableListOf() }.add(value)
+ size++
+ }
+
+ fun put(name: String, values: Iterable) = apply { values.forEach { put(name, it) } }
+
+ fun putAll(headers: Map>) = apply { headers.forEach(::put) }
+
+ fun putAll(headers: Headers) = apply {
+ headers.names().forEach { put(it, headers.values(it)) }
+ }
+
+ fun replace(name: String, value: String) = apply {
+ remove(name)
+ put(name, value)
+ }
+
+ fun replace(name: String, values: Iterable) = apply {
+ remove(name)
+ put(name, values)
+ }
+
+ fun replaceAll(headers: Map>) = apply {
+ headers.forEach(::replace)
+ }
+
+ fun replaceAll(headers: Headers) = apply {
+ headers.names().forEach { replace(it, headers.values(it)) }
+ }
+
+ fun remove(name: String) = apply { size -= map.remove(name).orEmpty().size }
+
+ fun removeAll(names: Set) = apply { names.forEach(::remove) }
+
+ fun clear() = apply {
+ map.clear()
+ size = 0
+ }
+
+ fun build() =
+ Headers(
+ map.mapValuesTo(TreeMap(String.CASE_INSENSITIVE_ORDER)) { (_, values) ->
+ values.toImmutable()
+ }
+ .toImmutable(),
+ size,
+ )
+ }
+
+ override fun hashCode(): Int = map.hashCode()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Headers && map == other.map
+ }
+
+ override fun toString(): String = "Headers{map=$map}"
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpClient.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpClient.kt
new file mode 100644
index 0000000..ae5773b
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpClient.kt
@@ -0,0 +1,17 @@
+package com.pioneer_intergration_app.api.core.http
+
+import com.pioneer_intergration_app.api.core.RequestOptions
+import java.lang.AutoCloseable
+
+interface HttpClient : AutoCloseable {
+
+ fun execute(
+ request: HttpRequest,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): HttpResponse
+
+ suspend fun executeAsync(
+ request: HttpRequest,
+ requestOptions: RequestOptions = RequestOptions.none(),
+ ): HttpResponse
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpMethod.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpMethod.kt
new file mode 100644
index 0000000..a7b82d7
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpMethod.kt
@@ -0,0 +1,13 @@
+package com.pioneer_intergration_app.api.core.http
+
+enum class HttpMethod {
+ GET,
+ HEAD,
+ POST,
+ PUT,
+ DELETE,
+ CONNECT,
+ OPTIONS,
+ TRACE,
+ PATCH,
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequest.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequest.kt
new file mode 100644
index 0000000..83db170
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequest.kt
@@ -0,0 +1,175 @@
+package com.pioneer_intergration_app.api.core.http
+
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.toImmutable
+import java.net.URLEncoder
+
+class HttpRequest
+private constructor(
+ val method: HttpMethod,
+ val baseUrl: String,
+ val pathSegments: List,
+ val headers: Headers,
+ val queryParams: QueryParams,
+ val body: HttpRequestBody?,
+) {
+
+ fun url(): String = buildString {
+ append(baseUrl)
+
+ pathSegments.forEach { segment ->
+ if (!endsWith("/")) {
+ append("/")
+ }
+ append(URLEncoder.encode(segment, "UTF-8"))
+ }
+
+ if (queryParams.isEmpty()) {
+ return@buildString
+ }
+
+ append("?")
+ var isFirst = true
+ queryParams.keys().forEach { key ->
+ queryParams.values(key).forEach { value ->
+ if (!isFirst) {
+ append("&")
+ }
+ append(URLEncoder.encode(key, "UTF-8"))
+ append("=")
+ append(URLEncoder.encode(value, "UTF-8"))
+ isFirst = false
+ }
+ }
+ }
+
+ fun toBuilder(): Builder = Builder().from(this)
+
+ override fun toString(): String =
+ "HttpRequest{method=$method, baseUrl=$baseUrl, pathSegments=$pathSegments, headers=$headers, queryParams=$queryParams, body=$body}"
+
+ companion object {
+ fun builder() = Builder()
+ }
+
+ class Builder internal constructor() {
+
+ private var method: HttpMethod? = null
+ private var baseUrl: String? = null
+ private var pathSegments: MutableList = mutableListOf()
+ private var headers: Headers.Builder = Headers.builder()
+ private var queryParams: QueryParams.Builder = QueryParams.builder()
+ private var body: HttpRequestBody? = null
+
+ internal fun from(request: HttpRequest) = apply {
+ method = request.method
+ baseUrl = request.baseUrl
+ pathSegments = request.pathSegments.toMutableList()
+ headers = request.headers.toBuilder()
+ queryParams = request.queryParams.toBuilder()
+ body = request.body
+ }
+
+ fun method(method: HttpMethod) = apply { this.method = method }
+
+ fun baseUrl(baseUrl: String) = apply { this.baseUrl = baseUrl }
+
+ fun addPathSegment(pathSegment: String) = apply { pathSegments.add(pathSegment) }
+
+ fun addPathSegments(vararg pathSegments: String) = apply {
+ this.pathSegments.addAll(pathSegments)
+ }
+
+ fun headers(headers: Headers) = apply {
+ this.headers.clear()
+ putAllHeaders(headers)
+ }
+
+ fun headers(headers: Map>) = apply {
+ this.headers.clear()
+ putAllHeaders(headers)
+ }
+
+ fun putHeader(name: String, value: String) = apply { headers.put(name, value) }
+
+ fun putHeaders(name: String, values: Iterable) = apply { headers.put(name, values) }
+
+ fun putAllHeaders(headers: Headers) = apply { this.headers.putAll(headers) }
+
+ fun putAllHeaders(headers: Map>) = apply {
+ this.headers.putAll(headers)
+ }
+
+ fun replaceHeaders(name: String, value: String) = apply { headers.replace(name, value) }
+
+ fun replaceHeaders(name: String, values: Iterable) = apply {
+ headers.replace(name, values)
+ }
+
+ fun replaceAllHeaders(headers: Headers) = apply { this.headers.replaceAll(headers) }
+
+ fun replaceAllHeaders(headers: Map>) = apply {
+ this.headers.replaceAll(headers)
+ }
+
+ fun removeHeaders(name: String) = apply { headers.remove(name) }
+
+ fun removeAllHeaders(names: Set) = apply { headers.removeAll(names) }
+
+ fun queryParams(queryParams: QueryParams) = apply {
+ this.queryParams.clear()
+ putAllQueryParams(queryParams)
+ }
+
+ fun queryParams(queryParams: Map>) = apply {
+ this.queryParams.clear()
+ putAllQueryParams(queryParams)
+ }
+
+ fun putQueryParam(key: String, value: String) = apply { queryParams.put(key, value) }
+
+ fun putQueryParams(key: String, values: Iterable) = apply {
+ queryParams.put(key, values)
+ }
+
+ fun putAllQueryParams(queryParams: QueryParams) = apply {
+ this.queryParams.putAll(queryParams)
+ }
+
+ fun putAllQueryParams(queryParams: Map>) = apply {
+ this.queryParams.putAll(queryParams)
+ }
+
+ fun replaceQueryParams(key: String, value: String) = apply {
+ queryParams.replace(key, value)
+ }
+
+ fun replaceQueryParams(key: String, values: Iterable) = apply {
+ queryParams.replace(key, values)
+ }
+
+ fun replaceAllQueryParams(queryParams: QueryParams) = apply {
+ this.queryParams.replaceAll(queryParams)
+ }
+
+ fun replaceAllQueryParams(queryParams: Map>) = apply {
+ this.queryParams.replaceAll(queryParams)
+ }
+
+ fun removeQueryParams(key: String) = apply { queryParams.remove(key) }
+
+ fun removeAllQueryParams(keys: Set) = apply { queryParams.removeAll(keys) }
+
+ fun body(body: HttpRequestBody) = apply { this.body = body }
+
+ fun build(): HttpRequest =
+ HttpRequest(
+ checkRequired("method", method),
+ checkRequired("baseUrl", baseUrl),
+ pathSegments.toImmutable(),
+ headers.build(),
+ queryParams.build(),
+ body,
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequestBodies.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequestBodies.kt
new file mode 100644
index 0000000..4e0c6a5
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequestBodies.kt
@@ -0,0 +1,125 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.core.http
+
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.json.JsonMapper
+import com.fasterxml.jackson.databind.node.JsonNodeType
+import com.pioneer_intergration_app.api.core.MultipartField
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppInvalidDataException
+import java.io.InputStream
+import java.io.OutputStream
+import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
+import org.apache.hc.core5.http.ContentType
+import org.apache.hc.core5.http.HttpEntity
+
+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() {}
+ }
+
+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()
+ 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,
+ )
+ }
+ }
+ }
+ .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 ->
+ sequenceOf(
+ name to
+ node
+ .elements()
+ .asSequence()
+ .mapNotNull { element ->
+ when (element.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> null
+ JsonNodeType.STRING -> node.textValue()
+ JsonNodeType.BOOLEAN -> node.booleanValue().toString()
+ JsonNodeType.NUMBER -> node.numberValue().toString()
+ null,
+ JsonNodeType.BINARY,
+ JsonNodeType.ARRAY,
+ JsonNodeType.OBJECT,
+ JsonNodeType.POJO ->
+ throw PioneerIntergrationAppInvalidDataException(
+ "Unexpected JsonNode type in array: ${node.nodeType}"
+ )
+ }
+ }
+ .joinToString(",")
+ .inputStream()
+ )
+ JsonNodeType.OBJECT ->
+ node.fields().asSequence().flatMap { (key, value) ->
+ serializePart("$name[$key]", value)
+ }
+ JsonNodeType.POJO,
+ null ->
+ throw PioneerIntergrationAppInvalidDataException(
+ "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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequestBody.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequestBody.kt
new file mode 100644
index 0000000..71b845a
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpRequestBody.kt
@@ -0,0 +1,22 @@
+package com.pioneer_intergration_app.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
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpResponse.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpResponse.kt
new file mode 100644
index 0000000..a01def6
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpResponse.kt
@@ -0,0 +1,19 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.core.http
+
+import java.io.InputStream
+
+interface HttpResponse : AutoCloseable {
+
+ fun statusCode(): Int
+
+ fun headers(): Headers
+
+ fun body(): InputStream
+
+ interface Handler {
+
+ fun handle(response: HttpResponse): T
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpResponseFor.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpResponseFor.kt
new file mode 100644
index 0000000..b091b8d
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/HttpResponseFor.kt
@@ -0,0 +1,24 @@
+package com.pioneer_intergration_app.api.core.http
+
+import java.io.InputStream
+
+interface HttpResponseFor : HttpResponse {
+
+ fun parse(): T
+}
+
+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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/PhantomReachableClosingHttpClient.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/PhantomReachableClosingHttpClient.kt
new file mode 100644
index 0000000..0c442c8
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/PhantomReachableClosingHttpClient.kt
@@ -0,0 +1,25 @@
+package com.pioneer_intergration_app.api.core.http
+
+import com.pioneer_intergration_app.api.core.RequestOptions
+import com.pioneer_intergration_app.api.core.closeWhenPhantomReachable
+
+/**
+ * A delegating wrapper around an `HttpClient` that closes it once it's only phantom reachable.
+ *
+ * This class ensures the `HttpClient` is closed even if the user forgets to close it.
+ */
+internal class PhantomReachableClosingHttpClient(private val httpClient: HttpClient) : HttpClient {
+ init {
+ closeWhenPhantomReachable(this, httpClient)
+ }
+
+ override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse =
+ httpClient.execute(request, requestOptions)
+
+ override suspend fun executeAsync(
+ request: HttpRequest,
+ requestOptions: RequestOptions,
+ ): HttpResponse = httpClient.executeAsync(request, requestOptions)
+
+ override fun close() = httpClient.close()
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/PhantomReachableClosingStreamResponse.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/PhantomReachableClosingStreamResponse.kt
new file mode 100644
index 0000000..ff6c2bb
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/PhantomReachableClosingStreamResponse.kt
@@ -0,0 +1,20 @@
+package com.pioneer_intergration_app.api.core.http
+
+import com.pioneer_intergration_app.api.core.closeWhenPhantomReachable
+
+/**
+ * A delegating wrapper around a `StreamResponse` that closes it once it's only phantom reachable.
+ *
+ * This class ensures the `StreamResponse` is closed even if the user forgets to close it.
+ */
+internal class PhantomReachableClosingStreamResponse(
+ private val streamResponse: StreamResponse
+) : StreamResponse {
+ init {
+ closeWhenPhantomReachable(this, streamResponse)
+ }
+
+ override fun asSequence(): Sequence = streamResponse.asSequence()
+
+ override fun close() = streamResponse.close()
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/QueryParams.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/QueryParams.kt
new file mode 100644
index 0000000..58081ab
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/QueryParams.kt
@@ -0,0 +1,125 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.core.http
+
+import com.pioneer_intergration_app.api.core.JsonArray
+import com.pioneer_intergration_app.api.core.JsonBoolean
+import com.pioneer_intergration_app.api.core.JsonMissing
+import com.pioneer_intergration_app.api.core.JsonNull
+import com.pioneer_intergration_app.api.core.JsonNumber
+import com.pioneer_intergration_app.api.core.JsonObject
+import com.pioneer_intergration_app.api.core.JsonString
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.toImmutable
+
+class QueryParams private constructor(private val map: Map>, 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 {
+
+ fun builder() = Builder()
+ }
+
+ class Builder internal constructor() {
+
+ private val map: MutableMap> = mutableMapOf()
+ private var size: Int = 0
+
+ fun put(key: String, value: JsonValue): Builder = apply {
+ when (value) {
+ is JsonMissing,
+ is JsonNull -> {}
+ is JsonBoolean -> put(key, value.value.toString())
+ is JsonNumber -> put(key, value.value.toString())
+ is JsonString -> put(key, value.value)
+ is JsonArray ->
+ put(
+ key,
+ value.values
+ .asSequence()
+ .mapNotNull {
+ when (it) {
+ is JsonMissing,
+ is JsonNull -> null
+ is JsonBoolean -> it.value.toString()
+ is JsonNumber -> it.value.toString()
+ is JsonString -> it.value
+ is JsonArray,
+ is JsonObject ->
+ throw IllegalArgumentException(
+ "Cannot comma separate non-primitives in query params"
+ )
+ }
+ }
+ .joinToString(","),
+ )
+ is JsonObject ->
+ value.values.forEach { (nestedKey, value) -> put("$key[$nestedKey]", value) }
+ }
+ }
+
+ fun put(key: String, value: String) = apply {
+ map.getOrPut(key) { mutableListOf() }.add(value)
+ size++
+ }
+
+ fun put(key: String, values: Iterable) = apply { values.forEach { put(key, it) } }
+
+ fun putAll(queryParams: Map>) = apply {
+ queryParams.forEach(::put)
+ }
+
+ fun putAll(queryParams: QueryParams) = apply {
+ queryParams.keys().forEach { put(it, queryParams.values(it)) }
+ }
+
+ fun replace(key: String, value: String) = apply {
+ remove(key)
+ put(key, value)
+ }
+
+ fun replace(key: String, values: Iterable) = apply {
+ remove(key)
+ put(key, values)
+ }
+
+ fun replaceAll(queryParams: Map>) = apply {
+ queryParams.forEach(::replace)
+ }
+
+ fun replaceAll(queryParams: QueryParams) = apply {
+ queryParams.keys().forEach { replace(it, queryParams.values(it)) }
+ }
+
+ fun remove(key: String) = apply { size -= map.remove(key).orEmpty().size }
+
+ fun removeAll(keys: Set) = apply { keys.forEach(::remove) }
+
+ fun clear() = apply {
+ map.clear()
+ size = 0
+ }
+
+ fun build() =
+ QueryParams(map.mapValues { (_, values) -> values.toImmutable() }.toImmutable(), size)
+ }
+
+ override fun hashCode(): Int = map.hashCode()
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is QueryParams && map == other.map
+ }
+
+ override fun toString(): String = "QueryParams{map=$map}"
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/RetryingHttpClient.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/RetryingHttpClient.kt
new file mode 100644
index 0000000..4a36544
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/RetryingHttpClient.kt
@@ -0,0 +1,248 @@
+package com.pioneer_intergration_app.api.core.http
+
+import com.pioneer_intergration_app.api.core.DefaultSleeper
+import com.pioneer_intergration_app.api.core.RequestOptions
+import com.pioneer_intergration_app.api.core.Sleeper
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppIoException
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppRetryableException
+import java.io.IOException
+import java.time.Clock
+import java.time.Duration
+import java.time.OffsetDateTime
+import java.time.format.DateTimeFormatter
+import java.time.format.DateTimeParseException
+import java.time.temporal.ChronoUnit
+import java.util.UUID
+import java.util.concurrent.ThreadLocalRandom
+import java.util.concurrent.TimeUnit
+import kotlin.math.min
+import kotlin.math.pow
+
+class RetryingHttpClient
+private constructor(
+ private val httpClient: HttpClient,
+ private val sleeper: Sleeper,
+ private val clock: Clock,
+ private val maxRetries: Int,
+ private val idempotencyHeader: String?,
+) : HttpClient {
+
+ override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
+ var modifiedRequest = maybeAddIdempotencyHeader(request)
+
+ // Don't send the current retry count in the headers if the caller set their own value.
+ val shouldSendRetryCount =
+ !modifiedRequest.headers.names().contains("X-Stainless-Retry-Count")
+
+ var retries = 0
+
+ while (true) {
+ if (shouldSendRetryCount) {
+ modifiedRequest = setRetryCountHeader(modifiedRequest, retries)
+ }
+
+ if (!isRetryable(modifiedRequest)) {
+ return httpClient.execute(modifiedRequest, requestOptions)
+ }
+
+ val response =
+ try {
+ val response = httpClient.execute(modifiedRequest, requestOptions)
+ if (++retries > maxRetries || !shouldRetry(response)) {
+ return response
+ }
+
+ response
+ } catch (throwable: Throwable) {
+ if (++retries > maxRetries || !shouldRetry(throwable)) {
+ throw throwable
+ }
+
+ null
+ }
+
+ val backoffDuration = getRetryBackoffDuration(retries, response)
+ // All responses must be closed, so close the failed one before retrying.
+ response?.close()
+ sleeper.sleep(backoffDuration)
+ }
+ }
+
+ override suspend fun executeAsync(
+ request: HttpRequest,
+ requestOptions: RequestOptions,
+ ): HttpResponse {
+ var modifiedRequest = maybeAddIdempotencyHeader(request)
+
+ // Don't send the current retry count in the headers if the caller set their own value.
+ val shouldSendRetryCount =
+ !modifiedRequest.headers.names().contains("X-Stainless-Retry-Count")
+
+ var retries = 0
+
+ while (true) {
+ if (shouldSendRetryCount) {
+ modifiedRequest = setRetryCountHeader(modifiedRequest, retries)
+ }
+
+ if (!isRetryable(modifiedRequest)) {
+ return httpClient.executeAsync(modifiedRequest, requestOptions)
+ }
+
+ val response =
+ try {
+ val response = httpClient.execute(modifiedRequest, requestOptions)
+ if (++retries > maxRetries || !shouldRetry(response)) {
+ return response
+ }
+
+ response
+ } catch (throwable: Throwable) {
+ if (++retries > maxRetries || !shouldRetry(throwable)) {
+ throw throwable
+ }
+
+ null
+ }
+
+ val backoffDuration = getRetryBackoffDuration(retries, response)
+ // All responses must be closed, so close the failed one before retrying.
+ response?.close()
+ sleeper.sleepAsync(backoffDuration)
+ }
+ }
+
+ override fun close() {
+ httpClient.close()
+ sleeper.close()
+ }
+
+ private fun isRetryable(request: HttpRequest): Boolean =
+ // Some requests, such as when a request body is being streamed, cannot be retried because
+ // the body data aren't available on subsequent attempts.
+ request.body?.repeatable() ?: true
+
+ private fun setRetryCountHeader(request: HttpRequest, retries: Int): HttpRequest =
+ request.toBuilder().replaceHeaders("X-Stainless-Retry-Count", retries.toString()).build()
+
+ private fun idempotencyKey(): String = "stainless-java-retry-${UUID.randomUUID()}"
+
+ private fun maybeAddIdempotencyHeader(request: HttpRequest): HttpRequest {
+ if (idempotencyHeader == null || request.headers.names().contains(idempotencyHeader)) {
+ return request
+ }
+
+ return request
+ .toBuilder()
+ // Set a header to uniquely identify the request when retried
+ .putHeader(idempotencyHeader, idempotencyKey())
+ .build()
+ }
+
+ private fun shouldRetry(response: HttpResponse): Boolean {
+ // Note: this is not a standard header
+ val shouldRetryHeader = response.headers().values("X-Should-Retry").getOrNull(0)
+ val statusCode = response.statusCode()
+
+ return when {
+ // If the server explicitly says whether to retry, obey
+ shouldRetryHeader == "true" -> true
+ shouldRetryHeader == "false" -> false
+
+ // Retry on request timeouts
+ statusCode == 408 -> true
+ // Retry on lock timeouts
+ statusCode == 409 -> true
+ // Retry on rate limits
+ statusCode == 429 -> true
+ // Retry internal errors
+ statusCode >= 500 -> true
+ else -> false
+ }
+ }
+
+ private fun shouldRetry(throwable: Throwable): Boolean =
+ // Only retry known retryable exceptions, other exceptions are not intended to be retried.
+ throwable is IOException ||
+ throwable is PioneerIntergrationAppIoException ||
+ throwable is PioneerIntergrationAppRetryableException
+
+ 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 {
+
+ fun builder() = Builder()
+ }
+
+ class Builder internal constructor() {
+
+ private var httpClient: HttpClient? = null
+ private var sleeper: Sleeper? = null
+ private var clock: Clock = Clock.systemUTC()
+ private var maxRetries: Int = 2
+ private var idempotencyHeader: String? = null
+
+ fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient }
+
+ fun sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper }
+
+ fun clock(clock: Clock) = apply { this.clock = clock }
+
+ fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries }
+
+ fun idempotencyHeader(header: String) = apply { this.idempotencyHeader = header }
+
+ fun build(): HttpClient =
+ RetryingHttpClient(
+ checkRequired("httpClient", httpClient),
+ sleeper ?: DefaultSleeper(),
+ clock,
+ maxRetries,
+ idempotencyHeader,
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/StreamResponse.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/StreamResponse.kt
new file mode 100644
index 0000000..6b984f6
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/core/http/StreamResponse.kt
@@ -0,0 +1,13 @@
+package com.pioneer_intergration_app.api.core.http
+
+interface StreamResponse : AutoCloseable {
+
+ fun asSequence(): Sequence
+}
+
+internal fun StreamResponse.map(transform: (T) -> R): StreamResponse =
+ object : StreamResponse {
+ override fun asSequence(): Sequence = this@map.asSequence().map(transform)
+
+ override fun close() = this@map.close()
+ }
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/BadRequestException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/BadRequestException.kt
new file mode 100644
index 0000000..edc6559
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/BadRequestException.kt
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class BadRequestException
+private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
+ PioneerIntergrationAppServiceException("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:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [BadRequestException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): BadRequestException =
+ BadRequestException(
+ checkRequired("headers", headers),
+ checkRequired("body", body),
+ cause,
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/InternalServerException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/InternalServerException.kt
new file mode 100644
index 0000000..73c61e8
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/InternalServerException.kt
@@ -0,0 +1,85 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class InternalServerException
+private constructor(
+ private val statusCode: Int,
+ private val headers: Headers,
+ private val body: JsonValue,
+ cause: Throwable?,
+) : PioneerIntergrationAppServiceException("$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:
+ * ```kotlin
+ * .statusCode()
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [InternalServerException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/NotFoundException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/NotFoundException.kt
new file mode 100644
index 0000000..6761c18
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/NotFoundException.kt
@@ -0,0 +1,70 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class NotFoundException
+private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
+ PioneerIntergrationAppServiceException("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:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [NotFoundException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): NotFoundException =
+ NotFoundException(checkRequired("headers", headers), checkRequired("body", body), cause)
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PermissionDeniedException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PermissionDeniedException.kt
new file mode 100644
index 0000000..73812ac
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PermissionDeniedException.kt
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class PermissionDeniedException
+private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
+ PioneerIntergrationAppServiceException("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:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [PermissionDeniedException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): PermissionDeniedException =
+ PermissionDeniedException(
+ checkRequired("headers", headers),
+ checkRequired("body", body),
+ cause,
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppException.kt
new file mode 100644
index 0000000..c573765
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppException.kt
@@ -0,0 +1,4 @@
+package com.pioneer_intergration_app.api.errors
+
+open class PioneerIntergrationAppException(message: String? = null, cause: Throwable? = null) :
+ RuntimeException(message, cause)
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppInvalidDataException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppInvalidDataException.kt
new file mode 100644
index 0000000..e5ccb27
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppInvalidDataException.kt
@@ -0,0 +1,6 @@
+package com.pioneer_intergration_app.api.errors
+
+class PioneerIntergrationAppInvalidDataException(
+ message: String? = null,
+ cause: Throwable? = null,
+) : PioneerIntergrationAppException(message, cause)
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppIoException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppIoException.kt
new file mode 100644
index 0000000..cc510dd
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppIoException.kt
@@ -0,0 +1,4 @@
+package com.pioneer_intergration_app.api.errors
+
+class PioneerIntergrationAppIoException(message: String? = null, cause: Throwable? = null) :
+ PioneerIntergrationAppException(message, cause)
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppRetryableException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppRetryableException.kt
new file mode 100644
index 0000000..35c0377
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppRetryableException.kt
@@ -0,0 +1,14 @@
+package com.pioneer_intergration_app.api.errors
+
+/**
+ * Exception that indicates a transient error that can be retried.
+ *
+ * When this exception is thrown during an HTTP request, the SDK will automatically retry the
+ * request up to the maximum number of retries.
+ *
+ * @param message A descriptive error message
+ * @param cause The underlying cause of this exception, if any
+ */
+class PioneerIntergrationAppRetryableException
+constructor(message: String? = null, cause: Throwable? = null) :
+ PioneerIntergrationAppException(message, cause)
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppServiceException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppServiceException.kt
new file mode 100644
index 0000000..d6a1afb
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/PioneerIntergrationAppServiceException.kt
@@ -0,0 +1,17 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.http.Headers
+
+abstract class PioneerIntergrationAppServiceException
+protected constructor(message: String, cause: Throwable? = null) :
+ PioneerIntergrationAppException(message, cause) {
+
+ abstract fun statusCode(): Int
+
+ abstract fun headers(): Headers
+
+ abstract fun body(): JsonValue
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/RateLimitException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/RateLimitException.kt
new file mode 100644
index 0000000..0da4d02
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/RateLimitException.kt
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class RateLimitException
+private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
+ PioneerIntergrationAppServiceException("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:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [RateLimitException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): RateLimitException =
+ RateLimitException(
+ checkRequired("headers", headers),
+ checkRequired("body", body),
+ cause,
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnauthorizedException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnauthorizedException.kt
new file mode 100644
index 0000000..aee9969
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnauthorizedException.kt
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class UnauthorizedException
+private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
+ PioneerIntergrationAppServiceException("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:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [UnauthorizedException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): UnauthorizedException =
+ UnauthorizedException(
+ checkRequired("headers", headers),
+ checkRequired("body", body),
+ cause,
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnexpectedStatusCodeException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnexpectedStatusCodeException.kt
new file mode 100644
index 0000000..535bf2a
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnexpectedStatusCodeException.kt
@@ -0,0 +1,86 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class UnexpectedStatusCodeException
+private constructor(
+ private val statusCode: Int,
+ private val headers: Headers,
+ private val body: JsonValue,
+ cause: Throwable?,
+) : PioneerIntergrationAppServiceException("$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:
+ * ```kotlin
+ * .statusCode()
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [UnexpectedStatusCodeException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .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/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnprocessableEntityException.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnprocessableEntityException.kt
new file mode 100644
index 0000000..349796d
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/errors/UnprocessableEntityException.kt
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.errors
+
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.core.checkRequired
+import com.pioneer_intergration_app.api.core.http.Headers
+
+class UnprocessableEntityException
+private constructor(private val headers: Headers, private val body: JsonValue, cause: Throwable?) :
+ PioneerIntergrationAppServiceException("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:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ */
+ 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
+
+ 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 }
+
+ /**
+ * Returns an immutable instance of [UnprocessableEntityException].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```kotlin
+ * .headers()
+ * .body()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): UnprocessableEntityException =
+ UnprocessableEntityException(
+ checkRequired("headers", headers),
+ checkRequired("body", body),
+ cause,
+ )
+ }
+}
diff --git a/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/models/Order.kt b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/models/Order.kt
new file mode 100644
index 0000000..1ebbfcd
--- /dev/null
+++ b/pioneer-intergration-app-kotlin-core/src/main/kotlin/com/pioneer_intergration_app/api/models/Order.kt
@@ -0,0 +1,454 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.pioneer_intergration_app.api.models
+
+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.pioneer_intergration_app.api.core.Enum
+import com.pioneer_intergration_app.api.core.ExcludeMissing
+import com.pioneer_intergration_app.api.core.JsonField
+import com.pioneer_intergration_app.api.core.JsonMissing
+import com.pioneer_intergration_app.api.core.JsonValue
+import com.pioneer_intergration_app.api.errors.PioneerIntergrationAppInvalidDataException
+import java.time.OffsetDateTime
+import java.util.Collections
+import java.util.Objects
+
+class Order
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val id: JsonField,
+ private val complete: JsonField,
+ private val petId: JsonField,
+ private val quantity: JsonField,
+ private val shipDate: JsonField,
+ private val status: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(),
+ @JsonProperty("complete") @ExcludeMissing complete: JsonField = JsonMissing.of(),
+ @JsonProperty("petId") @ExcludeMissing petId: JsonField = JsonMissing.of(),
+ @JsonProperty("quantity") @ExcludeMissing quantity: JsonField = JsonMissing.of(),
+ @JsonProperty("shipDate")
+ @ExcludeMissing
+ shipDate: JsonField = JsonMissing.of(),
+ @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(),
+ ) : this(id, complete, petId, quantity, shipDate, status, mutableMapOf())
+
+ /**
+ * @throws PioneerIntergrationAppInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun id(): Long? = id.getNullable("id")
+
+ /**
+ * @throws PioneerIntergrationAppInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun complete(): Boolean? = complete.getNullable("complete")
+
+ /**
+ * @throws PioneerIntergrationAppInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun petId(): Long? = petId.getNullable("petId")
+
+ /**
+ * @throws PioneerIntergrationAppInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun quantity(): Int? = quantity.getNullable("quantity")
+
+ /**
+ * @throws PioneerIntergrationAppInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun shipDate(): OffsetDateTime? = shipDate.getNullable("shipDate")
+
+ /**
+ * Order Status
+ *
+ * @throws PioneerIntergrationAppInvalidDataException if the JSON field has an unexpected type
+ * (e.g. if the server responded with an unexpected value).
+ */
+ fun status(): Status? = status.getNullable("status")
+
+ /**
+ * 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 [complete].
+ *
+ * Unlike [complete], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("complete") @ExcludeMissing fun _complete(): JsonField = complete
+
+ /**
+ * Returns the raw JSON value of [petId].
+ *
+ * Unlike [petId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("petId") @ExcludeMissing fun _petId(): JsonField = petId
+
+ /**
+ * Returns the raw JSON value of [quantity].
+ *
+ * Unlike [quantity], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("quantity") @ExcludeMissing fun _quantity(): JsonField = quantity
+
+ /**
+ * Returns the raw JSON value of [shipDate].
+ *
+ * Unlike [shipDate], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("shipDate") @ExcludeMissing fun _shipDate(): JsonField = shipDate
+
+ /**
+ * 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 [Order]. */
+ fun builder() = Builder()
+ }
+
+ /** A builder for [Order]. */
+ class Builder internal constructor() {
+
+ private var id: JsonField = JsonMissing.of()
+ private var complete: JsonField = JsonMissing.of()
+ private var petId: JsonField = JsonMissing.of()
+ private var quantity: JsonField = JsonMissing.of()
+ private var shipDate: JsonField = JsonMissing.of()
+ private var status: JsonField