diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ea98b61f..b7046597 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,14 +65,18 @@ jobs:
run: ./scripts/build
- name: Get GitHub OIDC Token
- if: github.repository == 'stainless-sdks/courier-java'
+ if: |-
+ github.repository == 'stainless-sdks/courier-java' &&
+ !startsWith(github.ref, 'refs/heads/stl/')
id: github-oidc
uses: actions/github-script@v8
with:
script: core.setOutput('github_token', await core.getIDToken());
- name: Build and upload Maven artifacts
- if: github.repository == 'stainless-sdks/courier-java'
+ if: |-
+ github.repository == 'stainless-sdks/courier-java' &&
+ !startsWith(github.ref, 'refs/heads/stl/')
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 0056f0ab..ad56a78c 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "4.9.1"
+ ".": "4.10.0"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 17749e26..2da22152 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
-configured_endpoints: 81
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-3fc1c86b4a83a16393aaf17d1fb3ac6098d30dd057ba872973b57285a7a3f0d0.yml
-openapi_spec_hash: 02a545d217b13399f311e99561f9de1d
-config_hash: 0789c3cddc625bb9712b3bded274ab6c
+configured_endpoints: 83
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-af83ac9631c6a9f5651adda37ef80ba220f4b54b80f029ab8f04a0bb165af6c2.yml
+openapi_spec_hash: a1d2bf40aec49ea35bcef11de0349e45
+config_hash: c1bd72f347662bde65ed9abec4c33b13
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d183d7d5..2a16bb76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,40 @@
# Changelog
+## 4.10.0 (2026-03-11)
+
+Full Changelog: [v4.9.1...v4.10.0](https://github.com/trycourier/courier-java/compare/v4.9.1...v4.10.0)
+
+### Features
+
+* **api:** add journeys resource with list/invoke methods ([2c7587e](https://github.com/trycourier/courier-java/commit/2c7587e7b3eb0ae29e77b5bbe26f96d19aed5076))
+* **client:** add connection pooling option ([b2e1bcb](https://github.com/trycourier/courier-java/commit/b2e1bcbd856bbe7d247497b2296dd8f368fdc6da))
+
+
+### Bug Fixes
+
+* **api:** restructure token add single request body, rename pathToken to token ([9577dec](https://github.com/trycourier/courier-java/commit/9577decaf5da849b9c35d2c7a6302ef2b105193e))
+* **client:** incorrect `Retry-After` parsing ([4719954](https://github.com/trycourier/courier-java/commit/471995496d38e95ca4f0b832064ae529fc02dabf))
+* **types:** remove brand field from ElementalContent model ([cb336a3](https://github.com/trycourier/courier-java/commit/cb336a3bb81b5e2bd25483be06c45bdfd518fe2b))
+
+
+### Chores
+
+* **ci:** skip uploading artifacts on stainless-internal branches ([330979b](https://github.com/trycourier/courier-java/commit/330979bc70a21d68fa33ca16f949bc378aa818c9))
+* drop apache dependency ([90094b9](https://github.com/trycourier/courier-java/commit/90094b91bb84ab5f9b01d351031431990495e2fa))
+* **internal:** bump palantir-java-format ([0057417](https://github.com/trycourier/courier-java/commit/0057417ffd2fcb14a33a42ebb8d81bce4232197a))
+* **internal:** codegen related update ([6a1cfae](https://github.com/trycourier/courier-java/commit/6a1cfaeee2212a162513951b903778d4a35e48b3))
+* **internal:** expand imports ([b1b8ef3](https://github.com/trycourier/courier-java/commit/b1b8ef384a3acddc0ce6cd79cfca840e6da4c748))
+* **internal:** make `OkHttp` constructor internal ([4e39320](https://github.com/trycourier/courier-java/commit/4e393200781dd186e45a32ff8d18be7083e83b31))
+* **internal:** remove mock server code ([b63945e](https://github.com/trycourier/courier-java/commit/b63945ee56673ba3e736de9d2170b0a27e7dcd82))
+* **internal:** update `TestServerExtension` comment ([26b5aed](https://github.com/trycourier/courier-java/commit/26b5aede00f47e7a0bae77e058e36a00361060b0))
+* make `Properties` more resilient to `null` ([401bc43](https://github.com/trycourier/courier-java/commit/401bc435edc5a6798afb1c1619a0de37cdb7a9fe))
+* update mock server docs ([ff62ef0](https://github.com/trycourier/courier-java/commit/ff62ef0c2c8c232f96a3b3e5ade9c6ac22a1b248))
+
+
+### Documentation
+
+* add AUTO-GENERATED-OVERVIEW markers for README sync ([#92](https://github.com/trycourier/courier-java/issues/92)) ([4bc2f46](https://github.com/trycourier/courier-java/commit/4bc2f4661c8c7d35d72234c1f5520eec71a39bcc))
+
## 4.9.1 (2026-02-07)
Full Changelog: [v4.9.0...v4.9.1](https://github.com/trycourier/courier-java/compare/v4.9.0...v4.9.1)
diff --git a/README.md b/README.md
index 1572f135..804ce850 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,8 @@
-[](https://central.sonatype.com/artifact/com.courier/courier-java/4.9.1)
-[](https://javadoc.io/doc/com.courier/courier-java/4.9.1)
+[](https://central.sonatype.com/artifact/com.courier/courier-java/4.10.0)
+[](https://javadoc.io/doc/com.courier/courier-java/4.10.0)
@@ -14,7 +14,7 @@ It is generated with [Stainless](https://www.stainless.com/).
-The REST API documentation can be found on [www.courier.com](https://www.courier.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.courier/courier-java/4.9.1).
+The REST API documentation can be found on [www.courier.com](https://www.courier.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.courier/courier-java/4.10.0).
@@ -25,7 +25,7 @@ The REST API documentation can be found on [www.courier.com](https://www.courier
### Gradle
```kotlin
-implementation("com.courier:courier-java:4.9.1")
+implementation("com.courier:courier-java:4.10.0")
```
### Maven
@@ -34,7 +34,7 @@ implementation("com.courier:courier-java:4.9.1")
com.courier
courier-java
- 4.9.1
+ 4.10.0
```
@@ -388,6 +388,25 @@ CourierClient client = CourierOkHttpClient.builder()
.build();
```
+### Connection pooling
+
+To customize the underlying OkHttp connection pool, configure the client using the `maxIdleConnections` and `keepAliveDuration` methods:
+
+```java
+import com.courier.client.CourierClient;
+import com.courier.client.okhttp.CourierOkHttpClient;
+import java.time.Duration;
+
+CourierClient client = CourierOkHttpClient.builder()
+ .fromEnv()
+ // If `maxIdleConnections` is set, then `keepAliveDuration` must be set, and vice versa.
+ .maxIdleConnections(10)
+ .keepAliveDuration(Duration.ofMinutes(2))
+ .build();
+```
+
+If both options are unset, OkHttp's default connection pool settings are used.
+
### HTTPS
> [!NOTE]
diff --git a/build.gradle.kts b/build.gradle.kts
index aae0be02..653fe829 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "com.courier"
- version = "4.9.1" // x-release-please-version
+ version = "4.10.0" // x-release-please-version
}
subprojects {
diff --git a/buildSrc/src/main/kotlin/courier.java.gradle.kts b/buildSrc/src/main/kotlin/courier.java.gradle.kts
index 70fc33f4..8f4f902a 100644
--- a/buildSrc/src/main/kotlin/courier.java.gradle.kts
+++ b/buildSrc/src/main/kotlin/courier.java.gradle.kts
@@ -45,7 +45,7 @@ tasks.withType().configureEach {
val palantir by configurations.creating
dependencies {
- palantir("com.palantir.javaformat:palantir-java-format:2.73.0")
+ palantir("com.palantir.javaformat:palantir-java-format:2.89.0")
}
fun registerPalantir(
diff --git a/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClient.kt b/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClient.kt
index 429e095e..73be7c55 100644
--- a/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClient.kt
+++ b/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClient.kt
@@ -47,6 +47,8 @@ class CourierOkHttpClient private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
@@ -75,6 +77,46 @@ class CourierOkHttpClient private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * The maximum number of idle connections kept by the underlying OkHttp connection pool.
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Alias for [Builder.maxIdleConnections].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int) =
+ maxIdleConnections(maxIdleConnections as Int?)
+
+ /**
+ * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
+ */
+ fun maxIdleConnections(maxIdleConnections: Optional) =
+ maxIdleConnections(maxIdleConnections.getOrNull())
+
+ /**
+ * The keep-alive duration for idle connections in the underlying OkHttp connection pool.
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
+ /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
+ fun keepAliveDuration(keepAliveDuration: Optional) =
+ keepAliveDuration(keepAliveDuration.getOrNull())
+
/**
* The socket factory used to secure HTTPS connections.
*
@@ -317,6 +359,8 @@ class CourierOkHttpClient private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .maxIdleConnections(maxIdleConnections)
+ .keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
diff --git a/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClientAsync.kt b/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClientAsync.kt
index 2e2adb16..8a1baa2a 100644
--- a/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClientAsync.kt
+++ b/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/CourierOkHttpClientAsync.kt
@@ -47,6 +47,8 @@ class CourierOkHttpClientAsync private constructor() {
private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
@@ -75,6 +77,46 @@ class CourierOkHttpClientAsync private constructor() {
/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
fun proxy(proxy: Optional) = proxy(proxy.getOrNull())
+ /**
+ * The maximum number of idle connections kept by the underlying OkHttp connection pool.
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Alias for [Builder.maxIdleConnections].
+ *
+ * This unboxed primitive overload exists for backwards compatibility.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int) =
+ maxIdleConnections(maxIdleConnections as Int?)
+
+ /**
+ * Alias for calling [Builder.maxIdleConnections] with `maxIdleConnections.orElse(null)`.
+ */
+ fun maxIdleConnections(maxIdleConnections: Optional) =
+ maxIdleConnections(maxIdleConnections.getOrNull())
+
+ /**
+ * The keep-alive duration for idle connections in the underlying OkHttp connection pool.
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
+ /** Alias for calling [Builder.keepAliveDuration] with `keepAliveDuration.orElse(null)`. */
+ fun keepAliveDuration(keepAliveDuration: Optional) =
+ keepAliveDuration(keepAliveDuration.getOrNull())
+
/**
* The socket factory used to secure HTTPS connections.
*
@@ -317,6 +359,8 @@ class CourierOkHttpClientAsync private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
+ .maxIdleConnections(maxIdleConnections)
+ .keepAliveDuration(keepAliveDuration)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
diff --git a/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/OkHttpClient.kt b/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/OkHttpClient.kt
index ca6ffdbe..86340f02 100644
--- a/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/OkHttpClient.kt
+++ b/courier-java-client-okhttp/src/main/kotlin/com/courier/client/okhttp/OkHttpClient.kt
@@ -16,11 +16,13 @@ import java.time.Duration
import java.util.concurrent.CancellationException
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutorService
+import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Call
import okhttp3.Callback
+import okhttp3.ConnectionPool
import okhttp3.Dispatcher
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType
@@ -33,7 +35,7 @@ import okhttp3.logging.HttpLoggingInterceptor
import okio.BufferedSink
class OkHttpClient
-private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient {
+internal constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClient) : HttpClient {
override fun execute(request: HttpRequest, requestOptions: RequestOptions): HttpResponse {
val call = newCall(request, requestOptions)
@@ -200,6 +202,8 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
private var timeout: Timeout = Timeout.default()
private var proxy: Proxy? = null
+ private var maxIdleConnections: Int? = null
+ private var keepAliveDuration: Duration? = null
private var dispatcherExecutorService: ExecutorService? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
@@ -211,6 +215,28 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }
+ /**
+ * Sets the maximum number of idle connections kept by the underlying [ConnectionPool].
+ *
+ * If this is set, then [keepAliveDuration] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun maxIdleConnections(maxIdleConnections: Int?) = apply {
+ this.maxIdleConnections = maxIdleConnections
+ }
+
+ /**
+ * Sets the keep-alive duration for idle connections in the underlying [ConnectionPool].
+ *
+ * If this is set, then [maxIdleConnections] must also be set.
+ *
+ * If unset, then OkHttp's default is used.
+ */
+ fun keepAliveDuration(keepAliveDuration: Duration?) = apply {
+ this.keepAliveDuration = keepAliveDuration
+ }
+
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
this.dispatcherExecutorService = dispatcherExecutorService
}
@@ -240,6 +266,22 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
.apply {
dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }
+ val maxIdleConnections = maxIdleConnections
+ val keepAliveDuration = keepAliveDuration
+ if (maxIdleConnections != null && keepAliveDuration != null) {
+ connectionPool(
+ ConnectionPool(
+ maxIdleConnections,
+ keepAliveDuration.toNanos(),
+ TimeUnit.NANOSECONDS,
+ )
+ )
+ } else {
+ check((maxIdleConnections != null) == (keepAliveDuration != null)) {
+ "Both or none of `maxIdleConnections` and `keepAliveDuration` must be set, but only one was set"
+ }
+ }
+
val sslSocketFactory = sslSocketFactory
val trustManager = trustManager
if (sslSocketFactory != null && trustManager != null) {
diff --git a/courier-java-core/build.gradle.kts b/courier-java-core/build.gradle.kts
index 15e9f371..d4f0b6c8 100644
--- a/courier-java-core/build.gradle.kts
+++ b/courier-java-core/build.gradle.kts
@@ -27,8 +27,6 @@ dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
- implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4")
- implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")
testImplementation(kotlin("test"))
testImplementation(project(":courier-java-client-okhttp"))
diff --git a/courier-java-core/src/main/kotlin/com/courier/client/CourierClient.kt b/courier-java-core/src/main/kotlin/com/courier/client/CourierClient.kt
index 953d9b6a..3f4207eb 100644
--- a/courier-java-core/src/main/kotlin/com/courier/client/CourierClient.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/client/CourierClient.kt
@@ -10,6 +10,7 @@ import com.courier.services.blocking.AutomationService
import com.courier.services.blocking.BrandService
import com.courier.services.blocking.BulkService
import com.courier.services.blocking.InboundService
+import com.courier.services.blocking.JourneyService
import com.courier.services.blocking.ListService
import com.courier.services.blocking.MessageService
import com.courier.services.blocking.NotificationService
@@ -67,6 +68,8 @@ interface CourierClient {
fun automations(): AutomationService
+ fun journeys(): JourneyService
+
fun brands(): BrandService
fun bulk(): BulkService
@@ -122,6 +125,8 @@ interface CourierClient {
fun automations(): AutomationService.WithRawResponse
+ fun journeys(): JourneyService.WithRawResponse
+
fun brands(): BrandService.WithRawResponse
fun bulk(): BulkService.WithRawResponse
diff --git a/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsync.kt b/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsync.kt
index 58cc94de..facdb00c 100644
--- a/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsync.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsync.kt
@@ -10,6 +10,7 @@ import com.courier.services.async.AutomationServiceAsync
import com.courier.services.async.BrandServiceAsync
import com.courier.services.async.BulkServiceAsync
import com.courier.services.async.InboundServiceAsync
+import com.courier.services.async.JourneyServiceAsync
import com.courier.services.async.ListServiceAsync
import com.courier.services.async.MessageServiceAsync
import com.courier.services.async.NotificationServiceAsync
@@ -67,6 +68,8 @@ interface CourierClientAsync {
fun automations(): AutomationServiceAsync
+ fun journeys(): JourneyServiceAsync
+
fun brands(): BrandServiceAsync
fun bulk(): BulkServiceAsync
@@ -126,6 +129,8 @@ interface CourierClientAsync {
fun automations(): AutomationServiceAsync.WithRawResponse
+ fun journeys(): JourneyServiceAsync.WithRawResponse
+
fun brands(): BrandServiceAsync.WithRawResponse
fun bulk(): BulkServiceAsync.WithRawResponse
diff --git a/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsyncImpl.kt b/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsyncImpl.kt
index 7155339b..a540243c 100644
--- a/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsyncImpl.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/client/CourierClientAsyncImpl.kt
@@ -18,6 +18,8 @@ import com.courier.services.async.BulkServiceAsync
import com.courier.services.async.BulkServiceAsyncImpl
import com.courier.services.async.InboundServiceAsync
import com.courier.services.async.InboundServiceAsyncImpl
+import com.courier.services.async.JourneyServiceAsync
+import com.courier.services.async.JourneyServiceAsyncImpl
import com.courier.services.async.ListServiceAsync
import com.courier.services.async.ListServiceAsyncImpl
import com.courier.services.async.MessageServiceAsync
@@ -71,6 +73,10 @@ class CourierClientAsyncImpl(private val clientOptions: ClientOptions) : Courier
AutomationServiceAsyncImpl(clientOptionsWithUserAgent)
}
+ private val journeys: JourneyServiceAsync by lazy {
+ JourneyServiceAsyncImpl(clientOptionsWithUserAgent)
+ }
+
private val brands: BrandServiceAsync by lazy {
BrandServiceAsyncImpl(clientOptionsWithUserAgent)
}
@@ -126,6 +132,8 @@ class CourierClientAsyncImpl(private val clientOptions: ClientOptions) : Courier
override fun automations(): AutomationServiceAsync = automations
+ override fun journeys(): JourneyServiceAsync = journeys
+
override fun brands(): BrandServiceAsync = brands
override fun bulk(): BulkServiceAsync = bulk
@@ -173,6 +181,10 @@ class CourierClientAsyncImpl(private val clientOptions: ClientOptions) : Courier
AutomationServiceAsyncImpl.WithRawResponseImpl(clientOptions)
}
+ private val journeys: JourneyServiceAsync.WithRawResponse by lazy {
+ JourneyServiceAsyncImpl.WithRawResponseImpl(clientOptions)
+ }
+
private val brands: BrandServiceAsync.WithRawResponse by lazy {
BrandServiceAsyncImpl.WithRawResponseImpl(clientOptions)
}
@@ -234,6 +246,8 @@ class CourierClientAsyncImpl(private val clientOptions: ClientOptions) : Courier
override fun automations(): AutomationServiceAsync.WithRawResponse = automations
+ override fun journeys(): JourneyServiceAsync.WithRawResponse = journeys
+
override fun brands(): BrandServiceAsync.WithRawResponse = brands
override fun bulk(): BulkServiceAsync.WithRawResponse = bulk
diff --git a/courier-java-core/src/main/kotlin/com/courier/client/CourierClientImpl.kt b/courier-java-core/src/main/kotlin/com/courier/client/CourierClientImpl.kt
index ee61b28d..2ab15a6c 100644
--- a/courier-java-core/src/main/kotlin/com/courier/client/CourierClientImpl.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/client/CourierClientImpl.kt
@@ -18,6 +18,8 @@ import com.courier.services.blocking.BulkService
import com.courier.services.blocking.BulkServiceImpl
import com.courier.services.blocking.InboundService
import com.courier.services.blocking.InboundServiceImpl
+import com.courier.services.blocking.JourneyService
+import com.courier.services.blocking.JourneyServiceImpl
import com.courier.services.blocking.ListService
import com.courier.services.blocking.ListServiceImpl
import com.courier.services.blocking.MessageService
@@ -71,6 +73,8 @@ class CourierClientImpl(private val clientOptions: ClientOptions) : CourierClien
AutomationServiceImpl(clientOptionsWithUserAgent)
}
+ private val journeys: JourneyService by lazy { JourneyServiceImpl(clientOptionsWithUserAgent) }
+
private val brands: BrandService by lazy { BrandServiceImpl(clientOptionsWithUserAgent) }
private val bulk: BulkService by lazy { BulkServiceImpl(clientOptionsWithUserAgent) }
@@ -114,6 +118,8 @@ class CourierClientImpl(private val clientOptions: ClientOptions) : CourierClien
override fun automations(): AutomationService = automations
+ override fun journeys(): JourneyService = journeys
+
override fun brands(): BrandService = brands
override fun bulk(): BulkService = bulk
@@ -161,6 +167,10 @@ class CourierClientImpl(private val clientOptions: ClientOptions) : CourierClien
AutomationServiceImpl.WithRawResponseImpl(clientOptions)
}
+ private val journeys: JourneyService.WithRawResponse by lazy {
+ JourneyServiceImpl.WithRawResponseImpl(clientOptions)
+ }
+
private val brands: BrandService.WithRawResponse by lazy {
BrandServiceImpl.WithRawResponseImpl(clientOptions)
}
@@ -222,6 +232,8 @@ class CourierClientImpl(private val clientOptions: ClientOptions) : CourierClien
override fun automations(): AutomationService.WithRawResponse = automations
+ override fun journeys(): JourneyService.WithRawResponse = journeys
+
override fun brands(): BrandService.WithRawResponse = brands
override fun bulk(): BulkService.WithRawResponse = bulk
diff --git a/courier-java-core/src/main/kotlin/com/courier/core/Properties.kt b/courier-java-core/src/main/kotlin/com/courier/core/Properties.kt
index f1fb24f6..20b78089 100644
--- a/courier-java-core/src/main/kotlin/com/courier/core/Properties.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/core/Properties.kt
@@ -34,9 +34,9 @@ fun getOsName(): String {
}
}
-fun getOsVersion(): String = System.getProperty("os.version", "unknown")
+fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown"
fun getPackageVersion(): String =
- CourierClient::class.java.`package`.implementationVersion ?: "unknown"
+ CourierClient::class.java.`package`?.implementationVersion ?: "unknown"
-fun getJavaVersion(): String = System.getProperty("java.version", "unknown")
+fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown"
diff --git a/courier-java-core/src/main/kotlin/com/courier/core/http/HttpRequestBodies.kt b/courier-java-core/src/main/kotlin/com/courier/core/http/HttpRequestBodies.kt
index 5736754c..c2ddbd06 100644
--- a/courier-java-core/src/main/kotlin/com/courier/core/http/HttpRequestBodies.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/core/http/HttpRequestBodies.kt
@@ -5,16 +5,16 @@
package com.courier.core.http
import com.courier.core.MultipartField
+import com.courier.core.toImmutable
import com.courier.errors.CourierInvalidDataException
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.node.JsonNodeType
+import java.io.ByteArrayInputStream
import java.io.InputStream
import java.io.OutputStream
+import java.util.UUID
import kotlin.jvm.optionals.getOrNull
-import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
-import org.apache.hc.core5.http.ContentType
-import org.apache.hc.core5.http.HttpEntity
@JvmSynthetic
internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody =
@@ -37,92 +37,231 @@ internal fun multipartFormData(
jsonMapper: JsonMapper,
fields: Map>,
): HttpRequestBody =
- object : HttpRequestBody {
- private val entity: HttpEntity by lazy {
- MultipartEntityBuilder.create()
- .apply {
- fields.forEach { (name, field) ->
- val knownValue = field.value.asKnown().getOrNull()
- val parts =
- if (knownValue is InputStream) {
- // Read directly from the `InputStream` instead of reading it all
- // into memory due to the `jsonMapper` serialization below.
- sequenceOf(name to knownValue)
- } else {
- val node = jsonMapper.valueToTree(field.value)
- serializePart(name, node)
+ MultipartBody.Builder()
+ .apply {
+ fields.forEach { (name, field) ->
+ val knownValue = field.value.asKnown().getOrNull()
+ val parts =
+ if (knownValue is InputStream) {
+ // Read directly from the `InputStream` instead of reading it all
+ // into memory due to the `jsonMapper` serialization below.
+ sequenceOf(name to knownValue)
+ } else {
+ val node = jsonMapper.valueToTree(field.value)
+ serializePart(name, node)
+ }
+
+ parts.forEach { (name, bytes) ->
+ val partBody =
+ if (bytes is ByteArrayInputStream) {
+ val byteArray = bytes.readBytes()
+
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ outputStream.write(byteArray)
+ }
+
+ override fun contentType(): String = field.contentType
+
+ override fun contentLength(): Long = byteArray.size.toLong()
+
+ override fun repeatable(): Boolean = true
+
+ override fun close() {}
}
+ } else {
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ bytes.copyTo(outputStream)
+ }
+
+ override fun contentType(): String = field.contentType
+
+ override fun contentLength(): Long = -1L
- parts.forEach { (name, bytes) ->
- addBinaryBody(
- name,
- bytes,
- ContentType.parseLenient(field.contentType),
- field.filename().getOrNull(),
- )
+ override fun repeatable(): Boolean = false
+
+ override fun close() = bytes.close()
+ }
}
- }
+
+ addPart(
+ MultipartBody.Part.create(
+ name,
+ field.filename().getOrNull(),
+ field.contentType,
+ partBody,
+ )
+ )
}
- .build()
+ }
}
+ .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 CourierInvalidDataException(
- "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 CourierInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
+private fun serializePart(name: String, node: JsonNode): Sequence> =
+ when (node.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> emptySequence()
+ JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
+ JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream())
+ JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream())
+ JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream())
+ JsonNodeType.ARRAY ->
+ sequenceOf(
+ name to
+ node
+ .elements()
+ .asSequence()
+ .mapNotNull { element ->
+ when (element.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> null
+ JsonNodeType.STRING -> element.textValue()
+ JsonNodeType.BOOLEAN -> element.booleanValue().toString()
+ JsonNodeType.NUMBER -> element.numberValue().toString()
+ null,
+ JsonNodeType.BINARY,
+ JsonNodeType.ARRAY,
+ JsonNodeType.OBJECT,
+ JsonNodeType.POJO ->
+ throw CourierInvalidDataException(
+ "Unexpected JsonNode type in array: ${element.nodeType}"
+ )
+ }
+ }
+ .joinToString(",")
+ .byteInputStream()
+ )
+ JsonNodeType.OBJECT ->
+ node.fields().asSequence().flatMap { (key, value) ->
+ serializePart("$name[$key]", value)
+ }
+ JsonNodeType.POJO,
+ null -> throw CourierInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
+ }
+
+private class MultipartBody
+private constructor(private val boundary: String, private val parts: List) : HttpRequestBody {
+ private val boundaryBytes: ByteArray = boundary.toByteArray()
+ private val contentType = "multipart/form-data; boundary=$boundary"
+
+ // This must remain in sync with `contentLength`.
+ override fun writeTo(outputStream: OutputStream) {
+ parts.forEach { part ->
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(CRLF)
+
+ outputStream.write(CONTENT_DISPOSITION)
+ outputStream.write(part.contentDisposition.toByteArray())
+ outputStream.write(CRLF)
+
+ outputStream.write(CONTENT_TYPE)
+ outputStream.write(part.contentType.toByteArray())
+ outputStream.write(CRLF)
+
+ outputStream.write(CRLF)
+ part.body.writeTo(outputStream)
+ outputStream.write(CRLF)
+ }
+
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(DASHDASH)
+ outputStream.write(CRLF)
+ }
+
+ override fun contentType(): String = contentType
+
+ // This must remain in sync with `writeTo`.
+ override fun contentLength(): Long {
+ var byteCount = 0L
+
+ parts.forEach { part ->
+ val contentLength = part.body.contentLength()
+ if (contentLength == -1L) {
+ return -1L
}
- private fun String.inputStream(): InputStream = toByteArray().inputStream()
+ byteCount +=
+ DASHDASH.size +
+ boundaryBytes.size +
+ CRLF.size +
+ CONTENT_DISPOSITION.size +
+ part.contentDisposition.toByteArray().size +
+ CRLF.size +
+ CONTENT_TYPE.size +
+ part.contentType.toByteArray().size +
+ CRLF.size +
+ CRLF.size +
+ contentLength +
+ CRLF.size
+ }
- override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream)
+ byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size
+ return byteCount
+ }
- override fun contentType(): String = entity.contentType
+ override fun repeatable(): Boolean = parts.all { it.body.repeatable() }
- override fun contentLength(): Long = entity.contentLength
+ override fun close() {
+ parts.forEach { it.body.close() }
+ }
- override fun repeatable(): Boolean = entity.isRepeatable
+ class Builder {
+ private val boundary = UUID.randomUUID().toString()
+ private val parts: MutableList = mutableListOf()
- override fun close() = entity.close()
+ fun addPart(part: Part) = apply { parts.add(part) }
+
+ fun build() = MultipartBody(boundary, parts.toImmutable())
+ }
+
+ class Part
+ private constructor(
+ val contentDisposition: String,
+ val contentType: String,
+ val body: HttpRequestBody,
+ ) {
+ companion object {
+ fun create(
+ name: String,
+ filename: String?,
+ contentType: String,
+ body: HttpRequestBody,
+ ): Part {
+ val disposition = buildString {
+ append("form-data; name=")
+ appendQuotedString(name)
+ if (filename != null) {
+ append("; filename=")
+ appendQuotedString(filename)
+ }
+ }
+ return Part(disposition, contentType, body)
+ }
+ }
+ }
+
+ companion object {
+ private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte())
+ private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte())
+ private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray()
+ private val CONTENT_TYPE = "Content-Type: ".toByteArray()
+
+ private fun StringBuilder.appendQuotedString(key: String) {
+ append('"')
+ for (ch in key) {
+ when (ch) {
+ '\n' -> append("%0A")
+ '\r' -> append("%0D")
+ '"' -> append("%22")
+ else -> append(ch)
+ }
+ }
+ append('"')
+ }
}
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/core/http/RetryingHttpClient.kt b/courier-java-core/src/main/kotlin/com/courier/core/http/RetryingHttpClient.kt
index 91211328..82315391 100644
--- a/courier-java-core/src/main/kotlin/com/courier/core/http/RetryingHttpClient.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/core/http/RetryingHttpClient.kt
@@ -1,3 +1,5 @@
+// File generated from our OpenAPI spec by Stainless.
+
package com.courier.core.http
import com.courier.core.DefaultSleeper
@@ -199,7 +201,7 @@ private constructor(
?: headers.values("Retry-After").getOrNull(0)?.let { retryAfter ->
retryAfter.toFloatOrNull()?.times(TimeUnit.SECONDS.toNanos(1))
?: try {
- ChronoUnit.MILLIS.between(
+ ChronoUnit.NANOS.between(
OffsetDateTime.now(clock),
OffsetDateTime.parse(
retryAfter,
@@ -212,13 +214,8 @@ private constructor(
}
}
?.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
- }
+ // If the API asks us to wait a certain amount of time, do what it says.
+ return Duration.ofNanos(retryAfterNanos.toLong())
}
// Apply exponential backoff, but not more than the max.
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/ElementalContent.kt b/courier-java-core/src/main/kotlin/com/courier/models/ElementalContent.kt
index ec80329c..e3659f80 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/ElementalContent.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/ElementalContent.kt
@@ -16,7 +16,6 @@ import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import java.util.Collections
import java.util.Objects
-import java.util.Optional
import kotlin.jvm.optionals.getOrNull
class ElementalContent
@@ -24,7 +23,6 @@ class ElementalContent
private constructor(
private val elements: JsonField>,
private val version: JsonField,
- private val brand: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -34,8 +32,7 @@ private constructor(
@ExcludeMissing
elements: JsonField> = JsonMissing.of(),
@JsonProperty("version") @ExcludeMissing version: JsonField = JsonMissing.of(),
- @JsonProperty("brand") @ExcludeMissing brand: JsonField = JsonMissing.of(),
- ) : this(elements, version, brand, mutableMapOf())
+ ) : this(elements, version, mutableMapOf())
/**
* @throws CourierInvalidDataException if the JSON field has an unexpected type or is
@@ -51,12 +48,6 @@ private constructor(
*/
fun version(): String = version.getRequired("version")
- /**
- * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
- * server responded with an unexpected value).
- */
- fun brand(): Optional = brand.getOptional("brand")
-
/**
* Returns the raw JSON value of [elements].
*
@@ -73,13 +64,6 @@ private constructor(
*/
@JsonProperty("version") @ExcludeMissing fun _version(): JsonField = version
- /**
- * Returns the raw JSON value of [brand].
- *
- * Unlike [brand], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("brand") @ExcludeMissing fun _brand(): JsonField = brand
-
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -111,14 +95,12 @@ private constructor(
private var elements: JsonField>? = null
private var version: JsonField? = null
- private var brand: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
internal fun from(elementalContent: ElementalContent) = apply {
elements = elementalContent.elements.map { it.toMutableList() }
version = elementalContent.version
- brand = elementalContent.brand
additionalProperties = elementalContent.additionalProperties.toMutableMap()
}
@@ -205,19 +187,6 @@ private constructor(
*/
fun version(version: JsonField) = apply { this.version = version }
- fun brand(brand: String?) = brand(JsonField.ofNullable(brand))
-
- /** Alias for calling [Builder.brand] with `brand.orElse(null)`. */
- fun brand(brand: Optional) = brand(brand.getOrNull())
-
- /**
- * Sets [Builder.brand] to an arbitrary JSON value.
- *
- * You should usually call [Builder.brand] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun brand(brand: JsonField) = apply { this.brand = brand }
-
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -254,7 +223,6 @@ private constructor(
ElementalContent(
checkRequired("elements", elements).map { it.toImmutable() },
checkRequired("version", version),
- brand,
additionalProperties.toMutableMap(),
)
}
@@ -268,7 +236,6 @@ private constructor(
elements().forEach { it.validate() }
version()
- brand()
validated = true
}
@@ -288,8 +255,7 @@ private constructor(
@JvmSynthetic
internal fun validity(): Int =
(elements.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
- (if (version.asKnown().isPresent) 1 else 0) +
- (if (brand.asKnown().isPresent) 1 else 0)
+ (if (version.asKnown().isPresent) 1 else 0)
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -299,16 +265,13 @@ private constructor(
return other is ElementalContent &&
elements == other.elements &&
version == other.version &&
- brand == other.brand &&
additionalProperties == other.additionalProperties
}
- private val hashCode: Int by lazy {
- Objects.hash(elements, version, brand, additionalProperties)
- }
+ private val hashCode: Int by lazy { Objects.hash(elements, version, additionalProperties) }
override fun hashCode(): Int = hashCode
override fun toString() =
- "ElementalContent{elements=$elements, version=$version, brand=$brand, additionalProperties=$additionalProperties}"
+ "ElementalContent{elements=$elements, version=$version, additionalProperties=$additionalProperties}"
}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/journeys/Journey.kt b/courier-java-core/src/main/kotlin/com/courier/models/journeys/Journey.kt
new file mode 100644
index 00000000..71feee7c
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/journeys/Journey.kt
@@ -0,0 +1,458 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models.journeys
+
+import com.courier.core.Enum
+import com.courier.core.ExcludeMissing
+import com.courier.core.JsonField
+import com.courier.core.JsonMissing
+import com.courier.core.JsonValue
+import com.courier.core.checkRequired
+import com.courier.errors.CourierInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.time.OffsetDateTime
+import java.util.Collections
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+/** A journey template representing an automation workflow. */
+class Journey
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val id: JsonField,
+ private val name: JsonField,
+ private val version: JsonField,
+ private val createdAt: JsonField,
+ private val updatedAt: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(),
+ @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(),
+ @JsonProperty("version") @ExcludeMissing version: JsonField = JsonMissing.of(),
+ @JsonProperty("createdAt")
+ @ExcludeMissing
+ createdAt: JsonField = JsonMissing.of(),
+ @JsonProperty("updatedAt")
+ @ExcludeMissing
+ updatedAt: JsonField = JsonMissing.of(),
+ ) : this(id, name, version, createdAt, updatedAt, mutableMapOf())
+
+ /**
+ * The unique identifier of the journey.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun id(): String = id.getRequired("id")
+
+ /**
+ * The name of the journey.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun name(): String = name.getRequired("name")
+
+ /**
+ * The version of the journey (published or draft).
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun version(): Version = version.getRequired("version")
+
+ /**
+ * ISO 8601 timestamp when the journey was created.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun createdAt(): Optional = createdAt.getOptional("createdAt")
+
+ /**
+ * ISO 8601 timestamp when the journey was last updated.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun updatedAt(): Optional = updatedAt.getOptional("updatedAt")
+
+ /**
+ * 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 [name].
+ *
+ * Unlike [name], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name
+
+ /**
+ * Returns the raw JSON value of [version].
+ *
+ * Unlike [version], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("version") @ExcludeMissing fun _version(): JsonField = version
+
+ /**
+ * Returns the raw JSON value of [createdAt].
+ *
+ * Unlike [createdAt], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("createdAt")
+ @ExcludeMissing
+ fun _createdAt(): JsonField = createdAt
+
+ /**
+ * Returns the raw JSON value of [updatedAt].
+ *
+ * Unlike [updatedAt], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("updatedAt")
+ @ExcludeMissing
+ fun _updatedAt(): JsonField = updatedAt
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Journey].
+ *
+ * The following fields are required:
+ * ```java
+ * .id()
+ * .name()
+ * .version()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Journey]. */
+ class Builder internal constructor() {
+
+ private var id: JsonField? = null
+ private var name: JsonField? = null
+ private var version: JsonField? = null
+ private var createdAt: JsonField = JsonMissing.of()
+ private var updatedAt: JsonField = JsonMissing.of()
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(journey: Journey) = apply {
+ id = journey.id
+ name = journey.name
+ version = journey.version
+ createdAt = journey.createdAt
+ updatedAt = journey.updatedAt
+ additionalProperties = journey.additionalProperties.toMutableMap()
+ }
+
+ /** The unique identifier of the journey. */
+ fun id(id: String) = id(JsonField.of(id))
+
+ /**
+ * Sets [Builder.id] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.id] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun id(id: JsonField) = apply { this.id = id }
+
+ /** The name of the journey. */
+ fun name(name: String) = name(JsonField.of(name))
+
+ /**
+ * Sets [Builder.name] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.name] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun name(name: JsonField) = apply { this.name = name }
+
+ /** The version of the journey (published or draft). */
+ fun version(version: Version) = version(JsonField.of(version))
+
+ /**
+ * Sets [Builder.version] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.version] with a well-typed [Version] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun version(version: JsonField) = apply { this.version = version }
+
+ /** ISO 8601 timestamp when the journey was created. */
+ fun createdAt(createdAt: OffsetDateTime) = createdAt(JsonField.of(createdAt))
+
+ /**
+ * Sets [Builder.createdAt] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.createdAt] with a well-typed [OffsetDateTime] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun createdAt(createdAt: JsonField) = apply { this.createdAt = createdAt }
+
+ /** ISO 8601 timestamp when the journey was last updated. */
+ fun updatedAt(updatedAt: OffsetDateTime) = updatedAt(JsonField.of(updatedAt))
+
+ /**
+ * Sets [Builder.updatedAt] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.updatedAt] with a well-typed [OffsetDateTime] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Journey].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .id()
+ * .name()
+ * .version()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Journey =
+ Journey(
+ checkRequired("id", id),
+ checkRequired("name", name),
+ checkRequired("version", version),
+ createdAt,
+ updatedAt,
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Journey = apply {
+ if (validated) {
+ return@apply
+ }
+
+ id()
+ name()
+ version().validate()
+ createdAt()
+ updatedAt()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (id.asKnown().isPresent) 1 else 0) +
+ (if (name.asKnown().isPresent) 1 else 0) +
+ (version.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (createdAt.asKnown().isPresent) 1 else 0) +
+ (if (updatedAt.asKnown().isPresent) 1 else 0)
+
+ /** The version of the journey (published or draft). */
+ class Version @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val PUBLISHED = of("published")
+
+ @JvmField val DRAFT = of("draft")
+
+ @JvmStatic fun of(value: String) = Version(JsonField.of(value))
+ }
+
+ /** An enum containing [Version]'s known values. */
+ enum class Known {
+ PUBLISHED,
+ DRAFT,
+ }
+
+ /**
+ * An enum containing [Version]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Version] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ PUBLISHED,
+ DRAFT,
+ /** An enum member indicating that [Version] was instantiated with an unknown value. */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ PUBLISHED -> Value.PUBLISHED
+ DRAFT -> Value.DRAFT
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws CourierInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ PUBLISHED -> Known.PUBLISHED
+ DRAFT -> Known.DRAFT
+ else -> throw CourierInvalidDataException("Unknown Version: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * This differs from the [toString] method because that method is primarily for debugging
+ * and generally doesn't throw.
+ *
+ * @throws CourierInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asString(): String =
+ _value().asString().orElseThrow { CourierInvalidDataException("Value is not a String") }
+
+ private var validated: Boolean = false
+
+ fun validate(): Version = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Version && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Journey &&
+ id == other.id &&
+ name == other.name &&
+ version == other.version &&
+ createdAt == other.createdAt &&
+ updatedAt == other.updatedAt &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(id, name, version, createdAt, updatedAt, additionalProperties)
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "Journey{id=$id, name=$name, version=$version, createdAt=$createdAt, updatedAt=$updatedAt, additionalProperties=$additionalProperties}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneyInvokeParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneyInvokeParams.kt
new file mode 100644
index 00000000..d4585542
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneyInvokeParams.kt
@@ -0,0 +1,234 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models.journeys
+
+import com.courier.core.JsonValue
+import com.courier.core.Params
+import com.courier.core.checkRequired
+import com.courier.core.http.Headers
+import com.courier.core.http.QueryParams
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+/** Invoke a journey run from a journey template. */
+class JourneyInvokeParams
+private constructor(
+ private val templateId: String?,
+ private val journeysInvokeRequest: JourneysInvokeRequest,
+ private val additionalHeaders: Headers,
+ private val additionalQueryParams: QueryParams,
+) : Params {
+
+ fun templateId(): Optional = Optional.ofNullable(templateId)
+
+ /**
+ * Request body for invoking a journey. Requires either a user identifier or a profile with
+ * contact information. User identifiers can be provided via user_id field, or resolved from
+ * profile/data objects (user_id, userId, or anonymousId fields).
+ */
+ fun journeysInvokeRequest(): JourneysInvokeRequest = journeysInvokeRequest
+
+ fun _additionalBodyProperties(): Map =
+ journeysInvokeRequest._additionalProperties()
+
+ /** Additional headers to send with the request. */
+ fun _additionalHeaders(): Headers = additionalHeaders
+
+ /** Additional query param to send with the request. */
+ fun _additionalQueryParams(): QueryParams = additionalQueryParams
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [JourneyInvokeParams].
+ *
+ * The following fields are required:
+ * ```java
+ * .journeysInvokeRequest()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [JourneyInvokeParams]. */
+ class Builder internal constructor() {
+
+ private var templateId: String? = null
+ private var journeysInvokeRequest: JourneysInvokeRequest? = null
+ private var additionalHeaders: Headers.Builder = Headers.builder()
+ private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
+
+ @JvmSynthetic
+ internal fun from(journeyInvokeParams: JourneyInvokeParams) = apply {
+ templateId = journeyInvokeParams.templateId
+ journeysInvokeRequest = journeyInvokeParams.journeysInvokeRequest
+ additionalHeaders = journeyInvokeParams.additionalHeaders.toBuilder()
+ additionalQueryParams = journeyInvokeParams.additionalQueryParams.toBuilder()
+ }
+
+ fun templateId(templateId: String?) = apply { this.templateId = templateId }
+
+ /** Alias for calling [Builder.templateId] with `templateId.orElse(null)`. */
+ fun templateId(templateId: Optional) = templateId(templateId.getOrNull())
+
+ /**
+ * Request body for invoking a journey. Requires either a user identifier or a profile with
+ * contact information. User identifiers can be provided via user_id field, or resolved from
+ * profile/data objects (user_id, userId, or anonymousId fields).
+ */
+ fun journeysInvokeRequest(journeysInvokeRequest: JourneysInvokeRequest) = apply {
+ this.journeysInvokeRequest = journeysInvokeRequest
+ }
+
+ fun additionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun additionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun putAdditionalHeader(name: String, value: String) = apply {
+ additionalHeaders.put(name, value)
+ }
+
+ fun putAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.put(name, values)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun replaceAdditionalHeaders(name: String, value: String) = apply {
+ additionalHeaders.replace(name, value)
+ }
+
+ fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.replace(name, values)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
+
+ fun removeAllAdditionalHeaders(names: Set) = apply {
+ additionalHeaders.removeAll(names)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: Map>) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun putAdditionalQueryParam(key: String, value: String) = apply {
+ additionalQueryParams.put(key, value)
+ }
+
+ fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.put(key, values)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, value: String) = apply {
+ additionalQueryParams.replace(key, value)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.replace(key, values)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
+
+ fun removeAllAdditionalQueryParams(keys: Set) = apply {
+ additionalQueryParams.removeAll(keys)
+ }
+
+ /**
+ * Returns an immutable instance of [JourneyInvokeParams].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .journeysInvokeRequest()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): JourneyInvokeParams =
+ JourneyInvokeParams(
+ templateId,
+ checkRequired("journeysInvokeRequest", journeysInvokeRequest),
+ additionalHeaders.build(),
+ additionalQueryParams.build(),
+ )
+ }
+
+ fun _body(): JourneysInvokeRequest = journeysInvokeRequest
+
+ fun _pathParam(index: Int): String =
+ when (index) {
+ 0 -> templateId ?: ""
+ else -> ""
+ }
+
+ override fun _headers(): Headers = additionalHeaders
+
+ override fun _queryParams(): QueryParams = additionalQueryParams
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is JourneyInvokeParams &&
+ templateId == other.templateId &&
+ journeysInvokeRequest == other.journeysInvokeRequest &&
+ additionalHeaders == other.additionalHeaders &&
+ additionalQueryParams == other.additionalQueryParams
+ }
+
+ override fun hashCode(): Int =
+ Objects.hash(templateId, journeysInvokeRequest, additionalHeaders, additionalQueryParams)
+
+ override fun toString() =
+ "JourneyInvokeParams{templateId=$templateId, journeysInvokeRequest=$journeysInvokeRequest, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneyListParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneyListParams.kt
new file mode 100644
index 00000000..33c24c7c
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneyListParams.kt
@@ -0,0 +1,356 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models.journeys
+
+import com.courier.core.Enum
+import com.courier.core.JsonField
+import com.courier.core.Params
+import com.courier.core.http.Headers
+import com.courier.core.http.QueryParams
+import com.courier.errors.CourierInvalidDataException
+import com.fasterxml.jackson.annotation.JsonCreator
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+/** Get the list of journeys. */
+class JourneyListParams
+private constructor(
+ private val cursor: String?,
+ private val version: Version?,
+ private val additionalHeaders: Headers,
+ private val additionalQueryParams: QueryParams,
+) : Params {
+
+ /**
+ * A cursor token for pagination. Use the cursor from the previous response to fetch the next
+ * page of results.
+ */
+ fun cursor(): Optional = Optional.ofNullable(cursor)
+
+ /**
+ * The version of journeys to retrieve. Accepted values are published (for published journeys)
+ * or draft (for draft journeys). Defaults to published.
+ */
+ fun version(): Optional = Optional.ofNullable(version)
+
+ /** Additional headers to send with the request. */
+ fun _additionalHeaders(): Headers = additionalHeaders
+
+ /** Additional query param to send with the request. */
+ fun _additionalQueryParams(): QueryParams = additionalQueryParams
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ @JvmStatic fun none(): JourneyListParams = builder().build()
+
+ /** Returns a mutable builder for constructing an instance of [JourneyListParams]. */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [JourneyListParams]. */
+ class Builder internal constructor() {
+
+ private var cursor: String? = null
+ private var version: Version? = null
+ private var additionalHeaders: Headers.Builder = Headers.builder()
+ private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
+
+ @JvmSynthetic
+ internal fun from(journeyListParams: JourneyListParams) = apply {
+ cursor = journeyListParams.cursor
+ version = journeyListParams.version
+ additionalHeaders = journeyListParams.additionalHeaders.toBuilder()
+ additionalQueryParams = journeyListParams.additionalQueryParams.toBuilder()
+ }
+
+ /**
+ * A cursor token for pagination. Use the cursor from the previous response to fetch the
+ * next page of results.
+ */
+ fun cursor(cursor: String?) = apply { this.cursor = cursor }
+
+ /** Alias for calling [Builder.cursor] with `cursor.orElse(null)`. */
+ fun cursor(cursor: Optional) = cursor(cursor.getOrNull())
+
+ /**
+ * The version of journeys to retrieve. Accepted values are published (for published
+ * journeys) or draft (for draft journeys). Defaults to published.
+ */
+ fun version(version: Version?) = apply { this.version = version }
+
+ /** Alias for calling [Builder.version] with `version.orElse(null)`. */
+ fun version(version: Optional) = version(version.getOrNull())
+
+ fun additionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun additionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.clear()
+ putAllAdditionalHeaders(additionalHeaders)
+ }
+
+ fun putAdditionalHeader(name: String, value: String) = apply {
+ additionalHeaders.put(name, value)
+ }
+
+ fun putAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.put(name, values)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.putAll(additionalHeaders)
+ }
+
+ fun replaceAdditionalHeaders(name: String, value: String) = apply {
+ additionalHeaders.replace(name, value)
+ }
+
+ fun replaceAdditionalHeaders(name: String, values: Iterable) = apply {
+ additionalHeaders.replace(name, values)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply {
+ this.additionalHeaders.replaceAll(additionalHeaders)
+ }
+
+ fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) }
+
+ fun removeAllAdditionalHeaders(names: Set) = apply {
+ additionalHeaders.removeAll(names)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun additionalQueryParams(additionalQueryParams: Map>) = apply {
+ this.additionalQueryParams.clear()
+ putAllAdditionalQueryParams(additionalQueryParams)
+ }
+
+ fun putAdditionalQueryParam(key: String, value: String) = apply {
+ additionalQueryParams.put(key, value)
+ }
+
+ fun putAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.put(key, values)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun putAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.putAll(additionalQueryParams)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, value: String) = apply {
+ additionalQueryParams.replace(key, value)
+ }
+
+ fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply {
+ additionalQueryParams.replace(key, values)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) =
+ apply {
+ this.additionalQueryParams.replaceAll(additionalQueryParams)
+ }
+
+ fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) }
+
+ fun removeAllAdditionalQueryParams(keys: Set) = apply {
+ additionalQueryParams.removeAll(keys)
+ }
+
+ /**
+ * Returns an immutable instance of [JourneyListParams].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): JourneyListParams =
+ JourneyListParams(
+ cursor,
+ version,
+ additionalHeaders.build(),
+ additionalQueryParams.build(),
+ )
+ }
+
+ override fun _headers(): Headers = additionalHeaders
+
+ override fun _queryParams(): QueryParams =
+ QueryParams.builder()
+ .apply {
+ cursor?.let { put("cursor", it) }
+ version?.let { put("version", it.toString()) }
+ putAll(additionalQueryParams)
+ }
+ .build()
+
+ /**
+ * The version of journeys to retrieve. Accepted values are published (for published journeys)
+ * or draft (for draft journeys). Defaults to published.
+ */
+ class Version @JsonCreator private constructor(private val value: JsonField) : Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val PUBLISHED = of("published")
+
+ @JvmField val DRAFT = of("draft")
+
+ @JvmStatic fun of(value: String) = Version(JsonField.of(value))
+ }
+
+ /** An enum containing [Version]'s known values. */
+ enum class Known {
+ PUBLISHED,
+ DRAFT,
+ }
+
+ /**
+ * An enum containing [Version]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Version] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ PUBLISHED,
+ DRAFT,
+ /** An enum member indicating that [Version] was instantiated with an unknown value. */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ PUBLISHED -> Value.PUBLISHED
+ DRAFT -> Value.DRAFT
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws CourierInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ PUBLISHED -> Known.PUBLISHED
+ DRAFT -> Known.DRAFT
+ else -> throw CourierInvalidDataException("Unknown Version: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * This differs from the [toString] method because that method is primarily for debugging
+ * and generally doesn't throw.
+ *
+ * @throws CourierInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asString(): String =
+ _value().asString().orElseThrow { CourierInvalidDataException("Value is not a String") }
+
+ private var validated: Boolean = false
+
+ fun validate(): Version = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Version && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is JourneyListParams &&
+ cursor == other.cursor &&
+ version == other.version &&
+ additionalHeaders == other.additionalHeaders &&
+ additionalQueryParams == other.additionalQueryParams
+ }
+
+ override fun hashCode(): Int =
+ Objects.hash(cursor, version, additionalHeaders, additionalQueryParams)
+
+ override fun toString() =
+ "JourneyListParams{cursor=$cursor, version=$version, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysInvokeRequest.kt b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysInvokeRequest.kt
new file mode 100644
index 00000000..91df020a
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysInvokeRequest.kt
@@ -0,0 +1,461 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models.journeys
+
+import com.courier.core.ExcludeMissing
+import com.courier.core.JsonField
+import com.courier.core.JsonMissing
+import com.courier.core.JsonValue
+import com.courier.core.toImmutable
+import com.courier.errors.CourierInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+/**
+ * Request body for invoking a journey. Requires either a user identifier or a profile with contact
+ * information. User identifiers can be provided via user_id field, or resolved from profile/data
+ * objects (user_id, userId, or anonymousId fields).
+ */
+class JourneysInvokeRequest
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val data: JsonField,
+ private val profile: JsonField,
+ private val userId: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("data") @ExcludeMissing data: JsonField = JsonMissing.of(),
+ @JsonProperty("profile") @ExcludeMissing profile: JsonField = JsonMissing.of(),
+ @JsonProperty("user_id") @ExcludeMissing userId: JsonField = JsonMissing.of(),
+ ) : this(data, profile, userId, mutableMapOf())
+
+ /**
+ * Data payload passed to the journey. The expected shape can be predefined using the schema
+ * builder in the journey editor. This data is available in journey steps for condition
+ * evaluation and template variable interpolation. Can also contain user identifiers (user_id,
+ * userId, anonymousId) if not provided elsewhere.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun data(): Optional = data.getOptional("data")
+
+ /**
+ * Profile data for the user. Can contain contact information (email, phone_number), user
+ * identifiers (user_id, userId, anonymousId), or any custom profile fields. Profile fields are
+ * merged with any existing stored profile for the user. Include context.tenant_id to load a
+ * tenant-scoped profile for multi-tenant scenarios.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun profile(): Optional = profile.getOptional("profile")
+
+ /**
+ * A unique identifier for the user. If not provided, the system will attempt to resolve the
+ * user identifier from profile or data objects.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun userId(): Optional = userId.getOptional("user_id")
+
+ /**
+ * Returns the raw JSON value of [data].
+ *
+ * Unlike [data], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("data") @ExcludeMissing fun _data(): JsonField = data
+
+ /**
+ * Returns the raw JSON value of [profile].
+ *
+ * Unlike [profile], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("profile") @ExcludeMissing fun _profile(): JsonField = profile
+
+ /**
+ * Returns the raw JSON value of [userId].
+ *
+ * Unlike [userId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("user_id") @ExcludeMissing fun _userId(): JsonField = userId
+
+ @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 [JourneysInvokeRequest]. */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [JourneysInvokeRequest]. */
+ class Builder internal constructor() {
+
+ private var data: JsonField = JsonMissing.of()
+ private var profile: JsonField = JsonMissing.of()
+ private var userId: JsonField = JsonMissing.of()
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(journeysInvokeRequest: JourneysInvokeRequest) = apply {
+ data = journeysInvokeRequest.data
+ profile = journeysInvokeRequest.profile
+ userId = journeysInvokeRequest.userId
+ additionalProperties = journeysInvokeRequest.additionalProperties.toMutableMap()
+ }
+
+ /**
+ * Data payload passed to the journey. The expected shape can be predefined using the schema
+ * builder in the journey editor. This data is available in journey steps for condition
+ * evaluation and template variable interpolation. Can also contain user identifiers
+ * (user_id, userId, anonymousId) if not provided elsewhere.
+ */
+ fun data(data: Data) = data(JsonField.of(data))
+
+ /**
+ * Sets [Builder.data] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.data] with a well-typed [Data] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun data(data: JsonField) = apply { this.data = data }
+
+ /**
+ * Profile data for the user. Can contain contact information (email, phone_number), user
+ * identifiers (user_id, userId, anonymousId), or any custom profile fields. Profile fields
+ * are merged with any existing stored profile for the user. Include context.tenant_id to
+ * load a tenant-scoped profile for multi-tenant scenarios.
+ */
+ fun profile(profile: Profile) = profile(JsonField.of(profile))
+
+ /**
+ * Sets [Builder.profile] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.profile] with a well-typed [Profile] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun profile(profile: JsonField) = apply { this.profile = profile }
+
+ /**
+ * A unique identifier for the user. If not provided, the system will attempt to resolve the
+ * user identifier from profile or data objects.
+ */
+ fun userId(userId: String) = userId(JsonField.of(userId))
+
+ /**
+ * Sets [Builder.userId] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.userId] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun userId(userId: JsonField) = apply { this.userId = userId }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [JourneysInvokeRequest].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): JourneysInvokeRequest =
+ JourneysInvokeRequest(data, profile, userId, additionalProperties.toMutableMap())
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): JourneysInvokeRequest = apply {
+ if (validated) {
+ return@apply
+ }
+
+ data().ifPresent { it.validate() }
+ profile().ifPresent { it.validate() }
+ userId()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (data.asKnown().getOrNull()?.validity() ?: 0) +
+ (profile.asKnown().getOrNull()?.validity() ?: 0) +
+ (if (userId.asKnown().isPresent) 1 else 0)
+
+ /**
+ * Data payload passed to the journey. The expected shape can be predefined using the schema
+ * builder in the journey editor. This data is available in journey steps for condition
+ * evaluation and template variable interpolation. Can also contain user identifiers (user_id,
+ * userId, anonymousId) if not provided elsewhere.
+ */
+ class Data
+ @JsonCreator
+ private constructor(
+ @com.fasterxml.jackson.annotation.JsonValue
+ private val additionalProperties: Map
+ ) {
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map = additionalProperties
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /** Returns a mutable builder for constructing an instance of [Data]. */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Data]. */
+ class Builder internal constructor() {
+
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(data: Data) = apply {
+ additionalProperties = data.additionalProperties.toMutableMap()
+ }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Data].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): Data = Data(additionalProperties.toImmutable())
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Data = apply {
+ if (validated) {
+ return@apply
+ }
+
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Data && additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() = "Data{additionalProperties=$additionalProperties}"
+ }
+
+ /**
+ * Profile data for the user. Can contain contact information (email, phone_number), user
+ * identifiers (user_id, userId, anonymousId), or any custom profile fields. Profile fields are
+ * merged with any existing stored profile for the user. Include context.tenant_id to load a
+ * tenant-scoped profile for multi-tenant scenarios.
+ */
+ class Profile
+ @JsonCreator
+ private constructor(
+ @com.fasterxml.jackson.annotation.JsonValue
+ private val additionalProperties: Map
+ ) {
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map = additionalProperties
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /** Returns a mutable builder for constructing an instance of [Profile]. */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Profile]. */
+ class Builder internal constructor() {
+
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(profile: Profile) = apply {
+ additionalProperties = profile.additionalProperties.toMutableMap()
+ }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Profile].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): Profile = Profile(additionalProperties.toImmutable())
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Profile = apply {
+ if (validated) {
+ return@apply
+ }
+
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ additionalProperties.count { (_, value) -> !value.isNull() && !value.isMissing() }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Profile && additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() = "Profile{additionalProperties=$additionalProperties}"
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is JourneysInvokeRequest &&
+ data == other.data &&
+ profile == other.profile &&
+ userId == other.userId &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(data, profile, userId, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "JourneysInvokeRequest{data=$data, profile=$profile, userId=$userId, additionalProperties=$additionalProperties}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysInvokeResponse.kt b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysInvokeResponse.kt
new file mode 100644
index 00000000..06b82bad
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysInvokeResponse.kt
@@ -0,0 +1,173 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models.journeys
+
+import com.courier.core.ExcludeMissing
+import com.courier.core.JsonField
+import com.courier.core.JsonMissing
+import com.courier.core.JsonValue
+import com.courier.core.checkRequired
+import com.courier.errors.CourierInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+
+class JourneysInvokeResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val runId: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("runId") @ExcludeMissing runId: JsonField = JsonMissing.of()
+ ) : this(runId, mutableMapOf())
+
+ /**
+ * A unique identifier for the journey run that was created.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun runId(): String = runId.getRequired("runId")
+
+ /**
+ * Returns the raw JSON value of [runId].
+ *
+ * Unlike [runId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("runId") @ExcludeMissing fun _runId(): JsonField = runId
+
+ @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 [JourneysInvokeResponse].
+ *
+ * The following fields are required:
+ * ```java
+ * .runId()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [JourneysInvokeResponse]. */
+ class Builder internal constructor() {
+
+ private var runId: JsonField? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(journeysInvokeResponse: JourneysInvokeResponse) = apply {
+ runId = journeysInvokeResponse.runId
+ additionalProperties = journeysInvokeResponse.additionalProperties.toMutableMap()
+ }
+
+ /** A unique identifier for the journey run that was created. */
+ fun runId(runId: String) = runId(JsonField.of(runId))
+
+ /**
+ * Sets [Builder.runId] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.runId] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun runId(runId: JsonField) = apply { this.runId = runId }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [JourneysInvokeResponse].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .runId()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): JourneysInvokeResponse =
+ JourneysInvokeResponse(
+ checkRequired("runId", runId),
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): JourneysInvokeResponse = apply {
+ if (validated) {
+ return@apply
+ }
+
+ runId()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = (if (runId.asKnown().isPresent) 1 else 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is JourneysInvokeResponse &&
+ runId == other.runId &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(runId, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "JourneysInvokeResponse{runId=$runId, additionalProperties=$additionalProperties}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysListResponse.kt b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysListResponse.kt
new file mode 100644
index 00000000..2ba010ef
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/journeys/JourneysListResponse.kt
@@ -0,0 +1,214 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models.journeys
+
+import com.courier.core.ExcludeMissing
+import com.courier.core.JsonField
+import com.courier.core.JsonMissing
+import com.courier.core.JsonValue
+import com.courier.core.checkKnown
+import com.courier.core.toImmutable
+import com.courier.errors.CourierInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import java.util.Collections
+import java.util.Objects
+import java.util.Optional
+import kotlin.jvm.optionals.getOrNull
+
+class JourneysListResponse
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val cursor: JsonField,
+ private val templates: JsonField>,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("cursor") @ExcludeMissing cursor: JsonField = JsonMissing.of(),
+ @JsonProperty("templates")
+ @ExcludeMissing
+ templates: JsonField> = JsonMissing.of(),
+ ) : this(cursor, templates, mutableMapOf())
+
+ /**
+ * A cursor token for pagination. Present when there are more results available.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun cursor(): Optional = cursor.getOptional("cursor")
+
+ /**
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun templates(): Optional> = templates.getOptional("templates")
+
+ /**
+ * Returns the raw JSON value of [cursor].
+ *
+ * Unlike [cursor], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("cursor") @ExcludeMissing fun _cursor(): JsonField = cursor
+
+ /**
+ * Returns the raw JSON value of [templates].
+ *
+ * Unlike [templates], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("templates")
+ @ExcludeMissing
+ fun _templates(): JsonField> = templates
+
+ @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 [JourneysListResponse]. */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [JourneysListResponse]. */
+ class Builder internal constructor() {
+
+ private var cursor: JsonField = JsonMissing.of()
+ private var templates: JsonField>? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(journeysListResponse: JourneysListResponse) = apply {
+ cursor = journeysListResponse.cursor
+ templates = journeysListResponse.templates.map { it.toMutableList() }
+ additionalProperties = journeysListResponse.additionalProperties.toMutableMap()
+ }
+
+ /** A cursor token for pagination. Present when there are more results available. */
+ fun cursor(cursor: String) = cursor(JsonField.of(cursor))
+
+ /**
+ * Sets [Builder.cursor] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.cursor] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun cursor(cursor: JsonField) = apply { this.cursor = cursor }
+
+ fun templates(templates: List) = templates(JsonField.of(templates))
+
+ /**
+ * Sets [Builder.templates] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.templates] with a well-typed `List` value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun templates(templates: JsonField>) = apply {
+ this.templates = templates.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [Journey] to [templates].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addTemplate(template: Journey) = apply {
+ templates =
+ (templates ?: JsonField.of(mutableListOf())).also {
+ checkKnown("templates", it).add(template)
+ }
+ }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [JourneysListResponse].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ */
+ fun build(): JourneysListResponse =
+ JourneysListResponse(
+ cursor,
+ (templates ?: JsonMissing.of()).map { it.toImmutable() },
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): JourneysListResponse = apply {
+ if (validated) {
+ return@apply
+ }
+
+ cursor()
+ templates().ifPresent { it.forEach { it.validate() } }
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (if (cursor.asKnown().isPresent) 1 else 0) +
+ (templates.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is JourneysListResponse &&
+ cursor == other.cursor &&
+ templates == other.templates &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(cursor, templates, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "JourneysListResponse{cursor=$cursor, templates=$templates, additionalProperties=$additionalProperties}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/TokenAddSingleParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/TokenAddSingleParams.kt
index 35140b34..3d80f81f 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/TokenAddSingleParams.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/users/tokens/TokenAddSingleParams.kt
@@ -2,11 +2,32 @@
package com.courier.models.users.tokens
+import com.courier.core.BaseDeserializer
+import com.courier.core.BaseSerializer
+import com.courier.core.Enum
+import com.courier.core.ExcludeMissing
+import com.courier.core.JsonField
+import com.courier.core.JsonMissing
import com.courier.core.JsonValue
import com.courier.core.Params
+import com.courier.core.allMaxBy
import com.courier.core.checkRequired
+import com.courier.core.getOrThrow
import com.courier.core.http.Headers
import com.courier.core.http.QueryParams
+import com.courier.errors.CourierInvalidDataException
+import com.fasterxml.jackson.annotation.JsonAnyGetter
+import com.fasterxml.jackson.annotation.JsonAnySetter
+import com.fasterxml.jackson.annotation.JsonCreator
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.core.JsonGenerator
+import com.fasterxml.jackson.core.ObjectCodec
+import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.SerializerProvider
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import java.util.Collections
import java.util.Objects
import java.util.Optional
import kotlin.jvm.optionals.getOrNull
@@ -15,19 +36,86 @@ import kotlin.jvm.optionals.getOrNull
class TokenAddSingleParams
private constructor(
private val userId: String,
- private val pathToken: String?,
- private val userToken: UserToken,
+ private val token: String?,
+ private val body: Body,
private val additionalHeaders: Headers,
private val additionalQueryParams: QueryParams,
) : Params {
fun userId(): String = userId
- fun pathToken(): Optional = Optional.ofNullable(pathToken)
+ fun token(): Optional = Optional.ofNullable(token)
- fun userToken(): UserToken = userToken
+ /**
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun providerKey(): ProviderKey = body.providerKey()
- fun _additionalBodyProperties(): Map = userToken._additionalProperties()
+ /**
+ * Information about the device the token came from.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun device(): Optional = body.device()
+
+ /**
+ * ISO 8601 formatted date the token expires. Defaults to 2 months. Set to false to disable
+ * expiration.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun expiryDate(): Optional = body.expiryDate()
+
+ /**
+ * Properties about the token.
+ *
+ * This arbitrary value can be deserialized into a custom type using the `convert` method:
+ * ```java
+ * MyClass myObject = tokenAddSingleParams.properties().convert(MyClass.class);
+ * ```
+ */
+ fun _properties(): JsonValue = body._properties()
+
+ /**
+ * Tracking information about the device the token came from.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun tracking(): Optional = body.tracking()
+
+ /**
+ * Returns the raw JSON value of [providerKey].
+ *
+ * Unlike [providerKey], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ fun _providerKey(): JsonField = body._providerKey()
+
+ /**
+ * Returns the raw JSON value of [device].
+ *
+ * Unlike [device], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ fun _device(): JsonField = body._device()
+
+ /**
+ * Returns the raw JSON value of [expiryDate].
+ *
+ * Unlike [expiryDate], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ fun _expiryDate(): JsonField = body._expiryDate()
+
+ /**
+ * Returns the raw JSON value of [tracking].
+ *
+ * Unlike [tracking], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ fun _tracking(): JsonField = body._tracking()
+
+ fun _additionalBodyProperties(): Map = body._additionalProperties()
/** Additional headers to send with the request. */
fun _additionalHeaders(): Headers = additionalHeaders
@@ -45,7 +133,7 @@ private constructor(
* The following fields are required:
* ```java
* .userId()
- * .userToken()
+ * .providerKey()
* ```
*/
@JvmStatic fun builder() = Builder()
@@ -55,28 +143,128 @@ private constructor(
class Builder internal constructor() {
private var userId: String? = null
- private var pathToken: String? = null
- private var userToken: UserToken? = null
+ private var token: String? = null
+ private var body: Body.Builder = Body.builder()
private var additionalHeaders: Headers.Builder = Headers.builder()
private var additionalQueryParams: QueryParams.Builder = QueryParams.builder()
@JvmSynthetic
internal fun from(tokenAddSingleParams: TokenAddSingleParams) = apply {
userId = tokenAddSingleParams.userId
- pathToken = tokenAddSingleParams.pathToken
- userToken = tokenAddSingleParams.userToken
+ token = tokenAddSingleParams.token
+ body = tokenAddSingleParams.body.toBuilder()
additionalHeaders = tokenAddSingleParams.additionalHeaders.toBuilder()
additionalQueryParams = tokenAddSingleParams.additionalQueryParams.toBuilder()
}
fun userId(userId: String) = apply { this.userId = userId }
- fun pathToken(pathToken: String?) = apply { this.pathToken = pathToken }
+ fun token(token: String?) = apply { this.token = token }
+
+ /** Alias for calling [Builder.token] with `token.orElse(null)`. */
+ fun token(token: Optional) = token(token.getOrNull())
+
+ /**
+ * Sets the entire request body.
+ *
+ * This is generally only useful if you are already constructing the body separately.
+ * Otherwise, it's more convenient to use the top-level setters instead:
+ * - [providerKey]
+ * - [device]
+ * - [expiryDate]
+ * - [properties]
+ * - [tracking]
+ * - etc.
+ */
+ fun body(body: Body) = apply { this.body = body.toBuilder() }
+
+ fun providerKey(providerKey: ProviderKey) = apply { body.providerKey(providerKey) }
+
+ /**
+ * Sets [Builder.providerKey] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.providerKey] with a well-typed [ProviderKey] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun providerKey(providerKey: JsonField) = apply {
+ body.providerKey(providerKey)
+ }
+
+ /** Information about the device the token came from. */
+ fun device(device: Device?) = apply { body.device(device) }
+
+ /** Alias for calling [Builder.device] with `device.orElse(null)`. */
+ fun device(device: Optional) = device(device.getOrNull())
+
+ /**
+ * Sets [Builder.device] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.device] with a well-typed [Device] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun device(device: JsonField) = apply { body.device(device) }
+
+ /**
+ * ISO 8601 formatted date the token expires. Defaults to 2 months. Set to false to disable
+ * expiration.
+ */
+ fun expiryDate(expiryDate: ExpiryDate?) = apply { body.expiryDate(expiryDate) }
+
+ /** Alias for calling [Builder.expiryDate] with `expiryDate.orElse(null)`. */
+ fun expiryDate(expiryDate: Optional) = expiryDate(expiryDate.getOrNull())
- /** Alias for calling [Builder.pathToken] with `pathToken.orElse(null)`. */
- fun pathToken(pathToken: Optional) = pathToken(pathToken.getOrNull())
+ /**
+ * Sets [Builder.expiryDate] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.expiryDate] with a well-typed [ExpiryDate] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun expiryDate(expiryDate: JsonField) = apply { body.expiryDate(expiryDate) }
+
+ /** Alias for calling [expiryDate] with `ExpiryDate.ofString(string)`. */
+ fun expiryDate(string: String) = apply { body.expiryDate(string) }
+
+ /** Alias for calling [expiryDate] with `ExpiryDate.ofBool(bool)`. */
+ fun expiryDate(bool: Boolean) = apply { body.expiryDate(bool) }
+
+ /** Properties about the token. */
+ fun properties(properties: JsonValue) = apply { body.properties(properties) }
+
+ /** Tracking information about the device the token came from. */
+ fun tracking(tracking: Tracking?) = apply { body.tracking(tracking) }
+
+ /** Alias for calling [Builder.tracking] with `tracking.orElse(null)`. */
+ fun tracking(tracking: Optional) = tracking(tracking.getOrNull())
- fun userToken(userToken: UserToken) = apply { this.userToken = userToken }
+ /**
+ * Sets [Builder.tracking] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.tracking] with a well-typed [Tracking] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun tracking(tracking: JsonField) = apply { body.tracking(tracking) }
+
+ fun additionalBodyProperties(additionalBodyProperties: Map) = apply {
+ body.additionalProperties(additionalBodyProperties)
+ }
+
+ fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply {
+ body.putAdditionalProperty(key, value)
+ }
+
+ fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) =
+ apply {
+ body.putAllAdditionalProperties(additionalBodyProperties)
+ }
+
+ fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) }
+
+ fun removeAllAdditionalBodyProperties(keys: Set) = apply {
+ body.removeAllAdditionalProperties(keys)
+ }
fun additionalHeaders(additionalHeaders: Headers) = apply {
this.additionalHeaders.clear()
@@ -184,7 +372,7 @@ private constructor(
* The following fields are required:
* ```java
* .userId()
- * .userToken()
+ * .providerKey()
* ```
*
* @throws IllegalStateException if any required field is unset.
@@ -192,19 +380,19 @@ private constructor(
fun build(): TokenAddSingleParams =
TokenAddSingleParams(
checkRequired("userId", userId),
- pathToken,
- checkRequired("userToken", userToken),
+ token,
+ body.build(),
additionalHeaders.build(),
additionalQueryParams.build(),
)
}
- fun _body(): UserToken = userToken
+ fun _body(): Body = body
fun _pathParam(index: Int): String =
when (index) {
0 -> userId
- 1 -> pathToken ?: ""
+ 1 -> token ?: ""
else -> ""
}
@@ -212,6 +400,1264 @@ private constructor(
override fun _queryParams(): QueryParams = additionalQueryParams
+ /**
+ * Request body for adding a single token. The token value itself is provided via the path
+ * parameter, so it is omitted from the body.
+ */
+ class Body
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val providerKey: JsonField,
+ private val device: JsonField,
+ private val expiryDate: JsonField,
+ private val properties: JsonValue,
+ private val tracking: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("provider_key")
+ @ExcludeMissing
+ providerKey: JsonField = JsonMissing.of(),
+ @JsonProperty("device") @ExcludeMissing device: JsonField = JsonMissing.of(),
+ @JsonProperty("expiry_date")
+ @ExcludeMissing
+ expiryDate: JsonField = JsonMissing.of(),
+ @JsonProperty("properties") @ExcludeMissing properties: JsonValue = JsonMissing.of(),
+ @JsonProperty("tracking")
+ @ExcludeMissing
+ tracking: JsonField = JsonMissing.of(),
+ ) : this(providerKey, device, expiryDate, properties, tracking, mutableMapOf())
+
+ /**
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type or is
+ * unexpectedly missing or null (e.g. if the server responded with an unexpected value).
+ */
+ fun providerKey(): ProviderKey = providerKey.getRequired("provider_key")
+
+ /**
+ * Information about the device the token came from.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun device(): Optional = device.getOptional("device")
+
+ /**
+ * ISO 8601 formatted date the token expires. Defaults to 2 months. Set to false to disable
+ * expiration.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun expiryDate(): Optional = expiryDate.getOptional("expiry_date")
+
+ /**
+ * Properties about the token.
+ *
+ * This arbitrary value can be deserialized into a custom type using the `convert` method:
+ * ```java
+ * MyClass myObject = body.properties().convert(MyClass.class);
+ * ```
+ */
+ @JsonProperty("properties") @ExcludeMissing fun _properties(): JsonValue = properties
+
+ /**
+ * Tracking information about the device the token came from.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun tracking(): Optional = tracking.getOptional("tracking")
+
+ /**
+ * Returns the raw JSON value of [providerKey].
+ *
+ * Unlike [providerKey], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("provider_key")
+ @ExcludeMissing
+ fun _providerKey(): JsonField = providerKey
+
+ /**
+ * Returns the raw JSON value of [device].
+ *
+ * Unlike [device], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("device") @ExcludeMissing fun _device(): JsonField = device
+
+ /**
+ * Returns the raw JSON value of [expiryDate].
+ *
+ * Unlike [expiryDate], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("expiry_date")
+ @ExcludeMissing
+ fun _expiryDate(): JsonField = expiryDate
+
+ /**
+ * Returns the raw JSON value of [tracking].
+ *
+ * Unlike [tracking], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("tracking") @ExcludeMissing fun _tracking(): JsonField = tracking
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [Body].
+ *
+ * The following fields are required:
+ * ```java
+ * .providerKey()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Body]. */
+ class Builder internal constructor() {
+
+ private var providerKey: JsonField? = null
+ private var device: JsonField = JsonMissing.of()
+ private var expiryDate: JsonField = JsonMissing.of()
+ private var properties: JsonValue = JsonMissing.of()
+ private var tracking: JsonField = JsonMissing.of()
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(body: Body) = apply {
+ providerKey = body.providerKey
+ device = body.device
+ expiryDate = body.expiryDate
+ properties = body.properties
+ tracking = body.tracking
+ additionalProperties = body.additionalProperties.toMutableMap()
+ }
+
+ fun providerKey(providerKey: ProviderKey) = providerKey(JsonField.of(providerKey))
+
+ /**
+ * Sets [Builder.providerKey] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.providerKey] with a well-typed [ProviderKey] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun providerKey(providerKey: JsonField) = apply {
+ this.providerKey = providerKey
+ }
+
+ /** Information about the device the token came from. */
+ fun device(device: Device?) = device(JsonField.ofNullable(device))
+
+ /** Alias for calling [Builder.device] with `device.orElse(null)`. */
+ fun device(device: Optional) = device(device.getOrNull())
+
+ /**
+ * Sets [Builder.device] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.device] with a well-typed [Device] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun device(device: JsonField) = apply { this.device = device }
+
+ /**
+ * ISO 8601 formatted date the token expires. Defaults to 2 months. Set to false to
+ * disable expiration.
+ */
+ fun expiryDate(expiryDate: ExpiryDate?) = expiryDate(JsonField.ofNullable(expiryDate))
+
+ /** Alias for calling [Builder.expiryDate] with `expiryDate.orElse(null)`. */
+ fun expiryDate(expiryDate: Optional) = expiryDate(expiryDate.getOrNull())
+
+ /**
+ * Sets [Builder.expiryDate] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.expiryDate] with a well-typed [ExpiryDate] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun expiryDate(expiryDate: JsonField) = apply {
+ this.expiryDate = expiryDate
+ }
+
+ /** Alias for calling [expiryDate] with `ExpiryDate.ofString(string)`. */
+ fun expiryDate(string: String) = expiryDate(ExpiryDate.ofString(string))
+
+ /** Alias for calling [expiryDate] with `ExpiryDate.ofBool(bool)`. */
+ fun expiryDate(bool: Boolean) = expiryDate(ExpiryDate.ofBool(bool))
+
+ /** Properties about the token. */
+ fun properties(properties: JsonValue) = apply { this.properties = properties }
+
+ /** Tracking information about the device the token came from. */
+ fun tracking(tracking: Tracking?) = tracking(JsonField.ofNullable(tracking))
+
+ /** Alias for calling [Builder.tracking] with `tracking.orElse(null)`. */
+ fun tracking(tracking: Optional) = tracking(tracking.getOrNull())
+
+ /**
+ * Sets [Builder.tracking] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.tracking] with a well-typed [Tracking] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun tracking(tracking: JsonField) = apply { this.tracking = tracking }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [Body].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .providerKey()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): Body =
+ Body(
+ checkRequired("providerKey", providerKey),
+ device,
+ expiryDate,
+ properties,
+ tracking,
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): Body = apply {
+ if (validated) {
+ return@apply
+ }
+
+ providerKey().validate()
+ device().ifPresent { it.validate() }
+ expiryDate().ifPresent { it.validate() }
+ tracking().ifPresent { it.validate() }
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic
+ internal fun validity(): Int =
+ (providerKey.asKnown().getOrNull()?.validity() ?: 0) +
+ (device.asKnown().getOrNull()?.validity() ?: 0) +
+ (expiryDate.asKnown().getOrNull()?.validity() ?: 0) +
+ (tracking.asKnown().getOrNull()?.validity() ?: 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is Body &&
+ providerKey == other.providerKey &&
+ device == other.device &&
+ expiryDate == other.expiryDate &&
+ properties == other.properties &&
+ tracking == other.tracking &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(
+ providerKey,
+ device,
+ expiryDate,
+ properties,
+ tracking,
+ additionalProperties,
+ )
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "Body{providerKey=$providerKey, device=$device, expiryDate=$expiryDate, properties=$properties, tracking=$tracking, additionalProperties=$additionalProperties}"
+ }
+
+ class ProviderKey @JsonCreator private constructor(private val value: JsonField) :
+ Enum {
+
+ /**
+ * Returns this class instance's raw value.
+ *
+ * This is usually only useful if this instance was deserialized from data that doesn't
+ * match any known member, and you want to know that value. For example, if the SDK is on an
+ * older version than the API, then the API may respond with new members that the SDK is
+ * unaware of.
+ */
+ @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value
+
+ companion object {
+
+ @JvmField val FIREBASE_FCM = of("firebase-fcm")
+
+ @JvmField val APN = of("apn")
+
+ @JvmField val EXPO = of("expo")
+
+ @JvmField val ONESIGNAL = of("onesignal")
+
+ @JvmStatic fun of(value: String) = ProviderKey(JsonField.of(value))
+ }
+
+ /** An enum containing [ProviderKey]'s known values. */
+ enum class Known {
+ FIREBASE_FCM,
+ APN,
+ EXPO,
+ ONESIGNAL,
+ }
+
+ /**
+ * An enum containing [ProviderKey]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [ProviderKey] can contain an unknown value in a couple of cases:
+ * - It was deserialized from data that doesn't match any known member. For example, if the
+ * SDK is on an older version than the API, then the API may respond with new members that
+ * the SDK is unaware of.
+ * - It was constructed with an arbitrary value using the [of] method.
+ */
+ enum class Value {
+ FIREBASE_FCM,
+ APN,
+ EXPO,
+ ONESIGNAL,
+ /**
+ * An enum member indicating that [ProviderKey] was instantiated with an unknown value.
+ */
+ _UNKNOWN,
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
+ * if the class was instantiated with an unknown value.
+ *
+ * Use the [known] method instead if you're certain the value is always known or if you want
+ * to throw for the unknown case.
+ */
+ fun value(): Value =
+ when (this) {
+ FIREBASE_FCM -> Value.FIREBASE_FCM
+ APN -> Value.APN
+ EXPO -> Value.EXPO
+ ONESIGNAL -> Value.ONESIGNAL
+ else -> Value._UNKNOWN
+ }
+
+ /**
+ * Returns an enum member corresponding to this class instance's value.
+ *
+ * Use the [value] method instead if you're uncertain the value is always known and don't
+ * want to throw for the unknown case.
+ *
+ * @throws CourierInvalidDataException if this class instance's value is a not a known
+ * member.
+ */
+ fun known(): Known =
+ when (this) {
+ FIREBASE_FCM -> Known.FIREBASE_FCM
+ APN -> Known.APN
+ EXPO -> Known.EXPO
+ ONESIGNAL -> Known.ONESIGNAL
+ else -> throw CourierInvalidDataException("Unknown ProviderKey: $value")
+ }
+
+ /**
+ * Returns this class instance's primitive wire representation.
+ *
+ * This differs from the [toString] method because that method is primarily for debugging
+ * and generally doesn't throw.
+ *
+ * @throws CourierInvalidDataException if this class instance's value does not have the
+ * expected primitive type.
+ */
+ fun asString(): String =
+ _value().asString().orElseThrow { CourierInvalidDataException("Value is not a String") }
+
+ private var validated: Boolean = false
+
+ fun validate(): ProviderKey = apply {
+ if (validated) {
+ return@apply
+ }
+
+ known()
+ validated = true
+ }
+
+ fun isValid(): Boolean =
+ try {
+ validate()
+ true
+ } catch (e: CourierInvalidDataException) {
+ false
+ }
+
+ /**
+ * Returns a score indicating how many valid values are contained in this object
+ * recursively.
+ *
+ * Used for best match union deserialization.
+ */
+ @JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is ProviderKey && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
+
+ /** Information about the device the token came from. */
+ class Device
+ @JsonCreator(mode = JsonCreator.Mode.DISABLED)
+ private constructor(
+ private val adId: JsonField,
+ private val appId: JsonField,
+ private val deviceId: JsonField,
+ private val manufacturer: JsonField,
+ private val model: JsonField,
+ private val platform: JsonField,
+ private val additionalProperties: MutableMap,
+ ) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("ad_id") @ExcludeMissing adId: JsonField = JsonMissing.of(),
+ @JsonProperty("app_id") @ExcludeMissing appId: JsonField = JsonMissing.of(),
+ @JsonProperty("device_id")
+ @ExcludeMissing
+ deviceId: JsonField = JsonMissing.of(),
+ @JsonProperty("manufacturer")
+ @ExcludeMissing
+ manufacturer: JsonField = JsonMissing.of(),
+ @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(),
+ @JsonProperty("platform") @ExcludeMissing platform: JsonField = JsonMissing.of(),
+ ) : this(adId, appId, deviceId, manufacturer, model, platform, mutableMapOf())
+
+ /**
+ * Id of the advertising identifier
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun adId(): Optional = adId.getOptional("ad_id")
+
+ /**
+ * Id of the application the token is used for
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun appId(): Optional = appId.getOptional("app_id")
+
+ /**
+ * Id of the device the token is associated with
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun deviceId(): Optional = deviceId.getOptional("device_id")
+
+ /**
+ * The device manufacturer
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun manufacturer(): Optional = manufacturer.getOptional("manufacturer")
+
+ /**
+ * The device model
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun model(): Optional = model.getOptional("model")
+
+ /**
+ * The device platform i.e. android, ios, web
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun platform(): Optional = platform.getOptional("platform")
+
+ /**
+ * Returns the raw JSON value of [adId].
+ *
+ * Unlike [adId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("ad_id") @ExcludeMissing fun _adId(): JsonField = adId
+
+ /**
+ * Returns the raw JSON value of [appId].
+ *
+ * Unlike [appId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("app_id") @ExcludeMissing fun _appId(): JsonField = appId
+
+ /**
+ * Returns the raw JSON value of [deviceId].
+ *
+ * Unlike [deviceId], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("device_id") @ExcludeMissing fun _deviceId(): JsonField = deviceId
+
+ /**
+ * Returns the raw JSON value of [manufacturer].
+ *
+ * Unlike [manufacturer], this method doesn't throw if the JSON field has an unexpected
+ * type.
+ */
+ @JsonProperty("manufacturer")
+ @ExcludeMissing
+ fun _manufacturer(): JsonField = manufacturer
+
+ /**
+ * Returns the raw JSON value of [model].
+ *
+ * Unlike [model], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model
+
+ /**
+ * Returns the raw JSON value of [platform].
+ *
+ * Unlike [platform], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("platform") @ExcludeMissing fun _platform(): JsonField = platform
+
+ @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 [Device]. */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [Device]. */
+ class Builder internal constructor() {
+
+ private var adId: JsonField = JsonMissing.of()
+ private var appId: JsonField = JsonMissing.of()
+ private var deviceId: JsonField = JsonMissing.of()
+ private var manufacturer: JsonField