diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f668ddf3..d0b5e4c3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -40,6 +40,9 @@ jobs:
build:
timeout-minutes: 15
name: build
+ permissions:
+ contents: read
+ id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/courier-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
@@ -61,6 +64,21 @@ jobs:
- name: Build SDK
run: ./scripts/build
+ - name: Get GitHub OIDC Token
+ if: github.repository == 'stainless-sdks/courier-java'
+ id: github-oidc
+ uses: actions/github-script@v6
+ with:
+ script: core.setOutput('github_token', await core.getIDToken());
+
+ - name: Build and upload Maven artifacts
+ if: github.repository == 'stainless-sdks/courier-java'
+ env:
+ URL: https://pkg.stainless.com/s
+ AUTH: ${{ steps.github-oidc.outputs.github_token }}
+ SHA: ${{ github.sha }}
+ PROJECT: courier-java
+ run: ./scripts/upload-artifacts
test:
timeout-minutes: 15
name: test
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 1eec10e9..f1a48d37 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "4.7.0"
+ ".": "4.7.1"
}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 647de165..92aa6915 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,4 +1,4 @@
configured_endpoints: 78
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-b79e0eb1ab06f4076c48fa519e2b2ad792a0c483a5d017e43c938ca4c4be6988.yml
-openapi_spec_hash: cb3cc2c1145503e5737a880326857aa4
-config_hash: ff903e824043dc81aca51a0ce21896d6
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier%2Fcourier-e3e54d99e2a73fd87519270f2685131050d342e86a4e96130247b854deae5c20.yml
+openapi_spec_hash: 897a3fbee24f24d021d6af0df480220c
+config_hash: 66a5c28bb74d78454456d9ce7d1c0a0c
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b1c3f1a..20855c1d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
# Changelog
+## 4.7.1 (2026-01-14)
+
+Full Changelog: [v4.7.0...v4.7.1](https://github.com/trycourier/courier-java/compare/v4.7.0...v4.7.1)
+
+### Chores
+
+* **internal:** regenerate SDK with no functional changes ([227578d](https://github.com/trycourier/courier-java/commit/227578dc96d7114cd01e4cac20567f46008364ec))
+* **internal:** regenerate SDK with no functional changes ([49599e7](https://github.com/trycourier/courier-java/commit/49599e729b1ed313141734486e484ca4b88cd8d9))
+* **internal:** support uploading Maven repo artifacts to stainless package server ([4a21584](https://github.com/trycourier/courier-java/commit/4a21584ee2daff5c18cf9ca259cce3a9920005ba))
+
## 4.7.0 (2026-01-12)
Full Changelog: [v4.6.0...v4.7.0](https://github.com/trycourier/courier-java/compare/v4.6.0...v4.7.0)
diff --git a/README.md b/README.md
index c8a8b3ed..47bcf43a 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.courier/courier-java/4.7.0)
-[](https://javadoc.io/doc/com.courier/courier-java/4.7.0)
+[](https://central.sonatype.com/artifact/com.courier/courier-java/4.7.1)
+[](https://javadoc.io/doc/com.courier/courier-java/4.7.1)
@@ -13,7 +13,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.7.0).
+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.7.1).
@@ -24,7 +24,7 @@ The REST API documentation can be found on [www.courier.com](https://www.courier
### Gradle
```kotlin
-implementation("com.courier:courier-java:4.7.0")
+implementation("com.courier:courier-java:4.7.1")
```
### Maven
@@ -33,7 +33,7 @@ implementation("com.courier:courier-java:4.7.0")
com.courier
courier-java
- 4.7.0
+ 4.7.1
```
diff --git a/build.gradle.kts b/build.gradle.kts
index d206be55..885270a0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "com.courier"
- version = "4.7.0" // x-release-please-version
+ version = "4.7.1" // x-release-please-version
}
subprojects {
diff --git a/buildSrc/src/main/kotlin/courier.publish.gradle.kts b/buildSrc/src/main/kotlin/courier.publish.gradle.kts
index e70f24d9..50c167b8 100644
--- a/buildSrc/src/main/kotlin/courier.publish.gradle.kts
+++ b/buildSrc/src/main/kotlin/courier.publish.gradle.kts
@@ -7,6 +7,17 @@ plugins {
id("com.vanniktech.maven.publish")
}
+publishing {
+ repositories {
+ if (project.hasProperty("publishLocal")) {
+ maven {
+ name = "LocalFileSystem"
+ url = uri("${rootProject.layout.buildDirectory.get()}/local-maven-repo")
+ }
+ }
+ }
+}
+
repositories {
gradlePluginPortal()
mavenCentral()
@@ -17,8 +28,10 @@ extra["signingInMemoryKeyId"] = System.getenv("GPG_SIGNING_KEY_ID")
extra["signingInMemoryKeyPassword"] = System.getenv("GPG_SIGNING_PASSWORD")
configure {
- signAllPublications()
- publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
+ if (!project.hasProperty("publishLocal")) {
+ signAllPublications()
+ publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
+ }
coordinates(project.group.toString(), project.name, project.version.toString())
configure(
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/AudienceFilterConfig.kt b/courier-java-core/src/main/kotlin/com/courier/models/AudienceFilterConfig.kt
new file mode 100644
index 00000000..a93282aa
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/AudienceFilterConfig.kt
@@ -0,0 +1,196 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models
+
+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.checkRequired
+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 kotlin.jvm.optionals.getOrNull
+
+/** Filter configuration for audience membership containing an array of filter rules */
+class AudienceFilterConfig
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val filters: JsonField>,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("filters")
+ @ExcludeMissing
+ filters: JsonField> = JsonMissing.of()
+ ) : this(filters, mutableMapOf())
+
+ /**
+ * Array of filter rules (single conditions or nested groups)
+ *
+ * @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 filters(): List = filters.getRequired("filters")
+
+ /**
+ * Returns the raw JSON value of [filters].
+ *
+ * Unlike [filters], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("filters") @ExcludeMissing fun _filters(): JsonField> = filters
+
+ @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 [AudienceFilterConfig].
+ *
+ * The following fields are required:
+ * ```java
+ * .filters()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [AudienceFilterConfig]. */
+ class Builder internal constructor() {
+
+ private var filters: JsonField>? = null
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(audienceFilterConfig: AudienceFilterConfig) = apply {
+ filters = audienceFilterConfig.filters.map { it.toMutableList() }
+ additionalProperties = audienceFilterConfig.additionalProperties.toMutableMap()
+ }
+
+ /** Array of filter rules (single conditions or nested groups) */
+ fun filters(filters: List) = filters(JsonField.of(filters))
+
+ /**
+ * Sets [Builder.filters] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.filters] with a well-typed `List` value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun filters(filters: JsonField>) = apply {
+ this.filters = filters.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [FilterConfig] to [filters].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addFilter(filter: FilterConfig) = apply {
+ filters =
+ (filters ?: JsonField.of(mutableListOf())).also {
+ checkKnown("filters", it).add(filter)
+ }
+ }
+
+ 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 [AudienceFilterConfig].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .filters()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): AudienceFilterConfig =
+ AudienceFilterConfig(
+ checkRequired("filters", filters).map { it.toImmutable() },
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): AudienceFilterConfig = apply {
+ if (validated) {
+ return@apply
+ }
+
+ filters().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 =
+ (filters.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is AudienceFilterConfig &&
+ filters == other.filters &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy { Objects.hash(filters, additionalProperties) }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "AudienceFilterConfig{filters=$filters, additionalProperties=$additionalProperties}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNode.kt b/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNode.kt
index 1b848899..6b481f4c 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNode.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNode.kt
@@ -7,7 +7,6 @@ import com.courier.core.JsonField
import com.courier.core.JsonMissing
import com.courier.core.JsonValue
import com.courier.core.checkKnown
-import com.courier.core.checkRequired
import com.courier.core.toImmutable
import com.courier.errors.CourierInvalidDataException
import com.fasterxml.jackson.annotation.JsonAnyGetter
@@ -85,10 +84,10 @@ private constructor(
* The channel the contents of this element should be applied to. Can be `email`, `push`,
* `direct_message`, `sms` or a provider such as slack
*
- * @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).
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
*/
- fun channel(): String = channel.getRequired("channel")
+ fun channel(): Optional = channel.getOptional("channel")
/**
* Raw data to apply to the channel. If `elements` has not been specified, `raw` is required.
@@ -154,14 +153,7 @@ private constructor(
companion object {
- /**
- * Returns a mutable builder for constructing an instance of [ElementalChannelNode].
- *
- * The following fields are required:
- * ```java
- * .channel()
- * ```
- */
+ /** Returns a mutable builder for constructing an instance of [ElementalChannelNode]. */
@JvmStatic fun builder() = Builder()
}
@@ -172,7 +164,7 @@ private constructor(
private var if_: JsonField = JsonMissing.of()
private var loop: JsonField = JsonMissing.of()
private var ref: JsonField = JsonMissing.of()
- private var channel: JsonField? = null
+ private var channel: JsonField = JsonMissing.of()
private var raw: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -308,13 +300,6 @@ private constructor(
* Returns an immutable instance of [ElementalChannelNode].
*
* Further updates to this [Builder] will not mutate the returned instance.
- *
- * The following fields are required:
- * ```java
- * .channel()
- * ```
- *
- * @throws IllegalStateException if any required field is unset.
*/
fun build(): ElementalChannelNode =
ElementalChannelNode(
@@ -322,7 +307,7 @@ private constructor(
if_,
loop,
ref,
- checkRequired("channel", channel),
+ channel,
raw,
additionalProperties.toMutableMap(),
)
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNodeWithType.kt b/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNodeWithType.kt
index ad6031e7..6db19a3f 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNodeWithType.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/ElementalChannelNodeWithType.kt
@@ -8,7 +8,6 @@ import com.courier.core.JsonField
import com.courier.core.JsonMissing
import com.courier.core.JsonValue
import com.courier.core.checkKnown
-import com.courier.core.checkRequired
import com.courier.core.toImmutable
import com.courier.errors.CourierInvalidDataException
import com.fasterxml.jackson.annotation.JsonAnyGetter
@@ -97,10 +96,10 @@ private constructor(
* The channel the contents of this element should be applied to. Can be `email`, `push`,
* `direct_message`, `sms` or a provider such as slack
*
- * @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).
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
*/
- fun channel(): String = channel.getRequired("channel")
+ fun channel(): Optional = channel.getOptional("channel")
/**
* Raw data to apply to the channel. If `elements` has not been specified, `raw` is required.
@@ -181,11 +180,6 @@ private constructor(
/**
* Returns a mutable builder for constructing an instance of [ElementalChannelNodeWithType].
- *
- * The following fields are required:
- * ```java
- * .channel()
- * ```
*/
@JvmStatic fun builder() = Builder()
}
@@ -197,7 +191,7 @@ private constructor(
private var if_: JsonField = JsonMissing.of()
private var loop: JsonField = JsonMissing.of()
private var ref: JsonField = JsonMissing.of()
- private var channel: JsonField? = null
+ private var channel: JsonField = JsonMissing.of()
private var raw: JsonField = JsonMissing.of()
private var type: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@@ -346,13 +340,6 @@ private constructor(
* Returns an immutable instance of [ElementalChannelNodeWithType].
*
* Further updates to this [Builder] will not mutate the returned instance.
- *
- * The following fields are required:
- * ```java
- * .channel()
- * ```
- *
- * @throws IllegalStateException if any required field is unset.
*/
fun build(): ElementalChannelNodeWithType =
ElementalChannelNodeWithType(
@@ -360,7 +347,7 @@ private constructor(
if_,
loop,
ref,
- checkRequired("channel", channel),
+ channel,
raw,
type,
additionalProperties.toMutableMap(),
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/FilterConfig.kt b/courier-java-core/src/main/kotlin/com/courier/models/FilterConfig.kt
new file mode 100644
index 00000000..6d0d0b29
--- /dev/null
+++ b/courier-java-core/src/main/kotlin/com/courier/models/FilterConfig.kt
@@ -0,0 +1,323 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models
+
+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.checkRequired
+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
+
+/**
+ * A filter rule that can be either a single condition (with path/value) or a nested group (with
+ * filters array). Use comparison operators (EQ, GT, etc.) for single conditions, and logical
+ * operators (AND, OR) for nested groups.
+ */
+class FilterConfig
+@JsonCreator(mode = JsonCreator.Mode.DISABLED)
+private constructor(
+ private val operator: JsonField,
+ private val filters: JsonField>,
+ private val path: JsonField,
+ private val value: JsonField,
+ private val additionalProperties: MutableMap,
+) {
+
+ @JsonCreator
+ private constructor(
+ @JsonProperty("operator") @ExcludeMissing operator: JsonField = JsonMissing.of(),
+ @JsonProperty("filters")
+ @ExcludeMissing
+ filters: JsonField> = JsonMissing.of(),
+ @JsonProperty("path") @ExcludeMissing path: JsonField = JsonMissing.of(),
+ @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(),
+ ) : this(operator, filters, path, value, mutableMapOf())
+
+ /**
+ * The operator for this filter. Use comparison operators (EQ, GT, LT, GTE, LTE, NEQ, EXISTS,
+ * INCLUDES, STARTS_WITH, ENDS_WITH, IS_BEFORE, IS_AFTER, OMIT) for single conditions, or
+ * logical operators (AND, OR) for nested filter groups.
+ *
+ * @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 operator(): String = operator.getRequired("operator")
+
+ /**
+ * Nested filter rules to combine with AND/OR. Required for nested filter groups, not used for
+ * single filter conditions.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun filters(): Optional> = filters.getOptional("filters")
+
+ /**
+ * The attribute path from the user profile to filter on. Required for single filter conditions,
+ * not used for nested filter groups.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun path(): Optional = path.getOptional("path")
+
+ /**
+ * The value to compare against. Required for single filter conditions, not used for nested
+ * filter groups.
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun value(): Optional = value.getOptional("value")
+
+ /**
+ * Returns the raw JSON value of [operator].
+ *
+ * Unlike [operator], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("operator") @ExcludeMissing fun _operator(): JsonField = operator
+
+ /**
+ * Returns the raw JSON value of [filters].
+ *
+ * Unlike [filters], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("filters") @ExcludeMissing fun _filters(): JsonField> = filters
+
+ /**
+ * Returns the raw JSON value of [path].
+ *
+ * Unlike [path], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("path") @ExcludeMissing fun _path(): JsonField = path
+
+ /**
+ * Returns the raw JSON value of [value].
+ *
+ * Unlike [value], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value
+
+ @JsonAnySetter
+ private fun putAdditionalProperty(key: String, value: JsonValue) {
+ additionalProperties.put(key, value)
+ }
+
+ @JsonAnyGetter
+ @ExcludeMissing
+ fun _additionalProperties(): Map =
+ Collections.unmodifiableMap(additionalProperties)
+
+ fun toBuilder() = Builder().from(this)
+
+ companion object {
+
+ /**
+ * Returns a mutable builder for constructing an instance of [FilterConfig].
+ *
+ * The following fields are required:
+ * ```java
+ * .operator()
+ * ```
+ */
+ @JvmStatic fun builder() = Builder()
+ }
+
+ /** A builder for [FilterConfig]. */
+ class Builder internal constructor() {
+
+ private var operator: JsonField? = null
+ private var filters: JsonField>? = null
+ private var path: JsonField = JsonMissing.of()
+ private var value: JsonField = JsonMissing.of()
+ private var additionalProperties: MutableMap = mutableMapOf()
+
+ @JvmSynthetic
+ internal fun from(filterConfig: FilterConfig) = apply {
+ operator = filterConfig.operator
+ filters = filterConfig.filters.map { it.toMutableList() }
+ path = filterConfig.path
+ value = filterConfig.value
+ additionalProperties = filterConfig.additionalProperties.toMutableMap()
+ }
+
+ /**
+ * The operator for this filter. Use comparison operators (EQ, GT, LT, GTE, LTE, NEQ,
+ * EXISTS, INCLUDES, STARTS_WITH, ENDS_WITH, IS_BEFORE, IS_AFTER, OMIT) for single
+ * conditions, or logical operators (AND, OR) for nested filter groups.
+ */
+ fun operator(operator: String) = operator(JsonField.of(operator))
+
+ /**
+ * Sets [Builder.operator] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.operator] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun operator(operator: JsonField) = apply { this.operator = operator }
+
+ /**
+ * Nested filter rules to combine with AND/OR. Required for nested filter groups, not used
+ * for single filter conditions.
+ */
+ fun filters(filters: List) = filters(JsonField.of(filters))
+
+ /**
+ * Sets [Builder.filters] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.filters] with a well-typed `List` value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun filters(filters: JsonField>) = apply {
+ this.filters = filters.map { it.toMutableList() }
+ }
+
+ /**
+ * Adds a single [FilterConfig] to [filters].
+ *
+ * @throws IllegalStateException if the field was previously set to a non-list.
+ */
+ fun addFilter(filter: FilterConfig) = apply {
+ filters =
+ (filters ?: JsonField.of(mutableListOf())).also {
+ checkKnown("filters", it).add(filter)
+ }
+ }
+
+ /**
+ * The attribute path from the user profile to filter on. Required for single filter
+ * conditions, not used for nested filter groups.
+ */
+ fun path(path: String) = path(JsonField.of(path))
+
+ /**
+ * Sets [Builder.path] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.path] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun path(path: JsonField) = apply { this.path = path }
+
+ /**
+ * The value to compare against. Required for single filter conditions, not used for nested
+ * filter groups.
+ */
+ fun value(value: String) = value(JsonField.of(value))
+
+ /**
+ * Sets [Builder.value] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.value] with a well-typed [String] value instead. This
+ * method is primarily for setting the field to an undocumented or not yet supported value.
+ */
+ fun value(value: JsonField) = apply { this.value = value }
+
+ fun additionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.clear()
+ putAllAdditionalProperties(additionalProperties)
+ }
+
+ fun putAdditionalProperty(key: String, value: JsonValue) = apply {
+ additionalProperties.put(key, value)
+ }
+
+ fun putAllAdditionalProperties(additionalProperties: Map) = apply {
+ this.additionalProperties.putAll(additionalProperties)
+ }
+
+ fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
+
+ fun removeAllAdditionalProperties(keys: Set) = apply {
+ keys.forEach(::removeAdditionalProperty)
+ }
+
+ /**
+ * Returns an immutable instance of [FilterConfig].
+ *
+ * Further updates to this [Builder] will not mutate the returned instance.
+ *
+ * The following fields are required:
+ * ```java
+ * .operator()
+ * ```
+ *
+ * @throws IllegalStateException if any required field is unset.
+ */
+ fun build(): FilterConfig =
+ FilterConfig(
+ checkRequired("operator", operator),
+ (filters ?: JsonMissing.of()).map { it.toImmutable() },
+ path,
+ value,
+ additionalProperties.toMutableMap(),
+ )
+ }
+
+ private var validated: Boolean = false
+
+ fun validate(): FilterConfig = apply {
+ if (validated) {
+ return@apply
+ }
+
+ operator()
+ filters().ifPresent { it.forEach { it.validate() } }
+ path()
+ value()
+ 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 (operator.asKnown().isPresent) 1 else 0) +
+ (filters.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
+ (if (path.asKnown().isPresent) 1 else 0) +
+ (if (value.asKnown().isPresent) 1 else 0)
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+
+ return other is FilterConfig &&
+ operator == other.operator &&
+ filters == other.filters &&
+ path == other.path &&
+ value == other.value &&
+ additionalProperties == other.additionalProperties
+ }
+
+ private val hashCode: Int by lazy {
+ Objects.hash(operator, filters, path, value, additionalProperties)
+ }
+
+ override fun hashCode(): Int = hashCode
+
+ override fun toString() =
+ "FilterConfig{operator=$operator, filters=$filters, path=$path, value=$value, additionalProperties=$additionalProperties}"
+}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/audiences/Audience.kt b/courier-java-core/src/main/kotlin/com/courier/models/audiences/Audience.kt
index 4cdf514a..a40c978a 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/audiences/Audience.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/audiences/Audience.kt
@@ -2,18 +2,21 @@
package com.courier.models.audiences
+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.courier.models.AudienceFilterConfig
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 Audience
@@ -22,9 +25,10 @@ private constructor(
private val id: JsonField,
private val createdAt: JsonField,
private val description: JsonField,
- private val filter: JsonField,
private val name: JsonField,
private val updatedAt: JsonField,
+ private val filter: JsonField,
+ private val operator: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -35,10 +39,13 @@ private constructor(
@JsonProperty("description")
@ExcludeMissing
description: JsonField = JsonMissing.of(),
- @JsonProperty("filter") @ExcludeMissing filter: JsonField = JsonMissing.of(),
@JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(),
@JsonProperty("updated_at") @ExcludeMissing updatedAt: JsonField = JsonMissing.of(),
- ) : this(id, createdAt, description, filter, name, updatedAt, mutableMapOf())
+ @JsonProperty("filter")
+ @ExcludeMissing
+ filter: JsonField = JsonMissing.of(),
+ @JsonProperty("operator") @ExcludeMissing operator: JsonField = JsonMissing.of(),
+ ) : this(id, createdAt, description, name, updatedAt, filter, operator, mutableMapOf())
/**
* A unique identifier representing the audience_id
@@ -62,14 +69,6 @@ private constructor(
*/
fun description(): String = description.getRequired("description")
- /**
- * A single filter to use for filtering
- *
- * @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 filter(): Filter = filter.getRequired("filter")
-
/**
* The name of the audience
*
@@ -84,6 +83,22 @@ private constructor(
*/
fun updatedAt(): String = updatedAt.getRequired("updated_at")
+ /**
+ * Filter configuration for audience membership containing an array of filter rules
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun filter(): Optional = filter.getOptional("filter")
+
+ /**
+ * The logical operator (AND/OR) for the top-level filter
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun operator(): Optional = operator.getOptional("operator")
+
/**
* Returns the raw JSON value of [id].
*
@@ -105,13 +120,6 @@ private constructor(
*/
@JsonProperty("description") @ExcludeMissing fun _description(): JsonField = description
- /**
- * Returns the raw JSON value of [filter].
- *
- * Unlike [filter], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("filter") @ExcludeMissing fun _filter(): JsonField = filter
-
/**
* Returns the raw JSON value of [name].
*
@@ -126,6 +134,20 @@ private constructor(
*/
@JsonProperty("updated_at") @ExcludeMissing fun _updatedAt(): JsonField = updatedAt
+ /**
+ * Returns the raw JSON value of [filter].
+ *
+ * Unlike [filter], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("filter") @ExcludeMissing fun _filter(): JsonField = filter
+
+ /**
+ * Returns the raw JSON value of [operator].
+ *
+ * Unlike [operator], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("operator") @ExcludeMissing fun _operator(): JsonField = operator
+
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -148,7 +170,6 @@ private constructor(
* .id()
* .createdAt()
* .description()
- * .filter()
* .name()
* .updatedAt()
* ```
@@ -162,9 +183,10 @@ private constructor(
private var id: JsonField? = null
private var createdAt: JsonField? = null
private var description: JsonField? = null
- private var filter: JsonField? = null
private var name: JsonField? = null
private var updatedAt: JsonField? = null
+ private var filter: JsonField = JsonMissing.of()
+ private var operator: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
@@ -172,9 +194,10 @@ private constructor(
id = audience.id
createdAt = audience.createdAt
description = audience.description
- filter = audience.filter
name = audience.name
updatedAt = audience.updatedAt
+ filter = audience.filter
+ operator = audience.operator
additionalProperties = audience.additionalProperties.toMutableMap()
}
@@ -212,25 +235,6 @@ private constructor(
*/
fun description(description: JsonField) = apply { this.description = description }
- /** A single filter to use for filtering */
- fun filter(filter: Filter) = filter(JsonField.of(filter))
-
- /**
- * Sets [Builder.filter] to an arbitrary JSON value.
- *
- * You should usually call [Builder.filter] with a well-typed [Filter] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun filter(filter: JsonField) = apply { this.filter = filter }
-
- /** Alias for calling [filter] with `Filter.ofSingleFilterConfig(singleFilterConfig)`. */
- fun filter(singleFilterConfig: SingleFilterConfig) =
- filter(Filter.ofSingleFilterConfig(singleFilterConfig))
-
- /** Alias for calling [filter] with `Filter.ofNestedFilterConfig(nestedFilterConfig)`. */
- fun filter(nestedFilterConfig: NestedFilterConfig) =
- filter(Filter.ofNestedFilterConfig(nestedFilterConfig))
-
/** The name of the audience */
fun name(name: String) = name(JsonField.of(name))
@@ -253,6 +257,33 @@ private constructor(
*/
fun updatedAt(updatedAt: JsonField) = apply { this.updatedAt = updatedAt }
+ /** Filter configuration for audience membership containing an array of filter rules */
+ fun filter(filter: AudienceFilterConfig?) = filter(JsonField.ofNullable(filter))
+
+ /** Alias for calling [Builder.filter] with `filter.orElse(null)`. */
+ fun filter(filter: Optional) = filter(filter.getOrNull())
+
+ /**
+ * Sets [Builder.filter] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.filter] with a well-typed [AudienceFilterConfig] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun filter(filter: JsonField) = apply { this.filter = filter }
+
+ /** The logical operator (AND/OR) for the top-level filter */
+ fun operator(operator: Operator) = operator(JsonField.of(operator))
+
+ /**
+ * Sets [Builder.operator] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.operator] with a well-typed [Operator] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun operator(operator: JsonField) = apply { this.operator = operator }
+
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -282,7 +313,6 @@ private constructor(
* .id()
* .createdAt()
* .description()
- * .filter()
* .name()
* .updatedAt()
* ```
@@ -294,9 +324,10 @@ private constructor(
checkRequired("id", id),
checkRequired("createdAt", createdAt),
checkRequired("description", description),
- checkRequired("filter", filter),
checkRequired("name", name),
checkRequired("updatedAt", updatedAt),
+ filter,
+ operator,
additionalProperties.toMutableMap(),
)
}
@@ -311,9 +342,10 @@ private constructor(
id()
createdAt()
description()
- filter().validate()
name()
updatedAt()
+ filter().ifPresent { it.validate() }
+ operator().ifPresent { it.validate() }
validated = true
}
@@ -335,9 +367,136 @@ private constructor(
(if (id.asKnown().isPresent) 1 else 0) +
(if (createdAt.asKnown().isPresent) 1 else 0) +
(if (description.asKnown().isPresent) 1 else 0) +
- (filter.asKnown().getOrNull()?.validity() ?: 0) +
(if (name.asKnown().isPresent) 1 else 0) +
- (if (updatedAt.asKnown().isPresent) 1 else 0)
+ (if (updatedAt.asKnown().isPresent) 1 else 0) +
+ (filter.asKnown().getOrNull()?.validity() ?: 0) +
+ (operator.asKnown().getOrNull()?.validity() ?: 0)
+
+ /** The logical operator (AND/OR) for the top-level filter */
+ class Operator @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 AND = of("AND")
+
+ @JvmField val OR = of("OR")
+
+ @JvmStatic fun of(value: String) = Operator(JsonField.of(value))
+ }
+
+ /** An enum containing [Operator]'s known values. */
+ enum class Known {
+ AND,
+ OR,
+ }
+
+ /**
+ * An enum containing [Operator]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Operator] 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 {
+ AND,
+ OR,
+ /** An enum member indicating that [Operator] 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) {
+ AND -> Value.AND
+ OR -> Value.OR
+ 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) {
+ AND -> Known.AND
+ OR -> Known.OR
+ else -> throw CourierInvalidDataException("Unknown Operator: $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(): Operator = 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 Operator && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
+ }
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -348,18 +507,28 @@ private constructor(
id == other.id &&
createdAt == other.createdAt &&
description == other.description &&
- filter == other.filter &&
name == other.name &&
updatedAt == other.updatedAt &&
+ filter == other.filter &&
+ operator == other.operator &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(id, createdAt, description, filter, name, updatedAt, additionalProperties)
+ Objects.hash(
+ id,
+ createdAt,
+ description,
+ name,
+ updatedAt,
+ filter,
+ operator,
+ additionalProperties,
+ )
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "Audience{id=$id, createdAt=$createdAt, description=$description, filter=$filter, name=$name, updatedAt=$updatedAt, additionalProperties=$additionalProperties}"
+ "Audience{id=$id, createdAt=$createdAt, description=$description, name=$name, updatedAt=$updatedAt, filter=$filter, operator=$operator, additionalProperties=$additionalProperties}"
}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/audiences/AudienceUpdateParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/audiences/AudienceUpdateParams.kt
index 415505bb..3344d3a4 100644
--- a/courier-java-core/src/main/kotlin/com/courier/models/audiences/AudienceUpdateParams.kt
+++ b/courier-java-core/src/main/kotlin/com/courier/models/audiences/AudienceUpdateParams.kt
@@ -2,6 +2,7 @@
package com.courier.models.audiences
+import com.courier.core.Enum
import com.courier.core.ExcludeMissing
import com.courier.core.JsonField
import com.courier.core.JsonMissing
@@ -10,6 +11,7 @@ import com.courier.core.Params
import com.courier.core.http.Headers
import com.courier.core.http.QueryParams
import com.courier.errors.CourierInvalidDataException
+import com.courier.models.AudienceFilterConfig
import com.fasterxml.jackson.annotation.JsonAnyGetter
import com.fasterxml.jackson.annotation.JsonAnySetter
import com.fasterxml.jackson.annotation.JsonCreator
@@ -39,12 +41,12 @@ private constructor(
fun description(): Optional = body.description()
/**
- * A single filter to use for filtering
+ * Filter configuration for audience membership containing an array of filter rules
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
- fun filter(): Optional = body.filter()
+ fun filter(): Optional = body.filter()
/**
* The name of the audience
@@ -54,6 +56,14 @@ private constructor(
*/
fun name(): Optional = body.name()
+ /**
+ * The logical operator (AND/OR) for the top-level filter
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun operator(): Optional = body.operator()
+
/**
* Returns the raw JSON value of [description].
*
@@ -66,7 +76,7 @@ private constructor(
*
* Unlike [filter], this method doesn't throw if the JSON field has an unexpected type.
*/
- fun _filter(): JsonField = body._filter()
+ fun _filter(): JsonField = body._filter()
/**
* Returns the raw JSON value of [name].
@@ -75,6 +85,13 @@ private constructor(
*/
fun _name(): JsonField = body._name()
+ /**
+ * Returns the raw JSON value of [operator].
+ *
+ * Unlike [operator], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ fun _operator(): JsonField = body._operator()
+
fun _additionalBodyProperties(): Map = body._additionalProperties()
/** Additional headers to send with the request. */
@@ -122,6 +139,7 @@ private constructor(
* - [description]
* - [filter]
* - [name]
+ * - [operator]
*/
fun body(body: Body) = apply { this.body = body.toBuilder() }
@@ -140,29 +158,20 @@ private constructor(
*/
fun description(description: JsonField) = apply { body.description(description) }
- /** A single filter to use for filtering */
- fun filter(filter: Filter?) = apply { body.filter(filter) }
+ /** Filter configuration for audience membership containing an array of filter rules */
+ fun filter(filter: AudienceFilterConfig?) = apply { body.filter(filter) }
/** Alias for calling [Builder.filter] with `filter.orElse(null)`. */
- fun filter(filter: Optional) = filter(filter.getOrNull())
+ fun filter(filter: Optional) = filter(filter.getOrNull())
/**
* Sets [Builder.filter] to an arbitrary JSON value.
*
- * You should usually call [Builder.filter] with a well-typed [Filter] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
+ * You should usually call [Builder.filter] with a well-typed [AudienceFilterConfig] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
*/
- fun filter(filter: JsonField) = apply { body.filter(filter) }
-
- /** Alias for calling [filter] with `Filter.ofSingleFilterConfig(singleFilterConfig)`. */
- fun filter(singleFilterConfig: SingleFilterConfig) = apply {
- body.filter(singleFilterConfig)
- }
-
- /** Alias for calling [filter] with `Filter.ofNestedFilterConfig(nestedFilterConfig)`. */
- fun filter(nestedFilterConfig: NestedFilterConfig) = apply {
- body.filter(nestedFilterConfig)
- }
+ fun filter(filter: JsonField) = apply { body.filter(filter) }
/** The name of the audience */
fun name(name: String?) = apply { body.name(name) }
@@ -178,6 +187,21 @@ private constructor(
*/
fun name(name: JsonField) = apply { body.name(name) }
+ /** The logical operator (AND/OR) for the top-level filter */
+ fun operator(operator: Operator?) = apply { body.operator(operator) }
+
+ /** Alias for calling [Builder.operator] with `operator.orElse(null)`. */
+ fun operator(operator: Optional) = operator(operator.getOrNull())
+
+ /**
+ * Sets [Builder.operator] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.operator] with a well-typed [Operator] value instead.
+ * This method is primarily for setting the field to an undocumented or not yet supported
+ * value.
+ */
+ fun operator(operator: JsonField) = apply { body.operator(operator) }
+
fun additionalBodyProperties(additionalBodyProperties: Map) = apply {
body.additionalProperties(additionalBodyProperties)
}
@@ -325,8 +349,9 @@ private constructor(
@JsonCreator(mode = JsonCreator.Mode.DISABLED)
private constructor(
private val description: JsonField,
- private val filter: JsonField,
+ private val filter: JsonField,
private val name: JsonField,
+ private val operator: JsonField,
private val additionalProperties: MutableMap,
) {
@@ -335,9 +360,14 @@ private constructor(
@JsonProperty("description")
@ExcludeMissing
description: JsonField = JsonMissing.of(),
- @JsonProperty("filter") @ExcludeMissing filter: JsonField = JsonMissing.of(),
+ @JsonProperty("filter")
+ @ExcludeMissing
+ filter: JsonField = JsonMissing.of(),
@JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(),
- ) : this(description, filter, name, mutableMapOf())
+ @JsonProperty("operator")
+ @ExcludeMissing
+ operator: JsonField = JsonMissing.of(),
+ ) : this(description, filter, name, operator, mutableMapOf())
/**
* A description of the audience
@@ -348,12 +378,12 @@ private constructor(
fun description(): Optional = description.getOptional("description")
/**
- * A single filter to use for filtering
+ * Filter configuration for audience membership containing an array of filter rules
*
* @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
- fun filter(): Optional = filter.getOptional("filter")
+ fun filter(): Optional = filter.getOptional("filter")
/**
* The name of the audience
@@ -363,6 +393,14 @@ private constructor(
*/
fun name(): Optional = name.getOptional("name")
+ /**
+ * The logical operator (AND/OR) for the top-level filter
+ *
+ * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the
+ * server responded with an unexpected value).
+ */
+ fun operator(): Optional = operator.getOptional("operator")
+
/**
* Returns the raw JSON value of [description].
*
@@ -377,7 +415,9 @@ private constructor(
*
* Unlike [filter], this method doesn't throw if the JSON field has an unexpected type.
*/
- @JsonProperty("filter") @ExcludeMissing fun _filter(): JsonField = filter
+ @JsonProperty("filter")
+ @ExcludeMissing
+ fun _filter(): JsonField = filter
/**
* Returns the raw JSON value of [name].
@@ -386,6 +426,13 @@ private constructor(
*/
@JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name
+ /**
+ * Returns the raw JSON value of [operator].
+ *
+ * Unlike [operator], this method doesn't throw if the JSON field has an unexpected type.
+ */
+ @JsonProperty("operator") @ExcludeMissing fun _operator(): JsonField = operator
+
@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
@@ -408,8 +455,9 @@ private constructor(
class Builder internal constructor() {
private var description: JsonField = JsonMissing.of()
- private var filter: JsonField = JsonMissing.of()
+ private var filter: JsonField = JsonMissing.of()
private var name: JsonField = JsonMissing.of()
+ private var operator: JsonField = JsonMissing.of()
private var additionalProperties: MutableMap = mutableMapOf()
@JvmSynthetic
@@ -417,6 +465,7 @@ private constructor(
description = body.description
filter = body.filter
name = body.name
+ operator = body.operator
additionalProperties = body.additionalProperties.toMutableMap()
}
@@ -437,32 +486,20 @@ private constructor(
this.description = description
}
- /** A single filter to use for filtering */
- fun filter(filter: Filter?) = filter(JsonField.ofNullable(filter))
+ /** Filter configuration for audience membership containing an array of filter rules */
+ fun filter(filter: AudienceFilterConfig?) = filter(JsonField.ofNullable(filter))
/** Alias for calling [Builder.filter] with `filter.orElse(null)`. */
- fun filter(filter: Optional) = filter(filter.getOrNull())
+ fun filter(filter: Optional) = filter(filter.getOrNull())
/**
* Sets [Builder.filter] to an arbitrary JSON value.
*
- * You should usually call [Builder.filter] with a well-typed [Filter] value instead.
- * This method is primarily for setting the field to an undocumented or not yet
- * supported value.
+ * You should usually call [Builder.filter] with a well-typed [AudienceFilterConfig]
+ * value instead. This method is primarily for setting the field to an undocumented or
+ * not yet supported value.
*/
- fun filter(filter: JsonField) = apply { this.filter = filter }
-
- /**
- * Alias for calling [filter] with `Filter.ofSingleFilterConfig(singleFilterConfig)`.
- */
- fun filter(singleFilterConfig: SingleFilterConfig) =
- filter(Filter.ofSingleFilterConfig(singleFilterConfig))
-
- /**
- * Alias for calling [filter] with `Filter.ofNestedFilterConfig(nestedFilterConfig)`.
- */
- fun filter(nestedFilterConfig: NestedFilterConfig) =
- filter(Filter.ofNestedFilterConfig(nestedFilterConfig))
+ fun filter(filter: JsonField) = apply { this.filter = filter }
/** The name of the audience */
fun name(name: String?) = name(JsonField.ofNullable(name))
@@ -479,6 +516,21 @@ private constructor(
*/
fun name(name: JsonField) = apply { this.name = name }
+ /** The logical operator (AND/OR) for the top-level filter */
+ fun operator(operator: Operator?) = operator(JsonField.ofNullable(operator))
+
+ /** Alias for calling [Builder.operator] with `operator.orElse(null)`. */
+ fun operator(operator: Optional) = operator(operator.getOrNull())
+
+ /**
+ * Sets [Builder.operator] to an arbitrary JSON value.
+ *
+ * You should usually call [Builder.operator] with a well-typed [Operator] value
+ * instead. This method is primarily for setting the field to an undocumented or not yet
+ * supported value.
+ */
+ fun operator(operator: JsonField) = apply { this.operator = operator }
+
fun additionalProperties(additionalProperties: Map) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
@@ -503,7 +555,8 @@ private constructor(
*
* Further updates to this [Builder] will not mutate the returned instance.
*/
- fun build(): Body = Body(description, filter, name, additionalProperties.toMutableMap())
+ fun build(): Body =
+ Body(description, filter, name, operator, additionalProperties.toMutableMap())
}
private var validated: Boolean = false
@@ -516,6 +569,7 @@ private constructor(
description()
filter().ifPresent { it.validate() }
name()
+ operator().ifPresent { it.validate() }
validated = true
}
@@ -537,7 +591,8 @@ private constructor(
internal fun validity(): Int =
(if (description.asKnown().isPresent) 1 else 0) +
(filter.asKnown().getOrNull()?.validity() ?: 0) +
- (if (name.asKnown().isPresent) 1 else 0)
+ (if (name.asKnown().isPresent) 1 else 0) +
+ (operator.asKnown().getOrNull()?.validity() ?: 0)
override fun equals(other: Any?): Boolean {
if (this === other) {
@@ -548,17 +603,144 @@ private constructor(
description == other.description &&
filter == other.filter &&
name == other.name &&
+ operator == other.operator &&
additionalProperties == other.additionalProperties
}
private val hashCode: Int by lazy {
- Objects.hash(description, filter, name, additionalProperties)
+ Objects.hash(description, filter, name, operator, additionalProperties)
}
override fun hashCode(): Int = hashCode
override fun toString() =
- "Body{description=$description, filter=$filter, name=$name, additionalProperties=$additionalProperties}"
+ "Body{description=$description, filter=$filter, name=$name, operator=$operator, additionalProperties=$additionalProperties}"
+ }
+
+ /** The logical operator (AND/OR) for the top-level filter */
+ class Operator @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 AND = of("AND")
+
+ @JvmField val OR = of("OR")
+
+ @JvmStatic fun of(value: String) = Operator(JsonField.of(value))
+ }
+
+ /** An enum containing [Operator]'s known values. */
+ enum class Known {
+ AND,
+ OR,
+ }
+
+ /**
+ * An enum containing [Operator]'s known values, as well as an [_UNKNOWN] member.
+ *
+ * An instance of [Operator] 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 {
+ AND,
+ OR,
+ /** An enum member indicating that [Operator] 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) {
+ AND -> Value.AND
+ OR -> Value.OR
+ 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) {
+ AND -> Known.AND
+ OR -> Known.OR
+ else -> throw CourierInvalidDataException("Unknown Operator: $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(): Operator = 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 Operator && value == other.value
+ }
+
+ override fun hashCode() = value.hashCode()
+
+ override fun toString() = value.toString()
}
override fun equals(other: Any?): Boolean {
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/audiences/Filter.kt b/courier-java-core/src/main/kotlin/com/courier/models/audiences/Filter.kt
deleted file mode 100644
index 14dab282..00000000
--- a/courier-java-core/src/main/kotlin/com/courier/models/audiences/Filter.kt
+++ /dev/null
@@ -1,206 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.courier.models.audiences
-
-import com.courier.core.BaseDeserializer
-import com.courier.core.BaseSerializer
-import com.courier.core.JsonValue
-import com.courier.core.allMaxBy
-import com.courier.core.getOrThrow
-import com.courier.errors.CourierInvalidDataException
-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.Objects
-import java.util.Optional
-
-/** A single filter to use for filtering */
-@JsonDeserialize(using = Filter.Deserializer::class)
-@JsonSerialize(using = Filter.Serializer::class)
-class Filter
-private constructor(
- private val singleFilterConfig: SingleFilterConfig? = null,
- private val nestedFilterConfig: NestedFilterConfig? = null,
- private val _json: JsonValue? = null,
-) {
-
- /** A single filter to use for filtering */
- fun singleFilterConfig(): Optional = Optional.ofNullable(singleFilterConfig)
-
- /** The operator to use for filtering */
- fun nestedFilterConfig(): Optional = Optional.ofNullable(nestedFilterConfig)
-
- fun isSingleFilterConfig(): Boolean = singleFilterConfig != null
-
- fun isNestedFilterConfig(): Boolean = nestedFilterConfig != null
-
- /** A single filter to use for filtering */
- fun asSingleFilterConfig(): SingleFilterConfig =
- singleFilterConfig.getOrThrow("singleFilterConfig")
-
- /** The operator to use for filtering */
- fun asNestedFilterConfig(): NestedFilterConfig =
- nestedFilterConfig.getOrThrow("nestedFilterConfig")
-
- fun _json(): Optional = Optional.ofNullable(_json)
-
- fun accept(visitor: Visitor): T =
- when {
- singleFilterConfig != null -> visitor.visitSingleFilterConfig(singleFilterConfig)
- nestedFilterConfig != null -> visitor.visitNestedFilterConfig(nestedFilterConfig)
- else -> visitor.unknown(_json)
- }
-
- private var validated: Boolean = false
-
- fun validate(): Filter = apply {
- if (validated) {
- return@apply
- }
-
- accept(
- object : Visitor {
- override fun visitSingleFilterConfig(singleFilterConfig: SingleFilterConfig) {
- singleFilterConfig.validate()
- }
-
- override fun visitNestedFilterConfig(nestedFilterConfig: NestedFilterConfig) {
- nestedFilterConfig.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 =
- accept(
- object : Visitor {
- override fun visitSingleFilterConfig(singleFilterConfig: SingleFilterConfig) =
- singleFilterConfig.validity()
-
- override fun visitNestedFilterConfig(nestedFilterConfig: NestedFilterConfig) =
- nestedFilterConfig.validity()
-
- override fun unknown(json: JsonValue?) = 0
- }
- )
-
- override fun equals(other: Any?): Boolean {
- if (this === other) {
- return true
- }
-
- return other is Filter &&
- singleFilterConfig == other.singleFilterConfig &&
- nestedFilterConfig == other.nestedFilterConfig
- }
-
- override fun hashCode(): Int = Objects.hash(singleFilterConfig, nestedFilterConfig)
-
- override fun toString(): String =
- when {
- singleFilterConfig != null -> "Filter{singleFilterConfig=$singleFilterConfig}"
- nestedFilterConfig != null -> "Filter{nestedFilterConfig=$nestedFilterConfig}"
- _json != null -> "Filter{_unknown=$_json}"
- else -> throw IllegalStateException("Invalid Filter")
- }
-
- companion object {
-
- /** A single filter to use for filtering */
- @JvmStatic
- fun ofSingleFilterConfig(singleFilterConfig: SingleFilterConfig) =
- Filter(singleFilterConfig = singleFilterConfig)
-
- /** The operator to use for filtering */
- @JvmStatic
- fun ofNestedFilterConfig(nestedFilterConfig: NestedFilterConfig) =
- Filter(nestedFilterConfig = nestedFilterConfig)
- }
-
- /** An interface that defines how to map each variant of [Filter] to a value of type [T]. */
- interface Visitor {
-
- /** A single filter to use for filtering */
- fun visitSingleFilterConfig(singleFilterConfig: SingleFilterConfig): T
-
- /** The operator to use for filtering */
- fun visitNestedFilterConfig(nestedFilterConfig: NestedFilterConfig): T
-
- /**
- * Maps an unknown variant of [Filter] to a value of type [T].
- *
- * An instance of [Filter] can contain an unknown variant if it was deserialized from data
- * that doesn't match any known variant. For example, if the SDK is on an older version than
- * the API, then the API may respond with new variants that the SDK is unaware of.
- *
- * @throws CourierInvalidDataException in the default implementation.
- */
- fun unknown(json: JsonValue?): T {
- throw CourierInvalidDataException("Unknown Filter: $json")
- }
- }
-
- internal class Deserializer : BaseDeserializer(Filter::class) {
-
- override fun ObjectCodec.deserialize(node: JsonNode): Filter {
- val json = JsonValue.fromJsonNode(node)
-
- val bestMatches =
- sequenceOf(
- tryDeserialize(node, jacksonTypeRef())?.let {
- Filter(singleFilterConfig = it, _json = json)
- },
- tryDeserialize(node, jacksonTypeRef())?.let {
- Filter(nestedFilterConfig = it, _json = json)
- },
- )
- .filterNotNull()
- .allMaxBy { it.validity() }
- .toList()
- return when (bestMatches.size) {
- // This can happen if what we're deserializing is completely incompatible with all
- // the possible variants (e.g. deserializing from boolean).
- 0 -> Filter(_json = json)
- 1 -> bestMatches.single()
- // If there's more than one match with the highest validity, then use the first
- // completely valid match, or simply the first match if none are completely valid.
- else -> bestMatches.firstOrNull { it.isValid() } ?: bestMatches.first()
- }
- }
- }
-
- internal class Serializer : BaseSerializer(Filter::class) {
-
- override fun serialize(
- value: Filter,
- generator: JsonGenerator,
- provider: SerializerProvider,
- ) {
- when {
- value.singleFilterConfig != null -> generator.writeObject(value.singleFilterConfig)
- value.nestedFilterConfig != null -> generator.writeObject(value.nestedFilterConfig)
- value._json != null -> generator.writeObject(value._json)
- else -> throw IllegalStateException("Invalid Filter")
- }
- }
- }
-}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/audiences/NestedFilterConfig.kt b/courier-java-core/src/main/kotlin/com/courier/models/audiences/NestedFilterConfig.kt
deleted file mode 100644
index e4518b44..00000000
--- a/courier-java-core/src/main/kotlin/com/courier/models/audiences/NestedFilterConfig.kt
+++ /dev/null
@@ -1,438 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.courier.models.audiences
-
-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.checkKnown
-import com.courier.core.checkRequired
-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 kotlin.jvm.optionals.getOrNull
-
-class NestedFilterConfig
-@JsonCreator(mode = JsonCreator.Mode.DISABLED)
-private constructor(
- private val operator: JsonField,
- private val rules: JsonField>,
- private val additionalProperties: MutableMap,
-) {
-
- @JsonCreator
- private constructor(
- @JsonProperty("operator") @ExcludeMissing operator: JsonField = JsonMissing.of(),
- @JsonProperty("rules") @ExcludeMissing rules: JsonField> = JsonMissing.of(),
- ) : this(operator, rules, mutableMapOf())
-
- /**
- * The operator to use for filtering
- *
- * @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 operator(): Operator = operator.getRequired("operator")
-
- /**
- * @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 rules(): List = rules.getRequired("rules")
-
- /**
- * Returns the raw JSON value of [operator].
- *
- * Unlike [operator], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("operator") @ExcludeMissing fun _operator(): JsonField = operator
-
- /**
- * Returns the raw JSON value of [rules].
- *
- * Unlike [rules], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("rules") @ExcludeMissing fun _rules(): JsonField> = rules
-
- @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 [NestedFilterConfig].
- *
- * The following fields are required:
- * ```java
- * .operator()
- * .rules()
- * ```
- */
- @JvmStatic fun builder() = Builder()
- }
-
- /** A builder for [NestedFilterConfig]. */
- class Builder internal constructor() {
-
- private var operator: JsonField? = null
- private var rules: JsonField>? = null
- private var additionalProperties: MutableMap = mutableMapOf()
-
- @JvmSynthetic
- internal fun from(nestedFilterConfig: NestedFilterConfig) = apply {
- operator = nestedFilterConfig.operator
- rules = nestedFilterConfig.rules.map { it.toMutableList() }
- additionalProperties = nestedFilterConfig.additionalProperties.toMutableMap()
- }
-
- /** The operator to use for filtering */
- fun operator(operator: Operator) = operator(JsonField.of(operator))
-
- /**
- * Sets [Builder.operator] to an arbitrary JSON value.
- *
- * You should usually call [Builder.operator] with a well-typed [Operator] value instead.
- * This method is primarily for setting the field to an undocumented or not yet supported
- * value.
- */
- fun operator(operator: JsonField) = apply { this.operator = operator }
-
- fun rules(rules: List) = rules(JsonField.of(rules))
-
- /**
- * Sets [Builder.rules] to an arbitrary JSON value.
- *
- * You should usually call [Builder.rules] with a well-typed `List` value instead.
- * This method is primarily for setting the field to an undocumented or not yet supported
- * value.
- */
- fun rules(rules: JsonField>) = apply {
- this.rules = rules.map { it.toMutableList() }
- }
-
- /**
- * Adds a single [Filter] to [rules].
- *
- * @throws IllegalStateException if the field was previously set to a non-list.
- */
- fun addRule(rule: Filter) = apply {
- rules =
- (rules ?: JsonField.of(mutableListOf())).also { checkKnown("rules", it).add(rule) }
- }
-
- /** Alias for calling [addRule] with `Filter.ofSingleFilterConfig(singleFilterConfig)`. */
- fun addRule(singleFilterConfig: SingleFilterConfig) =
- addRule(Filter.ofSingleFilterConfig(singleFilterConfig))
-
- /** Alias for calling [addRule] with `Filter.ofNestedFilterConfig(nestedFilterConfig)`. */
- fun addRule(nestedFilterConfig: NestedFilterConfig) =
- addRule(Filter.ofNestedFilterConfig(nestedFilterConfig))
-
- 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 [NestedFilterConfig].
- *
- * Further updates to this [Builder] will not mutate the returned instance.
- *
- * The following fields are required:
- * ```java
- * .operator()
- * .rules()
- * ```
- *
- * @throws IllegalStateException if any required field is unset.
- */
- fun build(): NestedFilterConfig =
- NestedFilterConfig(
- checkRequired("operator", operator),
- checkRequired("rules", rules).map { it.toImmutable() },
- additionalProperties.toMutableMap(),
- )
- }
-
- private var validated: Boolean = false
-
- fun validate(): NestedFilterConfig = apply {
- if (validated) {
- return@apply
- }
-
- operator().validate()
- rules().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 =
- (operator.asKnown().getOrNull()?.validity() ?: 0) +
- (rules.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0)
-
- /** The operator to use for filtering */
- class Operator @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 ENDS_WITH = of("ENDS_WITH")
-
- @JvmField val EQ = of("EQ")
-
- @JvmField val EXISTS = of("EXISTS")
-
- @JvmField val GT = of("GT")
-
- @JvmField val GTE = of("GTE")
-
- @JvmField val INCLUDES = of("INCLUDES")
-
- @JvmField val IS_AFTER = of("IS_AFTER")
-
- @JvmField val IS_BEFORE = of("IS_BEFORE")
-
- @JvmField val LT = of("LT")
-
- @JvmField val LTE = of("LTE")
-
- @JvmField val NEQ = of("NEQ")
-
- @JvmField val OMIT = of("OMIT")
-
- @JvmField val STARTS_WITH = of("STARTS_WITH")
-
- @JvmField val AND = of("AND")
-
- @JvmField val OR = of("OR")
-
- @JvmStatic fun of(value: String) = Operator(JsonField.of(value))
- }
-
- /** An enum containing [Operator]'s known values. */
- enum class Known {
- ENDS_WITH,
- EQ,
- EXISTS,
- GT,
- GTE,
- INCLUDES,
- IS_AFTER,
- IS_BEFORE,
- LT,
- LTE,
- NEQ,
- OMIT,
- STARTS_WITH,
- AND,
- OR,
- }
-
- /**
- * An enum containing [Operator]'s known values, as well as an [_UNKNOWN] member.
- *
- * An instance of [Operator] 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 {
- ENDS_WITH,
- EQ,
- EXISTS,
- GT,
- GTE,
- INCLUDES,
- IS_AFTER,
- IS_BEFORE,
- LT,
- LTE,
- NEQ,
- OMIT,
- STARTS_WITH,
- AND,
- OR,
- /** An enum member indicating that [Operator] 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) {
- ENDS_WITH -> Value.ENDS_WITH
- EQ -> Value.EQ
- EXISTS -> Value.EXISTS
- GT -> Value.GT
- GTE -> Value.GTE
- INCLUDES -> Value.INCLUDES
- IS_AFTER -> Value.IS_AFTER
- IS_BEFORE -> Value.IS_BEFORE
- LT -> Value.LT
- LTE -> Value.LTE
- NEQ -> Value.NEQ
- OMIT -> Value.OMIT
- STARTS_WITH -> Value.STARTS_WITH
- AND -> Value.AND
- OR -> Value.OR
- 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) {
- ENDS_WITH -> Known.ENDS_WITH
- EQ -> Known.EQ
- EXISTS -> Known.EXISTS
- GT -> Known.GT
- GTE -> Known.GTE
- INCLUDES -> Known.INCLUDES
- IS_AFTER -> Known.IS_AFTER
- IS_BEFORE -> Known.IS_BEFORE
- LT -> Known.LT
- LTE -> Known.LTE
- NEQ -> Known.NEQ
- OMIT -> Known.OMIT
- STARTS_WITH -> Known.STARTS_WITH
- AND -> Known.AND
- OR -> Known.OR
- else -> throw CourierInvalidDataException("Unknown Operator: $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(): Operator = 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 Operator && 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 NestedFilterConfig &&
- operator == other.operator &&
- rules == other.rules &&
- additionalProperties == other.additionalProperties
- }
-
- private val hashCode: Int by lazy { Objects.hash(operator, rules, additionalProperties) }
-
- override fun hashCode(): Int = hashCode
-
- override fun toString() =
- "NestedFilterConfig{operator=$operator, rules=$rules, additionalProperties=$additionalProperties}"
-}
diff --git a/courier-java-core/src/main/kotlin/com/courier/models/audiences/SingleFilterConfig.kt b/courier-java-core/src/main/kotlin/com/courier/models/audiences/SingleFilterConfig.kt
deleted file mode 100644
index f23a3c02..00000000
--- a/courier-java-core/src/main/kotlin/com/courier/models/audiences/SingleFilterConfig.kt
+++ /dev/null
@@ -1,454 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.courier.models.audiences
-
-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.util.Collections
-import java.util.Objects
-import kotlin.jvm.optionals.getOrNull
-
-class SingleFilterConfig
-@JsonCreator(mode = JsonCreator.Mode.DISABLED)
-private constructor(
- private val operator: JsonField,
- private val path: JsonField,
- private val value: JsonField,
- private val additionalProperties: MutableMap,
-) {
-
- @JsonCreator
- private constructor(
- @JsonProperty("operator") @ExcludeMissing operator: JsonField = JsonMissing.of(),
- @JsonProperty("path") @ExcludeMissing path: JsonField = JsonMissing.of(),
- @JsonProperty("value") @ExcludeMissing value: JsonField = JsonMissing.of(),
- ) : this(operator, path, value, mutableMapOf())
-
- /**
- * The operator to use for filtering
- *
- * @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 operator(): Operator = operator.getRequired("operator")
-
- /**
- * The attribute name from profile whose value will be operated against the filter value
- *
- * @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 path(): String = path.getRequired("path")
-
- /**
- * The value to use for filtering
- *
- * @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 value(): String = value.getRequired("value")
-
- /**
- * Returns the raw JSON value of [operator].
- *
- * Unlike [operator], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("operator") @ExcludeMissing fun _operator(): JsonField = operator
-
- /**
- * Returns the raw JSON value of [path].
- *
- * Unlike [path], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("path") @ExcludeMissing fun _path(): JsonField = path
-
- /**
- * Returns the raw JSON value of [value].
- *
- * Unlike [value], this method doesn't throw if the JSON field has an unexpected type.
- */
- @JsonProperty("value") @ExcludeMissing fun _value(): JsonField = value
-
- @JsonAnySetter
- private fun putAdditionalProperty(key: String, value: JsonValue) {
- additionalProperties.put(key, value)
- }
-
- @JsonAnyGetter
- @ExcludeMissing
- fun _additionalProperties(): Map =
- Collections.unmodifiableMap(additionalProperties)
-
- fun toBuilder() = Builder().from(this)
-
- companion object {
-
- /**
- * Returns a mutable builder for constructing an instance of [SingleFilterConfig].
- *
- * The following fields are required:
- * ```java
- * .operator()
- * .path()
- * .value()
- * ```
- */
- @JvmStatic fun builder() = Builder()
- }
-
- /** A builder for [SingleFilterConfig]. */
- class Builder internal constructor() {
-
- private var operator: JsonField? = null
- private var path: JsonField? = null
- private var value: JsonField? = null
- private var additionalProperties: MutableMap = mutableMapOf()
-
- @JvmSynthetic
- internal fun from(singleFilterConfig: SingleFilterConfig) = apply {
- operator = singleFilterConfig.operator
- path = singleFilterConfig.path
- value = singleFilterConfig.value
- additionalProperties = singleFilterConfig.additionalProperties.toMutableMap()
- }
-
- /** The operator to use for filtering */
- fun operator(operator: Operator) = operator(JsonField.of(operator))
-
- /**
- * Sets [Builder.operator] to an arbitrary JSON value.
- *
- * You should usually call [Builder.operator] with a well-typed [Operator] value instead.
- * This method is primarily for setting the field to an undocumented or not yet supported
- * value.
- */
- fun operator(operator: JsonField) = apply { this.operator = operator }
-
- /** The attribute name from profile whose value will be operated against the filter value */
- fun path(path: String) = path(JsonField.of(path))
-
- /**
- * Sets [Builder.path] to an arbitrary JSON value.
- *
- * You should usually call [Builder.path] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun path(path: JsonField) = apply { this.path = path }
-
- /** The value to use for filtering */
- fun value(value: String) = value(JsonField.of(value))
-
- /**
- * Sets [Builder.value] to an arbitrary JSON value.
- *
- * You should usually call [Builder.value] with a well-typed [String] value instead. This
- * method is primarily for setting the field to an undocumented or not yet supported value.
- */
- fun value(value: JsonField) = apply { this.value = value }
-
- fun additionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.clear()
- putAllAdditionalProperties(additionalProperties)
- }
-
- fun putAdditionalProperty(key: String, value: JsonValue) = apply {
- additionalProperties.put(key, value)
- }
-
- fun putAllAdditionalProperties(additionalProperties: Map) = apply {
- this.additionalProperties.putAll(additionalProperties)
- }
-
- fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) }
-
- fun removeAllAdditionalProperties(keys: Set) = apply {
- keys.forEach(::removeAdditionalProperty)
- }
-
- /**
- * Returns an immutable instance of [SingleFilterConfig].
- *
- * Further updates to this [Builder] will not mutate the returned instance.
- *
- * The following fields are required:
- * ```java
- * .operator()
- * .path()
- * .value()
- * ```
- *
- * @throws IllegalStateException if any required field is unset.
- */
- fun build(): SingleFilterConfig =
- SingleFilterConfig(
- checkRequired("operator", operator),
- checkRequired("path", path),
- checkRequired("value", value),
- additionalProperties.toMutableMap(),
- )
- }
-
- private var validated: Boolean = false
-
- fun validate(): SingleFilterConfig = apply {
- if (validated) {
- return@apply
- }
-
- operator().validate()
- path()
- value()
- 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 =
- (operator.asKnown().getOrNull()?.validity() ?: 0) +
- (if (path.asKnown().isPresent) 1 else 0) +
- (if (value.asKnown().isPresent) 1 else 0)
-
- /** The operator to use for filtering */
- class Operator @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 ENDS_WITH = of("ENDS_WITH")
-
- @JvmField val EQ = of("EQ")
-
- @JvmField val EXISTS = of("EXISTS")
-
- @JvmField val GT = of("GT")
-
- @JvmField val GTE = of("GTE")
-
- @JvmField val INCLUDES = of("INCLUDES")
-
- @JvmField val IS_AFTER = of("IS_AFTER")
-
- @JvmField val IS_BEFORE = of("IS_BEFORE")
-
- @JvmField val LT = of("LT")
-
- @JvmField val LTE = of("LTE")
-
- @JvmField val NEQ = of("NEQ")
-
- @JvmField val OMIT = of("OMIT")
-
- @JvmField val STARTS_WITH = of("STARTS_WITH")
-
- @JvmField val AND = of("AND")
-
- @JvmField val OR = of("OR")
-
- @JvmStatic fun of(value: String) = Operator(JsonField.of(value))
- }
-
- /** An enum containing [Operator]'s known values. */
- enum class Known {
- ENDS_WITH,
- EQ,
- EXISTS,
- GT,
- GTE,
- INCLUDES,
- IS_AFTER,
- IS_BEFORE,
- LT,
- LTE,
- NEQ,
- OMIT,
- STARTS_WITH,
- AND,
- OR,
- }
-
- /**
- * An enum containing [Operator]'s known values, as well as an [_UNKNOWN] member.
- *
- * An instance of [Operator] 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 {
- ENDS_WITH,
- EQ,
- EXISTS,
- GT,
- GTE,
- INCLUDES,
- IS_AFTER,
- IS_BEFORE,
- LT,
- LTE,
- NEQ,
- OMIT,
- STARTS_WITH,
- AND,
- OR,
- /** An enum member indicating that [Operator] 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) {
- ENDS_WITH -> Value.ENDS_WITH
- EQ -> Value.EQ
- EXISTS -> Value.EXISTS
- GT -> Value.GT
- GTE -> Value.GTE
- INCLUDES -> Value.INCLUDES
- IS_AFTER -> Value.IS_AFTER
- IS_BEFORE -> Value.IS_BEFORE
- LT -> Value.LT
- LTE -> Value.LTE
- NEQ -> Value.NEQ
- OMIT -> Value.OMIT
- STARTS_WITH -> Value.STARTS_WITH
- AND -> Value.AND
- OR -> Value.OR
- 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) {
- ENDS_WITH -> Known.ENDS_WITH
- EQ -> Known.EQ
- EXISTS -> Known.EXISTS
- GT -> Known.GT
- GTE -> Known.GTE
- INCLUDES -> Known.INCLUDES
- IS_AFTER -> Known.IS_AFTER
- IS_BEFORE -> Known.IS_BEFORE
- LT -> Known.LT
- LTE -> Known.LTE
- NEQ -> Known.NEQ
- OMIT -> Known.OMIT
- STARTS_WITH -> Known.STARTS_WITH
- AND -> Known.AND
- OR -> Known.OR
- else -> throw CourierInvalidDataException("Unknown Operator: $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(): Operator = 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 Operator && 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 SingleFilterConfig &&
- operator == other.operator &&
- path == other.path &&
- value == other.value &&
- additionalProperties == other.additionalProperties
- }
-
- private val hashCode: Int by lazy { Objects.hash(operator, path, value, additionalProperties) }
-
- override fun hashCode(): Int = hashCode
-
- override fun toString() =
- "SingleFilterConfig{operator=$operator, path=$path, value=$value, additionalProperties=$additionalProperties}"
-}
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/AudienceFilterConfigTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/AudienceFilterConfigTest.kt
new file mode 100644
index 00000000..42eb0fb0
--- /dev/null
+++ b/courier-java-core/src/test/kotlin/com/courier/models/AudienceFilterConfigTest.kt
@@ -0,0 +1,60 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models
+
+import com.courier.core.jsonMapper
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+internal class AudienceFilterConfigTest {
+
+ @Test
+ fun create() {
+ val audienceFilterConfig =
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
+ .build()
+
+ assertThat(audienceFilterConfig.filters())
+ .containsExactly(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
+ }
+
+ @Test
+ fun roundtrip() {
+ val jsonMapper = jsonMapper()
+ val audienceFilterConfig =
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
+ .build()
+
+ val roundtrippedAudienceFilterConfig =
+ jsonMapper.readValue(
+ jsonMapper.writeValueAsString(audienceFilterConfig),
+ jacksonTypeRef(),
+ )
+
+ assertThat(roundtrippedAudienceFilterConfig).isEqualTo(audienceFilterConfig)
+ }
+}
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/ElementalChannelNodeTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/ElementalChannelNodeTest.kt
index a6fc5984..39d5c45b 100644
--- a/courier-java-core/src/test/kotlin/com/courier/models/ElementalChannelNodeTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/models/ElementalChannelNodeTest.kt
@@ -31,7 +31,7 @@ internal class ElementalChannelNodeTest {
assertThat(elementalChannelNode.if_()).contains("if")
assertThat(elementalChannelNode.loop()).contains("loop")
assertThat(elementalChannelNode.ref()).contains("ref")
- assertThat(elementalChannelNode.channel()).isEqualTo("email")
+ assertThat(elementalChannelNode.channel()).contains("email")
assertThat(elementalChannelNode.raw())
.contains(
ElementalChannelNode.Raw.builder()
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/FilterConfigTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/FilterConfigTest.kt
new file mode 100644
index 00000000..7483e4cf
--- /dev/null
+++ b/courier-java-core/src/test/kotlin/com/courier/models/FilterConfigTest.kt
@@ -0,0 +1,48 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.courier.models
+
+import com.courier.core.jsonMapper
+import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import kotlin.jvm.optionals.getOrNull
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+internal class FilterConfigTest {
+
+ @Test
+ fun create() {
+ val filterConfig =
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+
+ assertThat(filterConfig.operator()).isEqualTo("operator")
+ assertThat(filterConfig.filters().getOrNull()).containsExactly()
+ assertThat(filterConfig.path()).contains("path")
+ assertThat(filterConfig.value()).contains("value")
+ }
+
+ @Test
+ fun roundtrip() {
+ val jsonMapper = jsonMapper()
+ val filterConfig =
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+
+ val roundtrippedFilterConfig =
+ jsonMapper.readValue(
+ jsonMapper.writeValueAsString(filterConfig),
+ jacksonTypeRef(),
+ )
+
+ assertThat(roundtrippedFilterConfig).isEqualTo(filterConfig)
+ }
+}
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceListResponseTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceListResponseTest.kt
index ac1c0c6d..6594c4a5 100644
--- a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceListResponseTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceListResponseTest.kt
@@ -3,6 +3,8 @@
package com.courier.models.audiences
import com.courier.core.jsonMapper
+import com.courier.models.AudienceFilterConfig
+import com.courier.models.FilterConfig
import com.courier.models.Paging
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import org.assertj.core.api.Assertions.assertThat
@@ -19,15 +21,21 @@ internal class AudienceListResponseTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
)
.paging(Paging.builder().more(true).cursor("cursor").build())
@@ -39,15 +47,21 @@ internal class AudienceListResponseTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
)
assertThat(audienceListResponse.paging())
@@ -64,15 +78,21 @@ internal class AudienceListResponseTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
)
.paging(Paging.builder().more(true).cursor("cursor").build())
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceTest.kt
index d2ff5000..1f10f768 100644
--- a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceTest.kt
@@ -3,6 +3,8 @@
package com.courier.models.audiences
import com.courier.core.jsonMapper
+import com.courier.models.AudienceFilterConfig
+import com.courier.models.FilterConfig
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
@@ -16,32 +18,42 @@ internal class AudienceTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
assertThat(audience.id()).isEqualTo("id")
assertThat(audience.createdAt()).isEqualTo("created_at")
assertThat(audience.description()).isEqualTo("description")
- assertThat(audience.filter())
- .isEqualTo(
- Filter.ofSingleFilterConfig(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
- )
assertThat(audience.name()).isEqualTo("name")
assertThat(audience.updatedAt()).isEqualTo("updated_at")
+ assertThat(audience.filter())
+ .contains(
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
+ .build()
+ )
+ assertThat(audience.operator()).contains(Audience.Operator.AND)
}
@Test
@@ -52,15 +64,21 @@ internal class AudienceTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
val roundtrippedAudience =
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateParamsTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateParamsTest.kt
index 9f793c64..33942f70 100644
--- a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateParamsTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateParamsTest.kt
@@ -2,6 +2,8 @@
package com.courier.models.audiences
+import com.courier.models.AudienceFilterConfig
+import com.courier.models.FilterConfig
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
@@ -13,13 +15,19 @@ internal class AudienceUpdateParamsTest {
.audienceId("audience_id")
.description("description")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
.name("name")
+ .operator(AudienceUpdateParams.Operator.AND)
.build()
}
@@ -39,13 +47,19 @@ internal class AudienceUpdateParamsTest {
.audienceId("audience_id")
.description("description")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
.name("name")
+ .operator(AudienceUpdateParams.Operator.AND)
.build()
val body = params._body()
@@ -53,15 +67,19 @@ internal class AudienceUpdateParamsTest {
assertThat(body.description()).contains("description")
assertThat(body.filter())
.contains(
- Filter.ofSingleFilterConfig(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
+ .build()
)
assertThat(body.name()).contains("name")
+ assertThat(body.operator()).contains(AudienceUpdateParams.Operator.AND)
}
@Test
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateResponseTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateResponseTest.kt
index 7917f856..ad48352a 100644
--- a/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateResponseTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/models/audiences/AudienceUpdateResponseTest.kt
@@ -3,6 +3,8 @@
package com.courier.models.audiences
import com.courier.core.jsonMapper
+import com.courier.models.AudienceFilterConfig
+import com.courier.models.FilterConfig
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
@@ -18,15 +20,21 @@ internal class AudienceUpdateResponseTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
)
.build()
@@ -37,15 +45,21 @@ internal class AudienceUpdateResponseTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
)
}
@@ -60,15 +74,21 @@ internal class AudienceUpdateResponseTest {
.id("id")
.createdAt("created_at")
.description("description")
+ .name("name")
+ .updatedAt("updated_at")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
- .name("name")
- .updatedAt("updated_at")
+ .operator(Audience.Operator.AND)
.build()
)
.build()
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/audiences/FilterTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/audiences/FilterTest.kt
deleted file mode 100644
index 9ab1d655..00000000
--- a/courier-java-core/src/test/kotlin/com/courier/models/audiences/FilterTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.courier.models.audiences
-
-import com.courier.core.JsonValue
-import com.courier.core.jsonMapper
-import com.courier.errors.CourierInvalidDataException
-import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.Test
-import org.junit.jupiter.api.assertThrows
-import org.junit.jupiter.params.ParameterizedTest
-import org.junit.jupiter.params.provider.EnumSource
-
-internal class FilterTest {
-
- @Test
- fun ofSingleFilterConfig() {
- val singleFilterConfig =
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
-
- val filter = Filter.ofSingleFilterConfig(singleFilterConfig)
-
- assertThat(filter.singleFilterConfig()).contains(singleFilterConfig)
- assertThat(filter.nestedFilterConfig()).isEmpty
- }
-
- @Test
- fun ofSingleFilterConfigRoundtrip() {
- val jsonMapper = jsonMapper()
- val filter =
- Filter.ofSingleFilterConfig(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
-
- val roundtrippedFilter =
- jsonMapper.readValue(jsonMapper.writeValueAsString(filter), jacksonTypeRef())
-
- assertThat(roundtrippedFilter).isEqualTo(filter)
- }
-
- @Test
- fun ofNestedFilterConfig() {
- val nestedFilterConfig =
- NestedFilterConfig.builder()
- .operator(NestedFilterConfig.Operator.ENDS_WITH)
- .addRule(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
- .build()
-
- val filter = Filter.ofNestedFilterConfig(nestedFilterConfig)
-
- assertThat(filter.singleFilterConfig()).isEmpty
- assertThat(filter.nestedFilterConfig()).contains(nestedFilterConfig)
- }
-
- @Test
- fun ofNestedFilterConfigRoundtrip() {
- val jsonMapper = jsonMapper()
- val filter =
- Filter.ofNestedFilterConfig(
- NestedFilterConfig.builder()
- .operator(NestedFilterConfig.Operator.ENDS_WITH)
- .addRule(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
- .build()
- )
-
- val roundtrippedFilter =
- jsonMapper.readValue(jsonMapper.writeValueAsString(filter), jacksonTypeRef())
-
- assertThat(roundtrippedFilter).isEqualTo(filter)
- }
-
- enum class IncompatibleJsonShapeTestCase(val value: JsonValue) {
- BOOLEAN(JsonValue.from(false)),
- STRING(JsonValue.from("invalid")),
- INTEGER(JsonValue.from(-1)),
- FLOAT(JsonValue.from(3.14)),
- ARRAY(JsonValue.from(listOf("invalid", "array"))),
- }
-
- @ParameterizedTest
- @EnumSource
- fun incompatibleJsonShapeDeserializesToUnknown(testCase: IncompatibleJsonShapeTestCase) {
- val filter = jsonMapper().convertValue(testCase.value, jacksonTypeRef())
-
- val e = assertThrows { filter.validate() }
- assertThat(e).hasMessageStartingWith("Unknown ")
- }
-}
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/audiences/NestedFilterConfigTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/audiences/NestedFilterConfigTest.kt
deleted file mode 100644
index 943f4e35..00000000
--- a/courier-java-core/src/test/kotlin/com/courier/models/audiences/NestedFilterConfigTest.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.courier.models.audiences
-
-import com.courier.core.jsonMapper
-import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.Test
-
-internal class NestedFilterConfigTest {
-
- @Test
- fun create() {
- val nestedFilterConfig =
- NestedFilterConfig.builder()
- .operator(NestedFilterConfig.Operator.ENDS_WITH)
- .addRule(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
- .build()
-
- assertThat(nestedFilterConfig.operator()).isEqualTo(NestedFilterConfig.Operator.ENDS_WITH)
- assertThat(nestedFilterConfig.rules())
- .containsExactly(
- Filter.ofSingleFilterConfig(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
- )
- }
-
- @Test
- fun roundtrip() {
- val jsonMapper = jsonMapper()
- val nestedFilterConfig =
- NestedFilterConfig.builder()
- .operator(NestedFilterConfig.Operator.ENDS_WITH)
- .addRule(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
- )
- .build()
-
- val roundtrippedNestedFilterConfig =
- jsonMapper.readValue(
- jsonMapper.writeValueAsString(nestedFilterConfig),
- jacksonTypeRef(),
- )
-
- assertThat(roundtrippedNestedFilterConfig).isEqualTo(nestedFilterConfig)
- }
-}
diff --git a/courier-java-core/src/test/kotlin/com/courier/models/audiences/SingleFilterConfigTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/audiences/SingleFilterConfigTest.kt
deleted file mode 100644
index 7a8bdfd5..00000000
--- a/courier-java-core/src/test/kotlin/com/courier/models/audiences/SingleFilterConfigTest.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-// File generated from our OpenAPI spec by Stainless.
-
-package com.courier.models.audiences
-
-import com.courier.core.jsonMapper
-import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
-import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.Test
-
-internal class SingleFilterConfigTest {
-
- @Test
- fun create() {
- val singleFilterConfig =
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
-
- assertThat(singleFilterConfig.operator()).isEqualTo(SingleFilterConfig.Operator.ENDS_WITH)
- assertThat(singleFilterConfig.path()).isEqualTo("path")
- assertThat(singleFilterConfig.value()).isEqualTo("value")
- }
-
- @Test
- fun roundtrip() {
- val jsonMapper = jsonMapper()
- val singleFilterConfig =
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
- .build()
-
- val roundtrippedSingleFilterConfig =
- jsonMapper.readValue(
- jsonMapper.writeValueAsString(singleFilterConfig),
- jacksonTypeRef(),
- )
-
- assertThat(roundtrippedSingleFilterConfig).isEqualTo(singleFilterConfig)
- }
-}
diff --git a/courier-java-core/src/test/kotlin/com/courier/services/async/AudienceServiceAsyncTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/async/AudienceServiceAsyncTest.kt
index cf2e9935..05dc648c 100644
--- a/courier-java-core/src/test/kotlin/com/courier/services/async/AudienceServiceAsyncTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/services/async/AudienceServiceAsyncTest.kt
@@ -4,10 +4,11 @@ package com.courier.services.async
import com.courier.TestServerExtension
import com.courier.client.okhttp.CourierOkHttpClientAsync
+import com.courier.models.AudienceFilterConfig
+import com.courier.models.FilterConfig
import com.courier.models.audiences.AudienceListMembersParams
import com.courier.models.audiences.AudienceListParams
import com.courier.models.audiences.AudienceUpdateParams
-import com.courier.models.audiences.SingleFilterConfig
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -47,13 +48,19 @@ internal class AudienceServiceAsyncTest {
.audienceId("audience_id")
.description("description")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
.name("name")
+ .operator(AudienceUpdateParams.Operator.AND)
.build()
)
diff --git a/courier-java-core/src/test/kotlin/com/courier/services/blocking/AudienceServiceTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/blocking/AudienceServiceTest.kt
index 07f1b335..3fc654a6 100644
--- a/courier-java-core/src/test/kotlin/com/courier/services/blocking/AudienceServiceTest.kt
+++ b/courier-java-core/src/test/kotlin/com/courier/services/blocking/AudienceServiceTest.kt
@@ -4,10 +4,11 @@ package com.courier.services.blocking
import com.courier.TestServerExtension
import com.courier.client.okhttp.CourierOkHttpClient
+import com.courier.models.AudienceFilterConfig
+import com.courier.models.FilterConfig
import com.courier.models.audiences.AudienceListMembersParams
import com.courier.models.audiences.AudienceListParams
import com.courier.models.audiences.AudienceUpdateParams
-import com.courier.models.audiences.SingleFilterConfig
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
@@ -46,13 +47,19 @@ internal class AudienceServiceTest {
.audienceId("audience_id")
.description("description")
.filter(
- SingleFilterConfig.builder()
- .operator(SingleFilterConfig.Operator.ENDS_WITH)
- .path("path")
- .value("value")
+ AudienceFilterConfig.builder()
+ .addFilter(
+ FilterConfig.builder()
+ .operator("operator")
+ .filters(listOf())
+ .path("path")
+ .value("value")
+ .build()
+ )
.build()
)
.name("name")
+ .operator(AudienceUpdateParams.Operator.AND)
.build()
)
diff --git a/scripts/upload-artifacts b/scripts/upload-artifacts
new file mode 100755
index 00000000..729e6f22
--- /dev/null
+++ b/scripts/upload-artifacts
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+# ANSI Color Codes
+GREEN='\033[32m'
+RED='\033[31m'
+NC='\033[0m' # No Color
+
+log_error() {
+ local msg="$1"
+ local headers="$2"
+ local body="$3"
+ echo -e "${RED}${msg}${NC}"
+ [[ -f "$headers" ]] && echo -e "${RED}Headers:$(cat "$headers")${NC}"
+ echo -e "${RED}Body: ${body}${NC}"
+ exit 1
+}
+
+upload_file() {
+ local file_name="$1"
+ local tmp_headers
+ tmp_headers=$(mktemp)
+
+ if [ -f "$file_name" ]; then
+ echo -e "${GREEN}Processing file: $file_name${NC}"
+ pkg_file_name="mvn${file_name#./build/local-maven-repo}"
+
+ # Get signed URL for uploading artifact file
+ signed_url_response=$(curl -X POST -G "$URL" \
+ -sS --retry 5 \
+ -D "$tmp_headers" \
+ --data-urlencode "filename=$pkg_file_name" \
+ -H "Authorization: Bearer $AUTH" \
+ -H "Content-Type: application/json")
+
+ # Validate JSON and extract URL
+ if ! signed_url=$(echo "$signed_url_response" | jq -e -r '.url' 2>/dev/null) || [[ "$signed_url" == "null" ]]; then
+ log_error "Failed to get valid signed URL" "$tmp_headers" "$signed_url_response"
+ fi
+
+ # Set content-type based on file extension
+ local extension="${file_name##*.}"
+ local content_type
+ case "$extension" in
+ jar) content_type="application/java-archive" ;;
+ md5|sha1|sha256|sha512) content_type="text/plain" ;;
+ module) content_type="application/json" ;;
+ pom|xml) content_type="application/xml" ;;
+ *) content_type="application/octet-stream" ;;
+ esac
+
+ # Upload file
+ upload_response=$(curl -v -X PUT \
+ --retry 5 \
+ -D "$tmp_headers" \
+ -H "Content-Type: $content_type" \
+ --data-binary "@${file_name}" "$signed_url" 2>&1)
+
+ if ! echo "$upload_response" | grep -q "HTTP/[0-9.]* 200"; then
+ log_error "Failed upload artifact file" "$tmp_headers" "$upload_response"
+ fi
+
+ # Insert small throttle to reduce rate limiting risk
+ sleep 0.1
+ fi
+}
+
+walk_tree() {
+ local current_dir="$1"
+
+ for entry in "$current_dir"/*; do
+ # Check that entry is valid
+ [ -e "$entry" ] || [ -h "$entry" ] || continue
+
+ if [ -d "$entry" ]; then
+ walk_tree "$entry"
+ else
+ upload_file "$entry"
+ fi
+ done
+}
+
+cd "$(dirname "$0")/.."
+
+echo "::group::Creating local Maven content"
+./gradlew publishMavenPublicationToLocalFileSystemRepository -PpublishLocal
+echo "::endgroup::"
+
+echo "::group::Uploading to pkg.stainless.com"
+walk_tree "./build/local-maven-repo"
+echo "::endgroup::"
+
+echo "::group::Generating instructions"
+echo "Configure maven or gradle to use the repo located at 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'"
+echo "::endgroup::"