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 7b156bde..ac4a64ae 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 @@ -37,6 +37,7 @@ private constructor( private val loop: JsonField, private val ref: JsonField, private val channel: JsonField, + private val elements: JsonField>, private val fontSize: JsonField, private val lineHeight: JsonField, private val padding: JsonField, @@ -53,13 +54,28 @@ private constructor( @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), @JsonProperty("channel") @ExcludeMissing channel: JsonField = JsonMissing.of(), + @JsonProperty("elements") + @ExcludeMissing + elements: JsonField> = JsonMissing.of(), @JsonProperty("font_size") @ExcludeMissing fontSize: JsonField = JsonMissing.of(), @JsonProperty("line_height") @ExcludeMissing lineHeight: JsonField = JsonMissing.of(), @JsonProperty("padding") @ExcludeMissing padding: JsonField = JsonMissing.of(), @JsonProperty("raw") @ExcludeMissing raw: JsonField = JsonMissing.of(), - ) : this(channels, if_, loop, ref, channel, fontSize, lineHeight, padding, raw, mutableMapOf()) + ) : this( + channels, + if_, + loop, + ref, + channel, + elements, + fontSize, + lineHeight, + padding, + raw, + mutableMapOf(), + ) fun toElementalBaseNode(): ElementalBaseNode = ElementalBaseNode.builder().channels(channels).if_(if_).loop(loop).ref(ref).build() @@ -97,6 +113,15 @@ private constructor( */ fun channel(): Optional = channel.getOptional("channel") + /** + * An array of elements to apply to the channel. If `raw` has not been specified, `elements` is + * `required`. Channel elements cannot nest, so these are any node except another channel block. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun elements(): Optional> = elements.getOptional("elements") + /** * Email only. Document-level base font size (CSS px, e.g. `16px`) for body content — text, * quote, list and action button labels. Heading styles (`h1`/`h2`/`h3`) and `subtext` keep @@ -168,6 +193,15 @@ private constructor( */ @JsonProperty("channel") @ExcludeMissing fun _channel(): JsonField = channel + /** + * Returns the raw JSON value of [elements]. + * + * Unlike [elements], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("elements") + @ExcludeMissing + fun _elements(): JsonField> = elements + /** * Returns the raw JSON value of [fontSize]. * @@ -222,6 +256,7 @@ private constructor( private var loop: JsonField = JsonMissing.of() private var ref: JsonField = JsonMissing.of() private var channel: JsonField = JsonMissing.of() + private var elements: JsonField>? = null private var fontSize: JsonField = JsonMissing.of() private var lineHeight: JsonField = JsonMissing.of() private var padding: JsonField = JsonMissing.of() @@ -235,6 +270,7 @@ private constructor( loop = elementalChannelNode.loop ref = elementalChannelNode.ref channel = elementalChannelNode.channel + elements = elementalChannelNode.elements.map { it.toMutableList() } fontSize = elementalChannelNode.fontSize lineHeight = elementalChannelNode.lineHeight padding = elementalChannelNode.padding @@ -323,6 +359,90 @@ private constructor( */ fun channel(channel: JsonField) = apply { this.channel = channel } + /** + * An array of elements to apply to the channel. If `raw` has not been specified, `elements` + * is `required`. Channel elements cannot nest, so these are any node except another channel + * block. + */ + fun elements(elements: List?) = + elements(JsonField.ofNullable(elements)) + + /** Alias for calling [Builder.elements] with `elements.orElse(null)`. */ + fun elements(elements: Optional>) = + elements(elements.getOrNull()) + + /** + * Sets [Builder.elements] to an arbitrary JSON value. + * + * You should usually call [Builder.elements] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun elements(elements: JsonField>) = apply { + this.elements = elements.map { it.toMutableList() } + } + + /** + * Adds a single [ElementalNodeNonChannel] to [elements]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addElement(element: ElementalNodeNonChannel) = apply { + elements = + (elements ?: JsonField.of(mutableListOf())).also { + checkKnown("elements", it).add(element) + } + } + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember0(unionMember0)`. + */ + fun addElement(unionMember0: ElementalNodeNonChannel.UnionMember0) = + addElement(ElementalNodeNonChannel.ofUnionMember0(unionMember0)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember1(unionMember1)`. + */ + fun addElement(unionMember1: ElementalNodeNonChannel.UnionMember1) = + addElement(ElementalNodeNonChannel.ofUnionMember1(unionMember1)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember2(unionMember2)`. + */ + fun addElement(unionMember2: ElementalNodeNonChannel.UnionMember2) = + addElement(ElementalNodeNonChannel.ofUnionMember2(unionMember2)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember3(unionMember3)`. + */ + fun addElement(unionMember3: ElementalNodeNonChannel.UnionMember3) = + addElement(ElementalNodeNonChannel.ofUnionMember3(unionMember3)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember4(unionMember4)`. + */ + fun addElement(unionMember4: ElementalNodeNonChannel.UnionMember4) = + addElement(ElementalNodeNonChannel.ofUnionMember4(unionMember4)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember5(unionMember5)`. + */ + fun addElement(unionMember5: ElementalNodeNonChannel.UnionMember5) = + addElement(ElementalNodeNonChannel.ofUnionMember5(unionMember5)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember6(unionMember6)`. + */ + fun addElement(unionMember6: ElementalNodeNonChannel.UnionMember6) = + addElement(ElementalNodeNonChannel.ofUnionMember6(unionMember6)) + /** * Email only. Document-level base font size (CSS px, e.g. `16px`) for body content — text, * quote, list and action button labels. Heading styles (`h1`/`h2`/`h3`) and `subtext` keep @@ -424,6 +544,7 @@ private constructor( loop, ref, channel, + (elements ?: JsonMissing.of()).map { it.toImmutable() }, fontSize, lineHeight, padding, @@ -452,6 +573,7 @@ private constructor( loop() ref() channel() + elements().ifPresent { it.forEach { it.validate() } } fontSize() lineHeight() padding() @@ -479,6 +601,7 @@ private constructor( (if (loop.asKnown().isPresent) 1 else 0) + (if (ref.asKnown().isPresent) 1 else 0) + (if (channel.asKnown().isPresent) 1 else 0) + + (elements.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + (if (fontSize.asKnown().isPresent) 1 else 0) + (if (lineHeight.asKnown().isPresent) 1 else 0) + (if (padding.asKnown().isPresent) 1 else 0) + @@ -606,6 +729,7 @@ private constructor( loop == other.loop && ref == other.ref && channel == other.channel && + elements == other.elements && fontSize == other.fontSize && lineHeight == other.lineHeight && padding == other.padding && @@ -620,6 +744,7 @@ private constructor( loop, ref, channel, + elements, fontSize, lineHeight, padding, @@ -631,5 +756,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "ElementalChannelNode{channels=$channels, if_=$if_, loop=$loop, ref=$ref, channel=$channel, fontSize=$fontSize, lineHeight=$lineHeight, padding=$padding, raw=$raw, additionalProperties=$additionalProperties}" + "ElementalChannelNode{channels=$channels, if_=$if_, loop=$loop, ref=$ref, channel=$channel, elements=$elements, fontSize=$fontSize, lineHeight=$lineHeight, padding=$padding, raw=$raw, additionalProperties=$additionalProperties}" } 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 93cb776a..c03461a9 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 @@ -38,6 +38,7 @@ private constructor( private val loop: JsonField, private val ref: JsonField, private val channel: JsonField, + private val elements: JsonField>, private val fontSize: JsonField, private val lineHeight: JsonField, private val padding: JsonField, @@ -55,6 +56,9 @@ private constructor( @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), @JsonProperty("channel") @ExcludeMissing channel: JsonField = JsonMissing.of(), + @JsonProperty("elements") + @ExcludeMissing + elements: JsonField> = JsonMissing.of(), @JsonProperty("font_size") @ExcludeMissing fontSize: JsonField = JsonMissing.of(), @JsonProperty("line_height") @ExcludeMissing @@ -70,6 +74,7 @@ private constructor( loop, ref, channel, + elements, fontSize, lineHeight, padding, @@ -85,6 +90,7 @@ private constructor( .loop(loop) .ref(ref) .channel(channel) + .elements(elements) .fontSize(fontSize) .lineHeight(lineHeight) .padding(padding) @@ -124,6 +130,15 @@ private constructor( */ fun channel(): Optional = channel.getOptional("channel") + /** + * An array of elements to apply to the channel. If `raw` has not been specified, `elements` is + * `required`. Channel elements cannot nest, so these are any node except another channel block. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun elements(): Optional> = elements.getOptional("elements") + /** * Email only. Document-level base font size (CSS px, e.g. `16px`) for body content — text, * quote, list and action button labels. Heading styles (`h1`/`h2`/`h3`) and `subtext` keep @@ -201,6 +216,15 @@ private constructor( */ @JsonProperty("channel") @ExcludeMissing fun _channel(): JsonField = channel + /** + * Returns the raw JSON value of [elements]. + * + * Unlike [elements], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("elements") + @ExcludeMissing + fun _elements(): JsonField> = elements + /** * Returns the raw JSON value of [fontSize]. * @@ -264,6 +288,7 @@ private constructor( private var loop: JsonField = JsonMissing.of() private var ref: JsonField = JsonMissing.of() private var channel: JsonField = JsonMissing.of() + private var elements: JsonField>? = null private var fontSize: JsonField = JsonMissing.of() private var lineHeight: JsonField = JsonMissing.of() private var padding: JsonField = JsonMissing.of() @@ -278,6 +303,7 @@ private constructor( loop = elementalChannelNodeWithType.loop ref = elementalChannelNodeWithType.ref channel = elementalChannelNodeWithType.channel + elements = elementalChannelNodeWithType.elements.map { it.toMutableList() } fontSize = elementalChannelNodeWithType.fontSize lineHeight = elementalChannelNodeWithType.lineHeight padding = elementalChannelNodeWithType.padding @@ -367,6 +393,90 @@ private constructor( */ fun channel(channel: JsonField) = apply { this.channel = channel } + /** + * An array of elements to apply to the channel. If `raw` has not been specified, `elements` + * is `required`. Channel elements cannot nest, so these are any node except another channel + * block. + */ + fun elements(elements: List?) = + elements(JsonField.ofNullable(elements)) + + /** Alias for calling [Builder.elements] with `elements.orElse(null)`. */ + fun elements(elements: Optional>) = + elements(elements.getOrNull()) + + /** + * Sets [Builder.elements] to an arbitrary JSON value. + * + * You should usually call [Builder.elements] with a well-typed + * `List` value instead. This method is primarily for setting the + * field to an undocumented or not yet supported value. + */ + fun elements(elements: JsonField>) = apply { + this.elements = elements.map { it.toMutableList() } + } + + /** + * Adds a single [ElementalNodeNonChannel] to [elements]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addElement(element: ElementalNodeNonChannel) = apply { + elements = + (elements ?: JsonField.of(mutableListOf())).also { + checkKnown("elements", it).add(element) + } + } + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember0(unionMember0)`. + */ + fun addElement(unionMember0: ElementalNodeNonChannel.UnionMember0) = + addElement(ElementalNodeNonChannel.ofUnionMember0(unionMember0)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember1(unionMember1)`. + */ + fun addElement(unionMember1: ElementalNodeNonChannel.UnionMember1) = + addElement(ElementalNodeNonChannel.ofUnionMember1(unionMember1)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember2(unionMember2)`. + */ + fun addElement(unionMember2: ElementalNodeNonChannel.UnionMember2) = + addElement(ElementalNodeNonChannel.ofUnionMember2(unionMember2)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember3(unionMember3)`. + */ + fun addElement(unionMember3: ElementalNodeNonChannel.UnionMember3) = + addElement(ElementalNodeNonChannel.ofUnionMember3(unionMember3)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember4(unionMember4)`. + */ + fun addElement(unionMember4: ElementalNodeNonChannel.UnionMember4) = + addElement(ElementalNodeNonChannel.ofUnionMember4(unionMember4)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember5(unionMember5)`. + */ + fun addElement(unionMember5: ElementalNodeNonChannel.UnionMember5) = + addElement(ElementalNodeNonChannel.ofUnionMember5(unionMember5)) + + /** + * Alias for calling [addElement] with + * `ElementalNodeNonChannel.ofUnionMember6(unionMember6)`. + */ + fun addElement(unionMember6: ElementalNodeNonChannel.UnionMember6) = + addElement(ElementalNodeNonChannel.ofUnionMember6(unionMember6)) + /** * Email only. Document-level base font size (CSS px, e.g. `16px`) for body content — text, * quote, list and action button labels. Heading styles (`h1`/`h2`/`h3`) and `subtext` keep @@ -479,6 +589,7 @@ private constructor( loop, ref, channel, + (elements ?: JsonMissing.of()).map { it.toImmutable() }, fontSize, lineHeight, padding, @@ -508,6 +619,7 @@ private constructor( loop() ref() channel() + elements().ifPresent { it.forEach { it.validate() } } fontSize() lineHeight() padding() @@ -536,6 +648,7 @@ private constructor( (if (loop.asKnown().isPresent) 1 else 0) + (if (ref.asKnown().isPresent) 1 else 0) + (if (channel.asKnown().isPresent) 1 else 0) + + (elements.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) + (if (fontSize.asKnown().isPresent) 1 else 0) + (if (lineHeight.asKnown().isPresent) 1 else 0) + (if (padding.asKnown().isPresent) 1 else 0) + @@ -681,6 +794,7 @@ private constructor( loop == other.loop && ref == other.ref && channel == other.channel && + elements == other.elements && fontSize == other.fontSize && lineHeight == other.lineHeight && padding == other.padding && @@ -696,6 +810,7 @@ private constructor( loop, ref, channel, + elements, fontSize, lineHeight, padding, @@ -708,5 +823,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "ElementalChannelNodeWithType{channels=$channels, if_=$if_, loop=$loop, ref=$ref, channel=$channel, fontSize=$fontSize, lineHeight=$lineHeight, padding=$padding, raw=$raw, type=$type, additionalProperties=$additionalProperties}" + "ElementalChannelNodeWithType{channels=$channels, if_=$if_, loop=$loop, ref=$ref, channel=$channel, elements=$elements, fontSize=$fontSize, lineHeight=$lineHeight, padding=$padding, raw=$raw, type=$type, additionalProperties=$additionalProperties}" } diff --git a/courier-java-core/src/main/kotlin/com/courier/models/ElementalNodeNonChannel.kt b/courier-java-core/src/main/kotlin/com/courier/models/ElementalNodeNonChannel.kt new file mode 100644 index 00000000..157ce50b --- /dev/null +++ b/courier-java-core/src/main/kotlin/com/courier/models/ElementalNodeNonChannel.kt @@ -0,0 +1,5616 @@ +// 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.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.allMaxBy +import com.courier.core.checkKnown +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 + +/** + * Any Elemental node except a channel block. Channel elements are only valid as top-level elements, + * so the `elements` nested inside one can never be another channel. Keeping this union channel-free + * also keeps the schema acyclic; a recursive `$ref` here breaks the generated Python models. + */ +@JsonDeserialize(using = ElementalNodeNonChannel.Deserializer::class) +@JsonSerialize(using = ElementalNodeNonChannel.Serializer::class) +class ElementalNodeNonChannel +private constructor( + private val unionMember0: UnionMember0? = null, + private val unionMember1: UnionMember1? = null, + private val unionMember2: UnionMember2? = null, + private val unionMember3: UnionMember3? = null, + private val unionMember4: UnionMember4? = null, + private val unionMember5: UnionMember5? = null, + private val unionMember6: UnionMember6? = null, + private val _json: JsonValue? = null, +) { + + /** Represents a body of text to be rendered inside of the notification. */ + fun unionMember0(): Optional = Optional.ofNullable(unionMember0) + + /** + * The meta element contains information describing the notification that may be used by a + * particular channel or provider. One important field is the title field which will be used as + * the title for channels that support it. + */ + fun unionMember1(): Optional = Optional.ofNullable(unionMember1) + + /** Used to embed an image into the notification. */ + fun unionMember2(): Optional = Optional.ofNullable(unionMember2) + + /** Allows the user to execute an action. Can be a button or a link. */ + fun unionMember3(): Optional = Optional.ofNullable(unionMember3) + + /** Renders a dividing line between elements. */ + fun unionMember4(): Optional = Optional.ofNullable(unionMember4) + + /** Renders a quote block. */ + fun unionMember5(): Optional = Optional.ofNullable(unionMember5) + + /** + * Raw HTML string inside an Elemental document. When rendering a message, this node is turned + * into output only for the email channel; for other channels it produces no blocks. + */ + fun unionMember6(): Optional = Optional.ofNullable(unionMember6) + + fun isUnionMember0(): Boolean = unionMember0 != null + + fun isUnionMember1(): Boolean = unionMember1 != null + + fun isUnionMember2(): Boolean = unionMember2 != null + + fun isUnionMember3(): Boolean = unionMember3 != null + + fun isUnionMember4(): Boolean = unionMember4 != null + + fun isUnionMember5(): Boolean = unionMember5 != null + + fun isUnionMember6(): Boolean = unionMember6 != null + + /** Represents a body of text to be rendered inside of the notification. */ + fun asUnionMember0(): UnionMember0 = unionMember0.getOrThrow("unionMember0") + + /** + * The meta element contains information describing the notification that may be used by a + * particular channel or provider. One important field is the title field which will be used as + * the title for channels that support it. + */ + fun asUnionMember1(): UnionMember1 = unionMember1.getOrThrow("unionMember1") + + /** Used to embed an image into the notification. */ + fun asUnionMember2(): UnionMember2 = unionMember2.getOrThrow("unionMember2") + + /** Allows the user to execute an action. Can be a button or a link. */ + fun asUnionMember3(): UnionMember3 = unionMember3.getOrThrow("unionMember3") + + /** Renders a dividing line between elements. */ + fun asUnionMember4(): UnionMember4 = unionMember4.getOrThrow("unionMember4") + + /** Renders a quote block. */ + fun asUnionMember5(): UnionMember5 = unionMember5.getOrThrow("unionMember5") + + /** + * Raw HTML string inside an Elemental document. When rendering a message, this node is turned + * into output only for the email channel; for other channels it produces no blocks. + */ + fun asUnionMember6(): UnionMember6 = unionMember6.getOrThrow("unionMember6") + + 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 = elementalNodeNonChannel.accept(new ElementalNodeNonChannel.Visitor>() { + * @Override + * public Optional visitUnionMember0(UnionMember0 unionMember0) { + * return Optional.of(unionMember0.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 { + unionMember0 != null -> visitor.visitUnionMember0(unionMember0) + unionMember1 != null -> visitor.visitUnionMember1(unionMember1) + unionMember2 != null -> visitor.visitUnionMember2(unionMember2) + unionMember3 != null -> visitor.visitUnionMember3(unionMember3) + unionMember4 != null -> visitor.visitUnionMember4(unionMember4) + unionMember5 != null -> visitor.visitUnionMember5(unionMember5) + unionMember6 != null -> visitor.visitUnionMember6(unionMember6) + 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(): ElementalNodeNonChannel = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitUnionMember0(unionMember0: UnionMember0) { + unionMember0.validate() + } + + override fun visitUnionMember1(unionMember1: UnionMember1) { + unionMember1.validate() + } + + override fun visitUnionMember2(unionMember2: UnionMember2) { + unionMember2.validate() + } + + override fun visitUnionMember3(unionMember3: UnionMember3) { + unionMember3.validate() + } + + override fun visitUnionMember4(unionMember4: UnionMember4) { + unionMember4.validate() + } + + override fun visitUnionMember5(unionMember5: UnionMember5) { + unionMember5.validate() + } + + override fun visitUnionMember6(unionMember6: UnionMember6) { + unionMember6.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 visitUnionMember0(unionMember0: UnionMember0) = unionMember0.validity() + + override fun visitUnionMember1(unionMember1: UnionMember1) = unionMember1.validity() + + override fun visitUnionMember2(unionMember2: UnionMember2) = unionMember2.validity() + + override fun visitUnionMember3(unionMember3: UnionMember3) = unionMember3.validity() + + override fun visitUnionMember4(unionMember4: UnionMember4) = unionMember4.validity() + + override fun visitUnionMember5(unionMember5: UnionMember5) = unionMember5.validity() + + override fun visitUnionMember6(unionMember6: UnionMember6) = unionMember6.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ElementalNodeNonChannel && + unionMember0 == other.unionMember0 && + unionMember1 == other.unionMember1 && + unionMember2 == other.unionMember2 && + unionMember3 == other.unionMember3 && + unionMember4 == other.unionMember4 && + unionMember5 == other.unionMember5 && + unionMember6 == other.unionMember6 + } + + override fun hashCode(): Int = + Objects.hash( + unionMember0, + unionMember1, + unionMember2, + unionMember3, + unionMember4, + unionMember5, + unionMember6, + ) + + override fun toString(): String = + when { + unionMember0 != null -> "ElementalNodeNonChannel{unionMember0=$unionMember0}" + unionMember1 != null -> "ElementalNodeNonChannel{unionMember1=$unionMember1}" + unionMember2 != null -> "ElementalNodeNonChannel{unionMember2=$unionMember2}" + unionMember3 != null -> "ElementalNodeNonChannel{unionMember3=$unionMember3}" + unionMember4 != null -> "ElementalNodeNonChannel{unionMember4=$unionMember4}" + unionMember5 != null -> "ElementalNodeNonChannel{unionMember5=$unionMember5}" + unionMember6 != null -> "ElementalNodeNonChannel{unionMember6=$unionMember6}" + _json != null -> "ElementalNodeNonChannel{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ElementalNodeNonChannel") + } + + companion object { + + /** Represents a body of text to be rendered inside of the notification. */ + @JvmStatic + fun ofUnionMember0(unionMember0: UnionMember0) = + ElementalNodeNonChannel(unionMember0 = unionMember0) + + /** + * The meta element contains information describing the notification that may be used by a + * particular channel or provider. One important field is the title field which will be used + * as the title for channels that support it. + */ + @JvmStatic + fun ofUnionMember1(unionMember1: UnionMember1) = + ElementalNodeNonChannel(unionMember1 = unionMember1) + + /** Used to embed an image into the notification. */ + @JvmStatic + fun ofUnionMember2(unionMember2: UnionMember2) = + ElementalNodeNonChannel(unionMember2 = unionMember2) + + /** Allows the user to execute an action. Can be a button or a link. */ + @JvmStatic + fun ofUnionMember3(unionMember3: UnionMember3) = + ElementalNodeNonChannel(unionMember3 = unionMember3) + + /** Renders a dividing line between elements. */ + @JvmStatic + fun ofUnionMember4(unionMember4: UnionMember4) = + ElementalNodeNonChannel(unionMember4 = unionMember4) + + /** Renders a quote block. */ + @JvmStatic + fun ofUnionMember5(unionMember5: UnionMember5) = + ElementalNodeNonChannel(unionMember5 = unionMember5) + + /** + * Raw HTML string inside an Elemental document. When rendering a message, this node is + * turned into output only for the email channel; for other channels it produces no blocks. + */ + @JvmStatic + fun ofUnionMember6(unionMember6: UnionMember6) = + ElementalNodeNonChannel(unionMember6 = unionMember6) + } + + /** + * An interface that defines how to map each variant of [ElementalNodeNonChannel] to a value of + * type [T]. + */ + interface Visitor { + + /** Represents a body of text to be rendered inside of the notification. */ + fun visitUnionMember0(unionMember0: UnionMember0): T + + /** + * The meta element contains information describing the notification that may be used by a + * particular channel or provider. One important field is the title field which will be used + * as the title for channels that support it. + */ + fun visitUnionMember1(unionMember1: UnionMember1): T + + /** Used to embed an image into the notification. */ + fun visitUnionMember2(unionMember2: UnionMember2): T + + /** Allows the user to execute an action. Can be a button or a link. */ + fun visitUnionMember3(unionMember3: UnionMember3): T + + /** Renders a dividing line between elements. */ + fun visitUnionMember4(unionMember4: UnionMember4): T + + /** Renders a quote block. */ + fun visitUnionMember5(unionMember5: UnionMember5): T + + /** + * Raw HTML string inside an Elemental document. When rendering a message, this node is + * turned into output only for the email channel; for other channels it produces no blocks. + */ + fun visitUnionMember6(unionMember6: UnionMember6): T + + /** + * Maps an unknown variant of [ElementalNodeNonChannel] to a value of type [T]. + * + * An instance of [ElementalNodeNonChannel] 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 ElementalNodeNonChannel: $json") + } + } + + internal class Deserializer : + BaseDeserializer(ElementalNodeNonChannel::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ElementalNodeNonChannel { + val json = JsonValue.fromJsonNode(node) + + val bestMatches = + sequenceOf( + tryDeserialize(node, jacksonTypeRef())?.let { + ElementalNodeNonChannel(unionMember0 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ElementalNodeNonChannel(unionMember1 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ElementalNodeNonChannel(unionMember2 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ElementalNodeNonChannel(unionMember3 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ElementalNodeNonChannel(unionMember4 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ElementalNodeNonChannel(unionMember5 = it, _json = json) + }, + tryDeserialize(node, jacksonTypeRef())?.let { + ElementalNodeNonChannel(unionMember6 = 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 -> ElementalNodeNonChannel(_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(ElementalNodeNonChannel::class) { + + override fun serialize( + value: ElementalNodeNonChannel, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.unionMember0 != null -> generator.writeObject(value.unionMember0) + value.unionMember1 != null -> generator.writeObject(value.unionMember1) + value.unionMember2 != null -> generator.writeObject(value.unionMember2) + value.unionMember3 != null -> generator.writeObject(value.unionMember3) + value.unionMember4 != null -> generator.writeObject(value.unionMember4) + value.unionMember5 != null -> generator.writeObject(value.unionMember5) + value.unionMember6 != null -> generator.writeObject(value.unionMember6) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ElementalNodeNonChannel") + } + } + } + + /** Represents a body of text to be rendered inside of the notification. */ + class UnionMember0 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val channels: JsonField>, + private val if_: JsonField, + private val loop: JsonField, + private val ref: JsonField, + private val align: JsonField, + private val bold: JsonField, + private val color: JsonField, + private val content: JsonField, + private val fontSize: JsonField, + private val format: JsonField, + private val italic: JsonField, + private val lineHeight: JsonField, + private val locales: JsonField, + private val strikethrough: JsonField, + private val textStyle: JsonField, + private val underline: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("channels") + @ExcludeMissing + channels: JsonField> = JsonMissing.of(), + @JsonProperty("if") @ExcludeMissing if_: JsonField = JsonMissing.of(), + @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), + @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), + @JsonProperty("align") + @ExcludeMissing + align: JsonField = JsonMissing.of(), + @JsonProperty("bold") @ExcludeMissing bold: JsonField = JsonMissing.of(), + @JsonProperty("color") @ExcludeMissing color: JsonField = JsonMissing.of(), + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("font_size") + @ExcludeMissing + fontSize: JsonField = JsonMissing.of(), + @JsonProperty("format") + @ExcludeMissing + format: JsonField = JsonMissing.of(), + @JsonProperty("italic") @ExcludeMissing italic: JsonField = JsonMissing.of(), + @JsonProperty("line_height") + @ExcludeMissing + lineHeight: JsonField = JsonMissing.of(), + @JsonProperty("locales") @ExcludeMissing locales: JsonField = JsonMissing.of(), + @JsonProperty("strikethrough") + @ExcludeMissing + strikethrough: JsonField = JsonMissing.of(), + @JsonProperty("text_style") + @ExcludeMissing + textStyle: JsonField = JsonMissing.of(), + @JsonProperty("underline") + @ExcludeMissing + underline: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this( + channels, + if_, + loop, + ref, + align, + bold, + color, + content, + fontSize, + format, + italic, + lineHeight, + locales, + strikethrough, + textStyle, + underline, + type, + mutableMapOf(), + ) + + fun toElementalTextNode(): ElementalTextNode = + ElementalTextNode.builder() + .channels(channels) + .if_(if_) + .loop(loop) + .ref(ref) + .align(align) + .bold(bold) + .color(color) + .content(content) + .fontSize(fontSize) + .format(format) + .italic(italic) + .lineHeight(lineHeight) + .locales(locales) + .strikethrough(strikethrough) + .textStyle(textStyle) + .underline(underline) + .build() + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channels(): Optional> = channels.getOptional("channels") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun if_(): Optional = if_.getOptional("if") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun loop(): Optional = loop.getOptional("loop") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun ref(): Optional = ref.getOptional("ref") + + /** + * Text alignment. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun align(): Optional = align.getOptional("align") + + /** + * Apply bold to the text + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun bold(): Optional = bold.getOptional("bold") + + /** + * Specifies the color of text. Can be any valid css color value + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun color(): Optional = color.getOptional("color") + + /** + * The text content displayed in the notification. Either this field must be specified, or + * the elements field + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun content(): Optional = content.getOptional("content") + + /** + * CSS px font size for this text block, e.g. `16px`. Overrides the size of the `text_style` + * preset. Email only. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fontSize(): Optional = fontSize.getOptional("font_size") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun format(): Optional = format.getOptional("format") + + /** + * Apply italics to the text + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun italic(): Optional = italic.getOptional("italic") + + /** + * CSS line height for this text block, as a px value or a unitless multiplier, e.g. `24px` + * or `1.5`. Email only. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lineHeight(): Optional = lineHeight.getOptional("line_height") + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for more + * details. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun locales(): Optional = locales.getOptional("locales") + + /** + * Apply a strike through the text + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun strikethrough(): Optional = strikethrough.getOptional("strikethrough") + + /** + * Allows the text to be rendered as a heading level. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun textStyle(): Optional = textStyle.getOptional("text_style") + + /** + * Apply an underline to the text + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun underline(): Optional = underline.getOptional("underline") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [channels]. + * + * Unlike [channels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channels") + @ExcludeMissing + fun _channels(): JsonField> = channels + + /** + * Returns the raw JSON value of [if_]. + * + * Unlike [if_], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("if") @ExcludeMissing fun _if_(): JsonField = if_ + + /** + * Returns the raw JSON value of [loop]. + * + * Unlike [loop], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("loop") @ExcludeMissing fun _loop(): JsonField = loop + + /** + * Returns the raw JSON value of [ref]. + * + * Unlike [ref], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ref") @ExcludeMissing fun _ref(): JsonField = ref + + /** + * Returns the raw JSON value of [align]. + * + * Unlike [align], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("align") + @ExcludeMissing + fun _align(): JsonField = align + + /** + * Returns the raw JSON value of [bold]. + * + * Unlike [bold], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("bold") @ExcludeMissing fun _bold(): JsonField = bold + + /** + * Returns the raw JSON value of [color]. + * + * Unlike [color], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("color") @ExcludeMissing fun _color(): JsonField = color + + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [fontSize]. + * + * Unlike [fontSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("font_size") @ExcludeMissing fun _fontSize(): JsonField = fontSize + + /** + * Returns the raw JSON value of [format]. + * + * Unlike [format], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("format") + @ExcludeMissing + fun _format(): JsonField = format + + /** + * Returns the raw JSON value of [italic]. + * + * Unlike [italic], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("italic") @ExcludeMissing fun _italic(): JsonField = italic + + /** + * Returns the raw JSON value of [lineHeight]. + * + * Unlike [lineHeight], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line_height") + @ExcludeMissing + fun _lineHeight(): JsonField = lineHeight + + /** + * Returns the raw JSON value of [locales]. + * + * Unlike [locales], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("locales") @ExcludeMissing fun _locales(): JsonField = locales + + /** + * Returns the raw JSON value of [strikethrough]. + * + * Unlike [strikethrough], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("strikethrough") + @ExcludeMissing + fun _strikethrough(): JsonField = strikethrough + + /** + * Returns the raw JSON value of [textStyle]. + * + * Unlike [textStyle], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("text_style") + @ExcludeMissing + fun _textStyle(): JsonField = textStyle + + /** + * Returns the raw JSON value of [underline]. + * + * Unlike [underline], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("underline") @ExcludeMissing fun _underline(): JsonField = underline + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @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 [UnionMember0]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember0]. */ + class Builder internal constructor() { + + private var channels: JsonField>? = null + private var if_: JsonField = JsonMissing.of() + private var loop: JsonField = JsonMissing.of() + private var ref: JsonField = JsonMissing.of() + private var align: JsonField = JsonMissing.of() + private var bold: JsonField = JsonMissing.of() + private var color: JsonField = JsonMissing.of() + private var content: JsonField = JsonMissing.of() + private var fontSize: JsonField = JsonMissing.of() + private var format: JsonField = JsonMissing.of() + private var italic: JsonField = JsonMissing.of() + private var lineHeight: JsonField = JsonMissing.of() + private var locales: JsonField = JsonMissing.of() + private var strikethrough: JsonField = JsonMissing.of() + private var textStyle: JsonField = JsonMissing.of() + private var underline: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember0: UnionMember0) = apply { + channels = unionMember0.channels.map { it.toMutableList() } + if_ = unionMember0.if_ + loop = unionMember0.loop + ref = unionMember0.ref + align = unionMember0.align + bold = unionMember0.bold + color = unionMember0.color + content = unionMember0.content + fontSize = unionMember0.fontSize + format = unionMember0.format + italic = unionMember0.italic + lineHeight = unionMember0.lineHeight + locales = unionMember0.locales + strikethrough = unionMember0.strikethrough + textStyle = unionMember0.textStyle + underline = unionMember0.underline + type = unionMember0.type + additionalProperties = unionMember0.additionalProperties.toMutableMap() + } + + fun channels(channels: List?) = channels(JsonField.ofNullable(channels)) + + /** Alias for calling [Builder.channels] with `channels.orElse(null)`. */ + fun channels(channels: Optional>) = channels(channels.getOrNull()) + + /** + * Sets [Builder.channels] to an arbitrary JSON value. + * + * You should usually call [Builder.channels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun channels(channels: JsonField>) = apply { + this.channels = channels.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [channels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addChannel(channel: String) = apply { + channels = + (channels ?: JsonField.of(mutableListOf())).also { + checkKnown("channels", it).add(channel) + } + } + + fun if_(if_: String?) = if_(JsonField.ofNullable(if_)) + + /** Alias for calling [Builder.if_] with `if_.orElse(null)`. */ + fun if_(if_: Optional) = if_(if_.getOrNull()) + + /** + * Sets [Builder.if_] to an arbitrary JSON value. + * + * You should usually call [Builder.if_] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun if_(if_: JsonField) = apply { this.if_ = if_ } + + fun loop(loop: String?) = loop(JsonField.ofNullable(loop)) + + /** Alias for calling [Builder.loop] with `loop.orElse(null)`. */ + fun loop(loop: Optional) = loop(loop.getOrNull()) + + /** + * Sets [Builder.loop] to an arbitrary JSON value. + * + * You should usually call [Builder.loop] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun loop(loop: JsonField) = apply { this.loop = loop } + + fun ref(ref: String?) = ref(JsonField.ofNullable(ref)) + + /** Alias for calling [Builder.ref] with `ref.orElse(null)`. */ + fun ref(ref: Optional) = ref(ref.getOrNull()) + + /** + * Sets [Builder.ref] to an arbitrary JSON value. + * + * You should usually call [Builder.ref] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun ref(ref: JsonField) = apply { this.ref = ref } + + /** Text alignment. */ + fun align(align: ElementalTextNode.Align) = align(JsonField.of(align)) + + /** + * Sets [Builder.align] to an arbitrary JSON value. + * + * You should usually call [Builder.align] with a well-typed [ElementalTextNode.Align] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun align(align: JsonField) = apply { this.align = align } + + /** Apply bold to the text */ + fun bold(bold: String?) = bold(JsonField.ofNullable(bold)) + + /** Alias for calling [Builder.bold] with `bold.orElse(null)`. */ + fun bold(bold: Optional) = bold(bold.getOrNull()) + + /** + * Sets [Builder.bold] to an arbitrary JSON value. + * + * You should usually call [Builder.bold] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun bold(bold: JsonField) = apply { this.bold = bold } + + /** Specifies the color of text. Can be any valid css color value */ + fun color(color: String?) = color(JsonField.ofNullable(color)) + + /** Alias for calling [Builder.color] with `color.orElse(null)`. */ + fun color(color: Optional) = color(color.getOrNull()) + + /** + * Sets [Builder.color] to an arbitrary JSON value. + * + * You should usually call [Builder.color] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun color(color: JsonField) = apply { this.color = color } + + /** + * The text content displayed in the notification. Either this field must be specified, + * or the elements field + */ + fun content(content: String) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** + * CSS px font size for this text block, e.g. `16px`. Overrides the size of the + * `text_style` preset. Email only. + */ + fun fontSize(fontSize: String?) = fontSize(JsonField.ofNullable(fontSize)) + + /** Alias for calling [Builder.fontSize] with `fontSize.orElse(null)`. */ + fun fontSize(fontSize: Optional) = fontSize(fontSize.getOrNull()) + + /** + * Sets [Builder.fontSize] to an arbitrary JSON value. + * + * You should usually call [Builder.fontSize] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fontSize(fontSize: JsonField) = apply { this.fontSize = fontSize } + + fun format(format: ElementalTextNode.Format?) = format(JsonField.ofNullable(format)) + + /** Alias for calling [Builder.format] with `format.orElse(null)`. */ + fun format(format: Optional) = format(format.getOrNull()) + + /** + * Sets [Builder.format] to an arbitrary JSON value. + * + * You should usually call [Builder.format] with a well-typed [ElementalTextNode.Format] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun format(format: JsonField) = apply { this.format = format } + + /** Apply italics to the text */ + fun italic(italic: String?) = italic(JsonField.ofNullable(italic)) + + /** Alias for calling [Builder.italic] with `italic.orElse(null)`. */ + fun italic(italic: Optional) = italic(italic.getOrNull()) + + /** + * Sets [Builder.italic] to an arbitrary JSON value. + * + * You should usually call [Builder.italic] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun italic(italic: JsonField) = apply { this.italic = italic } + + /** + * CSS line height for this text block, as a px value or a unitless multiplier, e.g. + * `24px` or `1.5`. Email only. + */ + fun lineHeight(lineHeight: String?) = lineHeight(JsonField.ofNullable(lineHeight)) + + /** Alias for calling [Builder.lineHeight] with `lineHeight.orElse(null)`. */ + fun lineHeight(lineHeight: Optional) = lineHeight(lineHeight.getOrNull()) + + /** + * Sets [Builder.lineHeight] to an arbitrary JSON value. + * + * You should usually call [Builder.lineHeight] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lineHeight(lineHeight: JsonField) = apply { this.lineHeight = lineHeight } + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for + * more details. + */ + fun locales(locales: Locales?) = locales(JsonField.ofNullable(locales)) + + /** Alias for calling [Builder.locales] with `locales.orElse(null)`. */ + fun locales(locales: Optional) = locales(locales.getOrNull()) + + /** + * Sets [Builder.locales] to an arbitrary JSON value. + * + * You should usually call [Builder.locales] with a well-typed [Locales] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun locales(locales: JsonField) = apply { this.locales = locales } + + /** Apply a strike through the text */ + fun strikethrough(strikethrough: String?) = + strikethrough(JsonField.ofNullable(strikethrough)) + + /** Alias for calling [Builder.strikethrough] with `strikethrough.orElse(null)`. */ + fun strikethrough(strikethrough: Optional) = + strikethrough(strikethrough.getOrNull()) + + /** + * Sets [Builder.strikethrough] to an arbitrary JSON value. + * + * You should usually call [Builder.strikethrough] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun strikethrough(strikethrough: JsonField) = apply { + this.strikethrough = strikethrough + } + + /** Allows the text to be rendered as a heading level. */ + fun textStyle(textStyle: TextStyle?) = textStyle(JsonField.ofNullable(textStyle)) + + /** Alias for calling [Builder.textStyle] with `textStyle.orElse(null)`. */ + fun textStyle(textStyle: Optional) = textStyle(textStyle.getOrNull()) + + /** + * Sets [Builder.textStyle] to an arbitrary JSON value. + * + * You should usually call [Builder.textStyle] with a well-typed [TextStyle] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun textStyle(textStyle: JsonField) = apply { this.textStyle = textStyle } + + /** Apply an underline to the text */ + fun underline(underline: String?) = underline(JsonField.ofNullable(underline)) + + /** Alias for calling [Builder.underline] with `underline.orElse(null)`. */ + fun underline(underline: Optional) = underline(underline.getOrNull()) + + /** + * Sets [Builder.underline] to an arbitrary JSON value. + * + * You should usually call [Builder.underline] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun underline(underline: JsonField) = apply { this.underline = underline } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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 [UnionMember0]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UnionMember0 = + UnionMember0( + (channels ?: JsonMissing.of()).map { it.toImmutable() }, + if_, + loop, + ref, + align, + bold, + color, + content, + fontSize, + format, + italic, + lineHeight, + locales, + strikethrough, + textStyle, + underline, + type, + additionalProperties.toMutableMap(), + ) + } + + 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(): UnionMember0 = apply { + if (validated) { + return@apply + } + + channels() + if_() + loop() + ref() + align().ifPresent { it.validate() } + bold() + color() + content() + fontSize() + format().ifPresent { it.validate() } + italic() + lineHeight() + locales().ifPresent { it.validate() } + strikethrough() + textStyle().ifPresent { it.validate() } + underline() + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CourierInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (channels.asKnown().getOrNull()?.size ?: 0) + + (if (if_.asKnown().isPresent) 1 else 0) + + (if (loop.asKnown().isPresent) 1 else 0) + + (if (ref.asKnown().isPresent) 1 else 0) + + (align.asKnown().getOrNull()?.validity() ?: 0) + + (if (bold.asKnown().isPresent) 1 else 0) + + (if (color.asKnown().isPresent) 1 else 0) + + (if (content.asKnown().isPresent) 1 else 0) + + (if (fontSize.asKnown().isPresent) 1 else 0) + + (format.asKnown().getOrNull()?.validity() ?: 0) + + (if (italic.asKnown().isPresent) 1 else 0) + + (if (lineHeight.asKnown().isPresent) 1 else 0) + + (locales.asKnown().getOrNull()?.validity() ?: 0) + + (if (strikethrough.asKnown().isPresent) 1 else 0) + + (textStyle.asKnown().getOrNull()?.validity() ?: 0) + + (if (underline.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val TEXT = of("text") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + TEXT + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + TEXT, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + TEXT -> Value.TEXT + 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) { + TEXT -> Known.TEXT + else -> throw CourierInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws 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 + + /** + * 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(): Type = 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 Type && 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 UnionMember0 && + channels == other.channels && + if_ == other.if_ && + loop == other.loop && + ref == other.ref && + align == other.align && + bold == other.bold && + color == other.color && + content == other.content && + fontSize == other.fontSize && + format == other.format && + italic == other.italic && + lineHeight == other.lineHeight && + locales == other.locales && + strikethrough == other.strikethrough && + textStyle == other.textStyle && + underline == other.underline && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + channels, + if_, + loop, + ref, + align, + bold, + color, + content, + fontSize, + format, + italic, + lineHeight, + locales, + strikethrough, + textStyle, + underline, + type, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember0{channels=$channels, if_=$if_, loop=$loop, ref=$ref, align=$align, bold=$bold, color=$color, content=$content, fontSize=$fontSize, format=$format, italic=$italic, lineHeight=$lineHeight, locales=$locales, strikethrough=$strikethrough, textStyle=$textStyle, underline=$underline, type=$type, additionalProperties=$additionalProperties}" + } + + /** + * The meta element contains information describing the notification that may be used by a + * particular channel or provider. One important field is the title field which will be used as + * the title for channels that support it. + */ + class UnionMember1 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val channels: JsonField>, + private val if_: JsonField, + private val loop: JsonField, + private val ref: JsonField, + private val title: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("channels") + @ExcludeMissing + channels: JsonField> = JsonMissing.of(), + @JsonProperty("if") @ExcludeMissing if_: JsonField = JsonMissing.of(), + @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), + @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), + @JsonProperty("title") @ExcludeMissing title: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(channels, if_, loop, ref, title, type, mutableMapOf()) + + fun toElementalMetaNode(): ElementalMetaNode = + ElementalMetaNode.builder() + .channels(channels) + .if_(if_) + .loop(loop) + .ref(ref) + .title(title) + .build() + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channels(): Optional> = channels.getOptional("channels") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun if_(): Optional = if_.getOptional("if") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun loop(): Optional = loop.getOptional("loop") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun ref(): Optional = ref.getOptional("ref") + + /** + * The title to be displayed by supported channels. For example, the email subject. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun title(): Optional = title.getOptional("title") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [channels]. + * + * Unlike [channels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channels") + @ExcludeMissing + fun _channels(): JsonField> = channels + + /** + * Returns the raw JSON value of [if_]. + * + * Unlike [if_], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("if") @ExcludeMissing fun _if_(): JsonField = if_ + + /** + * Returns the raw JSON value of [loop]. + * + * Unlike [loop], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("loop") @ExcludeMissing fun _loop(): JsonField = loop + + /** + * Returns the raw JSON value of [ref]. + * + * Unlike [ref], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ref") @ExcludeMissing fun _ref(): JsonField = ref + + /** + * Returns the raw JSON value of [title]. + * + * Unlike [title], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("title") @ExcludeMissing fun _title(): JsonField = title + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @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 [UnionMember1]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember1]. */ + class Builder internal constructor() { + + private var channels: JsonField>? = null + private var if_: JsonField = JsonMissing.of() + private var loop: JsonField = JsonMissing.of() + private var ref: JsonField = JsonMissing.of() + private var title: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember1: UnionMember1) = apply { + channels = unionMember1.channels.map { it.toMutableList() } + if_ = unionMember1.if_ + loop = unionMember1.loop + ref = unionMember1.ref + title = unionMember1.title + type = unionMember1.type + additionalProperties = unionMember1.additionalProperties.toMutableMap() + } + + fun channels(channels: List?) = channels(JsonField.ofNullable(channels)) + + /** Alias for calling [Builder.channels] with `channels.orElse(null)`. */ + fun channels(channels: Optional>) = channels(channels.getOrNull()) + + /** + * Sets [Builder.channels] to an arbitrary JSON value. + * + * You should usually call [Builder.channels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun channels(channels: JsonField>) = apply { + this.channels = channels.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [channels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addChannel(channel: String) = apply { + channels = + (channels ?: JsonField.of(mutableListOf())).also { + checkKnown("channels", it).add(channel) + } + } + + fun if_(if_: String?) = if_(JsonField.ofNullable(if_)) + + /** Alias for calling [Builder.if_] with `if_.orElse(null)`. */ + fun if_(if_: Optional) = if_(if_.getOrNull()) + + /** + * Sets [Builder.if_] to an arbitrary JSON value. + * + * You should usually call [Builder.if_] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun if_(if_: JsonField) = apply { this.if_ = if_ } + + fun loop(loop: String?) = loop(JsonField.ofNullable(loop)) + + /** Alias for calling [Builder.loop] with `loop.orElse(null)`. */ + fun loop(loop: Optional) = loop(loop.getOrNull()) + + /** + * Sets [Builder.loop] to an arbitrary JSON value. + * + * You should usually call [Builder.loop] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun loop(loop: JsonField) = apply { this.loop = loop } + + fun ref(ref: String?) = ref(JsonField.ofNullable(ref)) + + /** Alias for calling [Builder.ref] with `ref.orElse(null)`. */ + fun ref(ref: Optional) = ref(ref.getOrNull()) + + /** + * Sets [Builder.ref] to an arbitrary JSON value. + * + * You should usually call [Builder.ref] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun ref(ref: JsonField) = apply { this.ref = ref } + + /** The title to be displayed by supported channels. For example, the email subject. */ + fun title(title: String?) = title(JsonField.ofNullable(title)) + + /** Alias for calling [Builder.title] with `title.orElse(null)`. */ + fun title(title: Optional) = title(title.getOrNull()) + + /** + * Sets [Builder.title] to an arbitrary JSON value. + * + * You should usually call [Builder.title] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun title(title: JsonField) = apply { this.title = title } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [UnionMember1]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UnionMember1 = + UnionMember1( + (channels ?: JsonMissing.of()).map { it.toImmutable() }, + if_, + loop, + ref, + title, + type, + additionalProperties.toMutableMap(), + ) + } + + 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(): UnionMember1 = apply { + if (validated) { + return@apply + } + + channels() + if_() + loop() + ref() + title() + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CourierInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (channels.asKnown().getOrNull()?.size ?: 0) + + (if (if_.asKnown().isPresent) 1 else 0) + + (if (loop.asKnown().isPresent) 1 else 0) + + (if (ref.asKnown().isPresent) 1 else 0) + + (if (title.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val META = of("meta") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + META + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + META, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + META -> Value.META + 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) { + META -> Known.META + else -> throw CourierInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws 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 + + /** + * 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(): Type = 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 Type && 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 UnionMember1 && + channels == other.channels && + if_ == other.if_ && + loop == other.loop && + ref == other.ref && + title == other.title && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(channels, if_, loop, ref, title, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember1{channels=$channels, if_=$if_, loop=$loop, ref=$ref, title=$title, type=$type, additionalProperties=$additionalProperties}" + } + + /** Used to embed an image into the notification. */ + class UnionMember2 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val channels: JsonField>, + private val if_: JsonField, + private val loop: JsonField, + private val ref: JsonField, + private val src: JsonField, + private val align: JsonField, + private val altText: JsonField, + private val borderColor: JsonField, + private val borderSize: JsonField, + private val href: JsonField, + private val padding: JsonField, + private val width: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("channels") + @ExcludeMissing + channels: JsonField> = JsonMissing.of(), + @JsonProperty("if") @ExcludeMissing if_: JsonField = JsonMissing.of(), + @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), + @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), + @JsonProperty("src") @ExcludeMissing src: JsonField = JsonMissing.of(), + @JsonProperty("align") @ExcludeMissing align: JsonField = JsonMissing.of(), + @JsonProperty("alt_text") @ExcludeMissing altText: JsonField = JsonMissing.of(), + @JsonProperty("border_color") + @ExcludeMissing + borderColor: JsonField = JsonMissing.of(), + @JsonProperty("border_size") + @ExcludeMissing + borderSize: JsonField = JsonMissing.of(), + @JsonProperty("href") @ExcludeMissing href: JsonField = JsonMissing.of(), + @JsonProperty("padding") @ExcludeMissing padding: JsonField = JsonMissing.of(), + @JsonProperty("width") @ExcludeMissing width: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this( + channels, + if_, + loop, + ref, + src, + align, + altText, + borderColor, + borderSize, + href, + padding, + width, + type, + mutableMapOf(), + ) + + fun toElementalImageNode(): ElementalImageNode = + ElementalImageNode.builder() + .channels(channels) + .if_(if_) + .loop(loop) + .ref(ref) + .src(src) + .align(align) + .altText(altText) + .borderColor(borderColor) + .borderSize(borderSize) + .href(href) + .padding(padding) + .width(width) + .build() + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channels(): Optional> = channels.getOptional("channels") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun if_(): Optional = if_.getOptional("if") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun loop(): Optional = loop.getOptional("loop") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun ref(): Optional = ref.getOptional("ref") + + /** + * The source of the image. + * + * @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 src(): String = src.getRequired("src") + + /** + * The alignment of the image. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun align(): Optional = align.getOptional("align") + + /** + * Alternate text for the image. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun altText(): Optional = altText.getOptional("alt_text") + + /** + * CSS border color applied to the image. For example, `#ccc` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun borderColor(): Optional = borderColor.getOptional("border_color") + + /** + * CSS border width applied to the image. For example, `1px` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun borderSize(): Optional = borderSize.getOptional("border_size") + + /** + * A URL to link to when the image is clicked. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun href(): Optional = href.getOptional("href") + + /** + * CSS padding applied around the image. For example, `10px` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun padding(): Optional = padding.getOptional("padding") + + /** + * CSS width properties to apply to the image. For example, 50px + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun width(): Optional = width.getOptional("width") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [channels]. + * + * Unlike [channels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channels") + @ExcludeMissing + fun _channels(): JsonField> = channels + + /** + * Returns the raw JSON value of [if_]. + * + * Unlike [if_], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("if") @ExcludeMissing fun _if_(): JsonField = if_ + + /** + * Returns the raw JSON value of [loop]. + * + * Unlike [loop], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("loop") @ExcludeMissing fun _loop(): JsonField = loop + + /** + * Returns the raw JSON value of [ref]. + * + * Unlike [ref], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ref") @ExcludeMissing fun _ref(): JsonField = ref + + /** + * Returns the raw JSON value of [src]. + * + * Unlike [src], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("src") @ExcludeMissing fun _src(): JsonField = src + + /** + * Returns the raw JSON value of [align]. + * + * Unlike [align], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("align") @ExcludeMissing fun _align(): JsonField = align + + /** + * Returns the raw JSON value of [altText]. + * + * Unlike [altText], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("alt_text") @ExcludeMissing fun _altText(): JsonField = altText + + /** + * Returns the raw JSON value of [borderColor]. + * + * Unlike [borderColor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("border_color") + @ExcludeMissing + fun _borderColor(): JsonField = borderColor + + /** + * Returns the raw JSON value of [borderSize]. + * + * Unlike [borderSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("border_size") + @ExcludeMissing + fun _borderSize(): JsonField = borderSize + + /** + * Returns the raw JSON value of [href]. + * + * Unlike [href], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("href") @ExcludeMissing fun _href(): JsonField = href + + /** + * Returns the raw JSON value of [padding]. + * + * Unlike [padding], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("padding") @ExcludeMissing fun _padding(): JsonField = padding + + /** + * Returns the raw JSON value of [width]. + * + * Unlike [width], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("width") @ExcludeMissing fun _width(): JsonField = width + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @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 [UnionMember2]. + * + * The following fields are required: + * ```java + * .src() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember2]. */ + class Builder internal constructor() { + + private var channels: JsonField>? = null + private var if_: JsonField = JsonMissing.of() + private var loop: JsonField = JsonMissing.of() + private var ref: JsonField = JsonMissing.of() + private var src: JsonField? = null + private var align: JsonField = JsonMissing.of() + private var altText: JsonField = JsonMissing.of() + private var borderColor: JsonField = JsonMissing.of() + private var borderSize: JsonField = JsonMissing.of() + private var href: JsonField = JsonMissing.of() + private var padding: JsonField = JsonMissing.of() + private var width: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember2: UnionMember2) = apply { + channels = unionMember2.channels.map { it.toMutableList() } + if_ = unionMember2.if_ + loop = unionMember2.loop + ref = unionMember2.ref + src = unionMember2.src + align = unionMember2.align + altText = unionMember2.altText + borderColor = unionMember2.borderColor + borderSize = unionMember2.borderSize + href = unionMember2.href + padding = unionMember2.padding + width = unionMember2.width + type = unionMember2.type + additionalProperties = unionMember2.additionalProperties.toMutableMap() + } + + fun channels(channels: List?) = channels(JsonField.ofNullable(channels)) + + /** Alias for calling [Builder.channels] with `channels.orElse(null)`. */ + fun channels(channels: Optional>) = channels(channels.getOrNull()) + + /** + * Sets [Builder.channels] to an arbitrary JSON value. + * + * You should usually call [Builder.channels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun channels(channels: JsonField>) = apply { + this.channels = channels.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [channels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addChannel(channel: String) = apply { + channels = + (channels ?: JsonField.of(mutableListOf())).also { + checkKnown("channels", it).add(channel) + } + } + + fun if_(if_: String?) = if_(JsonField.ofNullable(if_)) + + /** Alias for calling [Builder.if_] with `if_.orElse(null)`. */ + fun if_(if_: Optional) = if_(if_.getOrNull()) + + /** + * Sets [Builder.if_] to an arbitrary JSON value. + * + * You should usually call [Builder.if_] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun if_(if_: JsonField) = apply { this.if_ = if_ } + + fun loop(loop: String?) = loop(JsonField.ofNullable(loop)) + + /** Alias for calling [Builder.loop] with `loop.orElse(null)`. */ + fun loop(loop: Optional) = loop(loop.getOrNull()) + + /** + * Sets [Builder.loop] to an arbitrary JSON value. + * + * You should usually call [Builder.loop] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun loop(loop: JsonField) = apply { this.loop = loop } + + fun ref(ref: String?) = ref(JsonField.ofNullable(ref)) + + /** Alias for calling [Builder.ref] with `ref.orElse(null)`. */ + fun ref(ref: Optional) = ref(ref.getOrNull()) + + /** + * Sets [Builder.ref] to an arbitrary JSON value. + * + * You should usually call [Builder.ref] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun ref(ref: JsonField) = apply { this.ref = ref } + + /** The source of the image. */ + fun src(src: String) = src(JsonField.of(src)) + + /** + * Sets [Builder.src] to an arbitrary JSON value. + * + * You should usually call [Builder.src] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun src(src: JsonField) = apply { this.src = src } + + /** The alignment of the image. */ + fun align(align: Alignment?) = align(JsonField.ofNullable(align)) + + /** Alias for calling [Builder.align] with `align.orElse(null)`. */ + fun align(align: Optional) = align(align.getOrNull()) + + /** + * Sets [Builder.align] to an arbitrary JSON value. + * + * You should usually call [Builder.align] with a well-typed [Alignment] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun align(align: JsonField) = apply { this.align = align } + + /** Alternate text for the image. */ + fun altText(altText: String?) = altText(JsonField.ofNullable(altText)) + + /** Alias for calling [Builder.altText] with `altText.orElse(null)`. */ + fun altText(altText: Optional) = altText(altText.getOrNull()) + + /** + * Sets [Builder.altText] to an arbitrary JSON value. + * + * You should usually call [Builder.altText] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun altText(altText: JsonField) = apply { this.altText = altText } + + /** CSS border color applied to the image. For example, `#ccc` */ + fun borderColor(borderColor: String?) = borderColor(JsonField.ofNullable(borderColor)) + + /** Alias for calling [Builder.borderColor] with `borderColor.orElse(null)`. */ + fun borderColor(borderColor: Optional) = borderColor(borderColor.getOrNull()) + + /** + * Sets [Builder.borderColor] to an arbitrary JSON value. + * + * You should usually call [Builder.borderColor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun borderColor(borderColor: JsonField) = apply { + this.borderColor = borderColor + } + + /** CSS border width applied to the image. For example, `1px` */ + fun borderSize(borderSize: String?) = borderSize(JsonField.ofNullable(borderSize)) + + /** Alias for calling [Builder.borderSize] with `borderSize.orElse(null)`. */ + fun borderSize(borderSize: Optional) = borderSize(borderSize.getOrNull()) + + /** + * Sets [Builder.borderSize] to an arbitrary JSON value. + * + * You should usually call [Builder.borderSize] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun borderSize(borderSize: JsonField) = apply { this.borderSize = borderSize } + + /** A URL to link to when the image is clicked. */ + fun href(href: String?) = href(JsonField.ofNullable(href)) + + /** Alias for calling [Builder.href] with `href.orElse(null)`. */ + fun href(href: Optional) = href(href.getOrNull()) + + /** + * Sets [Builder.href] to an arbitrary JSON value. + * + * You should usually call [Builder.href] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun href(href: JsonField) = apply { this.href = href } + + /** CSS padding applied around the image. For example, `10px` */ + fun padding(padding: String?) = padding(JsonField.ofNullable(padding)) + + /** Alias for calling [Builder.padding] with `padding.orElse(null)`. */ + fun padding(padding: Optional) = padding(padding.getOrNull()) + + /** + * Sets [Builder.padding] to an arbitrary JSON value. + * + * You should usually call [Builder.padding] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun padding(padding: JsonField) = apply { this.padding = padding } + + /** CSS width properties to apply to the image. For example, 50px */ + fun width(width: String?) = width(JsonField.ofNullable(width)) + + /** Alias for calling [Builder.width] with `width.orElse(null)`. */ + fun width(width: Optional) = width(width.getOrNull()) + + /** + * Sets [Builder.width] to an arbitrary JSON value. + * + * You should usually call [Builder.width] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun width(width: JsonField) = apply { this.width = width } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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 [UnionMember2]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .src() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnionMember2 = + UnionMember2( + (channels ?: JsonMissing.of()).map { it.toImmutable() }, + if_, + loop, + ref, + checkRequired("src", src), + align, + altText, + borderColor, + borderSize, + href, + padding, + width, + type, + additionalProperties.toMutableMap(), + ) + } + + 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(): UnionMember2 = apply { + if (validated) { + return@apply + } + + channels() + if_() + loop() + ref() + src() + align().ifPresent { it.validate() } + altText() + borderColor() + borderSize() + href() + padding() + width() + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CourierInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (channels.asKnown().getOrNull()?.size ?: 0) + + (if (if_.asKnown().isPresent) 1 else 0) + + (if (loop.asKnown().isPresent) 1 else 0) + + (if (ref.asKnown().isPresent) 1 else 0) + + (if (src.asKnown().isPresent) 1 else 0) + + (align.asKnown().getOrNull()?.validity() ?: 0) + + (if (altText.asKnown().isPresent) 1 else 0) + + (if (borderColor.asKnown().isPresent) 1 else 0) + + (if (borderSize.asKnown().isPresent) 1 else 0) + + (if (href.asKnown().isPresent) 1 else 0) + + (if (padding.asKnown().isPresent) 1 else 0) + + (if (width.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val IMAGE = of("image") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + IMAGE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + IMAGE, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + IMAGE -> Value.IMAGE + 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) { + IMAGE -> Known.IMAGE + else -> throw CourierInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws 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 + + /** + * 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(): Type = 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 Type && 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 UnionMember2 && + channels == other.channels && + if_ == other.if_ && + loop == other.loop && + ref == other.ref && + src == other.src && + align == other.align && + altText == other.altText && + borderColor == other.borderColor && + borderSize == other.borderSize && + href == other.href && + padding == other.padding && + width == other.width && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + channels, + if_, + loop, + ref, + src, + align, + altText, + borderColor, + borderSize, + href, + padding, + width, + type, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember2{channels=$channels, if_=$if_, loop=$loop, ref=$ref, src=$src, align=$align, altText=$altText, borderColor=$borderColor, borderSize=$borderSize, href=$href, padding=$padding, width=$width, type=$type, additionalProperties=$additionalProperties}" + } + + /** Allows the user to execute an action. Can be a button or a link. */ + class UnionMember3 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val channels: JsonField>, + private val if_: JsonField, + private val loop: JsonField, + private val ref: JsonField, + private val content: JsonField, + private val href: JsonField, + private val actionId: JsonField, + private val align: JsonField, + private val backgroundColor: JsonField, + private val borderRadius: JsonField, + private val borderSize: JsonField, + private val disableTracking: JsonField, + private val fontSize: JsonField, + private val locales: JsonField, + private val padding: JsonField, + private val style: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("channels") + @ExcludeMissing + channels: JsonField> = JsonMissing.of(), + @JsonProperty("if") @ExcludeMissing if_: JsonField = JsonMissing.of(), + @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), + @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("href") @ExcludeMissing href: JsonField = JsonMissing.of(), + @JsonProperty("action_id") + @ExcludeMissing + actionId: JsonField = JsonMissing.of(), + @JsonProperty("align") @ExcludeMissing align: JsonField = JsonMissing.of(), + @JsonProperty("background_color") + @ExcludeMissing + backgroundColor: JsonField = JsonMissing.of(), + @JsonProperty("border_radius") + @ExcludeMissing + borderRadius: JsonField = JsonMissing.of(), + @JsonProperty("border_size") + @ExcludeMissing + borderSize: JsonField = JsonMissing.of(), + @JsonProperty("disable_tracking") + @ExcludeMissing + disableTracking: JsonField = JsonMissing.of(), + @JsonProperty("font_size") + @ExcludeMissing + fontSize: JsonField = JsonMissing.of(), + @JsonProperty("locales") @ExcludeMissing locales: JsonField = JsonMissing.of(), + @JsonProperty("padding") @ExcludeMissing padding: JsonField = JsonMissing.of(), + @JsonProperty("style") + @ExcludeMissing + style: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this( + channels, + if_, + loop, + ref, + content, + href, + actionId, + align, + backgroundColor, + borderRadius, + borderSize, + disableTracking, + fontSize, + locales, + padding, + style, + type, + mutableMapOf(), + ) + + fun toElementalActionNode(): ElementalActionNode = + ElementalActionNode.builder() + .channels(channels) + .if_(if_) + .loop(loop) + .ref(ref) + .content(content) + .href(href) + .actionId(actionId) + .align(align) + .backgroundColor(backgroundColor) + .borderRadius(borderRadius) + .borderSize(borderSize) + .disableTracking(disableTracking) + .fontSize(fontSize) + .locales(locales) + .padding(padding) + .style(style) + .build() + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channels(): Optional> = channels.getOptional("channels") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun if_(): Optional = if_.getOptional("if") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun loop(): Optional = loop.getOptional("loop") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun ref(): Optional = ref.getOptional("ref") + + /** + * The text content of the action shown to the user. + * + * @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 content(): String = content.getRequired("content") + + /** + * The target URL of the action. + * + * @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 href(): String = href.getRequired("href") + + /** + * A unique id used to identify the action when it is executed. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun actionId(): Optional = actionId.getOptional("action_id") + + /** + * The alignment of the action button. Defaults to "center". + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun align(): Optional = align.getOptional("align") + + /** + * The background color of the action button. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun backgroundColor(): Optional = backgroundColor.getOptional("background_color") + + /** + * CSS border-radius applied to the action button. For example, `4px` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun borderRadius(): Optional = borderRadius.getOptional("border_radius") + + /** + * CSS border width applied to the action button. For example, `1px` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun borderSize(): Optional = borderSize.getOptional("border_size") + + /** + * When true, the action's href is not rewritten for click-through tracking, even when + * click-through tracking is enabled for the workspace. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun disableTracking(): Optional = disableTracking.getOptional("disable_tracking") + + /** + * CSS font-size applied to the action button label. For example, `14px` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fontSize(): Optional = fontSize.getOptional("font_size") + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for more + * details. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun locales(): Optional = locales.getOptional("locales") + + /** + * CSS padding applied to the action button. For example, `8px 16px` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun padding(): Optional = padding.getOptional("padding") + + /** + * Defaults to `button`. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun style(): Optional = style.getOptional("style") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [channels]. + * + * Unlike [channels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channels") + @ExcludeMissing + fun _channels(): JsonField> = channels + + /** + * Returns the raw JSON value of [if_]. + * + * Unlike [if_], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("if") @ExcludeMissing fun _if_(): JsonField = if_ + + /** + * Returns the raw JSON value of [loop]. + * + * Unlike [loop], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("loop") @ExcludeMissing fun _loop(): JsonField = loop + + /** + * Returns the raw JSON value of [ref]. + * + * Unlike [ref], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ref") @ExcludeMissing fun _ref(): JsonField = ref + + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [href]. + * + * Unlike [href], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("href") @ExcludeMissing fun _href(): JsonField = href + + /** + * Returns the raw JSON value of [actionId]. + * + * Unlike [actionId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("action_id") @ExcludeMissing fun _actionId(): JsonField = actionId + + /** + * Returns the raw JSON value of [align]. + * + * Unlike [align], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("align") @ExcludeMissing fun _align(): JsonField = align + + /** + * Returns the raw JSON value of [backgroundColor]. + * + * Unlike [backgroundColor], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("background_color") + @ExcludeMissing + fun _backgroundColor(): JsonField = backgroundColor + + /** + * Returns the raw JSON value of [borderRadius]. + * + * Unlike [borderRadius], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("border_radius") + @ExcludeMissing + fun _borderRadius(): JsonField = borderRadius + + /** + * Returns the raw JSON value of [borderSize]. + * + * Unlike [borderSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("border_size") + @ExcludeMissing + fun _borderSize(): JsonField = borderSize + + /** + * Returns the raw JSON value of [disableTracking]. + * + * Unlike [disableTracking], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("disable_tracking") + @ExcludeMissing + fun _disableTracking(): JsonField = disableTracking + + /** + * Returns the raw JSON value of [fontSize]. + * + * Unlike [fontSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("font_size") @ExcludeMissing fun _fontSize(): JsonField = fontSize + + /** + * Returns the raw JSON value of [locales]. + * + * Unlike [locales], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("locales") @ExcludeMissing fun _locales(): JsonField = locales + + /** + * Returns the raw JSON value of [padding]. + * + * Unlike [padding], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("padding") @ExcludeMissing fun _padding(): JsonField = padding + + /** + * Returns the raw JSON value of [style]. + * + * Unlike [style], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("style") + @ExcludeMissing + fun _style(): JsonField = style + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @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 [UnionMember3]. + * + * The following fields are required: + * ```java + * .content() + * .href() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember3]. */ + class Builder internal constructor() { + + private var channels: JsonField>? = null + private var if_: JsonField = JsonMissing.of() + private var loop: JsonField = JsonMissing.of() + private var ref: JsonField = JsonMissing.of() + private var content: JsonField? = null + private var href: JsonField? = null + private var actionId: JsonField = JsonMissing.of() + private var align: JsonField = JsonMissing.of() + private var backgroundColor: JsonField = JsonMissing.of() + private var borderRadius: JsonField = JsonMissing.of() + private var borderSize: JsonField = JsonMissing.of() + private var disableTracking: JsonField = JsonMissing.of() + private var fontSize: JsonField = JsonMissing.of() + private var locales: JsonField = JsonMissing.of() + private var padding: JsonField = JsonMissing.of() + private var style: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember3: UnionMember3) = apply { + channels = unionMember3.channels.map { it.toMutableList() } + if_ = unionMember3.if_ + loop = unionMember3.loop + ref = unionMember3.ref + content = unionMember3.content + href = unionMember3.href + actionId = unionMember3.actionId + align = unionMember3.align + backgroundColor = unionMember3.backgroundColor + borderRadius = unionMember3.borderRadius + borderSize = unionMember3.borderSize + disableTracking = unionMember3.disableTracking + fontSize = unionMember3.fontSize + locales = unionMember3.locales + padding = unionMember3.padding + style = unionMember3.style + type = unionMember3.type + additionalProperties = unionMember3.additionalProperties.toMutableMap() + } + + fun channels(channels: List?) = channels(JsonField.ofNullable(channels)) + + /** Alias for calling [Builder.channels] with `channels.orElse(null)`. */ + fun channels(channels: Optional>) = channels(channels.getOrNull()) + + /** + * Sets [Builder.channels] to an arbitrary JSON value. + * + * You should usually call [Builder.channels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun channels(channels: JsonField>) = apply { + this.channels = channels.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [channels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addChannel(channel: String) = apply { + channels = + (channels ?: JsonField.of(mutableListOf())).also { + checkKnown("channels", it).add(channel) + } + } + + fun if_(if_: String?) = if_(JsonField.ofNullable(if_)) + + /** Alias for calling [Builder.if_] with `if_.orElse(null)`. */ + fun if_(if_: Optional) = if_(if_.getOrNull()) + + /** + * Sets [Builder.if_] to an arbitrary JSON value. + * + * You should usually call [Builder.if_] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun if_(if_: JsonField) = apply { this.if_ = if_ } + + fun loop(loop: String?) = loop(JsonField.ofNullable(loop)) + + /** Alias for calling [Builder.loop] with `loop.orElse(null)`. */ + fun loop(loop: Optional) = loop(loop.getOrNull()) + + /** + * Sets [Builder.loop] to an arbitrary JSON value. + * + * You should usually call [Builder.loop] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun loop(loop: JsonField) = apply { this.loop = loop } + + fun ref(ref: String?) = ref(JsonField.ofNullable(ref)) + + /** Alias for calling [Builder.ref] with `ref.orElse(null)`. */ + fun ref(ref: Optional) = ref(ref.getOrNull()) + + /** + * Sets [Builder.ref] to an arbitrary JSON value. + * + * You should usually call [Builder.ref] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun ref(ref: JsonField) = apply { this.ref = ref } + + /** The text content of the action shown to the user. */ + fun content(content: String) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** The target URL of the action. */ + fun href(href: String) = href(JsonField.of(href)) + + /** + * Sets [Builder.href] to an arbitrary JSON value. + * + * You should usually call [Builder.href] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun href(href: JsonField) = apply { this.href = href } + + /** A unique id used to identify the action when it is executed. */ + fun actionId(actionId: String?) = actionId(JsonField.ofNullable(actionId)) + + /** Alias for calling [Builder.actionId] with `actionId.orElse(null)`. */ + fun actionId(actionId: Optional) = actionId(actionId.getOrNull()) + + /** + * Sets [Builder.actionId] to an arbitrary JSON value. + * + * You should usually call [Builder.actionId] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun actionId(actionId: JsonField) = apply { this.actionId = actionId } + + /** The alignment of the action button. Defaults to "center". */ + fun align(align: Alignment?) = align(JsonField.ofNullable(align)) + + /** Alias for calling [Builder.align] with `align.orElse(null)`. */ + fun align(align: Optional) = align(align.getOrNull()) + + /** + * Sets [Builder.align] to an arbitrary JSON value. + * + * You should usually call [Builder.align] with a well-typed [Alignment] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun align(align: JsonField) = apply { this.align = align } + + /** The background color of the action button. */ + fun backgroundColor(backgroundColor: String?) = + backgroundColor(JsonField.ofNullable(backgroundColor)) + + /** Alias for calling [Builder.backgroundColor] with `backgroundColor.orElse(null)`. */ + fun backgroundColor(backgroundColor: Optional) = + backgroundColor(backgroundColor.getOrNull()) + + /** + * Sets [Builder.backgroundColor] to an arbitrary JSON value. + * + * You should usually call [Builder.backgroundColor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun backgroundColor(backgroundColor: JsonField) = apply { + this.backgroundColor = backgroundColor + } + + /** CSS border-radius applied to the action button. For example, `4px` */ + fun borderRadius(borderRadius: String?) = + borderRadius(JsonField.ofNullable(borderRadius)) + + /** Alias for calling [Builder.borderRadius] with `borderRadius.orElse(null)`. */ + fun borderRadius(borderRadius: Optional) = + borderRadius(borderRadius.getOrNull()) + + /** + * Sets [Builder.borderRadius] to an arbitrary JSON value. + * + * You should usually call [Builder.borderRadius] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun borderRadius(borderRadius: JsonField) = apply { + this.borderRadius = borderRadius + } + + /** CSS border width applied to the action button. For example, `1px` */ + fun borderSize(borderSize: String?) = borderSize(JsonField.ofNullable(borderSize)) + + /** Alias for calling [Builder.borderSize] with `borderSize.orElse(null)`. */ + fun borderSize(borderSize: Optional) = borderSize(borderSize.getOrNull()) + + /** + * Sets [Builder.borderSize] to an arbitrary JSON value. + * + * You should usually call [Builder.borderSize] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun borderSize(borderSize: JsonField) = apply { this.borderSize = borderSize } + + /** + * When true, the action's href is not rewritten for click-through tracking, even when + * click-through tracking is enabled for the workspace. + */ + fun disableTracking(disableTracking: Boolean?) = + disableTracking(JsonField.ofNullable(disableTracking)) + + /** + * Alias for [Builder.disableTracking]. + * + * This unboxed primitive overload exists for backwards compatibility. + */ + fun disableTracking(disableTracking: Boolean) = + disableTracking(disableTracking as Boolean?) + + /** Alias for calling [Builder.disableTracking] with `disableTracking.orElse(null)`. */ + fun disableTracking(disableTracking: Optional) = + disableTracking(disableTracking.getOrNull()) + + /** + * Sets [Builder.disableTracking] to an arbitrary JSON value. + * + * You should usually call [Builder.disableTracking] with a well-typed [Boolean] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun disableTracking(disableTracking: JsonField) = apply { + this.disableTracking = disableTracking + } + + /** CSS font-size applied to the action button label. For example, `14px` */ + fun fontSize(fontSize: String?) = fontSize(JsonField.ofNullable(fontSize)) + + /** Alias for calling [Builder.fontSize] with `fontSize.orElse(null)`. */ + fun fontSize(fontSize: Optional) = fontSize(fontSize.getOrNull()) + + /** + * Sets [Builder.fontSize] to an arbitrary JSON value. + * + * You should usually call [Builder.fontSize] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fontSize(fontSize: JsonField) = apply { this.fontSize = fontSize } + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for + * more details. + */ + fun locales(locales: Locales?) = locales(JsonField.ofNullable(locales)) + + /** Alias for calling [Builder.locales] with `locales.orElse(null)`. */ + fun locales(locales: Optional) = locales(locales.getOrNull()) + + /** + * Sets [Builder.locales] to an arbitrary JSON value. + * + * You should usually call [Builder.locales] with a well-typed [Locales] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun locales(locales: JsonField) = apply { this.locales = locales } + + /** CSS padding applied to the action button. For example, `8px 16px` */ + fun padding(padding: String?) = padding(JsonField.ofNullable(padding)) + + /** Alias for calling [Builder.padding] with `padding.orElse(null)`. */ + fun padding(padding: Optional) = padding(padding.getOrNull()) + + /** + * Sets [Builder.padding] to an arbitrary JSON value. + * + * You should usually call [Builder.padding] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun padding(padding: JsonField) = apply { this.padding = padding } + + /** Defaults to `button`. */ + fun style(style: ElementalActionNode.Style?) = style(JsonField.ofNullable(style)) + + /** Alias for calling [Builder.style] with `style.orElse(null)`. */ + fun style(style: Optional) = style(style.getOrNull()) + + /** + * Sets [Builder.style] to an arbitrary JSON value. + * + * You should usually call [Builder.style] with a well-typed [ElementalActionNode.Style] + * value instead. This method is primarily for setting the field to an undocumented or + * not yet supported value. + */ + fun style(style: JsonField) = apply { this.style = style } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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 [UnionMember3]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * .href() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnionMember3 = + UnionMember3( + (channels ?: JsonMissing.of()).map { it.toImmutable() }, + if_, + loop, + ref, + checkRequired("content", content), + checkRequired("href", href), + actionId, + align, + backgroundColor, + borderRadius, + borderSize, + disableTracking, + fontSize, + locales, + padding, + style, + type, + additionalProperties.toMutableMap(), + ) + } + + 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(): UnionMember3 = apply { + if (validated) { + return@apply + } + + channels() + if_() + loop() + ref() + content() + href() + actionId() + align().ifPresent { it.validate() } + backgroundColor() + borderRadius() + borderSize() + disableTracking() + fontSize() + locales().ifPresent { it.validate() } + padding() + style().ifPresent { it.validate() } + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CourierInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (channels.asKnown().getOrNull()?.size ?: 0) + + (if (if_.asKnown().isPresent) 1 else 0) + + (if (loop.asKnown().isPresent) 1 else 0) + + (if (ref.asKnown().isPresent) 1 else 0) + + (if (content.asKnown().isPresent) 1 else 0) + + (if (href.asKnown().isPresent) 1 else 0) + + (if (actionId.asKnown().isPresent) 1 else 0) + + (align.asKnown().getOrNull()?.validity() ?: 0) + + (if (backgroundColor.asKnown().isPresent) 1 else 0) + + (if (borderRadius.asKnown().isPresent) 1 else 0) + + (if (borderSize.asKnown().isPresent) 1 else 0) + + (if (disableTracking.asKnown().isPresent) 1 else 0) + + (if (fontSize.asKnown().isPresent) 1 else 0) + + (locales.asKnown().getOrNull()?.validity() ?: 0) + + (if (padding.asKnown().isPresent) 1 else 0) + + (style.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val ACTION = of("action") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + ACTION + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + ACTION, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + ACTION -> Value.ACTION + 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) { + ACTION -> Known.ACTION + else -> throw CourierInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws 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 + + /** + * 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(): Type = 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 Type && 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 UnionMember3 && + channels == other.channels && + if_ == other.if_ && + loop == other.loop && + ref == other.ref && + content == other.content && + href == other.href && + actionId == other.actionId && + align == other.align && + backgroundColor == other.backgroundColor && + borderRadius == other.borderRadius && + borderSize == other.borderSize && + disableTracking == other.disableTracking && + fontSize == other.fontSize && + locales == other.locales && + padding == other.padding && + style == other.style && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + channels, + if_, + loop, + ref, + content, + href, + actionId, + align, + backgroundColor, + borderRadius, + borderSize, + disableTracking, + fontSize, + locales, + padding, + style, + type, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember3{channels=$channels, if_=$if_, loop=$loop, ref=$ref, content=$content, href=$href, actionId=$actionId, align=$align, backgroundColor=$backgroundColor, borderRadius=$borderRadius, borderSize=$borderSize, disableTracking=$disableTracking, fontSize=$fontSize, locales=$locales, padding=$padding, style=$style, type=$type, additionalProperties=$additionalProperties}" + } + + /** Renders a dividing line between elements. */ + class UnionMember4 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val channels: JsonField>, + private val if_: JsonField, + private val loop: JsonField, + private val ref: JsonField, + private val color: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("channels") + @ExcludeMissing + channels: JsonField> = JsonMissing.of(), + @JsonProperty("if") @ExcludeMissing if_: JsonField = JsonMissing.of(), + @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), + @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), + @JsonProperty("color") @ExcludeMissing color: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(channels, if_, loop, ref, color, type, mutableMapOf()) + + fun toElementalDividerNode(): ElementalDividerNode = + ElementalDividerNode.builder() + .channels(channels) + .if_(if_) + .loop(loop) + .ref(ref) + .color(color) + .build() + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channels(): Optional> = channels.getOptional("channels") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun if_(): Optional = if_.getOptional("if") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun loop(): Optional = loop.getOptional("loop") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun ref(): Optional = ref.getOptional("ref") + + /** + * The CSS color to render the line with. For example, `#fff` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun color(): Optional = color.getOptional("color") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [channels]. + * + * Unlike [channels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channels") + @ExcludeMissing + fun _channels(): JsonField> = channels + + /** + * Returns the raw JSON value of [if_]. + * + * Unlike [if_], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("if") @ExcludeMissing fun _if_(): JsonField = if_ + + /** + * Returns the raw JSON value of [loop]. + * + * Unlike [loop], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("loop") @ExcludeMissing fun _loop(): JsonField = loop + + /** + * Returns the raw JSON value of [ref]. + * + * Unlike [ref], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ref") @ExcludeMissing fun _ref(): JsonField = ref + + /** + * Returns the raw JSON value of [color]. + * + * Unlike [color], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("color") @ExcludeMissing fun _color(): JsonField = color + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @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 [UnionMember4]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember4]. */ + class Builder internal constructor() { + + private var channels: JsonField>? = null + private var if_: JsonField = JsonMissing.of() + private var loop: JsonField = JsonMissing.of() + private var ref: JsonField = JsonMissing.of() + private var color: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember4: UnionMember4) = apply { + channels = unionMember4.channels.map { it.toMutableList() } + if_ = unionMember4.if_ + loop = unionMember4.loop + ref = unionMember4.ref + color = unionMember4.color + type = unionMember4.type + additionalProperties = unionMember4.additionalProperties.toMutableMap() + } + + fun channels(channels: List?) = channels(JsonField.ofNullable(channels)) + + /** Alias for calling [Builder.channels] with `channels.orElse(null)`. */ + fun channels(channels: Optional>) = channels(channels.getOrNull()) + + /** + * Sets [Builder.channels] to an arbitrary JSON value. + * + * You should usually call [Builder.channels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun channels(channels: JsonField>) = apply { + this.channels = channels.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [channels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addChannel(channel: String) = apply { + channels = + (channels ?: JsonField.of(mutableListOf())).also { + checkKnown("channels", it).add(channel) + } + } + + fun if_(if_: String?) = if_(JsonField.ofNullable(if_)) + + /** Alias for calling [Builder.if_] with `if_.orElse(null)`. */ + fun if_(if_: Optional) = if_(if_.getOrNull()) + + /** + * Sets [Builder.if_] to an arbitrary JSON value. + * + * You should usually call [Builder.if_] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun if_(if_: JsonField) = apply { this.if_ = if_ } + + fun loop(loop: String?) = loop(JsonField.ofNullable(loop)) + + /** Alias for calling [Builder.loop] with `loop.orElse(null)`. */ + fun loop(loop: Optional) = loop(loop.getOrNull()) + + /** + * Sets [Builder.loop] to an arbitrary JSON value. + * + * You should usually call [Builder.loop] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun loop(loop: JsonField) = apply { this.loop = loop } + + fun ref(ref: String?) = ref(JsonField.ofNullable(ref)) + + /** Alias for calling [Builder.ref] with `ref.orElse(null)`. */ + fun ref(ref: Optional) = ref(ref.getOrNull()) + + /** + * Sets [Builder.ref] to an arbitrary JSON value. + * + * You should usually call [Builder.ref] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun ref(ref: JsonField) = apply { this.ref = ref } + + /** The CSS color to render the line with. For example, `#fff` */ + fun color(color: String?) = color(JsonField.ofNullable(color)) + + /** Alias for calling [Builder.color] with `color.orElse(null)`. */ + fun color(color: Optional) = color(color.getOrNull()) + + /** + * Sets [Builder.color] to an arbitrary JSON value. + * + * You should usually call [Builder.color] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun color(color: JsonField) = apply { this.color = color } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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 [UnionMember4]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): UnionMember4 = + UnionMember4( + (channels ?: JsonMissing.of()).map { it.toImmutable() }, + if_, + loop, + ref, + color, + type, + additionalProperties.toMutableMap(), + ) + } + + 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(): UnionMember4 = apply { + if (validated) { + return@apply + } + + channels() + if_() + loop() + ref() + color() + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CourierInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (channels.asKnown().getOrNull()?.size ?: 0) + + (if (if_.asKnown().isPresent) 1 else 0) + + (if (loop.asKnown().isPresent) 1 else 0) + + (if (ref.asKnown().isPresent) 1 else 0) + + (if (color.asKnown().isPresent) 1 else 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val DIVIDER = of("divider") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + DIVIDER + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + DIVIDER, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + DIVIDER -> Value.DIVIDER + 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) { + DIVIDER -> Known.DIVIDER + else -> throw CourierInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws 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 + + /** + * 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(): Type = 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 Type && 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 UnionMember4 && + channels == other.channels && + if_ == other.if_ && + loop == other.loop && + ref == other.ref && + color == other.color && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(channels, if_, loop, ref, color, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember4{channels=$channels, if_=$if_, loop=$loop, ref=$ref, color=$color, type=$type, additionalProperties=$additionalProperties}" + } + + /** Renders a quote block. */ + class UnionMember5 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val channels: JsonField>, + private val if_: JsonField, + private val loop: JsonField, + private val ref: JsonField, + private val content: JsonField, + private val align: JsonField, + private val borderColor: JsonField, + private val fontSize: JsonField, + private val lineHeight: JsonField, + private val locales: JsonField, + private val textStyle: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("channels") + @ExcludeMissing + channels: JsonField> = JsonMissing.of(), + @JsonProperty("if") @ExcludeMissing if_: JsonField = JsonMissing.of(), + @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), + @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("align") @ExcludeMissing align: JsonField = JsonMissing.of(), + @JsonProperty("border_color") + @ExcludeMissing + borderColor: JsonField = JsonMissing.of(), + @JsonProperty("font_size") + @ExcludeMissing + fontSize: JsonField = JsonMissing.of(), + @JsonProperty("line_height") + @ExcludeMissing + lineHeight: JsonField = JsonMissing.of(), + @JsonProperty("locales") @ExcludeMissing locales: JsonField = JsonMissing.of(), + @JsonProperty("text_style") + @ExcludeMissing + textStyle: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this( + channels, + if_, + loop, + ref, + content, + align, + borderColor, + fontSize, + lineHeight, + locales, + textStyle, + type, + mutableMapOf(), + ) + + fun toElementalQuoteNode(): ElementalQuoteNode = + ElementalQuoteNode.builder() + .channels(channels) + .if_(if_) + .loop(loop) + .ref(ref) + .content(content) + .align(align) + .borderColor(borderColor) + .fontSize(fontSize) + .lineHeight(lineHeight) + .locales(locales) + .textStyle(textStyle) + .build() + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channels(): Optional> = channels.getOptional("channels") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun if_(): Optional = if_.getOptional("if") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun loop(): Optional = loop.getOptional("loop") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun ref(): Optional = ref.getOptional("ref") + + /** + * The text value of the quote. + * + * @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 content(): String = content.getRequired("content") + + /** + * Alignment of the quote. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun align(): Optional = align.getOptional("align") + + /** + * CSS border color property. For example, `#fff` + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun borderColor(): Optional = borderColor.getOptional("border_color") + + /** + * CSS px font size for this quote block, e.g. `16px`. Overrides the size of the + * `text_style` preset. Email only. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fontSize(): Optional = fontSize.getOptional("font_size") + + /** + * CSS line height for this quote block, as a px value or a unitless multiplier, e.g. `24px` + * or `1.5`. Email only. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lineHeight(): Optional = lineHeight.getOptional("line_height") + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for more + * details. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun locales(): Optional = locales.getOptional("locales") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun textStyle(): Optional = textStyle.getOptional("text_style") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [channels]. + * + * Unlike [channels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channels") + @ExcludeMissing + fun _channels(): JsonField> = channels + + /** + * Returns the raw JSON value of [if_]. + * + * Unlike [if_], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("if") @ExcludeMissing fun _if_(): JsonField = if_ + + /** + * Returns the raw JSON value of [loop]. + * + * Unlike [loop], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("loop") @ExcludeMissing fun _loop(): JsonField = loop + + /** + * Returns the raw JSON value of [ref]. + * + * Unlike [ref], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ref") @ExcludeMissing fun _ref(): JsonField = ref + + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [align]. + * + * Unlike [align], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("align") @ExcludeMissing fun _align(): JsonField = align + + /** + * Returns the raw JSON value of [borderColor]. + * + * Unlike [borderColor], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("border_color") + @ExcludeMissing + fun _borderColor(): JsonField = borderColor + + /** + * Returns the raw JSON value of [fontSize]. + * + * Unlike [fontSize], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("font_size") @ExcludeMissing fun _fontSize(): JsonField = fontSize + + /** + * Returns the raw JSON value of [lineHeight]. + * + * Unlike [lineHeight], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("line_height") + @ExcludeMissing + fun _lineHeight(): JsonField = lineHeight + + /** + * Returns the raw JSON value of [locales]. + * + * Unlike [locales], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("locales") @ExcludeMissing fun _locales(): JsonField = locales + + /** + * Returns the raw JSON value of [textStyle]. + * + * Unlike [textStyle], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("text_style") + @ExcludeMissing + fun _textStyle(): JsonField = textStyle + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @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 [UnionMember5]. + * + * The following fields are required: + * ```java + * .content() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember5]. */ + class Builder internal constructor() { + + private var channels: JsonField>? = null + private var if_: JsonField = JsonMissing.of() + private var loop: JsonField = JsonMissing.of() + private var ref: JsonField = JsonMissing.of() + private var content: JsonField? = null + private var align: JsonField = JsonMissing.of() + private var borderColor: JsonField = JsonMissing.of() + private var fontSize: JsonField = JsonMissing.of() + private var lineHeight: JsonField = JsonMissing.of() + private var locales: JsonField = JsonMissing.of() + private var textStyle: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember5: UnionMember5) = apply { + channels = unionMember5.channels.map { it.toMutableList() } + if_ = unionMember5.if_ + loop = unionMember5.loop + ref = unionMember5.ref + content = unionMember5.content + align = unionMember5.align + borderColor = unionMember5.borderColor + fontSize = unionMember5.fontSize + lineHeight = unionMember5.lineHeight + locales = unionMember5.locales + textStyle = unionMember5.textStyle + type = unionMember5.type + additionalProperties = unionMember5.additionalProperties.toMutableMap() + } + + fun channels(channels: List?) = channels(JsonField.ofNullable(channels)) + + /** Alias for calling [Builder.channels] with `channels.orElse(null)`. */ + fun channels(channels: Optional>) = channels(channels.getOrNull()) + + /** + * Sets [Builder.channels] to an arbitrary JSON value. + * + * You should usually call [Builder.channels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun channels(channels: JsonField>) = apply { + this.channels = channels.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [channels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addChannel(channel: String) = apply { + channels = + (channels ?: JsonField.of(mutableListOf())).also { + checkKnown("channels", it).add(channel) + } + } + + fun if_(if_: String?) = if_(JsonField.ofNullable(if_)) + + /** Alias for calling [Builder.if_] with `if_.orElse(null)`. */ + fun if_(if_: Optional) = if_(if_.getOrNull()) + + /** + * Sets [Builder.if_] to an arbitrary JSON value. + * + * You should usually call [Builder.if_] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun if_(if_: JsonField) = apply { this.if_ = if_ } + + fun loop(loop: String?) = loop(JsonField.ofNullable(loop)) + + /** Alias for calling [Builder.loop] with `loop.orElse(null)`. */ + fun loop(loop: Optional) = loop(loop.getOrNull()) + + /** + * Sets [Builder.loop] to an arbitrary JSON value. + * + * You should usually call [Builder.loop] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun loop(loop: JsonField) = apply { this.loop = loop } + + fun ref(ref: String?) = ref(JsonField.ofNullable(ref)) + + /** Alias for calling [Builder.ref] with `ref.orElse(null)`. */ + fun ref(ref: Optional) = ref(ref.getOrNull()) + + /** + * Sets [Builder.ref] to an arbitrary JSON value. + * + * You should usually call [Builder.ref] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun ref(ref: JsonField) = apply { this.ref = ref } + + /** The text value of the quote. */ + fun content(content: String) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** Alignment of the quote. */ + fun align(align: Alignment?) = align(JsonField.ofNullable(align)) + + /** Alias for calling [Builder.align] with `align.orElse(null)`. */ + fun align(align: Optional) = align(align.getOrNull()) + + /** + * Sets [Builder.align] to an arbitrary JSON value. + * + * You should usually call [Builder.align] with a well-typed [Alignment] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun align(align: JsonField) = apply { this.align = align } + + /** CSS border color property. For example, `#fff` */ + fun borderColor(borderColor: String?) = borderColor(JsonField.ofNullable(borderColor)) + + /** Alias for calling [Builder.borderColor] with `borderColor.orElse(null)`. */ + fun borderColor(borderColor: Optional) = borderColor(borderColor.getOrNull()) + + /** + * Sets [Builder.borderColor] to an arbitrary JSON value. + * + * You should usually call [Builder.borderColor] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun borderColor(borderColor: JsonField) = apply { + this.borderColor = borderColor + } + + /** + * CSS px font size for this quote block, e.g. `16px`. Overrides the size of the + * `text_style` preset. Email only. + */ + fun fontSize(fontSize: String?) = fontSize(JsonField.ofNullable(fontSize)) + + /** Alias for calling [Builder.fontSize] with `fontSize.orElse(null)`. */ + fun fontSize(fontSize: Optional) = fontSize(fontSize.getOrNull()) + + /** + * Sets [Builder.fontSize] to an arbitrary JSON value. + * + * You should usually call [Builder.fontSize] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun fontSize(fontSize: JsonField) = apply { this.fontSize = fontSize } + + /** + * CSS line height for this quote block, as a px value or a unitless multiplier, e.g. + * `24px` or `1.5`. Email only. + */ + fun lineHeight(lineHeight: String?) = lineHeight(JsonField.ofNullable(lineHeight)) + + /** Alias for calling [Builder.lineHeight] with `lineHeight.orElse(null)`. */ + fun lineHeight(lineHeight: Optional) = lineHeight(lineHeight.getOrNull()) + + /** + * Sets [Builder.lineHeight] to an arbitrary JSON value. + * + * You should usually call [Builder.lineHeight] with a well-typed [String] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lineHeight(lineHeight: JsonField) = apply { this.lineHeight = lineHeight } + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for + * more details. + */ + fun locales(locales: Locales?) = locales(JsonField.ofNullable(locales)) + + /** Alias for calling [Builder.locales] with `locales.orElse(null)`. */ + fun locales(locales: Optional) = locales(locales.getOrNull()) + + /** + * Sets [Builder.locales] to an arbitrary JSON value. + * + * You should usually call [Builder.locales] with a well-typed [Locales] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun locales(locales: JsonField) = apply { this.locales = locales } + + fun textStyle(textStyle: TextStyle) = textStyle(JsonField.of(textStyle)) + + /** + * Sets [Builder.textStyle] to an arbitrary JSON value. + * + * You should usually call [Builder.textStyle] with a well-typed [TextStyle] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun textStyle(textStyle: JsonField) = apply { this.textStyle = textStyle } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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 [UnionMember5]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnionMember5 = + UnionMember5( + (channels ?: JsonMissing.of()).map { it.toImmutable() }, + if_, + loop, + ref, + checkRequired("content", content), + align, + borderColor, + fontSize, + lineHeight, + locales, + textStyle, + type, + additionalProperties.toMutableMap(), + ) + } + + 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(): UnionMember5 = apply { + if (validated) { + return@apply + } + + channels() + if_() + loop() + ref() + content() + align().ifPresent { it.validate() } + borderColor() + fontSize() + lineHeight() + locales().ifPresent { it.validate() } + textStyle().ifPresent { it.validate() } + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CourierInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (channels.asKnown().getOrNull()?.size ?: 0) + + (if (if_.asKnown().isPresent) 1 else 0) + + (if (loop.asKnown().isPresent) 1 else 0) + + (if (ref.asKnown().isPresent) 1 else 0) + + (if (content.asKnown().isPresent) 1 else 0) + + (align.asKnown().getOrNull()?.validity() ?: 0) + + (if (borderColor.asKnown().isPresent) 1 else 0) + + (if (fontSize.asKnown().isPresent) 1 else 0) + + (if (lineHeight.asKnown().isPresent) 1 else 0) + + (locales.asKnown().getOrNull()?.validity() ?: 0) + + (textStyle.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val QUOTE = of("quote") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + QUOTE + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + QUOTE, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + QUOTE -> Value.QUOTE + 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) { + QUOTE -> Known.QUOTE + else -> throw CourierInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws 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 + + /** + * 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(): Type = 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 Type && 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 UnionMember5 && + channels == other.channels && + if_ == other.if_ && + loop == other.loop && + ref == other.ref && + content == other.content && + align == other.align && + borderColor == other.borderColor && + fontSize == other.fontSize && + lineHeight == other.lineHeight && + locales == other.locales && + textStyle == other.textStyle && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + channels, + if_, + loop, + ref, + content, + align, + borderColor, + fontSize, + lineHeight, + locales, + textStyle, + type, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember5{channels=$channels, if_=$if_, loop=$loop, ref=$ref, content=$content, align=$align, borderColor=$borderColor, fontSize=$fontSize, lineHeight=$lineHeight, locales=$locales, textStyle=$textStyle, type=$type, additionalProperties=$additionalProperties}" + } + + /** + * Raw HTML string inside an Elemental document. When rendering a message, this node is turned + * into output only for the email channel; for other channels it produces no blocks. + */ + class UnionMember6 + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val channels: JsonField>, + private val if_: JsonField, + private val loop: JsonField, + private val ref: JsonField, + private val content: JsonField, + private val locales: JsonField, + private val type: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("channels") + @ExcludeMissing + channels: JsonField> = JsonMissing.of(), + @JsonProperty("if") @ExcludeMissing if_: JsonField = JsonMissing.of(), + @JsonProperty("loop") @ExcludeMissing loop: JsonField = JsonMissing.of(), + @JsonProperty("ref") @ExcludeMissing ref: JsonField = JsonMissing.of(), + @JsonProperty("content") @ExcludeMissing content: JsonField = JsonMissing.of(), + @JsonProperty("locales") @ExcludeMissing locales: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonField = JsonMissing.of(), + ) : this(channels, if_, loop, ref, content, locales, type, mutableMapOf()) + + fun toElementalHtmlNode(): ElementalHtmlNode = + ElementalHtmlNode.builder() + .channels(channels) + .if_(if_) + .loop(loop) + .ref(ref) + .content(content) + .locales(locales) + .build() + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun channels(): Optional> = channels.getOptional("channels") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun if_(): Optional = if_.getOptional("if") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun loop(): Optional = loop.getOptional("loop") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun ref(): Optional = ref.getOptional("ref") + + /** + * Raw HTML string to render inside the notification. + * + * @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 content(): String = content.getRequired("content") + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for more + * details. + * + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun locales(): Optional = locales.getOptional("locales") + + /** + * @throws CourierInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun type(): Optional = type.getOptional("type") + + /** + * Returns the raw JSON value of [channels]. + * + * Unlike [channels], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("channels") + @ExcludeMissing + fun _channels(): JsonField> = channels + + /** + * Returns the raw JSON value of [if_]. + * + * Unlike [if_], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("if") @ExcludeMissing fun _if_(): JsonField = if_ + + /** + * Returns the raw JSON value of [loop]. + * + * Unlike [loop], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("loop") @ExcludeMissing fun _loop(): JsonField = loop + + /** + * Returns the raw JSON value of [ref]. + * + * Unlike [ref], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("ref") @ExcludeMissing fun _ref(): JsonField = ref + + /** + * Returns the raw JSON value of [content]. + * + * Unlike [content], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("content") @ExcludeMissing fun _content(): JsonField = content + + /** + * Returns the raw JSON value of [locales]. + * + * Unlike [locales], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("locales") @ExcludeMissing fun _locales(): JsonField = locales + + /** + * Returns the raw JSON value of [type]. + * + * Unlike [type], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonField = type + + @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 [UnionMember6]. + * + * The following fields are required: + * ```java + * .content() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [UnionMember6]. */ + class Builder internal constructor() { + + private var channels: JsonField>? = null + private var if_: JsonField = JsonMissing.of() + private var loop: JsonField = JsonMissing.of() + private var ref: JsonField = JsonMissing.of() + private var content: JsonField? = null + private var locales: JsonField = JsonMissing.of() + private var type: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(unionMember6: UnionMember6) = apply { + channels = unionMember6.channels.map { it.toMutableList() } + if_ = unionMember6.if_ + loop = unionMember6.loop + ref = unionMember6.ref + content = unionMember6.content + locales = unionMember6.locales + type = unionMember6.type + additionalProperties = unionMember6.additionalProperties.toMutableMap() + } + + fun channels(channels: List?) = channels(JsonField.ofNullable(channels)) + + /** Alias for calling [Builder.channels] with `channels.orElse(null)`. */ + fun channels(channels: Optional>) = channels(channels.getOrNull()) + + /** + * Sets [Builder.channels] to an arbitrary JSON value. + * + * You should usually call [Builder.channels] with a well-typed `List` value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun channels(channels: JsonField>) = apply { + this.channels = channels.map { it.toMutableList() } + } + + /** + * Adds a single [String] to [channels]. + * + * @throws IllegalStateException if the field was previously set to a non-list. + */ + fun addChannel(channel: String) = apply { + channels = + (channels ?: JsonField.of(mutableListOf())).also { + checkKnown("channels", it).add(channel) + } + } + + fun if_(if_: String?) = if_(JsonField.ofNullable(if_)) + + /** Alias for calling [Builder.if_] with `if_.orElse(null)`. */ + fun if_(if_: Optional) = if_(if_.getOrNull()) + + /** + * Sets [Builder.if_] to an arbitrary JSON value. + * + * You should usually call [Builder.if_] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun if_(if_: JsonField) = apply { this.if_ = if_ } + + fun loop(loop: String?) = loop(JsonField.ofNullable(loop)) + + /** Alias for calling [Builder.loop] with `loop.orElse(null)`. */ + fun loop(loop: Optional) = loop(loop.getOrNull()) + + /** + * Sets [Builder.loop] to an arbitrary JSON value. + * + * You should usually call [Builder.loop] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun loop(loop: JsonField) = apply { this.loop = loop } + + fun ref(ref: String?) = ref(JsonField.ofNullable(ref)) + + /** Alias for calling [Builder.ref] with `ref.orElse(null)`. */ + fun ref(ref: Optional) = ref(ref.getOrNull()) + + /** + * Sets [Builder.ref] to an arbitrary JSON value. + * + * You should usually call [Builder.ref] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun ref(ref: JsonField) = apply { this.ref = ref } + + /** Raw HTML string to render inside the notification. */ + fun content(content: String) = content(JsonField.of(content)) + + /** + * Sets [Builder.content] to an arbitrary JSON value. + * + * You should usually call [Builder.content] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun content(content: JsonField) = apply { this.content = content } + + /** + * Region specific content. See + * [locales docs](https://www.courier.com/docs/platform/content/elemental/locales/) for + * more details. + */ + fun locales(locales: Locales?) = locales(JsonField.ofNullable(locales)) + + /** Alias for calling [Builder.locales] with `locales.orElse(null)`. */ + fun locales(locales: Optional) = locales(locales.getOrNull()) + + /** + * Sets [Builder.locales] to an arbitrary JSON value. + * + * You should usually call [Builder.locales] with a well-typed [Locales] value instead. + * This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun locales(locales: JsonField) = apply { this.locales = locales } + + fun type(type: Type) = type(JsonField.of(type)) + + /** + * Sets [Builder.type] to an arbitrary JSON value. + * + * You should usually call [Builder.type] with a well-typed [Type] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonField) = apply { this.type = type } + + 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 [UnionMember6]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .content() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): UnionMember6 = + UnionMember6( + (channels ?: JsonMissing.of()).map { it.toImmutable() }, + if_, + loop, + ref, + checkRequired("content", content), + locales, + type, + additionalProperties.toMutableMap(), + ) + } + + 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(): UnionMember6 = apply { + if (validated) { + return@apply + } + + channels() + if_() + loop() + ref() + content() + locales().ifPresent { it.validate() } + type().ifPresent { it.validate() } + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: CourierInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (channels.asKnown().getOrNull()?.size ?: 0) + + (if (if_.asKnown().isPresent) 1 else 0) + + (if (loop.asKnown().isPresent) 1 else 0) + + (if (ref.asKnown().isPresent) 1 else 0) + + (if (content.asKnown().isPresent) 1 else 0) + + (locales.asKnown().getOrNull()?.validity() ?: 0) + + (type.asKnown().getOrNull()?.validity() ?: 0) + + class Type @JsonCreator private constructor(private val value: JsonField) : Enum { + + /** + * Returns this class instance's raw value. + * + * This is usually only useful if this instance was deserialized from data that doesn't + * match any known member, and you want to know that value. For example, if the SDK is + * on an older version than the API, then the API may respond with new members that the + * SDK is unaware of. + */ + @com.fasterxml.jackson.annotation.JsonValue fun _value(): JsonField = value + + companion object { + + @JvmField val HTML = of("html") + + @JvmStatic fun of(value: String) = Type(JsonField.of(value)) + } + + /** An enum containing [Type]'s known values. */ + enum class Known { + HTML + } + + /** + * An enum containing [Type]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Type] can contain an unknown value in a couple of cases: + * - It was deserialized from data that doesn't match any known member. For example, if + * the SDK is on an older version than the API, then the API may respond with new + * members that the SDK is unaware of. + * - It was constructed with an arbitrary value using the [of] method. + */ + enum class Value { + HTML, + /** An enum member indicating that [Type] was instantiated with an unknown value. */ + _UNKNOWN, + } + + /** + * Returns an enum member corresponding to this class instance's value, or + * [Value._UNKNOWN] if the class was instantiated with an unknown value. + * + * Use the [known] method instead if you're certain the value is always known or if you + * want to throw for the unknown case. + */ + fun value(): Value = + when (this) { + HTML -> Value.HTML + 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) { + HTML -> Known.HTML + else -> throw CourierInvalidDataException("Unknown Type: $value") + } + + /** + * Returns this class instance's primitive wire representation. + * + * This differs from the [toString] method because that method is primarily for + * debugging and generally doesn't throw. + * + * @throws 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 + + /** + * 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(): Type = 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 Type && 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 UnionMember6 && + channels == other.channels && + if_ == other.if_ && + loop == other.loop && + ref == other.ref && + content == other.content && + locales == other.locales && + type == other.type && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(channels, if_, loop, ref, content, locales, type, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "UnionMember6{channels=$channels, if_=$if_, loop=$loop, ref=$ref, content=$content, locales=$locales, type=$type, additionalProperties=$additionalProperties}" + } +} diff --git a/courier-java-core/src/main/kotlin/com/courier/models/journeys/templates/TemplateCreateParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/journeys/templates/TemplateCreateParams.kt index 888cb1cc..21b7fe3b 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/journeys/templates/TemplateCreateParams.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/journeys/templates/TemplateCreateParams.kt @@ -15,6 +15,12 @@ import kotlin.jvm.optionals.getOrNull /** * Create a notification template scoped to this journey. Defaults to `DRAFT` state; pass `state: * "PUBLISHED"` to publish on create. + * + * The content tree must contain exactly one channel block whose `channel` matches the `channel` on + * the request — a journey-scoped template carries a single channel. Top-level elements, or a block + * for a different channel, return `400`. The template designer renders only the channel block + * matching the tab it draws, so content stored without one cannot be opened. An empty `elements` + * array is accepted. */ class TemplateCreateParams private constructor( diff --git a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationCreateParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationCreateParams.kt index d7a939c6..40ce90f5 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationCreateParams.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationCreateParams.kt @@ -14,6 +14,14 @@ import kotlin.jvm.optionals.getOrNull /** * Create a notification template. Requires all fields in the notification object. Templates are * created in draft state by default. + * + * Content must place its elements inside a channel block — `{ "type": "channel", "channel": + * "email", "elements": [...] }` — or the request returns `400`. The template designer renders only + * the channel block matching the tab it draws, so content stored without one cannot be opened. An + * empty `elements` array is accepted, and the requirement applies to creation only: `PUT + * /notifications/{id}` still accepts unwrapped content. Note this endpoint takes versioned content + * only — the `{ title, body }` shorthand accepted by `/send` is rejected here with an + * `invalid_request_error` on `notification.content.version`. */ class NotificationCreateParams private constructor( diff --git a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationReplaceParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationReplaceParams.kt index b591e428..07b5fa43 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationReplaceParams.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationReplaceParams.kt @@ -26,9 +26,11 @@ private constructor( fun id(): Optional = Optional.ofNullable(id) /** - * Request body for replacing a notification template. Same shape as create. All fields required - * (PUT = full replacement), except `alias`, whose omission means "leave the existing aliases - * alone". + * Request body for replacing a notification template. All fields are required, since `PUT` is a + * full replacement, except `alias`, whose omission leaves the existing aliases in place. Unlike + * `NotificationTemplateCreateRequest`, `notification.content` is not required to place its + * elements inside a channel block: the requirement applies to creation only, so templates + * already stored without one stay editable. */ fun notificationTemplateUpdateRequest(): NotificationTemplateUpdateRequest = notificationTemplateUpdateRequest @@ -80,9 +82,11 @@ private constructor( fun id(id: Optional) = id(id.getOrNull()) /** - * Request body for replacing a notification template. Same shape as create. All fields - * required (PUT = full replacement), except `alias`, whose omission means "leave the - * existing aliases alone". + * Request body for replacing a notification template. All fields are required, since `PUT` + * is a full replacement, except `alias`, whose omission leaves the existing aliases in + * place. Unlike `NotificationTemplateCreateRequest`, `notification.content` is not required + * to place its elements inside a channel block: the requirement applies to creation only, + * so templates already stored without one stay editable. */ fun notificationTemplateUpdateRequest( notificationTemplateUpdateRequest: NotificationTemplateUpdateRequest diff --git a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationTemplateUpdateRequest.kt b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationTemplateUpdateRequest.kt index d99642cc..fafbb1dd 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationTemplateUpdateRequest.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/notifications/NotificationTemplateUpdateRequest.kt @@ -19,9 +19,11 @@ import java.util.Optional import kotlin.jvm.optionals.getOrNull /** - * Request body for replacing a notification template. Same shape as create. All fields required - * (PUT = full replacement), except `alias`, whose omission means "leave the existing aliases - * alone". + * Request body for replacing a notification template. All fields are required, since `PUT` is a + * full replacement, except `alias`, whose omission leaves the existing aliases in place. Unlike + * `NotificationTemplateCreateRequest`, `notification.content` is not required to place its elements + * inside a channel block: the requirement applies to creation only, so templates already stored + * without one stay editable. */ class NotificationTemplateUpdateRequest @JsonCreator(mode = JsonCreator.Mode.DISABLED) diff --git a/courier-java-core/src/main/kotlin/com/courier/models/tenants/templates/TemplateReplaceParams.kt b/courier-java-core/src/main/kotlin/com/courier/models/tenants/templates/TemplateReplaceParams.kt index 29cb9b3a..feb8ab01 100644 --- a/courier-java-core/src/main/kotlin/com/courier/models/tenants/templates/TemplateReplaceParams.kt +++ b/courier-java-core/src/main/kotlin/com/courier/models/tenants/templates/TemplateReplaceParams.kt @@ -15,6 +15,14 @@ import kotlin.jvm.optionals.getOrNull /** * Creates or updates a notification template scoped to one tenant, letting a tenant override the * content the workspace template would send. + * + * This is an upsert: it creates when the tenant has no template under `template_id`, and updates + * when it does. On the create half, content must place its elements inside a channel block — `{ + * "type": "channel", "channel": "email", "elements": [...] }` — or the request returns `400`. The + * template designer renders only the channel block matching the tab it draws, so content stored + * without one cannot be opened. An empty `elements` array is accepted, as is the `{ title, body }` + * shorthand, which has no elements to wrap. Updates are not checked, so tenant templates already + * stored without a wrapper stay editable. */ class TemplateReplaceParams private constructor( 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 fc35ec10..00fc6d2e 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 @@ -50,6 +50,14 @@ interface NotificationServiceAsync { /** * Create a notification template. Requires all fields in the notification object. Templates are * created in draft state by default. + * + * Content must place its elements inside a channel block — `{ "type": "channel", "channel": + * "email", "elements": [...] }` — or the request returns `400`. The template designer renders + * only the channel block matching the tab it draws, so content stored without one cannot be + * opened. An empty `elements` array is accepted, and the requirement applies to creation only: + * `PUT /notifications/{id}` still accepts unwrapped content. Note this endpoint takes versioned + * content only — the `{ title, body }` shorthand accepted by `/send` is rejected here with an + * `invalid_request_error` on `notification.content.version`. */ fun create(params: NotificationCreateParams): CompletableFuture = create(params, RequestOptions.none()) diff --git a/courier-java-core/src/main/kotlin/com/courier/services/async/journeys/TemplateServiceAsync.kt b/courier-java-core/src/main/kotlin/com/courier/services/async/journeys/TemplateServiceAsync.kt index 546d7917..a4a1a5b5 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/async/journeys/TemplateServiceAsync.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/async/journeys/TemplateServiceAsync.kt @@ -45,6 +45,12 @@ interface TemplateServiceAsync { /** * Create a notification template scoped to this journey. Defaults to `DRAFT` state; pass * `state: "PUBLISHED"` to publish on create. + * + * The content tree must contain exactly one channel block whose `channel` matches the `channel` + * on the request — a journey-scoped template carries a single channel. Top-level elements, or a + * block for a different channel, return `400`. The template designer renders only the channel + * block matching the tab it draws, so content stored without one cannot be opened. An empty + * `elements` array is accepted. */ fun create( templateId: String, diff --git a/courier-java-core/src/main/kotlin/com/courier/services/async/tenants/TemplateServiceAsync.kt b/courier-java-core/src/main/kotlin/com/courier/services/async/tenants/TemplateServiceAsync.kt index 4e36c538..62f1dd67 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/async/tenants/TemplateServiceAsync.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/async/tenants/TemplateServiceAsync.kt @@ -166,6 +166,14 @@ interface TemplateServiceAsync { /** * Creates or updates a notification template scoped to one tenant, letting a tenant override * the content the workspace template would send. + * + * This is an upsert: it creates when the tenant has no template under `template_id`, and + * updates when it does. On the create half, content must place its elements inside a channel + * block — `{ "type": "channel", "channel": "email", "elements": [...] }` — or the request + * returns `400`. The template designer renders only the channel block matching the tab it + * draws, so content stored without one cannot be opened. An empty `elements` array is accepted, + * as is the `{ title, body }` shorthand, which has no elements to wrap. Updates are not + * checked, so tenant templates already stored without a wrapper stay editable. */ fun replace( templateId: String, 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 6abee34c..847aa599 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 @@ -50,6 +50,14 @@ interface NotificationService { /** * Create a notification template. Requires all fields in the notification object. Templates are * created in draft state by default. + * + * Content must place its elements inside a channel block — `{ "type": "channel", "channel": + * "email", "elements": [...] }` — or the request returns `400`. The template designer renders + * only the channel block matching the tab it draws, so content stored without one cannot be + * opened. An empty `elements` array is accepted, and the requirement applies to creation only: + * `PUT /notifications/{id}` still accepts unwrapped content. Note this endpoint takes versioned + * content only — the `{ title, body }` shorthand accepted by `/send` is rejected here with an + * `invalid_request_error` on `notification.content.version`. */ fun create(params: NotificationCreateParams): NotificationTemplateResponse = create(params, RequestOptions.none()) diff --git a/courier-java-core/src/main/kotlin/com/courier/services/blocking/journeys/TemplateService.kt b/courier-java-core/src/main/kotlin/com/courier/services/blocking/journeys/TemplateService.kt index c7357522..8289d5e7 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/blocking/journeys/TemplateService.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/blocking/journeys/TemplateService.kt @@ -45,6 +45,12 @@ interface TemplateService { /** * Create a notification template scoped to this journey. Defaults to `DRAFT` state; pass * `state: "PUBLISHED"` to publish on create. + * + * The content tree must contain exactly one channel block whose `channel` matches the `channel` + * on the request — a journey-scoped template carries a single channel. Top-level elements, or a + * block for a different channel, return `400`. The template designer renders only the channel + * block matching the tab it draws, so content stored without one cannot be opened. An empty + * `elements` array is accepted. */ fun create(templateId: String, params: TemplateCreateParams): JourneyTemplateGetResponse = create(templateId, params, RequestOptions.none()) diff --git a/courier-java-core/src/main/kotlin/com/courier/services/blocking/tenants/TemplateService.kt b/courier-java-core/src/main/kotlin/com/courier/services/blocking/tenants/TemplateService.kt index c4474f16..8c8a71e7 100644 --- a/courier-java-core/src/main/kotlin/com/courier/services/blocking/tenants/TemplateService.kt +++ b/courier-java-core/src/main/kotlin/com/courier/services/blocking/tenants/TemplateService.kt @@ -152,6 +152,14 @@ interface TemplateService { /** * Creates or updates a notification template scoped to one tenant, letting a tenant override * the content the workspace template would send. + * + * This is an upsert: it creates when the tenant has no template under `template_id`, and + * updates when it does. On the create half, content must place its elements inside a channel + * block — `{ "type": "channel", "channel": "email", "elements": [...] }` — or the request + * returns `400`. The template designer renders only the channel block matching the tab it + * draws, so content stored without one cannot be opened. An empty `elements` array is accepted, + * as is the `{ title, body }` shorthand, which has no elements to wrap. Updates are not + * checked, so tenant templates already stored without a wrapper stay editable. */ fun replace(templateId: String, params: TemplateReplaceParams): PutTenantTemplateResponse = replace(templateId, params, RequestOptions.none()) 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 dec76649..97bc6e70 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 @@ -20,6 +20,11 @@ internal class ElementalChannelNodeTest { .loop("loop") .ref("ref") .channel("email") + .addElement( + ElementalNodeNonChannel.UnionMember0.builder() + .type(ElementalNodeNonChannel.UnionMember0.Type.TEXT) + .build() + ) .fontSize("font_size") .lineHeight("line_height") .padding("padding") @@ -35,6 +40,14 @@ internal class ElementalChannelNodeTest { assertThat(elementalChannelNode.loop()).contains("loop") assertThat(elementalChannelNode.ref()).contains("ref") assertThat(elementalChannelNode.channel()).contains("email") + assertThat(elementalChannelNode.elements().getOrNull()) + .containsExactly( + ElementalNodeNonChannel.ofUnionMember0( + ElementalNodeNonChannel.UnionMember0.builder() + .type(ElementalNodeNonChannel.UnionMember0.Type.TEXT) + .build() + ) + ) assertThat(elementalChannelNode.fontSize()).contains("font_size") assertThat(elementalChannelNode.lineHeight()).contains("line_height") assertThat(elementalChannelNode.padding()).contains("padding") @@ -56,6 +69,11 @@ internal class ElementalChannelNodeTest { .loop("loop") .ref("ref") .channel("email") + .addElement( + ElementalNodeNonChannel.UnionMember0.builder() + .type(ElementalNodeNonChannel.UnionMember0.Type.TEXT) + .build() + ) .fontSize("font_size") .lineHeight("line_height") .padding("padding") diff --git a/courier-java-core/src/test/kotlin/com/courier/models/ElementalNodeNonChannelTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/ElementalNodeNonChannelTest.kt new file mode 100644 index 00000000..111cd6cf --- /dev/null +++ b/courier-java-core/src/test/kotlin/com/courier/models/ElementalNodeNonChannelTest.kt @@ -0,0 +1,293 @@ +// 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 ElementalNodeNonChannelTest { + + @Test + fun ofUnionMember0() { + val unionMember0 = + ElementalNodeNonChannel.UnionMember0.builder() + .type(ElementalNodeNonChannel.UnionMember0.Type.TEXT) + .build() + + val elementalNodeNonChannel = ElementalNodeNonChannel.ofUnionMember0(unionMember0) + + assertThat(elementalNodeNonChannel.unionMember0()).contains(unionMember0) + assertThat(elementalNodeNonChannel.unionMember1()).isEmpty + assertThat(elementalNodeNonChannel.unionMember2()).isEmpty + assertThat(elementalNodeNonChannel.unionMember3()).isEmpty + assertThat(elementalNodeNonChannel.unionMember4()).isEmpty + assertThat(elementalNodeNonChannel.unionMember5()).isEmpty + assertThat(elementalNodeNonChannel.unionMember6()).isEmpty + } + + @Test + fun ofUnionMember0Roundtrip() { + val jsonMapper = jsonMapper() + val elementalNodeNonChannel = + ElementalNodeNonChannel.ofUnionMember0( + ElementalNodeNonChannel.UnionMember0.builder() + .type(ElementalNodeNonChannel.UnionMember0.Type.TEXT) + .build() + ) + + val roundtrippedElementalNodeNonChannel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(elementalNodeNonChannel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedElementalNodeNonChannel).isEqualTo(elementalNodeNonChannel) + } + + @Test + fun ofUnionMember1() { + val unionMember1 = + ElementalNodeNonChannel.UnionMember1.builder() + .type(ElementalNodeNonChannel.UnionMember1.Type.META) + .build() + + val elementalNodeNonChannel = ElementalNodeNonChannel.ofUnionMember1(unionMember1) + + assertThat(elementalNodeNonChannel.unionMember0()).isEmpty + assertThat(elementalNodeNonChannel.unionMember1()).contains(unionMember1) + assertThat(elementalNodeNonChannel.unionMember2()).isEmpty + assertThat(elementalNodeNonChannel.unionMember3()).isEmpty + assertThat(elementalNodeNonChannel.unionMember4()).isEmpty + assertThat(elementalNodeNonChannel.unionMember5()).isEmpty + assertThat(elementalNodeNonChannel.unionMember6()).isEmpty + } + + @Test + fun ofUnionMember1Roundtrip() { + val jsonMapper = jsonMapper() + val elementalNodeNonChannel = + ElementalNodeNonChannel.ofUnionMember1( + ElementalNodeNonChannel.UnionMember1.builder() + .type(ElementalNodeNonChannel.UnionMember1.Type.META) + .build() + ) + + val roundtrippedElementalNodeNonChannel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(elementalNodeNonChannel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedElementalNodeNonChannel).isEqualTo(elementalNodeNonChannel) + } + + @Test + fun ofUnionMember2() { + val unionMember2 = + ElementalNodeNonChannel.UnionMember2.builder() + .type(ElementalNodeNonChannel.UnionMember2.Type.IMAGE) + .build() + + val elementalNodeNonChannel = ElementalNodeNonChannel.ofUnionMember2(unionMember2) + + assertThat(elementalNodeNonChannel.unionMember0()).isEmpty + assertThat(elementalNodeNonChannel.unionMember1()).isEmpty + assertThat(elementalNodeNonChannel.unionMember2()).contains(unionMember2) + assertThat(elementalNodeNonChannel.unionMember3()).isEmpty + assertThat(elementalNodeNonChannel.unionMember4()).isEmpty + assertThat(elementalNodeNonChannel.unionMember5()).isEmpty + assertThat(elementalNodeNonChannel.unionMember6()).isEmpty + } + + @Test + fun ofUnionMember2Roundtrip() { + val jsonMapper = jsonMapper() + val elementalNodeNonChannel = + ElementalNodeNonChannel.ofUnionMember2( + ElementalNodeNonChannel.UnionMember2.builder() + .type(ElementalNodeNonChannel.UnionMember2.Type.IMAGE) + .build() + ) + + val roundtrippedElementalNodeNonChannel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(elementalNodeNonChannel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedElementalNodeNonChannel).isEqualTo(elementalNodeNonChannel) + } + + @Test + fun ofUnionMember3() { + val unionMember3 = + ElementalNodeNonChannel.UnionMember3.builder() + .type(ElementalNodeNonChannel.UnionMember3.Type.ACTION) + .build() + + val elementalNodeNonChannel = ElementalNodeNonChannel.ofUnionMember3(unionMember3) + + assertThat(elementalNodeNonChannel.unionMember0()).isEmpty + assertThat(elementalNodeNonChannel.unionMember1()).isEmpty + assertThat(elementalNodeNonChannel.unionMember2()).isEmpty + assertThat(elementalNodeNonChannel.unionMember3()).contains(unionMember3) + assertThat(elementalNodeNonChannel.unionMember4()).isEmpty + assertThat(elementalNodeNonChannel.unionMember5()).isEmpty + assertThat(elementalNodeNonChannel.unionMember6()).isEmpty + } + + @Test + fun ofUnionMember3Roundtrip() { + val jsonMapper = jsonMapper() + val elementalNodeNonChannel = + ElementalNodeNonChannel.ofUnionMember3( + ElementalNodeNonChannel.UnionMember3.builder() + .type(ElementalNodeNonChannel.UnionMember3.Type.ACTION) + .build() + ) + + val roundtrippedElementalNodeNonChannel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(elementalNodeNonChannel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedElementalNodeNonChannel).isEqualTo(elementalNodeNonChannel) + } + + @Test + fun ofUnionMember4() { + val unionMember4 = + ElementalNodeNonChannel.UnionMember4.builder() + .type(ElementalNodeNonChannel.UnionMember4.Type.DIVIDER) + .build() + + val elementalNodeNonChannel = ElementalNodeNonChannel.ofUnionMember4(unionMember4) + + assertThat(elementalNodeNonChannel.unionMember0()).isEmpty + assertThat(elementalNodeNonChannel.unionMember1()).isEmpty + assertThat(elementalNodeNonChannel.unionMember2()).isEmpty + assertThat(elementalNodeNonChannel.unionMember3()).isEmpty + assertThat(elementalNodeNonChannel.unionMember4()).contains(unionMember4) + assertThat(elementalNodeNonChannel.unionMember5()).isEmpty + assertThat(elementalNodeNonChannel.unionMember6()).isEmpty + } + + @Test + fun ofUnionMember4Roundtrip() { + val jsonMapper = jsonMapper() + val elementalNodeNonChannel = + ElementalNodeNonChannel.ofUnionMember4( + ElementalNodeNonChannel.UnionMember4.builder() + .type(ElementalNodeNonChannel.UnionMember4.Type.DIVIDER) + .build() + ) + + val roundtrippedElementalNodeNonChannel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(elementalNodeNonChannel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedElementalNodeNonChannel).isEqualTo(elementalNodeNonChannel) + } + + @Test + fun ofUnionMember5() { + val unionMember5 = + ElementalNodeNonChannel.UnionMember5.builder() + .type(ElementalNodeNonChannel.UnionMember5.Type.QUOTE) + .build() + + val elementalNodeNonChannel = ElementalNodeNonChannel.ofUnionMember5(unionMember5) + + assertThat(elementalNodeNonChannel.unionMember0()).isEmpty + assertThat(elementalNodeNonChannel.unionMember1()).isEmpty + assertThat(elementalNodeNonChannel.unionMember2()).isEmpty + assertThat(elementalNodeNonChannel.unionMember3()).isEmpty + assertThat(elementalNodeNonChannel.unionMember4()).isEmpty + assertThat(elementalNodeNonChannel.unionMember5()).contains(unionMember5) + assertThat(elementalNodeNonChannel.unionMember6()).isEmpty + } + + @Test + fun ofUnionMember5Roundtrip() { + val jsonMapper = jsonMapper() + val elementalNodeNonChannel = + ElementalNodeNonChannel.ofUnionMember5( + ElementalNodeNonChannel.UnionMember5.builder() + .type(ElementalNodeNonChannel.UnionMember5.Type.QUOTE) + .build() + ) + + val roundtrippedElementalNodeNonChannel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(elementalNodeNonChannel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedElementalNodeNonChannel).isEqualTo(elementalNodeNonChannel) + } + + @Test + fun ofUnionMember6() { + val unionMember6 = + ElementalNodeNonChannel.UnionMember6.builder() + .type(ElementalNodeNonChannel.UnionMember6.Type.HTML) + .build() + + val elementalNodeNonChannel = ElementalNodeNonChannel.ofUnionMember6(unionMember6) + + assertThat(elementalNodeNonChannel.unionMember0()).isEmpty + assertThat(elementalNodeNonChannel.unionMember1()).isEmpty + assertThat(elementalNodeNonChannel.unionMember2()).isEmpty + assertThat(elementalNodeNonChannel.unionMember3()).isEmpty + assertThat(elementalNodeNonChannel.unionMember4()).isEmpty + assertThat(elementalNodeNonChannel.unionMember5()).isEmpty + assertThat(elementalNodeNonChannel.unionMember6()).contains(unionMember6) + } + + @Test + fun ofUnionMember6Roundtrip() { + val jsonMapper = jsonMapper() + val elementalNodeNonChannel = + ElementalNodeNonChannel.ofUnionMember6( + ElementalNodeNonChannel.UnionMember6.builder() + .type(ElementalNodeNonChannel.UnionMember6.Type.HTML) + .build() + ) + + val roundtrippedElementalNodeNonChannel = + jsonMapper.readValue( + jsonMapper.writeValueAsString(elementalNodeNonChannel), + jacksonTypeRef(), + ) + + assertThat(roundtrippedElementalNodeNonChannel).isEqualTo(elementalNodeNonChannel) + } + + 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 elementalNodeNonChannel = + jsonMapper().convertValue(testCase.value, jacksonTypeRef()) + + val e = assertThrows { elementalNodeNonChannel.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/courier-java-core/src/test/kotlin/com/courier/models/journeys/templates/TemplateCreateParamsTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/journeys/templates/TemplateCreateParamsTest.kt index 867b9fde..469b9f20 100644 --- a/courier-java-core/src/test/kotlin/com/courier/models/journeys/templates/TemplateCreateParamsTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/models/journeys/templates/TemplateCreateParamsTest.kt @@ -3,6 +3,7 @@ package com.courier.models.journeys.templates import com.courier.core.http.Headers +import com.courier.models.ElementalChannelNodeWithType import com.courier.models.ElementalTextNodeWithType import com.courier.models.journeys.JourneyTemplateCreateRequest import org.assertj.core.api.Assertions.assertThat @@ -29,8 +30,8 @@ internal class TemplateCreateParamsTest { .content( JourneyTemplateCreateRequest.Notification.Content.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version( @@ -122,8 +123,8 @@ internal class TemplateCreateParamsTest { .content( JourneyTemplateCreateRequest.Notification.Content.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version( @@ -226,8 +227,8 @@ internal class TemplateCreateParamsTest { .content( JourneyTemplateCreateRequest.Notification.Content.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version( @@ -272,8 +273,8 @@ internal class TemplateCreateParamsTest { .content( JourneyTemplateCreateRequest.Notification.Content.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version( diff --git a/courier-java-core/src/test/kotlin/com/courier/models/tenants/templates/TemplateReplaceParamsTest.kt b/courier-java-core/src/test/kotlin/com/courier/models/tenants/templates/TemplateReplaceParamsTest.kt index 0bf80c2a..8c76339f 100644 --- a/courier-java-core/src/test/kotlin/com/courier/models/tenants/templates/TemplateReplaceParamsTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/models/tenants/templates/TemplateReplaceParamsTest.kt @@ -3,6 +3,7 @@ package com.courier.models.tenants.templates import com.courier.core.JsonValue +import com.courier.models.ElementalChannelNodeWithType import com.courier.models.ElementalContent import com.courier.models.ElementalTextNodeWithType import com.courier.models.MessageChannels @@ -27,8 +28,8 @@ internal class TemplateReplaceParamsTest { .content( ElementalContent.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version("2022-01-01") @@ -142,8 +143,8 @@ internal class TemplateReplaceParamsTest { .content( ElementalContent.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version("2022-01-01") @@ -226,8 +227,8 @@ internal class TemplateReplaceParamsTest { .content( ElementalContent.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version("2022-01-01") diff --git a/courier-java-core/src/test/kotlin/com/courier/services/async/journeys/TemplateServiceAsyncTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/async/journeys/TemplateServiceAsyncTest.kt index 0156c142..02f834f8 100644 --- a/courier-java-core/src/test/kotlin/com/courier/services/async/journeys/TemplateServiceAsyncTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/services/async/journeys/TemplateServiceAsyncTest.kt @@ -51,8 +51,8 @@ internal class TemplateServiceAsyncTest { .content( JourneyTemplateCreateRequest.Notification.Content.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version( diff --git a/courier-java-core/src/test/kotlin/com/courier/services/async/tenants/TemplateServiceAsyncTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/async/tenants/TemplateServiceAsyncTest.kt index 94e155b9..fe5d3f52 100644 --- a/courier-java-core/src/test/kotlin/com/courier/services/async/tenants/TemplateServiceAsyncTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/services/async/tenants/TemplateServiceAsyncTest.kt @@ -4,8 +4,8 @@ package com.courier.services.async.tenants import com.courier.client.okhttp.CourierOkHttpClientAsync import com.courier.core.JsonValue +import com.courier.models.ElementalChannelNodeWithType import com.courier.models.ElementalContent -import com.courier.models.ElementalTextNodeWithType import com.courier.models.MessageChannels import com.courier.models.MessageProviders import com.courier.models.MessageRouting @@ -115,8 +115,8 @@ internal class TemplateServiceAsyncTest { .content( ElementalContent.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version("2022-01-01") diff --git a/courier-java-core/src/test/kotlin/com/courier/services/blocking/journeys/TemplateServiceTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/blocking/journeys/TemplateServiceTest.kt index 497ab4ca..9469dbc5 100644 --- a/courier-java-core/src/test/kotlin/com/courier/services/blocking/journeys/TemplateServiceTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/services/blocking/journeys/TemplateServiceTest.kt @@ -51,8 +51,8 @@ internal class TemplateServiceTest { .content( JourneyTemplateCreateRequest.Notification.Content.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version( diff --git a/courier-java-core/src/test/kotlin/com/courier/services/blocking/tenants/TemplateServiceTest.kt b/courier-java-core/src/test/kotlin/com/courier/services/blocking/tenants/TemplateServiceTest.kt index 9654beca..623d9004 100644 --- a/courier-java-core/src/test/kotlin/com/courier/services/blocking/tenants/TemplateServiceTest.kt +++ b/courier-java-core/src/test/kotlin/com/courier/services/blocking/tenants/TemplateServiceTest.kt @@ -4,8 +4,8 @@ package com.courier.services.blocking.tenants import com.courier.client.okhttp.CourierOkHttpClient import com.courier.core.JsonValue +import com.courier.models.ElementalChannelNodeWithType import com.courier.models.ElementalContent -import com.courier.models.ElementalTextNodeWithType import com.courier.models.MessageChannels import com.courier.models.MessageProviders import com.courier.models.MessageRouting @@ -106,8 +106,8 @@ internal class TemplateServiceTest { .content( ElementalContent.builder() .addElement( - ElementalTextNodeWithType.builder() - .type(ElementalTextNodeWithType.Type.TEXT) + ElementalChannelNodeWithType.builder() + .type(ElementalChannelNodeWithType.Type.CHANNEL) .build() ) .version("2022-01-01")