From e0089ec26c6ac7d86a3705f49d7788a76aeffe68 Mon Sep 17 00:00:00 2001 From: courier-codegen Date: Mon, 17 Aug 2026 23:30:09 +0000 Subject: [PATCH] =?UTF-8?q?feat(api)!:=20match=20the=20spec=20to=20the=20b?= =?UTF-8?q?ackend=20=E2=80=94=20apn/expo=20token=20types=20and=20the=20pha?= =?UTF-8?q?ntom=20notifications.duplicate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .stats.yml | 2 +- .../src/main/kotlin/com/courier/models/Apn.kt | 226 ++++++++++++++ .../main/kotlin/com/courier/models/Expo.kt | 1 + .../com/courier/models/MultipleTokens.kt | 280 +++++++++++++++--- .../kotlin/com/courier/models/UserProfile.kt | 41 ++- .../NotificationDuplicateParams.kt | 235 --------------- .../async/NotificationServiceAsync.kt | 84 ------ .../async/NotificationServiceAsyncImpl.kt | 42 --- .../services/blocking/NotificationService.kt | 81 ----- .../blocking/NotificationServiceImpl.kt | 39 --- .../test/kotlin/com/courier/models/ApnTest.kt | 75 +++++ .../kotlin/com/courier/models/ExpoTest.kt | 8 +- .../com/courier/models/MultipleTokensTest.kt | 8 +- .../com/courier/models/UserProfileTest.kt | 6 +- .../NotificationDuplicateParamsTest.kt | 23 -- .../async/NotificationServiceAsyncTest.kt | 12 - .../blocking/NotificationServiceTest.kt | 11 - .../proguard/ProGuardCompatibilityTest.kt | 15 +- 18 files changed, 594 insertions(+), 595 deletions(-) create mode 100644 courier-java-core/src/main/kotlin/com/courier/models/Apn.kt delete mode 100644 courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationDuplicateParams.kt create mode 100644 courier-java-core/src/test/kotlin/com/courier/models/ApnTest.kt delete mode 100644 courier-java-core/src/test/kotlin/com/courier/models/notifications/NotificationDuplicateParamsTest.kt diff --git a/.stats.yml b/.stats.yml index 77de7290..9f97df3f 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 150 +configured_endpoints: 149 diff --git a/courier-java-core/src/main/kotlin/com/courier/models/Apn.kt b/courier-java-core/src/main/kotlin/com/courier/models/Apn.kt new file mode 100644 index 00000000..e54939ef --- /dev/null +++ b/courier-java-core/src/main/kotlin/com/courier/models/Apn.kt @@ -0,0 +1,226 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.courier.models + +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 + +/** + * Apple Push Notification device tokens. Supply either a single `token` or a `tokens` value. A bare + * string is rejected by the provider — the token must be wrapped in this object. + */ +@JsonDeserialize(using = Apn.Deserializer::class) +@JsonSerialize(using = Apn.Serializer::class) +class Apn +private constructor( + private val token: Token? = null, + private val multipleTokens: MultipleTokens? = null, + private val _json: JsonValue? = null, +) { + + fun token(): Optional = Optional.ofNullable(token) + + fun multipleTokens(): Optional = Optional.ofNullable(multipleTokens) + + fun isToken(): Boolean = token != null + + fun isMultipleTokens(): Boolean = multipleTokens != null + + fun asToken(): Token = token.getOrThrow("token") + + fun asMultipleTokens(): MultipleTokens = multipleTokens.getOrThrow("multipleTokens") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of the + * SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.courier.core.JsonValue; + * import java.util.Optional; + * + * Optional result = apn.accept(new Apn.Visitor>() { + * @Override + * public Optional visitToken(Token token) { + * return Optional.of(token.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws CourierInvalidDataException if [Visitor.unknown] is not overridden in [visitor] and + * the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + token != null -> visitor.visitToken(token) + multipleTokens != null -> visitor.visitMultipleTokens(multipleTokens) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CourierInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Apn = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitToken(token: Token) { + token.validate() + } + + override fun visitMultipleTokens(multipleTokens: MultipleTokens) { + multipleTokens.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 visitToken(token: Token) = token.validity() + + override fun visitMultipleTokens(multipleTokens: MultipleTokens) = + multipleTokens.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Apn && token == other.token && multipleTokens == other.multipleTokens + } + + override fun hashCode(): Int = Objects.hash(token, multipleTokens) + + override fun toString(): String = + when { + token != null -> "Apn{token=$token}" + multipleTokens != null -> "Apn{multipleTokens=$multipleTokens}" + _json != null -> "Apn{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Apn") + } + + companion object { + + @JvmStatic fun ofToken(token: Token) = Apn(token = token) + + @JvmStatic + fun ofMultipleTokens(multipleTokens: MultipleTokens) = Apn(multipleTokens = multipleTokens) + } + + /** An interface that defines how to map each variant of [Apn] to a value of type [T]. */ + interface Visitor { + + fun visitToken(token: Token): T + + fun visitMultipleTokens(multipleTokens: MultipleTokens): T + + /** + * Maps an unknown variant of [Apn] to a value of type [T]. + * + * An instance of [Apn] 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 Apn: $json") + } + } + + internal class Deserializer : BaseDeserializer(Apn::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Apn { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Apn(token = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + Apn(multipleTokens = 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 -> Apn(_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(Apn::class) { + + override fun serialize(value: Apn, generator: JsonGenerator, provider: SerializerProvider) { + when { + value.token != null -> generator.writeObject(value.token) + value.multipleTokens != null -> generator.writeObject(value.multipleTokens) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Apn") + } + } + } +} diff --git a/courier-java-core/src/main/kotlin/com/courier/models/Expo.kt b/courier-java-core/src/main/kotlin/com/courier/models/Expo.kt index 1991e762..79bd3532 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/Expo.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/Expo.kt @@ -18,6 +18,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import java.util.Objects import java.util.Optional +/** Expo push tokens. Supply either a single `token` or a `tokens` value. */ @JsonDeserialize(using = Expo.Deserializer::class) @JsonSerialize(using = Expo.Serializer::class) class Expo diff --git a/courier-java-core/src/main/kotlin/com/courier/models/MultipleTokens.kt b/courier-java-core/src/main/kotlin/com/courier/models/MultipleTokens.kt index 9d6dac50..af1bd55d 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/MultipleTokens.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/MultipleTokens.kt @@ -2,46 +2,60 @@ package com.courier.models +import com.courier.core.BaseDeserializer +import com.courier.core.BaseSerializer 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.allMaxBy import com.courier.core.checkRequired +import com.courier.core.getOrThrow 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 com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.ObjectCodec +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import java.util.Collections import java.util.Objects +import java.util.Optional import kotlin.jvm.optionals.getOrNull class MultipleTokens @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( - private val tokens: JsonField>, + private val tokens: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("tokens") @ExcludeMissing tokens: JsonField> = JsonMissing.of() + @JsonProperty("tokens") @ExcludeMissing tokens: JsonField = JsonMissing.of() ) : this(tokens, mutableMapOf()) /** + * One device token, or an array of them. The values are the token strings themselves — not + * objects. + * * @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 tokens(): List = tokens.getRequired("tokens") + fun tokens(): Tokens = tokens.getRequired("tokens") /** * Returns the raw JSON value of [tokens]. * * Unlike [tokens], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("tokens") @ExcludeMissing fun _tokens(): JsonField> = tokens + @JsonProperty("tokens") @ExcludeMissing fun _tokens(): JsonField = tokens @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { @@ -71,39 +85,34 @@ private constructor( /** A builder for [MultipleTokens]. */ class Builder internal constructor() { - private var tokens: JsonField>? = null + private var tokens: JsonField? = null private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(multipleTokens: MultipleTokens) = apply { - tokens = multipleTokens.tokens.map { it.toMutableList() } + tokens = multipleTokens.tokens additionalProperties = multipleTokens.additionalProperties.toMutableMap() } - fun tokens(tokens: List) = tokens(JsonField.of(tokens)) - /** - * Sets [Builder.tokens] to an arbitrary JSON value. - * - * You should usually call [Builder.tokens] with a well-typed `List` value instead. - * This method is primarily for setting the field to an undocumented or not yet supported - * value. + * One device token, or an array of them. The values are the token strings themselves — not + * objects. */ - fun tokens(tokens: JsonField>) = apply { - this.tokens = tokens.map { it.toMutableList() } - } + fun tokens(tokens: Tokens) = tokens(JsonField.of(tokens)) /** - * Adds a single [Token] to [tokens]. + * Sets [Builder.tokens] to an arbitrary JSON value. * - * @throws IllegalStateException if the field was previously set to a non-list. + * You should usually call [Builder.tokens] with a well-typed [Tokens] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. */ - fun addToken(token: Token) = apply { - tokens = - (tokens ?: JsonField.of(mutableListOf())).also { - checkKnown("tokens", it).add(token) - } - } + fun tokens(tokens: JsonField) = apply { this.tokens = tokens } + + /** Alias for calling [tokens] with `Tokens.ofString(string)`. */ + fun tokens(string: String) = tokens(Tokens.ofString(string)) + + /** Alias for calling [tokens] with `Tokens.ofStrings(strings)`. */ + fun tokensOfStrings(strings: List) = tokens(Tokens.ofStrings(strings)) fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() @@ -137,10 +146,7 @@ private constructor( * @throws IllegalStateException if any required field is unset. */ fun build(): MultipleTokens = - MultipleTokens( - checkRequired("tokens", tokens).map { it.toImmutable() }, - additionalProperties.toMutableMap(), - ) + MultipleTokens(checkRequired("tokens", tokens), additionalProperties.toMutableMap()) } private var validated: Boolean = false @@ -158,7 +164,7 @@ private constructor( return@apply } - tokens().forEach { it.validate() } + tokens().validate() validated = true } @@ -175,9 +181,217 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic - internal fun validity(): Int = - (tokens.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + @JvmSynthetic internal fun validity(): Int = (tokens.asKnown().getOrNull()?.validity() ?: 0) + + /** + * One device token, or an array of them. The values are the token strings themselves — not + * objects. + */ + @JsonDeserialize(using = Tokens.Deserializer::class) + @JsonSerialize(using = Tokens.Serializer::class) + class Tokens + private constructor( + private val string: String? = null, + private val strings: List? = null, + private val _json: JsonValue? = null, + ) { + + fun string(): Optional = Optional.ofNullable(string) + + fun strings(): Optional> = Optional.ofNullable(strings) + + fun isString(): Boolean = string != null + + fun isStrings(): Boolean = strings != null + + fun asString(): String = string.getOrThrow("string") + + fun asStrings(): List = strings.getOrThrow("strings") + + fun _json(): Optional = Optional.ofNullable(_json) + + /** + * Maps this instance's current variant to a value of type [T] using the given [visitor]. + * + * Note that this method is _not_ forwards compatible with new variants from the API, unless + * [visitor] overrides [Visitor.unknown]. To handle variants not known to this version of + * the SDK gracefully, consider overriding [Visitor.unknown]: + * ```java + * import com.courier.core.JsonValue; + * import java.util.Optional; + * + * Optional result = tokens.accept(new Tokens.Visitor>() { + * @Override + * public Optional visitString(String string) { + * return Optional.of(string.toString()); + * } + * + * // ... + * + * @Override + * public Optional unknown(JsonValue json) { + * // Or inspect the `json`. + * return Optional.empty(); + * } + * }); + * ``` + * + * @throws CourierInvalidDataException if [Visitor.unknown] is not overridden in [visitor] + * and the current variant is unknown. + */ + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + strings != null -> visitor.visitStrings(strings) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + /** + * Validates that the types of all values in this object match their expected types + * recursively. + * + * This method is _not_ forwards compatible with new types from the API for existing fields. + * + * @throws CourierInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Tokens = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitStrings(strings: List) {} + } + ) + 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 visitString(string: String) = 1 + + override fun visitStrings(strings: List) = strings.size + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Tokens && string == other.string && strings == other.strings + } + + override fun hashCode(): Int = Objects.hash(string, strings) + + override fun toString(): String = + when { + string != null -> "Tokens{string=$string}" + strings != null -> "Tokens{strings=$strings}" + _json != null -> "Tokens{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Tokens") + } + + companion object { + + @JvmStatic fun ofString(string: String) = Tokens(string = string) + + @JvmStatic + fun ofStrings(strings: List) = Tokens(strings = strings.toImmutable()) + } + + /** An interface that defines how to map each variant of [Tokens] to a value of type [T]. */ + interface Visitor { + + fun visitString(string: String): T + + fun visitStrings(strings: List): T + + /** + * Maps an unknown variant of [Tokens] to a value of type [T]. + * + * An instance of [Tokens] 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 Tokens: $json") + } + } + + internal class Deserializer : BaseDeserializer(Tokens::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): Tokens { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + Tokens(string = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef>())?.let { + Tokens(strings = 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 -> Tokens(_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(Tokens::class) { + + override fun serialize( + value: Tokens, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.string != null -> generator.writeObject(value.string) + value.strings != null -> generator.writeObject(value.strings) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Tokens") + } + } + } + } override fun equals(other: Any?): Boolean { if (this === other) { diff --git a/courier-java-core/src/main/kotlin/com/courier/models/UserProfile.kt b/courier-java-core/src/main/kotlin/com/courier/models/UserProfile.kt index c7f496ec..05f67d95 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/UserProfile.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/UserProfile.kt @@ -23,7 +23,7 @@ class UserProfile private constructor( private val address: JsonField
, private val airship: JsonField, - private val apn: JsonField, + private val apn: JsonField, private val awsSns: JsonField, private val birthdate: JsonField, private val custom: JsonField, @@ -61,7 +61,7 @@ private constructor( @JsonProperty("airship") @ExcludeMissing airship: JsonField = JsonMissing.of(), - @JsonProperty("apn") @ExcludeMissing apn: JsonField = JsonMissing.of(), + @JsonProperty("apn") @ExcludeMissing apn: JsonField = JsonMissing.of(), @JsonProperty("aws_sns") @ExcludeMissing awsSns: JsonField = JsonMissing.of(), @JsonProperty("birthdate") @ExcludeMissing birthdate: JsonField = JsonMissing.of(), @JsonProperty("custom") @ExcludeMissing custom: JsonField = JsonMissing.of(), @@ -154,10 +154,13 @@ private constructor( fun airship(): Optional = airship.getOptional("airship") /** + * Apple Push Notification device tokens. Supply either a single `token` or a `tokens` value. A + * bare string is rejected by the provider — the token must be wrapped in this object. + * * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ - fun apn(): Optional = apn.getOptional("apn") + fun apn(): Optional = apn.getOptional("apn") /** * Routes a push notification through the AWS SNS provider. The target ARN must be nested under @@ -202,6 +205,8 @@ private constructor( fun emailVerified(): Optional = emailVerified.getOptional("email_verified") /** + * Expo push tokens. Supply either a single `token` or a `tokens` value. + * * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the * server responded with an unexpected value). */ @@ -354,7 +359,7 @@ private constructor( * * Unlike [apn], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("apn") @ExcludeMissing fun _apn(): JsonField = apn + @JsonProperty("apn") @ExcludeMissing fun _apn(): JsonField = apn /** * Returns the raw JSON value of [awsSns]. @@ -588,7 +593,7 @@ private constructor( private var address: JsonField
= JsonMissing.of() private var airship: JsonField = JsonMissing.of() - private var apn: JsonField = JsonMissing.of() + private var apn: JsonField = JsonMissing.of() private var awsSns: JsonField = JsonMissing.of() private var birthdate: JsonField = JsonMissing.of() private var custom: JsonField = JsonMissing.of() @@ -682,18 +687,29 @@ private constructor( */ fun airship(airship: JsonField) = apply { this.airship = airship } - fun apn(apn: String?) = apn(JsonField.ofNullable(apn)) + /** + * Apple Push Notification device tokens. Supply either a single `token` or a `tokens` + * value. A bare string is rejected by the provider — the token must be wrapped in this + * object. + */ + fun apn(apn: Apn?) = apn(JsonField.ofNullable(apn)) /** Alias for calling [Builder.apn] with `apn.orElse(null)`. */ - fun apn(apn: Optional) = apn(apn.getOrNull()) + fun apn(apn: Optional) = apn(apn.getOrNull()) /** * Sets [Builder.apn] to an arbitrary JSON value. * - * You should usually call [Builder.apn] with a well-typed [String] value instead. This - * method is primarily for setting the field to an undocumented or not yet supported value. + * You should usually call [Builder.apn] with a well-typed [Apn] value instead. This method + * is primarily for setting the field to an undocumented or not yet supported value. */ - fun apn(apn: JsonField) = apply { this.apn = apn } + fun apn(apn: JsonField) = apply { this.apn = apn } + + /** Alias for calling [apn] with `Apn.ofToken(token)`. */ + fun apn(token: Token) = apn(Apn.ofToken(token)) + + /** Alias for calling [apn] with `Apn.ofMultipleTokens(multipleTokens)`. */ + fun apn(multipleTokens: MultipleTokens) = apn(Apn.ofMultipleTokens(multipleTokens)) /** * Routes a push notification through the AWS SNS provider. The target ARN must be nested @@ -801,6 +817,7 @@ private constructor( this.emailVerified = emailVerified } + /** Expo push tokens. Supply either a single `token` or a `tokens` value. */ fun expo(expo: Expo?) = expo(JsonField.ofNullable(expo)) /** Alias for calling [Builder.expo] with `expo.orElse(null)`. */ @@ -1255,7 +1272,7 @@ private constructor( address().ifPresent { it.validate() } airship().ifPresent { it.validate() } - apn() + apn().ifPresent { it.validate() } awsSns().ifPresent { it.validate() } birthdate() custom().ifPresent { it.validate() } @@ -1304,7 +1321,7 @@ private constructor( internal fun validity(): Int = (address.asKnown().getOrNull()?.validity() ?: 0) + (airship.asKnown().getOrNull()?.validity() ?: 0) + - (if (apn.asKnown().isPresent) 1 else 0) + + (apn.asKnown().getOrNull()?.validity() ?: 0) + (awsSns.asKnown().getOrNull()?.validity() ?: 0) + (if (birthdate.asKnown().isPresent) 1 else 0) + (custom.asKnown().getOrNull()?.validity() ?: 0) + diff --git a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationDuplicateParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationDuplicateParams.kt deleted file mode 100644 index d7dc4560..00000000 --- a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationDuplicateParams.kt +++ /dev/null @@ -1,235 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.courier.models.notifications - -import com.courier.core.JsonValue -import com.courier.core.Params -import com.courier.core.http.Headers -import com.courier.core.http.QueryParams -import com.courier.core.toImmutable -import java.util.Objects -import java.util.Optional -import kotlin.jvm.optionals.getOrNull - -/** - * Copies a notification template within the same workspace and environment, appending " COPY" to - * the title. The copy is standalone and independently editable. - */ -class NotificationDuplicateParams -private constructor( - private val id: String?, - private val additionalHeaders: Headers, - private val additionalQueryParams: QueryParams, - private val additionalBodyProperties: Map, -) : Params { - - fun id(): Optional = Optional.ofNullable(id) - - /** Additional body properties to send with the request. */ - fun _additionalBodyProperties(): Map = additionalBodyProperties - - /** Additional headers to send with the request. */ - fun _additionalHeaders(): Headers = additionalHeaders - - /** Additional query param to send with the request. */ - fun _additionalQueryParams(): QueryParams = additionalQueryParams - - fun toBuilder() = Builder().from(this) - - companion object { - - @JvmStatic fun none(): NotificationDuplicateParams = builder().build() - - /** - * Returns a mutable builder for constructing an instance of [NotificationDuplicateParams]. - */ - @JvmStatic fun builder() = Builder() - } - - /** A builder for [NotificationDuplicateParams]. */ - class Builder internal constructor() { - - private var id: String? = null - private var additionalHeaders: Headers.Builder = Headers.builder() - private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() - private var additionalBodyProperties: MutableMap = mutableMapOf() - - @JvmSynthetic - internal fun from(notificationDuplicateParams: NotificationDuplicateParams) = apply { - id = notificationDuplicateParams.id - additionalHeaders = notificationDuplicateParams.additionalHeaders.toBuilder() - additionalQueryParams = notificationDuplicateParams.additionalQueryParams.toBuilder() - additionalBodyProperties = - notificationDuplicateParams.additionalBodyProperties.toMutableMap() - } - - fun id(id: String?) = apply { this.id = id } - - /** Alias for calling [Builder.id] with `id.orElse(null)`. */ - fun id(id: Optional) = id(id.getOrNull()) - - fun additionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.clear() - putAllAdditionalHeaders(additionalHeaders) - } - - fun additionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.clear() - putAllAdditionalHeaders(additionalHeaders) - } - - fun putAdditionalHeader(name: String, value: String) = apply { - additionalHeaders.put(name, value) - } - - fun putAdditionalHeaders(name: String, values: Iterable) = apply { - additionalHeaders.put(name, values) - } - - fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.putAll(additionalHeaders) - } - - fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.putAll(additionalHeaders) - } - - fun replaceAdditionalHeaders(name: String, value: String) = apply { - additionalHeaders.replace(name, value) - } - - fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { - additionalHeaders.replace(name, values) - } - - fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { - this.additionalHeaders.replaceAll(additionalHeaders) - } - - fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { - this.additionalHeaders.replaceAll(additionalHeaders) - } - - fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } - - fun removeAllAdditionalHeaders(names: Set) = apply { - additionalHeaders.removeAll(names) - } - - fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.clear() - putAllAdditionalQueryParams(additionalQueryParams) - } - - fun additionalQueryParams(additionalQueryParams: Map>) = apply { - this.additionalQueryParams.clear() - putAllAdditionalQueryParams(additionalQueryParams) - } - - fun putAdditionalQueryParam(key: String, value: String) = apply { - additionalQueryParams.put(key, value) - } - - fun putAdditionalQueryParams(key: String, values: Iterable) = apply { - additionalQueryParams.put(key, values) - } - - fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.putAll(additionalQueryParams) - } - - fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = - apply { - this.additionalQueryParams.putAll(additionalQueryParams) - } - - fun replaceAdditionalQueryParams(key: String, value: String) = apply { - additionalQueryParams.replace(key, value) - } - - fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { - additionalQueryParams.replace(key, values) - } - - fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { - this.additionalQueryParams.replaceAll(additionalQueryParams) - } - - fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = - apply { - this.additionalQueryParams.replaceAll(additionalQueryParams) - } - - fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } - - fun removeAllAdditionalQueryParams(keys: Set) = apply { - additionalQueryParams.removeAll(keys) - } - - fun additionalBodyProperties(additionalBodyProperties: Map) = apply { - this.additionalBodyProperties.clear() - putAllAdditionalBodyProperties(additionalBodyProperties) - } - - fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { - additionalBodyProperties.put(key, value) - } - - fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = - apply { - this.additionalBodyProperties.putAll(additionalBodyProperties) - } - - fun removeAdditionalBodyProperty(key: String) = apply { - additionalBodyProperties.remove(key) - } - - fun removeAllAdditionalBodyProperties(keys: Set) = apply { - keys.forEach(::removeAdditionalBodyProperty) - } - - /** - * Returns an immutable instance of [NotificationDuplicateParams]. - * - * Further updates to this [Builder] will not mutate the returned instance. - */ - fun build(): NotificationDuplicateParams = - NotificationDuplicateParams( - id, - additionalHeaders.build(), - additionalQueryParams.build(), - additionalBodyProperties.toImmutable(), - ) - } - - fun _body(): Optional> = - Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) - - fun _pathParam(index: Int): String = - when (index) { - 0 -> id ?: "" - else -> "" - } - - override fun _headers(): Headers = additionalHeaders - - override fun _queryParams(): QueryParams = additionalQueryParams - - override fun equals(other: Any?): Boolean { - if (this === other) { - return true - } - - return other is NotificationDuplicateParams && - id == other.id && - additionalHeaders == other.additionalHeaders && - additionalQueryParams == other.additionalQueryParams && - additionalBodyProperties == other.additionalBodyProperties - } - - override fun hashCode(): Int = - Objects.hash(id, additionalHeaders, additionalQueryParams, additionalBodyProperties) - - override fun toString() = - "NotificationDuplicateParams{id=$id, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" -} diff --git a/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsync.kt b/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsync.kt index f8b2d4d1..fb710b9e 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsync.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsync.kt @@ -9,7 +9,6 @@ import com.courier.core.http.HttpResponseFor import com.courier.models.notifications.NotificationArchiveParams import com.courier.models.notifications.NotificationContentMutationResponse import com.courier.models.notifications.NotificationCreateParams -import com.courier.models.notifications.NotificationDuplicateParams import com.courier.models.notifications.NotificationListParams import com.courier.models.notifications.NotificationListResponse import com.courier.models.notifications.NotificationListVersionsParams @@ -171,46 +170,6 @@ interface NotificationServiceAsync { fun archive(id: String, requestOptions: RequestOptions): CompletableFuture = archive(id, NotificationArchiveParams.none(), requestOptions) - /** - * Copies a notification template within the same workspace and environment, appending " COPY" - * to the title. The copy is standalone and independently editable. - */ - fun duplicate(id: String): CompletableFuture = - duplicate(id, NotificationDuplicateParams.none()) - - /** @see duplicate */ - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture = - duplicate(params.toBuilder().id(id).build(), requestOptions) - - /** @see duplicate */ - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - ): CompletableFuture = - duplicate(id, params, RequestOptions.none()) - - /** @see duplicate */ - fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture - - /** @see duplicate */ - fun duplicate( - params: NotificationDuplicateParams - ): CompletableFuture = duplicate(params, RequestOptions.none()) - - /** @see duplicate */ - fun duplicate( - id: String, - requestOptions: RequestOptions, - ): CompletableFuture = - duplicate(id, NotificationDuplicateParams.none(), requestOptions) - /** * Returns a notification template's published versions, most recent first, for comparison or * rollback. Paged. @@ -599,49 +558,6 @@ interface NotificationServiceAsync { fun archive(id: String, requestOptions: RequestOptions): CompletableFuture = archive(id, NotificationArchiveParams.none(), requestOptions) - /** - * Returns a raw HTTP response for `post /notifications/{id}/duplicate`, but is otherwise - * the same as [NotificationServiceAsync.duplicate]. - */ - fun duplicate( - id: String - ): CompletableFuture> = - duplicate(id, NotificationDuplicateParams.none()) - - /** @see duplicate */ - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> = - duplicate(params.toBuilder().id(id).build(), requestOptions) - - /** @see duplicate */ - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - ): CompletableFuture> = - duplicate(id, params, RequestOptions.none()) - - /** @see duplicate */ - fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions = RequestOptions.none(), - ): CompletableFuture> - - /** @see duplicate */ - fun duplicate( - params: NotificationDuplicateParams - ): CompletableFuture> = - duplicate(params, RequestOptions.none()) - - /** @see duplicate */ - fun duplicate( - id: String, - requestOptions: RequestOptions, - ): CompletableFuture> = - duplicate(id, NotificationDuplicateParams.none(), requestOptions) - /** * Returns a raw HTTP response for `get /notifications/{id}/versions`, but is otherwise the * same as [NotificationServiceAsync.listVersions]. diff --git a/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsyncImpl.kt b/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsyncImpl.kt index 615e243a..0a5e205e 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsyncImpl.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/async/NotificationServiceAsyncImpl.kt @@ -20,7 +20,6 @@ import com.courier.core.prepareAsync import com.courier.models.notifications.NotificationArchiveParams import com.courier.models.notifications.NotificationContentMutationResponse import com.courier.models.notifications.NotificationCreateParams -import com.courier.models.notifications.NotificationDuplicateParams import com.courier.models.notifications.NotificationListParams import com.courier.models.notifications.NotificationListResponse import com.courier.models.notifications.NotificationListVersionsParams @@ -86,13 +85,6 @@ class NotificationServiceAsyncImpl internal constructor(private val clientOption // delete /notifications/{id} withRawResponse().archive(params, requestOptions).thenAccept {} - override fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions, - ): CompletableFuture = - // post /notifications/{id}/duplicate - withRawResponse().duplicate(params, requestOptions).thenApply { it.parse() } - override fun listVersions( params: NotificationListVersionsParams, requestOptions: RequestOptions, @@ -285,40 +277,6 @@ class NotificationServiceAsyncImpl internal constructor(private val clientOption } } - private val duplicateHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - - override fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions, - ): CompletableFuture> { - // We check here instead of in the params builder because this can be specified - // positionally or in the params class. - checkRequired("id", params.id().getOrNull()) - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .baseUrl(clientOptions.baseUrl()) - .addPathSegments("notifications", params._pathParam(0), "duplicate") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepareAsync(clientOptions, params) - val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) - return request - .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } - .thenApply { response -> - errorHandler.handle(response).parseable { - response - .use { duplicateHandler.handle(it) } - .also { - if (requestOptions.responseValidation!!) { - it.validate() - } - } - } - } - } - private val listVersionsHandler: Handler = jsonHandler(clientOptions.jsonMapper) diff --git a/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationService.kt b/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationService.kt index c4c3c1c4..887ff1a6 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationService.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationService.kt @@ -9,7 +9,6 @@ import com.courier.core.http.HttpResponseFor import com.courier.models.notifications.NotificationArchiveParams import com.courier.models.notifications.NotificationContentMutationResponse import com.courier.models.notifications.NotificationCreateParams -import com.courier.models.notifications.NotificationDuplicateParams import com.courier.models.notifications.NotificationListParams import com.courier.models.notifications.NotificationListResponse import com.courier.models.notifications.NotificationListVersionsParams @@ -162,40 +161,6 @@ interface NotificationService { fun archive(id: String, requestOptions: RequestOptions) = archive(id, NotificationArchiveParams.none(), requestOptions) - /** - * Copies a notification template within the same workspace and environment, appending " COPY" - * to the title. The copy is standalone and independently editable. - */ - fun duplicate(id: String): NotificationTemplateResponse = - duplicate(id, NotificationDuplicateParams.none()) - - /** @see duplicate */ - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - requestOptions: RequestOptions = RequestOptions.none(), - ): NotificationTemplateResponse = duplicate(params.toBuilder().id(id).build(), requestOptions) - - /** @see duplicate */ - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - ): NotificationTemplateResponse = duplicate(id, params, RequestOptions.none()) - - /** @see duplicate */ - fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions = RequestOptions.none(), - ): NotificationTemplateResponse - - /** @see duplicate */ - fun duplicate(params: NotificationDuplicateParams): NotificationTemplateResponse = - duplicate(params, RequestOptions.none()) - - /** @see duplicate */ - fun duplicate(id: String, requestOptions: RequestOptions): NotificationTemplateResponse = - duplicate(id, NotificationDuplicateParams.none(), requestOptions) - /** * Returns a notification template's published versions, most recent first, for comparison or * rollback. Paged. @@ -574,52 +539,6 @@ interface NotificationService { fun archive(id: String, requestOptions: RequestOptions): HttpResponse = archive(id, NotificationArchiveParams.none(), requestOptions) - /** - * Returns a raw HTTP response for `post /notifications/{id}/duplicate`, but is otherwise - * the same as [NotificationService.duplicate]. - */ - @MustBeClosed - fun duplicate(id: String): HttpResponseFor = - duplicate(id, NotificationDuplicateParams.none()) - - /** @see duplicate */ - @MustBeClosed - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor = - duplicate(params.toBuilder().id(id).build(), requestOptions) - - /** @see duplicate */ - @MustBeClosed - fun duplicate( - id: String, - params: NotificationDuplicateParams = NotificationDuplicateParams.none(), - ): HttpResponseFor = - duplicate(id, params, RequestOptions.none()) - - /** @see duplicate */ - @MustBeClosed - fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions = RequestOptions.none(), - ): HttpResponseFor - - /** @see duplicate */ - @MustBeClosed - fun duplicate( - params: NotificationDuplicateParams - ): HttpResponseFor = duplicate(params, RequestOptions.none()) - - /** @see duplicate */ - @MustBeClosed - fun duplicate( - id: String, - requestOptions: RequestOptions, - ): HttpResponseFor = - duplicate(id, NotificationDuplicateParams.none(), requestOptions) - /** * Returns a raw HTTP response for `get /notifications/{id}/versions`, but is otherwise the * same as [NotificationService.listVersions]. diff --git a/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationServiceImpl.kt b/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationServiceImpl.kt index 29841e06..09656302 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationServiceImpl.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/blocking/NotificationServiceImpl.kt @@ -20,7 +20,6 @@ import com.courier.core.prepare import com.courier.models.notifications.NotificationArchiveParams import com.courier.models.notifications.NotificationContentMutationResponse import com.courier.models.notifications.NotificationCreateParams -import com.courier.models.notifications.NotificationDuplicateParams import com.courier.models.notifications.NotificationListParams import com.courier.models.notifications.NotificationListResponse import com.courier.models.notifications.NotificationListVersionsParams @@ -83,13 +82,6 @@ class NotificationServiceImpl internal constructor(private val clientOptions: Cl withRawResponse().archive(params, requestOptions) } - override fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions, - ): NotificationTemplateResponse = - // post /notifications/{id}/duplicate - withRawResponse().duplicate(params, requestOptions).parse() - override fun listVersions( params: NotificationListVersionsParams, requestOptions: RequestOptions, @@ -268,37 +260,6 @@ class NotificationServiceImpl internal constructor(private val clientOptions: Cl } } - private val duplicateHandler: Handler = - jsonHandler(clientOptions.jsonMapper) - - override fun duplicate( - params: NotificationDuplicateParams, - requestOptions: RequestOptions, - ): HttpResponseFor { - // We check here instead of in the params builder because this can be specified - // positionally or in the params class. - checkRequired("id", params.id().getOrNull()) - val request = - HttpRequest.builder() - .method(HttpMethod.POST) - .baseUrl(clientOptions.baseUrl()) - .addPathSegments("notifications", params._pathParam(0), "duplicate") - .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } - .build() - .prepare(clientOptions, params) - val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) - val response = clientOptions.httpClient.execute(request, requestOptions) - return errorHandler.handle(response).parseable { - response - .use { duplicateHandler.handle(it) } - .also { - if (requestOptions.responseValidation!!) { - it.validate() - } - } - } - } - private val listVersionsHandler: Handler = jsonHandler(clientOptions.jsonMapper) diff --git a/courier-java-core/src/test/kotlin/com/courier/models/ApnTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/ApnTest.kt new file mode 100644 index 00000000..102efe06 --- /dev/null +++ b/courier-java-core/src/test/kotlin/com/courier/models/ApnTest.kt @@ -0,0 +1,75 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.courier.models + +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 ApnTest { + + @Test + fun ofToken() { + val token = Token.builder().token("token").build() + + val apn = Apn.ofToken(token) + + assertThat(apn.token()).contains(token) + assertThat(apn.multipleTokens()).isEmpty + } + + @Test + fun ofTokenRoundtrip() { + val jsonMapper = jsonMapper() + val apn = Apn.ofToken(Token.builder().token("token").build()) + + val roundtrippedApn = + jsonMapper.readValue(jsonMapper.writeValueAsString(apn), jacksonTypeRef()) + + assertThat(roundtrippedApn).isEqualTo(apn) + } + + @Test + fun ofMultipleTokens() { + val multipleTokens = MultipleTokens.builder().tokens("string").build() + + val apn = Apn.ofMultipleTokens(multipleTokens) + + assertThat(apn.token()).isEmpty + assertThat(apn.multipleTokens()).contains(multipleTokens) + } + + @Test + fun ofMultipleTokensRoundtrip() { + val jsonMapper = jsonMapper() + val apn = Apn.ofMultipleTokens(MultipleTokens.builder().tokens("string").build()) + + val roundtrippedApn = + jsonMapper.readValue(jsonMapper.writeValueAsString(apn), jacksonTypeRef()) + + assertThat(roundtrippedApn).isEqualTo(apn) + } + + 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 apn = jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { apn.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/courier-java-core/src/test/kotlin/com/courier/models/ExpoTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/ExpoTest.kt index aa590fcf..e021a626 100644 --- a/courier-java-core/src/test/kotlin/com/courier/models/ExpoTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/models/ExpoTest.kt @@ -37,8 +37,7 @@ internal class ExpoTest { @Test fun ofMultipleTokens() { - val multipleTokens = - MultipleTokens.builder().addToken(Token.builder().token("token").build()).build() + val multipleTokens = MultipleTokens.builder().tokens("string").build() val expo = Expo.ofMultipleTokens(multipleTokens) @@ -49,10 +48,7 @@ internal class ExpoTest { @Test fun ofMultipleTokensRoundtrip() { val jsonMapper = jsonMapper() - val expo = - Expo.ofMultipleTokens( - MultipleTokens.builder().addToken(Token.builder().token("token").build()).build() - ) + val expo = Expo.ofMultipleTokens(MultipleTokens.builder().tokens("string").build()) val roundtrippedExpo = jsonMapper.readValue(jsonMapper.writeValueAsString(expo), jacksonTypeRef()) diff --git a/courier-java-core/src/test/kotlin/com/courier/models/MultipleTokensTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/MultipleTokensTest.kt index b415d98e..de89c773 100644 --- a/courier-java-core/src/test/kotlin/com/courier/models/MultipleTokensTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/models/MultipleTokensTest.kt @@ -11,17 +11,15 @@ internal class MultipleTokensTest { @Test fun create() { - val multipleTokens = - MultipleTokens.builder().addToken(Token.builder().token("token").build()).build() + val multipleTokens = MultipleTokens.builder().tokens("string").build() - assertThat(multipleTokens.tokens()).containsExactly(Token.builder().token("token").build()) + assertThat(multipleTokens.tokens()).isEqualTo(MultipleTokens.Tokens.ofString("string")) } @Test fun roundtrip() { val jsonMapper = jsonMapper() - val multipleTokens = - MultipleTokens.builder().addToken(Token.builder().token("token").build()).build() + val multipleTokens = MultipleTokens.builder().tokens("string").build() val roundtrippedMultipleTokens = jsonMapper.readValue( diff --git a/courier-java-core/src/test/kotlin/com/courier/models/UserProfileTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/UserProfileTest.kt index 68577dd0..d43f0c38 100644 --- a/courier-java-core/src/test/kotlin/com/courier/models/UserProfileTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/models/UserProfileTest.kt @@ -30,7 +30,7 @@ internal class UserProfileTest { .addDeviceType("string") .build() ) - .apn("apn") + .apn(Token.builder().token("token").build()) .awsSns(AwsSns.builder().targetArn("target_arn").build()) .birthdate("birthdate") .custom( @@ -99,7 +99,7 @@ internal class UserProfileTest { .addDeviceType("string") .build() ) - assertThat(userProfile.apn()).contains("apn") + assertThat(userProfile.apn()).contains(Apn.ofToken(Token.builder().token("token").build())) assertThat(userProfile.awsSns()).contains(AwsSns.builder().targetArn("target_arn").build()) assertThat(userProfile.birthdate()).contains("birthdate") assertThat(userProfile.custom()) @@ -184,7 +184,7 @@ internal class UserProfileTest { .addDeviceType("string") .build() ) - .apn("apn") + .apn(Token.builder().token("token").build()) .awsSns(AwsSns.builder().targetArn("target_arn").build()) .birthdate("birthdate") .custom( diff --git a/courier-java-core/src/test/kotlin/com/courier/models/notifications/NotificationDuplicateParamsTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/notifications/NotificationDuplicateParamsTest.kt deleted file mode 100644 index 48c3c0e3..00000000 --- a/courier-java-core/src/test/kotlin/com/courier/models/notifications/NotificationDuplicateParamsTest.kt +++ /dev/null @@ -1,23 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. - -package com.courier.models.notifications - -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.Test - -internal class NotificationDuplicateParamsTest { - - @Test - fun create() { - NotificationDuplicateParams.builder().id("id").build() - } - - @Test - fun pathParams() { - val params = NotificationDuplicateParams.builder().id("id").build() - - assertThat(params._pathParam(0)).isEqualTo("id") - // out-of-bound path param - assertThat(params._pathParam(1)).isEqualTo("") - } -} diff --git a/courier-java-core/src/test/kotlin/com/courier/services/async/NotificationServiceAsyncTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/async/NotificationServiceAsyncTest.kt index 3ce702e5..e546c5be 100644 --- a/courier-java-core/src/test/kotlin/com/courier/services/async/NotificationServiceAsyncTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/services/async/NotificationServiceAsyncTest.kt @@ -131,18 +131,6 @@ internal class NotificationServiceAsyncTest { val response = future.get() } - @Disabled("Mock server tests are disabled") - @Test - fun duplicate() { - val client = CourierOkHttpClientAsync.builder().apiKey("My API Key").build() - val notificationServiceAsync = client.notifications() - - val notificationTemplateResponseFuture = notificationServiceAsync.duplicate("id") - - val notificationTemplateResponse = notificationTemplateResponseFuture.get() - notificationTemplateResponse.validate() - } - @Disabled("Mock server tests are disabled") @Test fun listVersions() { diff --git a/courier-java-core/src/test/kotlin/com/courier/services/blocking/NotificationServiceTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/blocking/NotificationServiceTest.kt index 13549cc5..33a101ba 100644 --- a/courier-java-core/src/test/kotlin/com/courier/services/blocking/NotificationServiceTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/services/blocking/NotificationServiceTest.kt @@ -126,17 +126,6 @@ internal class NotificationServiceTest { notificationService.archive("id") } - @Disabled("Mock server tests are disabled") - @Test - fun duplicate() { - val client = CourierOkHttpClient.builder().apiKey("My API Key").build() - val notificationService = client.notifications() - - val notificationTemplateResponse = notificationService.duplicate("id") - - notificationTemplateResponse.validate() - } - @Disabled("Mock server tests are disabled") @Test fun listVersions() { diff --git a/courier-java-proguard-test/src/test/kotlin/com/courier/proguard/ProGuardCompatibilityTest.kt b/courier-java-proguard-test/src/test/kotlin/com/courier/proguard/ProGuardCompatibilityTest.kt index 7cc30e6f..7656c852 100644 --- a/courier-java-proguard-test/src/test/kotlin/com/courier/proguard/ProGuardCompatibilityTest.kt +++ b/courier-java-proguard-test/src/test/kotlin/com/courier/proguard/ProGuardCompatibilityTest.kt @@ -5,8 +5,8 @@ package com.courier.proguard import com.courier.client.okhttp.CourierOkHttpClient import com.courier.core.jsonMapper import com.courier.models.Alignment -import com.courier.models.Discord -import com.courier.models.SendToChannel +import com.courier.models.Apn +import com.courier.models.Token import com.courier.models.send.SendMessageResponse import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import kotlin.reflect.full.memberFunctions @@ -90,15 +90,14 @@ internal class ProGuardCompatibilityTest { } @Test - fun discordRoundtrip() { + fun apnRoundtrip() { val jsonMapper = jsonMapper() - val discord = - Discord.ofSendToChannel(SendToChannel.builder().channelId("channel_id").build()) + val apn = Apn.ofToken(Token.builder().token("token").build()) - val roundtrippedDiscord = - jsonMapper.readValue(jsonMapper.writeValueAsString(discord), jacksonTypeRef()) + val roundtrippedApn = + jsonMapper.readValue(jsonMapper.writeValueAsString(apn), jacksonTypeRef()) - assertThat(roundtrippedDiscord).isEqualTo(discord) + assertThat(roundtrippedApn).isEqualTo(apn) } @Test