From 1274d6b40a43e103b9b0aa3ddaa2744b9b6d02c8 Mon Sep 17 00:00:00 2001 From: stlc-bot Date: Tue, 29 Sep 2026 17:23:16 +0000 Subject: [PATCH] chore(stlc): seal custom-code tracking files Stainless-Generated-From: 96a538b4d04bcc8a69fa713d1810629068e55130 --- .stats.yml | 2 +- .../workspaces/apikeys/ApiKeyCreateParams.kt | 109 ++- .../apikeys/ApiKeyCreateResponse.kt | 405 ++++++++- .../workspaces/apikeys/ApiKeyDeleteParams.kt | 261 ++++++ .../workspaces/apikeys/ApiKeyListParams.kt | 193 +++++ .../workspaces/apikeys/ApiKeyListResponse.kt | 799 ++++++++++++++++++ .../apikeys/ApiKeyRetrieveParams.kt | 220 +++++ .../apikeys/ApiKeyRetrieveResponse.kt | 799 ++++++++++++++++++ .../workspaces/apikeys/ApiKeyRotateParams.kt | 554 ++++++++++++ .../apikeys/ApiKeyRotateResponse.kt | 799 ++++++++++++++++++ .../workspaces/apikeys/ApiKeyUpdateParams.kt | 448 ++++++++++ .../apikeys/ApiKeyUpdateResponse.kt | 799 ++++++++++++++++++ .../async/workspaces/ApiKeyServiceAsync.kt | 325 ++++++- .../workspaces/ApiKeyServiceAsyncImpl.kt | 227 +++++ .../blocking/workspaces/ApiKeyService.kt | 316 ++++++- .../blocking/workspaces/ApiKeyServiceImpl.kt | 210 +++++ .../apikeys/ApiKeyCreateParamsTest.kt | 4 + .../apikeys/ApiKeyCreateResponseTest.kt | 25 +- .../apikeys/ApiKeyDeleteParamsTest.kt | 31 + .../apikeys/ApiKeyListParamsTest.kt | 24 + .../apikeys/ApiKeyListResponseTest.kt | 75 ++ .../apikeys/ApiKeyRetrieveParamsTest.kt | 31 + .../apikeys/ApiKeyRetrieveResponseTest.kt | 75 ++ .../apikeys/ApiKeyRotateParamsTest.kt | 61 ++ .../apikeys/ApiKeyRotateResponseTest.kt | 75 ++ .../apikeys/ApiKeyUpdateParamsTest.kt | 57 ++ .../apikeys/ApiKeyUpdateResponseTest.kt | 75 ++ .../workspaces/ApiKeyServiceAsyncTest.kt | 107 +++ .../blocking/workspaces/ApiKeyServiceTest.kt | 100 +++ scripts/mock | 2 +- 30 files changed, 7188 insertions(+), 20 deletions(-) create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParams.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParams.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponse.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParams.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponse.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParams.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponse.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParams.kt create mode 100644 openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponse.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParamsTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParamsTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponseTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParamsTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponseTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParamsTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponseTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParamsTest.kt create mode 100644 openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponseTest.kt diff --git a/.stats.yml b/.stats.yml index b4620cc9..c9a9bfa4 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1 +1 @@ -configured_endpoints: 58 +configured_endpoints: 63 diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParams.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParams.kt index 748833b9..2a06a5fb 100644 --- a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParams.kt +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParams.kt @@ -14,12 +14,18 @@ import com.openlayer.api.core.Params import com.openlayer.api.core.http.Headers import com.openlayer.api.core.http.QueryParams import com.openlayer.api.errors.OpenlayerInvalidDataException +import java.time.OffsetDateTime import java.util.Collections import java.util.Objects import java.util.Optional import kotlin.jvm.optionals.getOrNull -/** Create a new API key in a workspace. */ +/** + * Create a new API key in a workspace. The full secret is returned in `secret`, only in this + * response. Optionally set `expiresAt`. When you authenticate with an API key that expires, the new + * key can't outlive it: omit `expiresAt` to inherit that expiry, and a later expiry (or `null`) is + * rejected with 400. + */ class ApiKeyCreateParams private constructor( private val workspaceId: String?, @@ -30,6 +36,16 @@ private constructor( fun workspaceId(): Optional = Optional.ofNullable(workspaceId) + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key is + * created or rotated, and must be in the future. When the request is authenticated with an API + * key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): Optional = body.expiresAt() + /** * The API key name. * @@ -38,6 +54,13 @@ private constructor( */ fun name(): Optional = body.name() + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _expiresAt(): JsonField = body._expiresAt() + /** * Returns the raw JSON value of [name]. * @@ -89,10 +112,30 @@ private constructor( * * This is generally only useful if you are already constructing the body separately. * Otherwise, it's more convenient to use the top-level setters instead: + * - [expiresAt] * - [name] */ fun body(body: Body) = apply { this.body = body.toBuilder() } + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = apply { body.expiresAt(expiresAt) } + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { body.expiresAt(expiresAt) } + /** The API key name. */ fun name(name: String?) = apply { body.name(name) } @@ -253,14 +296,28 @@ private constructor( class Body @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( + private val expiresAt: JsonField, private val name: JsonField, private val additionalProperties: MutableMap, ) { @JsonCreator private constructor( - @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of() - ) : this(name, mutableMapOf()) + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + ) : this(expiresAt, name, mutableMapOf()) + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun expiresAt(): Optional = expiresAt.getOptional("expiresAt") /** * The API key name. @@ -270,6 +327,15 @@ private constructor( */ fun name(): Optional = name.getOptional("name") + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + /** * Returns the raw JSON value of [name]. * @@ -298,15 +364,39 @@ private constructor( /** A builder for [Body]. */ class Builder internal constructor() { + private var expiresAt: JsonField = JsonMissing.of() private var name: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic internal fun from(body: Body) = apply { + expiresAt = body.expiresAt name = body.name additionalProperties = body.additionalProperties.toMutableMap() } + /** + * When the key stops authenticating. `null` means the key never expires. Set when the + * key is created or rotated, and must be in the future. When the request is + * authenticated with an API key that expires, the result can't be later than that key's + * expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = expiresAt(JsonField.ofNullable(expiresAt)) + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { + this.expiresAt = expiresAt + } + /** The API key name. */ fun name(name: String?) = name(JsonField.ofNullable(name)) @@ -346,7 +436,7 @@ private constructor( * * Further updates to this [Builder] will not mutate the returned instance. */ - fun build(): Body = Body(name, additionalProperties.toMutableMap()) + fun build(): Body = Body(expiresAt, name, additionalProperties.toMutableMap()) } private var validated: Boolean = false @@ -365,6 +455,7 @@ private constructor( return@apply } + expiresAt() name() validated = true } @@ -383,7 +474,9 @@ private constructor( * * Used for best match union deserialization. */ - @JvmSynthetic internal fun validity(): Int = (if (name.asKnown().isPresent) 1 else 0) + @JvmSynthetic + internal fun validity(): Int = + (if (expiresAt.asKnown().isPresent) 1 else 0) + (if (name.asKnown().isPresent) 1 else 0) override fun equals(other: Any?): Boolean { if (this === other) { @@ -391,15 +484,17 @@ private constructor( } return other is Body && + expiresAt == other.expiresAt && name == other.name && additionalProperties == other.additionalProperties } - private val hashCode: Int by lazy { Objects.hash(name, additionalProperties) } + private val hashCode: Int by lazy { Objects.hash(expiresAt, name, additionalProperties) } override fun hashCode(): Int = hashCode - override fun toString() = "Body{name=$name, additionalProperties=$additionalProperties}" + override fun toString() = + "Body{expiresAt=$expiresAt, name=$name, additionalProperties=$additionalProperties}" } override fun equals(other: Any?): Boolean { diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponse.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponse.kt index 63985cfb..c7012406 100644 --- a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponse.kt +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponse.kt @@ -6,6 +6,7 @@ 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.openlayer.api.core.Enum import com.openlayer.api.core.ExcludeMissing import com.openlayer.api.core.JsonField import com.openlayer.api.core.JsonMissing @@ -26,7 +27,12 @@ private constructor( private val dateLastUsed: JsonField, private val dateUpdated: JsonField, private val secureKey: JsonField, + private val status: JsonField, + private val expiresAt: JsonField, + private val lastRotatedAt: JsonField, private val name: JsonField, + private val previousKeyExpiresAt: JsonField, + private val secret: JsonField, private val additionalProperties: MutableMap, ) { @@ -43,8 +49,32 @@ private constructor( @ExcludeMissing dateUpdated: JsonField = JsonMissing.of(), @JsonProperty("secureKey") @ExcludeMissing secureKey: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("lastRotatedAt") + @ExcludeMissing + lastRotatedAt: JsonField = JsonMissing.of(), @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), - ) : this(id, dateCreated, dateLastUsed, dateUpdated, secureKey, name, mutableMapOf()) + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + previousKeyExpiresAt: JsonField = JsonMissing.of(), + @JsonProperty("secret") @ExcludeMissing secret: JsonField = JsonMissing.of(), + ) : this( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + mutableMapOf(), + ) /** * The API key id. @@ -79,13 +109,42 @@ private constructor( fun dateUpdated(): OffsetDateTime = dateUpdated.getRequired("dateUpdated") /** - * The API key value. + * An obfuscated hint of the API key value. When a key is created or rotated this also holds the + * full secret, for backward compatibility; prefer `secret`. * * @throws OpenlayerInvalidDataException 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 secureKey(): String = secureKey.getRequired("secureKey") + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + * + * @throws OpenlayerInvalidDataException 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 status(): Status = status.getRequired("status") + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key is + * created or rotated, and must be in the future. When the request is authenticated with an API + * key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): Optional = expiresAt.getOptional("expiresAt") + + /** + * When the key was last rotated. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lastRotatedAt(): Optional = lastRotatedAt.getOptional("lastRotatedAt") + /** * The API key name. * @@ -94,6 +153,24 @@ private constructor( */ fun name(): Optional = name.getOptional("name") + /** + * While `status` is `rotating`, when the previous secret stops authenticating. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun previousKeyExpiresAt(): Optional = + previousKeyExpiresAt.getOptional("previousKeyExpiresAt") + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun secret(): Optional = secret.getOptional("secret") + /** * Returns the raw JSON value of [id]. * @@ -135,6 +212,31 @@ private constructor( */ @JsonProperty("secureKey") @ExcludeMissing fun _secureKey(): JsonField = secureKey + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [lastRotatedAt]. + * + * Unlike [lastRotatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lastRotatedAt") + @ExcludeMissing + fun _lastRotatedAt(): JsonField = lastRotatedAt + /** * Returns the raw JSON value of [name]. * @@ -142,6 +244,23 @@ private constructor( */ @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + /** + * Returns the raw JSON value of [previousKeyExpiresAt]. + * + * Unlike [previousKeyExpiresAt], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + fun _previousKeyExpiresAt(): JsonField = previousKeyExpiresAt + + /** + * Returns the raw JSON value of [secret]. + * + * Unlike [secret], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secret") @ExcludeMissing fun _secret(): JsonField = secret + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -166,6 +285,7 @@ private constructor( * .dateLastUsed() * .dateUpdated() * .secureKey() + * .status() * ``` */ @JvmStatic fun builder() = Builder() @@ -179,7 +299,12 @@ private constructor( private var dateLastUsed: JsonField? = null private var dateUpdated: JsonField? = null private var secureKey: JsonField? = null + private var status: JsonField? = null + private var expiresAt: JsonField = JsonMissing.of() + private var lastRotatedAt: JsonField = JsonMissing.of() private var name: JsonField = JsonMissing.of() + private var previousKeyExpiresAt: JsonField = JsonMissing.of() + private var secret: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -189,7 +314,12 @@ private constructor( dateLastUsed = apiKeyCreateResponse.dateLastUsed dateUpdated = apiKeyCreateResponse.dateUpdated secureKey = apiKeyCreateResponse.secureKey + status = apiKeyCreateResponse.status + expiresAt = apiKeyCreateResponse.expiresAt + lastRotatedAt = apiKeyCreateResponse.lastRotatedAt name = apiKeyCreateResponse.name + previousKeyExpiresAt = apiKeyCreateResponse.previousKeyExpiresAt + secret = apiKeyCreateResponse.secret additionalProperties = apiKeyCreateResponse.additionalProperties.toMutableMap() } @@ -251,7 +381,10 @@ private constructor( this.dateUpdated = dateUpdated } - /** The API key value. */ + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds + * the full secret, for backward compatibility; prefer `secret`. + */ fun secureKey(secureKey: String) = secureKey(JsonField.of(secureKey)) /** @@ -263,6 +396,59 @@ private constructor( */ fun secureKey(secureKey: JsonField) = apply { this.secureKey = secureKey } + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the + * key was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = expiresAt(JsonField.ofNullable(expiresAt)) + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + + /** When the key was last rotated. */ + fun lastRotatedAt(lastRotatedAt: OffsetDateTime?) = + lastRotatedAt(JsonField.ofNullable(lastRotatedAt)) + + /** Alias for calling [Builder.lastRotatedAt] with `lastRotatedAt.orElse(null)`. */ + fun lastRotatedAt(lastRotatedAt: Optional) = + lastRotatedAt(lastRotatedAt.getOrNull()) + + /** + * Sets [Builder.lastRotatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.lastRotatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lastRotatedAt(lastRotatedAt: JsonField) = apply { + this.lastRotatedAt = lastRotatedAt + } + /** The API key name. */ fun name(name: String?) = name(JsonField.ofNullable(name)) @@ -277,6 +463,42 @@ private constructor( */ fun name(name: JsonField) = apply { this.name = name } + /** While `status` is `rotating`, when the previous secret stops authenticating. */ + fun previousKeyExpiresAt(previousKeyExpiresAt: OffsetDateTime?) = + previousKeyExpiresAt(JsonField.ofNullable(previousKeyExpiresAt)) + + /** + * Alias for calling [Builder.previousKeyExpiresAt] with + * `previousKeyExpiresAt.orElse(null)`. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: Optional) = + previousKeyExpiresAt(previousKeyExpiresAt.getOrNull()) + + /** + * Sets [Builder.previousKeyExpiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.previousKeyExpiresAt] with a well-typed [OffsetDateTime] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: JsonField) = apply { + this.previousKeyExpiresAt = previousKeyExpiresAt + } + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + */ + fun secret(secret: String) = secret(JsonField.of(secret)) + + /** + * Sets [Builder.secret] to an arbitrary JSON value. + * + * You should usually call [Builder.secret] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun secret(secret: JsonField) = apply { this.secret = secret } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -308,6 +530,7 @@ private constructor( * .dateLastUsed() * .dateUpdated() * .secureKey() + * .status() * ``` * * @throws IllegalStateException if any required field is unset. @@ -319,7 +542,12 @@ private constructor( checkRequired("dateLastUsed", dateLastUsed), checkRequired("dateUpdated", dateUpdated), checkRequired("secureKey", secureKey), + checkRequired("status", status), + expiresAt, + lastRotatedAt, name, + previousKeyExpiresAt, + secret, additionalProperties.toMutableMap(), ) } @@ -344,7 +572,12 @@ private constructor( dateLastUsed() dateUpdated() secureKey() + status().validate() + expiresAt() + lastRotatedAt() name() + previousKeyExpiresAt() + secret() validated = true } @@ -368,7 +601,159 @@ private constructor( (if (dateLastUsed.asKnown().isPresent) 1 else 0) + (if (dateUpdated.asKnown().isPresent) 1 else 0) + (if (secureKey.asKnown().isPresent) 1 else 0) + - (if (name.asKnown().isPresent) 1 else 0) + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (lastRotatedAt.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (previousKeyExpiresAt.asKnown().isPresent) 1 else 0) + + (if (secret.asKnown().isPresent) 1 else 0) + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + class Status @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 ACTIVE = of("active") + + @JvmField val ROTATING = of("rotating") + + @JvmField val EXPIRED = of("expired") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + ROTATING, + EXPIRED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + ROTATING, + EXPIRED, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + ROTATING -> Value.ROTATING + EXPIRED -> Value.EXPIRED + 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 OpenlayerInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACTIVE -> Known.ACTIVE + ROTATING -> Known.ROTATING + EXPIRED -> Known.EXPIRED + else -> throw OpenlayerInvalidDataException("Unknown Status: $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 OpenlayerInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenlayerInvalidDataException("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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + 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 Status && value == other.value + } + + override fun hashCode() = value.hashCode() + + override fun toString() = value.toString() + } override fun equals(other: Any?): Boolean { if (this === other) { @@ -381,7 +766,12 @@ private constructor( dateLastUsed == other.dateLastUsed && dateUpdated == other.dateUpdated && secureKey == other.secureKey && + status == other.status && + expiresAt == other.expiresAt && + lastRotatedAt == other.lastRotatedAt && name == other.name && + previousKeyExpiresAt == other.previousKeyExpiresAt && + secret == other.secret && additionalProperties == other.additionalProperties } @@ -392,7 +782,12 @@ private constructor( dateLastUsed, dateUpdated, secureKey, + status, + expiresAt, + lastRotatedAt, name, + previousKeyExpiresAt, + secret, additionalProperties, ) } @@ -400,5 +795,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "ApiKeyCreateResponse{id=$id, dateCreated=$dateCreated, dateLastUsed=$dateLastUsed, dateUpdated=$dateUpdated, secureKey=$secureKey, name=$name, additionalProperties=$additionalProperties}" + "ApiKeyCreateResponse{id=$id, dateCreated=$dateCreated, dateLastUsed=$dateLastUsed, dateUpdated=$dateUpdated, secureKey=$secureKey, status=$status, expiresAt=$expiresAt, lastRotatedAt=$lastRotatedAt, name=$name, previousKeyExpiresAt=$previousKeyExpiresAt, secret=$secret, additionalProperties=$additionalProperties}" } diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParams.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParams.kt new file mode 100644 index 00000000..d0f0e5a9 --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParams.kt @@ -0,0 +1,261 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import com.openlayer.api.core.JsonValue +import com.openlayer.api.core.Params +import com.openlayer.api.core.checkRequired +import com.openlayer.api.core.http.Headers +import com.openlayer.api.core.http.QueryParams +import com.openlayer.api.core.toImmutable +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Delete one of your API keys. Every secret for the key stops working immediately, including a + * previous secret still in its rotation grace period. + */ +class ApiKeyDeleteParams +private constructor( + private val workspaceId: String, + private val apiKeyId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, + private val additionalBodyProperties: Map, +) : Params { + + fun workspaceId(): String = workspaceId + + fun apiKeyId(): Optional = Optional.ofNullable(apiKeyId) + + /** Additional body properties to send with the request. */ + fun _additionalBodyProperties(): Map = additionalBodyProperties + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ApiKeyDeleteParams]. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyDeleteParams]. */ + class Builder internal constructor() { + + private var workspaceId: String? = null + private var apiKeyId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + private var additionalBodyProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(apiKeyDeleteParams: ApiKeyDeleteParams) = apply { + workspaceId = apiKeyDeleteParams.workspaceId + apiKeyId = apiKeyDeleteParams.apiKeyId + additionalHeaders = apiKeyDeleteParams.additionalHeaders.toBuilder() + additionalQueryParams = apiKeyDeleteParams.additionalQueryParams.toBuilder() + additionalBodyProperties = apiKeyDeleteParams.additionalBodyProperties.toMutableMap() + } + + fun workspaceId(workspaceId: String) = apply { this.workspaceId = workspaceId } + + fun apiKeyId(apiKeyId: String?) = apply { this.apiKeyId = apiKeyId } + + /** Alias for calling [Builder.apiKeyId] with `apiKeyId.orElse(null)`. */ + fun apiKeyId(apiKeyId: Optional) = apiKeyId(apiKeyId.getOrNull()) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + this.additionalBodyProperties.clear() + putAllAdditionalBodyProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + additionalBodyProperties.put(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + this.additionalBodyProperties.putAll(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { + additionalBodyProperties.remove(key) + } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalBodyProperty) + } + + /** + * Returns an immutable instance of [ApiKeyDeleteParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyDeleteParams = + ApiKeyDeleteParams( + checkRequired("workspaceId", workspaceId), + apiKeyId, + additionalHeaders.build(), + additionalQueryParams.build(), + additionalBodyProperties.toImmutable(), + ) + } + + fun _body(): Optional> = + Optional.ofNullable(additionalBodyProperties.ifEmpty { null }) + + fun _pathParam(index: Int): String = + when (index) { + 0 -> workspaceId + 1 -> apiKeyId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ApiKeyDeleteParams && + workspaceId == other.workspaceId && + apiKeyId == other.apiKeyId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams && + additionalBodyProperties == other.additionalBodyProperties + } + + override fun hashCode(): Int = + Objects.hash( + workspaceId, + apiKeyId, + additionalHeaders, + additionalQueryParams, + additionalBodyProperties, + ) + + override fun toString() = + "ApiKeyDeleteParams{workspaceId=$workspaceId, apiKeyId=$apiKeyId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams, additionalBodyProperties=$additionalBodyProperties}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParams.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParams.kt new file mode 100644 index 00000000..81cdbb66 --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParams.kt @@ -0,0 +1,193 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import com.openlayer.api.core.Params +import com.openlayer.api.core.http.Headers +import com.openlayer.api.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * List the API keys you own in a workspace, with their lifecycle status. Secrets are never + * returned; `secureKey` is an obfuscated hint. + */ +class ApiKeyListParams +private constructor( + private val workspaceId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun workspaceId(): Optional = Optional.ofNullable(workspaceId) + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + @JvmStatic fun none(): ApiKeyListParams = builder().build() + + /** Returns a mutable builder for constructing an instance of [ApiKeyListParams]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyListParams]. */ + class Builder internal constructor() { + + private var workspaceId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(apiKeyListParams: ApiKeyListParams) = apply { + workspaceId = apiKeyListParams.workspaceId + additionalHeaders = apiKeyListParams.additionalHeaders.toBuilder() + additionalQueryParams = apiKeyListParams.additionalQueryParams.toBuilder() + } + + fun workspaceId(workspaceId: String?) = apply { this.workspaceId = workspaceId } + + /** Alias for calling [Builder.workspaceId] with `workspaceId.orElse(null)`. */ + fun workspaceId(workspaceId: Optional) = workspaceId(workspaceId.getOrNull()) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ApiKeyListParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ApiKeyListParams = + ApiKeyListParams(workspaceId, additionalHeaders.build(), additionalQueryParams.build()) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> workspaceId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ApiKeyListParams && + workspaceId == other.workspaceId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(workspaceId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ApiKeyListParams{workspaceId=$workspaceId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponse.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponse.kt new file mode 100644 index 00000000..aa89cae0 --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponse.kt @@ -0,0 +1,799 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +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.openlayer.api.core.Enum +import com.openlayer.api.core.ExcludeMissing +import com.openlayer.api.core.JsonField +import com.openlayer.api.core.JsonMissing +import com.openlayer.api.core.JsonValue +import com.openlayer.api.core.checkRequired +import com.openlayer.api.errors.OpenlayerInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class ApiKeyListResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val dateCreated: JsonField, + private val dateLastUsed: JsonField, + private val dateUpdated: JsonField, + private val secureKey: JsonField, + private val status: JsonField, + private val expiresAt: JsonField, + private val lastRotatedAt: JsonField, + private val name: JsonField, + private val previousKeyExpiresAt: JsonField, + private val secret: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("dateCreated") + @ExcludeMissing + dateCreated: JsonField = JsonMissing.of(), + @JsonProperty("dateLastUsed") + @ExcludeMissing + dateLastUsed: JsonField = JsonMissing.of(), + @JsonProperty("dateUpdated") + @ExcludeMissing + dateUpdated: JsonField = JsonMissing.of(), + @JsonProperty("secureKey") @ExcludeMissing secureKey: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("lastRotatedAt") + @ExcludeMissing + lastRotatedAt: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + previousKeyExpiresAt: JsonField = JsonMissing.of(), + @JsonProperty("secret") @ExcludeMissing secret: JsonField = JsonMissing.of(), + ) : this( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + mutableMapOf(), + ) + + /** + * The API key id. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * The API key creation date. + * + * @throws OpenlayerInvalidDataException 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 dateCreated(): OffsetDateTime = dateCreated.getRequired("dateCreated") + + /** + * The API key last use date. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun dateLastUsed(): Optional = dateLastUsed.getOptional("dateLastUsed") + + /** + * The API key last update date. + * + * @throws OpenlayerInvalidDataException 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 dateUpdated(): OffsetDateTime = dateUpdated.getRequired("dateUpdated") + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds the + * full secret, for backward compatibility; prefer `secret`. + * + * @throws OpenlayerInvalidDataException 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 secureKey(): String = secureKey.getRequired("secureKey") + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + * + * @throws OpenlayerInvalidDataException 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 status(): Status = status.getRequired("status") + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key is + * created or rotated, and must be in the future. When the request is authenticated with an API + * key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): Optional = expiresAt.getOptional("expiresAt") + + /** + * When the key was last rotated. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lastRotatedAt(): Optional = lastRotatedAt.getOptional("lastRotatedAt") + + /** + * The API key name. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun name(): Optional = name.getOptional("name") + + /** + * While `status` is `rotating`, when the previous secret stops authenticating. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun previousKeyExpiresAt(): Optional = + previousKeyExpiresAt.getOptional("previousKeyExpiresAt") + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun secret(): Optional = secret.getOptional("secret") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [dateCreated]. + * + * Unlike [dateCreated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateCreated") + @ExcludeMissing + fun _dateCreated(): JsonField = dateCreated + + /** + * Returns the raw JSON value of [dateLastUsed]. + * + * Unlike [dateLastUsed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateLastUsed") + @ExcludeMissing + fun _dateLastUsed(): JsonField = dateLastUsed + + /** + * Returns the raw JSON value of [dateUpdated]. + * + * Unlike [dateUpdated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateUpdated") + @ExcludeMissing + fun _dateUpdated(): JsonField = dateUpdated + + /** + * Returns the raw JSON value of [secureKey]. + * + * Unlike [secureKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secureKey") @ExcludeMissing fun _secureKey(): JsonField = secureKey + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [lastRotatedAt]. + * + * Unlike [lastRotatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lastRotatedAt") + @ExcludeMissing + fun _lastRotatedAt(): JsonField = lastRotatedAt + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [previousKeyExpiresAt]. + * + * Unlike [previousKeyExpiresAt], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + fun _previousKeyExpiresAt(): JsonField = previousKeyExpiresAt + + /** + * Returns the raw JSON value of [secret]. + * + * Unlike [secret], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secret") @ExcludeMissing fun _secret(): JsonField = secret + + @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 [ApiKeyListResponse]. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyListResponse]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var dateCreated: JsonField? = null + private var dateLastUsed: JsonField? = null + private var dateUpdated: JsonField? = null + private var secureKey: JsonField? = null + private var status: JsonField? = null + private var expiresAt: JsonField = JsonMissing.of() + private var lastRotatedAt: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var previousKeyExpiresAt: JsonField = JsonMissing.of() + private var secret: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(apiKeyListResponse: ApiKeyListResponse) = apply { + id = apiKeyListResponse.id + dateCreated = apiKeyListResponse.dateCreated + dateLastUsed = apiKeyListResponse.dateLastUsed + dateUpdated = apiKeyListResponse.dateUpdated + secureKey = apiKeyListResponse.secureKey + status = apiKeyListResponse.status + expiresAt = apiKeyListResponse.expiresAt + lastRotatedAt = apiKeyListResponse.lastRotatedAt + name = apiKeyListResponse.name + previousKeyExpiresAt = apiKeyListResponse.previousKeyExpiresAt + secret = apiKeyListResponse.secret + additionalProperties = apiKeyListResponse.additionalProperties.toMutableMap() + } + + /** The API key id. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The API key creation date. */ + fun dateCreated(dateCreated: OffsetDateTime) = dateCreated(JsonField.of(dateCreated)) + + /** + * Sets [Builder.dateCreated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateCreated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateCreated(dateCreated: JsonField) = apply { + this.dateCreated = dateCreated + } + + /** The API key last use date. */ + fun dateLastUsed(dateLastUsed: OffsetDateTime?) = + dateLastUsed(JsonField.ofNullable(dateLastUsed)) + + /** Alias for calling [Builder.dateLastUsed] with `dateLastUsed.orElse(null)`. */ + fun dateLastUsed(dateLastUsed: Optional) = + dateLastUsed(dateLastUsed.getOrNull()) + + /** + * Sets [Builder.dateLastUsed] to an arbitrary JSON value. + * + * You should usually call [Builder.dateLastUsed] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateLastUsed(dateLastUsed: JsonField) = apply { + this.dateLastUsed = dateLastUsed + } + + /** The API key last update date. */ + fun dateUpdated(dateUpdated: OffsetDateTime) = dateUpdated(JsonField.of(dateUpdated)) + + /** + * Sets [Builder.dateUpdated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateUpdated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateUpdated(dateUpdated: JsonField) = apply { + this.dateUpdated = dateUpdated + } + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds + * the full secret, for backward compatibility; prefer `secret`. + */ + fun secureKey(secureKey: String) = secureKey(JsonField.of(secureKey)) + + /** + * Sets [Builder.secureKey] to an arbitrary JSON value. + * + * You should usually call [Builder.secureKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun secureKey(secureKey: JsonField) = apply { this.secureKey = secureKey } + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the + * key was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = expiresAt(JsonField.ofNullable(expiresAt)) + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + + /** When the key was last rotated. */ + fun lastRotatedAt(lastRotatedAt: OffsetDateTime?) = + lastRotatedAt(JsonField.ofNullable(lastRotatedAt)) + + /** Alias for calling [Builder.lastRotatedAt] with `lastRotatedAt.orElse(null)`. */ + fun lastRotatedAt(lastRotatedAt: Optional) = + lastRotatedAt(lastRotatedAt.getOrNull()) + + /** + * Sets [Builder.lastRotatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.lastRotatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lastRotatedAt(lastRotatedAt: JsonField) = apply { + this.lastRotatedAt = lastRotatedAt + } + + /** The API key name. */ + fun name(name: String?) = name(JsonField.ofNullable(name)) + + /** Alias for calling [Builder.name] with `name.orElse(null)`. */ + fun name(name: Optional) = name(name.getOrNull()) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** While `status` is `rotating`, when the previous secret stops authenticating. */ + fun previousKeyExpiresAt(previousKeyExpiresAt: OffsetDateTime?) = + previousKeyExpiresAt(JsonField.ofNullable(previousKeyExpiresAt)) + + /** + * Alias for calling [Builder.previousKeyExpiresAt] with + * `previousKeyExpiresAt.orElse(null)`. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: Optional) = + previousKeyExpiresAt(previousKeyExpiresAt.getOrNull()) + + /** + * Sets [Builder.previousKeyExpiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.previousKeyExpiresAt] with a well-typed [OffsetDateTime] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: JsonField) = apply { + this.previousKeyExpiresAt = previousKeyExpiresAt + } + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + */ + fun secret(secret: String) = secret(JsonField.of(secret)) + + /** + * Sets [Builder.secret] to an arbitrary JSON value. + * + * You should usually call [Builder.secret] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun secret(secret: JsonField) = apply { this.secret = secret } + + 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 [ApiKeyListResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyListResponse = + ApiKeyListResponse( + checkRequired("id", id), + checkRequired("dateCreated", dateCreated), + checkRequired("dateLastUsed", dateLastUsed), + checkRequired("dateUpdated", dateUpdated), + checkRequired("secureKey", secureKey), + checkRequired("status", status), + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + 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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): ApiKeyListResponse = apply { + if (validated) { + return@apply + } + + id() + dateCreated() + dateLastUsed() + dateUpdated() + secureKey() + status().validate() + expiresAt() + lastRotatedAt() + name() + previousKeyExpiresAt() + secret() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (id.asKnown().isPresent) 1 else 0) + + (if (dateCreated.asKnown().isPresent) 1 else 0) + + (if (dateLastUsed.asKnown().isPresent) 1 else 0) + + (if (dateUpdated.asKnown().isPresent) 1 else 0) + + (if (secureKey.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (lastRotatedAt.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (previousKeyExpiresAt.asKnown().isPresent) 1 else 0) + + (if (secret.asKnown().isPresent) 1 else 0) + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + class Status @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 ACTIVE = of("active") + + @JvmField val ROTATING = of("rotating") + + @JvmField val EXPIRED = of("expired") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + ROTATING, + EXPIRED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + ROTATING, + EXPIRED, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + ROTATING -> Value.ROTATING + EXPIRED -> Value.EXPIRED + 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 OpenlayerInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACTIVE -> Known.ACTIVE + ROTATING -> Known.ROTATING + EXPIRED -> Known.EXPIRED + else -> throw OpenlayerInvalidDataException("Unknown Status: $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 OpenlayerInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenlayerInvalidDataException("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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + 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 Status && 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 ApiKeyListResponse && + id == other.id && + dateCreated == other.dateCreated && + dateLastUsed == other.dateLastUsed && + dateUpdated == other.dateUpdated && + secureKey == other.secureKey && + status == other.status && + expiresAt == other.expiresAt && + lastRotatedAt == other.lastRotatedAt && + name == other.name && + previousKeyExpiresAt == other.previousKeyExpiresAt && + secret == other.secret && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ApiKeyListResponse{id=$id, dateCreated=$dateCreated, dateLastUsed=$dateLastUsed, dateUpdated=$dateUpdated, secureKey=$secureKey, status=$status, expiresAt=$expiresAt, lastRotatedAt=$lastRotatedAt, name=$name, previousKeyExpiresAt=$previousKeyExpiresAt, secret=$secret, additionalProperties=$additionalProperties}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParams.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParams.kt new file mode 100644 index 00000000..158e51dd --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParams.kt @@ -0,0 +1,220 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import com.openlayer.api.core.Params +import com.openlayer.api.core.checkRequired +import com.openlayer.api.core.http.Headers +import com.openlayer.api.core.http.QueryParams +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Retrieve one of your API keys, with its lifecycle status. The secret is never returned; + * `secureKey` is an obfuscated hint. + */ +class ApiKeyRetrieveParams +private constructor( + private val workspaceId: String, + private val apiKeyId: String?, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun workspaceId(): String = workspaceId + + fun apiKeyId(): Optional = Optional.ofNullable(apiKeyId) + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ApiKeyRetrieveParams]. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyRetrieveParams]. */ + class Builder internal constructor() { + + private var workspaceId: String? = null + private var apiKeyId: String? = null + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(apiKeyRetrieveParams: ApiKeyRetrieveParams) = apply { + workspaceId = apiKeyRetrieveParams.workspaceId + apiKeyId = apiKeyRetrieveParams.apiKeyId + additionalHeaders = apiKeyRetrieveParams.additionalHeaders.toBuilder() + additionalQueryParams = apiKeyRetrieveParams.additionalQueryParams.toBuilder() + } + + fun workspaceId(workspaceId: String) = apply { this.workspaceId = workspaceId } + + fun apiKeyId(apiKeyId: String?) = apply { this.apiKeyId = apiKeyId } + + /** Alias for calling [Builder.apiKeyId] with `apiKeyId.orElse(null)`. */ + fun apiKeyId(apiKeyId: Optional) = apiKeyId(apiKeyId.getOrNull()) + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ApiKeyRetrieveParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyRetrieveParams = + ApiKeyRetrieveParams( + checkRequired("workspaceId", workspaceId), + apiKeyId, + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _pathParam(index: Int): String = + when (index) { + 0 -> workspaceId + 1 -> apiKeyId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ApiKeyRetrieveParams && + workspaceId == other.workspaceId && + apiKeyId == other.apiKeyId && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(workspaceId, apiKeyId, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ApiKeyRetrieveParams{workspaceId=$workspaceId, apiKeyId=$apiKeyId, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponse.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponse.kt new file mode 100644 index 00000000..765caa6c --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponse.kt @@ -0,0 +1,799 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +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.openlayer.api.core.Enum +import com.openlayer.api.core.ExcludeMissing +import com.openlayer.api.core.JsonField +import com.openlayer.api.core.JsonMissing +import com.openlayer.api.core.JsonValue +import com.openlayer.api.core.checkRequired +import com.openlayer.api.errors.OpenlayerInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class ApiKeyRetrieveResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val dateCreated: JsonField, + private val dateLastUsed: JsonField, + private val dateUpdated: JsonField, + private val secureKey: JsonField, + private val status: JsonField, + private val expiresAt: JsonField, + private val lastRotatedAt: JsonField, + private val name: JsonField, + private val previousKeyExpiresAt: JsonField, + private val secret: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("dateCreated") + @ExcludeMissing + dateCreated: JsonField = JsonMissing.of(), + @JsonProperty("dateLastUsed") + @ExcludeMissing + dateLastUsed: JsonField = JsonMissing.of(), + @JsonProperty("dateUpdated") + @ExcludeMissing + dateUpdated: JsonField = JsonMissing.of(), + @JsonProperty("secureKey") @ExcludeMissing secureKey: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("lastRotatedAt") + @ExcludeMissing + lastRotatedAt: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + previousKeyExpiresAt: JsonField = JsonMissing.of(), + @JsonProperty("secret") @ExcludeMissing secret: JsonField = JsonMissing.of(), + ) : this( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + mutableMapOf(), + ) + + /** + * The API key id. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * The API key creation date. + * + * @throws OpenlayerInvalidDataException 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 dateCreated(): OffsetDateTime = dateCreated.getRequired("dateCreated") + + /** + * The API key last use date. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun dateLastUsed(): Optional = dateLastUsed.getOptional("dateLastUsed") + + /** + * The API key last update date. + * + * @throws OpenlayerInvalidDataException 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 dateUpdated(): OffsetDateTime = dateUpdated.getRequired("dateUpdated") + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds the + * full secret, for backward compatibility; prefer `secret`. + * + * @throws OpenlayerInvalidDataException 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 secureKey(): String = secureKey.getRequired("secureKey") + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + * + * @throws OpenlayerInvalidDataException 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 status(): Status = status.getRequired("status") + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key is + * created or rotated, and must be in the future. When the request is authenticated with an API + * key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): Optional = expiresAt.getOptional("expiresAt") + + /** + * When the key was last rotated. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lastRotatedAt(): Optional = lastRotatedAt.getOptional("lastRotatedAt") + + /** + * The API key name. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun name(): Optional = name.getOptional("name") + + /** + * While `status` is `rotating`, when the previous secret stops authenticating. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun previousKeyExpiresAt(): Optional = + previousKeyExpiresAt.getOptional("previousKeyExpiresAt") + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun secret(): Optional = secret.getOptional("secret") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [dateCreated]. + * + * Unlike [dateCreated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateCreated") + @ExcludeMissing + fun _dateCreated(): JsonField = dateCreated + + /** + * Returns the raw JSON value of [dateLastUsed]. + * + * Unlike [dateLastUsed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateLastUsed") + @ExcludeMissing + fun _dateLastUsed(): JsonField = dateLastUsed + + /** + * Returns the raw JSON value of [dateUpdated]. + * + * Unlike [dateUpdated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateUpdated") + @ExcludeMissing + fun _dateUpdated(): JsonField = dateUpdated + + /** + * Returns the raw JSON value of [secureKey]. + * + * Unlike [secureKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secureKey") @ExcludeMissing fun _secureKey(): JsonField = secureKey + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [lastRotatedAt]. + * + * Unlike [lastRotatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lastRotatedAt") + @ExcludeMissing + fun _lastRotatedAt(): JsonField = lastRotatedAt + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [previousKeyExpiresAt]. + * + * Unlike [previousKeyExpiresAt], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + fun _previousKeyExpiresAt(): JsonField = previousKeyExpiresAt + + /** + * Returns the raw JSON value of [secret]. + * + * Unlike [secret], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secret") @ExcludeMissing fun _secret(): JsonField = secret + + @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 [ApiKeyRetrieveResponse]. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyRetrieveResponse]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var dateCreated: JsonField? = null + private var dateLastUsed: JsonField? = null + private var dateUpdated: JsonField? = null + private var secureKey: JsonField? = null + private var status: JsonField? = null + private var expiresAt: JsonField = JsonMissing.of() + private var lastRotatedAt: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var previousKeyExpiresAt: JsonField = JsonMissing.of() + private var secret: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(apiKeyRetrieveResponse: ApiKeyRetrieveResponse) = apply { + id = apiKeyRetrieveResponse.id + dateCreated = apiKeyRetrieveResponse.dateCreated + dateLastUsed = apiKeyRetrieveResponse.dateLastUsed + dateUpdated = apiKeyRetrieveResponse.dateUpdated + secureKey = apiKeyRetrieveResponse.secureKey + status = apiKeyRetrieveResponse.status + expiresAt = apiKeyRetrieveResponse.expiresAt + lastRotatedAt = apiKeyRetrieveResponse.lastRotatedAt + name = apiKeyRetrieveResponse.name + previousKeyExpiresAt = apiKeyRetrieveResponse.previousKeyExpiresAt + secret = apiKeyRetrieveResponse.secret + additionalProperties = apiKeyRetrieveResponse.additionalProperties.toMutableMap() + } + + /** The API key id. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The API key creation date. */ + fun dateCreated(dateCreated: OffsetDateTime) = dateCreated(JsonField.of(dateCreated)) + + /** + * Sets [Builder.dateCreated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateCreated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateCreated(dateCreated: JsonField) = apply { + this.dateCreated = dateCreated + } + + /** The API key last use date. */ + fun dateLastUsed(dateLastUsed: OffsetDateTime?) = + dateLastUsed(JsonField.ofNullable(dateLastUsed)) + + /** Alias for calling [Builder.dateLastUsed] with `dateLastUsed.orElse(null)`. */ + fun dateLastUsed(dateLastUsed: Optional) = + dateLastUsed(dateLastUsed.getOrNull()) + + /** + * Sets [Builder.dateLastUsed] to an arbitrary JSON value. + * + * You should usually call [Builder.dateLastUsed] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateLastUsed(dateLastUsed: JsonField) = apply { + this.dateLastUsed = dateLastUsed + } + + /** The API key last update date. */ + fun dateUpdated(dateUpdated: OffsetDateTime) = dateUpdated(JsonField.of(dateUpdated)) + + /** + * Sets [Builder.dateUpdated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateUpdated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateUpdated(dateUpdated: JsonField) = apply { + this.dateUpdated = dateUpdated + } + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds + * the full secret, for backward compatibility; prefer `secret`. + */ + fun secureKey(secureKey: String) = secureKey(JsonField.of(secureKey)) + + /** + * Sets [Builder.secureKey] to an arbitrary JSON value. + * + * You should usually call [Builder.secureKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun secureKey(secureKey: JsonField) = apply { this.secureKey = secureKey } + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the + * key was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = expiresAt(JsonField.ofNullable(expiresAt)) + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + + /** When the key was last rotated. */ + fun lastRotatedAt(lastRotatedAt: OffsetDateTime?) = + lastRotatedAt(JsonField.ofNullable(lastRotatedAt)) + + /** Alias for calling [Builder.lastRotatedAt] with `lastRotatedAt.orElse(null)`. */ + fun lastRotatedAt(lastRotatedAt: Optional) = + lastRotatedAt(lastRotatedAt.getOrNull()) + + /** + * Sets [Builder.lastRotatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.lastRotatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lastRotatedAt(lastRotatedAt: JsonField) = apply { + this.lastRotatedAt = lastRotatedAt + } + + /** The API key name. */ + fun name(name: String?) = name(JsonField.ofNullable(name)) + + /** Alias for calling [Builder.name] with `name.orElse(null)`. */ + fun name(name: Optional) = name(name.getOrNull()) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** While `status` is `rotating`, when the previous secret stops authenticating. */ + fun previousKeyExpiresAt(previousKeyExpiresAt: OffsetDateTime?) = + previousKeyExpiresAt(JsonField.ofNullable(previousKeyExpiresAt)) + + /** + * Alias for calling [Builder.previousKeyExpiresAt] with + * `previousKeyExpiresAt.orElse(null)`. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: Optional) = + previousKeyExpiresAt(previousKeyExpiresAt.getOrNull()) + + /** + * Sets [Builder.previousKeyExpiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.previousKeyExpiresAt] with a well-typed [OffsetDateTime] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: JsonField) = apply { + this.previousKeyExpiresAt = previousKeyExpiresAt + } + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + */ + fun secret(secret: String) = secret(JsonField.of(secret)) + + /** + * Sets [Builder.secret] to an arbitrary JSON value. + * + * You should usually call [Builder.secret] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun secret(secret: JsonField) = apply { this.secret = secret } + + 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 [ApiKeyRetrieveResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyRetrieveResponse = + ApiKeyRetrieveResponse( + checkRequired("id", id), + checkRequired("dateCreated", dateCreated), + checkRequired("dateLastUsed", dateLastUsed), + checkRequired("dateUpdated", dateUpdated), + checkRequired("secureKey", secureKey), + checkRequired("status", status), + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + 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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): ApiKeyRetrieveResponse = apply { + if (validated) { + return@apply + } + + id() + dateCreated() + dateLastUsed() + dateUpdated() + secureKey() + status().validate() + expiresAt() + lastRotatedAt() + name() + previousKeyExpiresAt() + secret() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (id.asKnown().isPresent) 1 else 0) + + (if (dateCreated.asKnown().isPresent) 1 else 0) + + (if (dateLastUsed.asKnown().isPresent) 1 else 0) + + (if (dateUpdated.asKnown().isPresent) 1 else 0) + + (if (secureKey.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (lastRotatedAt.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (previousKeyExpiresAt.asKnown().isPresent) 1 else 0) + + (if (secret.asKnown().isPresent) 1 else 0) + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + class Status @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 ACTIVE = of("active") + + @JvmField val ROTATING = of("rotating") + + @JvmField val EXPIRED = of("expired") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + ROTATING, + EXPIRED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + ROTATING, + EXPIRED, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + ROTATING -> Value.ROTATING + EXPIRED -> Value.EXPIRED + 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 OpenlayerInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACTIVE -> Known.ACTIVE + ROTATING -> Known.ROTATING + EXPIRED -> Known.EXPIRED + else -> throw OpenlayerInvalidDataException("Unknown Status: $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 OpenlayerInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenlayerInvalidDataException("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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + 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 Status && 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 ApiKeyRetrieveResponse && + id == other.id && + dateCreated == other.dateCreated && + dateLastUsed == other.dateLastUsed && + dateUpdated == other.dateUpdated && + secureKey == other.secureKey && + status == other.status && + expiresAt == other.expiresAt && + lastRotatedAt == other.lastRotatedAt && + name == other.name && + previousKeyExpiresAt == other.previousKeyExpiresAt && + secret == other.secret && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ApiKeyRetrieveResponse{id=$id, dateCreated=$dateCreated, dateLastUsed=$dateLastUsed, dateUpdated=$dateUpdated, secureKey=$secureKey, status=$status, expiresAt=$expiresAt, lastRotatedAt=$lastRotatedAt, name=$name, previousKeyExpiresAt=$previousKeyExpiresAt, secret=$secret, additionalProperties=$additionalProperties}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParams.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParams.kt new file mode 100644 index 00000000..e3aff585 --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParams.kt @@ -0,0 +1,554 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +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.openlayer.api.core.ExcludeMissing +import com.openlayer.api.core.JsonField +import com.openlayer.api.core.JsonMissing +import com.openlayer.api.core.JsonValue +import com.openlayer.api.core.Params +import com.openlayer.api.core.checkRequired +import com.openlayer.api.core.http.Headers +import com.openlayer.api.core.http.QueryParams +import com.openlayer.api.errors.OpenlayerInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Replace an API key's secret now. The new secret is returned in `secret`, only in this response. + * Send `expiresAt` to change the key's expiry (`null` for never); omit it to keep the current one. + * The previous secret keeps authenticating for `gracePeriodHours` (default 0, so it stops working + * immediately), and never past `expiresAt`. The key keeps its id and name. Expired keys cannot be + * rotated. Only one previous secret is kept, so rotating again during a grace period retires the + * older one immediately. + */ +class ApiKeyRotateParams +private constructor( + private val workspaceId: String, + private val apiKeyId: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun workspaceId(): String = workspaceId + + fun apiKeyId(): Optional = Optional.ofNullable(apiKeyId) + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key is + * created or rotated, and must be in the future. When the request is authenticated with an API + * key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): Optional = body.expiresAt() + + /** + * Hours the previous secret keeps authenticating. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun gracePeriodHours(): Optional = body.gracePeriodHours() + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _expiresAt(): JsonField = body._expiresAt() + + /** + * Returns the raw JSON value of [gracePeriodHours]. + * + * Unlike [gracePeriodHours], this method doesn't throw if the JSON field has an unexpected + * type. + */ + fun _gracePeriodHours(): JsonField = body._gracePeriodHours() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ApiKeyRotateParams]. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyRotateParams]. */ + class Builder internal constructor() { + + private var workspaceId: String? = null + private var apiKeyId: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(apiKeyRotateParams: ApiKeyRotateParams) = apply { + workspaceId = apiKeyRotateParams.workspaceId + apiKeyId = apiKeyRotateParams.apiKeyId + body = apiKeyRotateParams.body.toBuilder() + additionalHeaders = apiKeyRotateParams.additionalHeaders.toBuilder() + additionalQueryParams = apiKeyRotateParams.additionalQueryParams.toBuilder() + } + + fun workspaceId(workspaceId: String) = apply { this.workspaceId = workspaceId } + + fun apiKeyId(apiKeyId: String?) = apply { this.apiKeyId = apiKeyId } + + /** Alias for calling [Builder.apiKeyId] with `apiKeyId.orElse(null)`. */ + fun apiKeyId(apiKeyId: Optional) = apiKeyId(apiKeyId.getOrNull()) + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [expiresAt] + * - [gracePeriodHours] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = apply { body.expiresAt(expiresAt) } + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { body.expiresAt(expiresAt) } + + /** Hours the previous secret keeps authenticating. */ + fun gracePeriodHours(gracePeriodHours: Long) = apply { + body.gracePeriodHours(gracePeriodHours) + } + + /** + * Sets [Builder.gracePeriodHours] to an arbitrary JSON value. + * + * You should usually call [Builder.gracePeriodHours] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun gracePeriodHours(gracePeriodHours: JsonField) = apply { + body.gracePeriodHours(gracePeriodHours) + } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ApiKeyRotateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyRotateParams = + ApiKeyRotateParams( + checkRequired("workspaceId", workspaceId), + apiKeyId, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> workspaceId + 1 -> apiKeyId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val expiresAt: JsonField, + private val gracePeriodHours: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("gracePeriodHours") + @ExcludeMissing + gracePeriodHours: JsonField = JsonMissing.of(), + ) : this(expiresAt, gracePeriodHours, mutableMapOf()) + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun expiresAt(): Optional = expiresAt.getOptional("expiresAt") + + /** + * Hours the previous secret keeps authenticating. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun gracePeriodHours(): Optional = gracePeriodHours.getOptional("gracePeriodHours") + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [gracePeriodHours]. + * + * Unlike [gracePeriodHours], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("gracePeriodHours") + @ExcludeMissing + fun _gracePeriodHours(): JsonField = gracePeriodHours + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Body]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var expiresAt: JsonField = JsonMissing.of() + private var gracePeriodHours: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + expiresAt = body.expiresAt + gracePeriodHours = body.gracePeriodHours + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the + * key is created or rotated, and must be in the future. When the request is + * authenticated with an API key that expires, the result can't be later than that key's + * expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = expiresAt(JsonField.ofNullable(expiresAt)) + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { + this.expiresAt = expiresAt + } + + /** Hours the previous secret keeps authenticating. */ + fun gracePeriodHours(gracePeriodHours: Long) = + gracePeriodHours(JsonField.of(gracePeriodHours)) + + /** + * Sets [Builder.gracePeriodHours] to an arbitrary JSON value. + * + * You should usually call [Builder.gracePeriodHours] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun gracePeriodHours(gracePeriodHours: JsonField) = apply { + this.gracePeriodHours = gracePeriodHours + } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Body = + Body(expiresAt, gracePeriodHours, 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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + expiresAt() + gracePeriodHours() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + 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 (expiresAt.asKnown().isPresent) 1 else 0) + + (if (gracePeriodHours.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + expiresAt == other.expiresAt && + gracePeriodHours == other.gracePeriodHours && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(expiresAt, gracePeriodHours, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "Body{expiresAt=$expiresAt, gracePeriodHours=$gracePeriodHours, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ApiKeyRotateParams && + workspaceId == other.workspaceId && + apiKeyId == other.apiKeyId && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(workspaceId, apiKeyId, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ApiKeyRotateParams{workspaceId=$workspaceId, apiKeyId=$apiKeyId, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponse.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponse.kt new file mode 100644 index 00000000..b7b32e21 --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponse.kt @@ -0,0 +1,799 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +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.openlayer.api.core.Enum +import com.openlayer.api.core.ExcludeMissing +import com.openlayer.api.core.JsonField +import com.openlayer.api.core.JsonMissing +import com.openlayer.api.core.JsonValue +import com.openlayer.api.core.checkRequired +import com.openlayer.api.errors.OpenlayerInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class ApiKeyRotateResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val dateCreated: JsonField, + private val dateLastUsed: JsonField, + private val dateUpdated: JsonField, + private val secureKey: JsonField, + private val status: JsonField, + private val expiresAt: JsonField, + private val lastRotatedAt: JsonField, + private val name: JsonField, + private val previousKeyExpiresAt: JsonField, + private val secret: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("dateCreated") + @ExcludeMissing + dateCreated: JsonField = JsonMissing.of(), + @JsonProperty("dateLastUsed") + @ExcludeMissing + dateLastUsed: JsonField = JsonMissing.of(), + @JsonProperty("dateUpdated") + @ExcludeMissing + dateUpdated: JsonField = JsonMissing.of(), + @JsonProperty("secureKey") @ExcludeMissing secureKey: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("lastRotatedAt") + @ExcludeMissing + lastRotatedAt: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + previousKeyExpiresAt: JsonField = JsonMissing.of(), + @JsonProperty("secret") @ExcludeMissing secret: JsonField = JsonMissing.of(), + ) : this( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + mutableMapOf(), + ) + + /** + * The API key id. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * The API key creation date. + * + * @throws OpenlayerInvalidDataException 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 dateCreated(): OffsetDateTime = dateCreated.getRequired("dateCreated") + + /** + * The API key last use date. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun dateLastUsed(): Optional = dateLastUsed.getOptional("dateLastUsed") + + /** + * The API key last update date. + * + * @throws OpenlayerInvalidDataException 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 dateUpdated(): OffsetDateTime = dateUpdated.getRequired("dateUpdated") + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds the + * full secret, for backward compatibility; prefer `secret`. + * + * @throws OpenlayerInvalidDataException 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 secureKey(): String = secureKey.getRequired("secureKey") + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + * + * @throws OpenlayerInvalidDataException 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 status(): Status = status.getRequired("status") + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key is + * created or rotated, and must be in the future. When the request is authenticated with an API + * key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): Optional = expiresAt.getOptional("expiresAt") + + /** + * When the key was last rotated. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lastRotatedAt(): Optional = lastRotatedAt.getOptional("lastRotatedAt") + + /** + * The API key name. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun name(): Optional = name.getOptional("name") + + /** + * While `status` is `rotating`, when the previous secret stops authenticating. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun previousKeyExpiresAt(): Optional = + previousKeyExpiresAt.getOptional("previousKeyExpiresAt") + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun secret(): Optional = secret.getOptional("secret") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [dateCreated]. + * + * Unlike [dateCreated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateCreated") + @ExcludeMissing + fun _dateCreated(): JsonField = dateCreated + + /** + * Returns the raw JSON value of [dateLastUsed]. + * + * Unlike [dateLastUsed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateLastUsed") + @ExcludeMissing + fun _dateLastUsed(): JsonField = dateLastUsed + + /** + * Returns the raw JSON value of [dateUpdated]. + * + * Unlike [dateUpdated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateUpdated") + @ExcludeMissing + fun _dateUpdated(): JsonField = dateUpdated + + /** + * Returns the raw JSON value of [secureKey]. + * + * Unlike [secureKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secureKey") @ExcludeMissing fun _secureKey(): JsonField = secureKey + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [lastRotatedAt]. + * + * Unlike [lastRotatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lastRotatedAt") + @ExcludeMissing + fun _lastRotatedAt(): JsonField = lastRotatedAt + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [previousKeyExpiresAt]. + * + * Unlike [previousKeyExpiresAt], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + fun _previousKeyExpiresAt(): JsonField = previousKeyExpiresAt + + /** + * Returns the raw JSON value of [secret]. + * + * Unlike [secret], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secret") @ExcludeMissing fun _secret(): JsonField = secret + + @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 [ApiKeyRotateResponse]. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyRotateResponse]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var dateCreated: JsonField? = null + private var dateLastUsed: JsonField? = null + private var dateUpdated: JsonField? = null + private var secureKey: JsonField? = null + private var status: JsonField? = null + private var expiresAt: JsonField = JsonMissing.of() + private var lastRotatedAt: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var previousKeyExpiresAt: JsonField = JsonMissing.of() + private var secret: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(apiKeyRotateResponse: ApiKeyRotateResponse) = apply { + id = apiKeyRotateResponse.id + dateCreated = apiKeyRotateResponse.dateCreated + dateLastUsed = apiKeyRotateResponse.dateLastUsed + dateUpdated = apiKeyRotateResponse.dateUpdated + secureKey = apiKeyRotateResponse.secureKey + status = apiKeyRotateResponse.status + expiresAt = apiKeyRotateResponse.expiresAt + lastRotatedAt = apiKeyRotateResponse.lastRotatedAt + name = apiKeyRotateResponse.name + previousKeyExpiresAt = apiKeyRotateResponse.previousKeyExpiresAt + secret = apiKeyRotateResponse.secret + additionalProperties = apiKeyRotateResponse.additionalProperties.toMutableMap() + } + + /** The API key id. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The API key creation date. */ + fun dateCreated(dateCreated: OffsetDateTime) = dateCreated(JsonField.of(dateCreated)) + + /** + * Sets [Builder.dateCreated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateCreated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateCreated(dateCreated: JsonField) = apply { + this.dateCreated = dateCreated + } + + /** The API key last use date. */ + fun dateLastUsed(dateLastUsed: OffsetDateTime?) = + dateLastUsed(JsonField.ofNullable(dateLastUsed)) + + /** Alias for calling [Builder.dateLastUsed] with `dateLastUsed.orElse(null)`. */ + fun dateLastUsed(dateLastUsed: Optional) = + dateLastUsed(dateLastUsed.getOrNull()) + + /** + * Sets [Builder.dateLastUsed] to an arbitrary JSON value. + * + * You should usually call [Builder.dateLastUsed] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateLastUsed(dateLastUsed: JsonField) = apply { + this.dateLastUsed = dateLastUsed + } + + /** The API key last update date. */ + fun dateUpdated(dateUpdated: OffsetDateTime) = dateUpdated(JsonField.of(dateUpdated)) + + /** + * Sets [Builder.dateUpdated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateUpdated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateUpdated(dateUpdated: JsonField) = apply { + this.dateUpdated = dateUpdated + } + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds + * the full secret, for backward compatibility; prefer `secret`. + */ + fun secureKey(secureKey: String) = secureKey(JsonField.of(secureKey)) + + /** + * Sets [Builder.secureKey] to an arbitrary JSON value. + * + * You should usually call [Builder.secureKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun secureKey(secureKey: JsonField) = apply { this.secureKey = secureKey } + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the + * key was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = expiresAt(JsonField.ofNullable(expiresAt)) + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + + /** When the key was last rotated. */ + fun lastRotatedAt(lastRotatedAt: OffsetDateTime?) = + lastRotatedAt(JsonField.ofNullable(lastRotatedAt)) + + /** Alias for calling [Builder.lastRotatedAt] with `lastRotatedAt.orElse(null)`. */ + fun lastRotatedAt(lastRotatedAt: Optional) = + lastRotatedAt(lastRotatedAt.getOrNull()) + + /** + * Sets [Builder.lastRotatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.lastRotatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lastRotatedAt(lastRotatedAt: JsonField) = apply { + this.lastRotatedAt = lastRotatedAt + } + + /** The API key name. */ + fun name(name: String?) = name(JsonField.ofNullable(name)) + + /** Alias for calling [Builder.name] with `name.orElse(null)`. */ + fun name(name: Optional) = name(name.getOrNull()) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** While `status` is `rotating`, when the previous secret stops authenticating. */ + fun previousKeyExpiresAt(previousKeyExpiresAt: OffsetDateTime?) = + previousKeyExpiresAt(JsonField.ofNullable(previousKeyExpiresAt)) + + /** + * Alias for calling [Builder.previousKeyExpiresAt] with + * `previousKeyExpiresAt.orElse(null)`. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: Optional) = + previousKeyExpiresAt(previousKeyExpiresAt.getOrNull()) + + /** + * Sets [Builder.previousKeyExpiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.previousKeyExpiresAt] with a well-typed [OffsetDateTime] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: JsonField) = apply { + this.previousKeyExpiresAt = previousKeyExpiresAt + } + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + */ + fun secret(secret: String) = secret(JsonField.of(secret)) + + /** + * Sets [Builder.secret] to an arbitrary JSON value. + * + * You should usually call [Builder.secret] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun secret(secret: JsonField) = apply { this.secret = secret } + + 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 [ApiKeyRotateResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyRotateResponse = + ApiKeyRotateResponse( + checkRequired("id", id), + checkRequired("dateCreated", dateCreated), + checkRequired("dateLastUsed", dateLastUsed), + checkRequired("dateUpdated", dateUpdated), + checkRequired("secureKey", secureKey), + checkRequired("status", status), + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + 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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): ApiKeyRotateResponse = apply { + if (validated) { + return@apply + } + + id() + dateCreated() + dateLastUsed() + dateUpdated() + secureKey() + status().validate() + expiresAt() + lastRotatedAt() + name() + previousKeyExpiresAt() + secret() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (id.asKnown().isPresent) 1 else 0) + + (if (dateCreated.asKnown().isPresent) 1 else 0) + + (if (dateLastUsed.asKnown().isPresent) 1 else 0) + + (if (dateUpdated.asKnown().isPresent) 1 else 0) + + (if (secureKey.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (lastRotatedAt.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (previousKeyExpiresAt.asKnown().isPresent) 1 else 0) + + (if (secret.asKnown().isPresent) 1 else 0) + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + class Status @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 ACTIVE = of("active") + + @JvmField val ROTATING = of("rotating") + + @JvmField val EXPIRED = of("expired") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + ROTATING, + EXPIRED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + ROTATING, + EXPIRED, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + ROTATING -> Value.ROTATING + EXPIRED -> Value.EXPIRED + 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 OpenlayerInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACTIVE -> Known.ACTIVE + ROTATING -> Known.ROTATING + EXPIRED -> Known.EXPIRED + else -> throw OpenlayerInvalidDataException("Unknown Status: $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 OpenlayerInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenlayerInvalidDataException("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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + 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 Status && 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 ApiKeyRotateResponse && + id == other.id && + dateCreated == other.dateCreated && + dateLastUsed == other.dateLastUsed && + dateUpdated == other.dateUpdated && + secureKey == other.secureKey && + status == other.status && + expiresAt == other.expiresAt && + lastRotatedAt == other.lastRotatedAt && + name == other.name && + previousKeyExpiresAt == other.previousKeyExpiresAt && + secret == other.secret && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ApiKeyRotateResponse{id=$id, dateCreated=$dateCreated, dateLastUsed=$dateLastUsed, dateUpdated=$dateUpdated, secureKey=$secureKey, status=$status, expiresAt=$expiresAt, lastRotatedAt=$lastRotatedAt, name=$name, previousKeyExpiresAt=$previousKeyExpiresAt, secret=$secret, additionalProperties=$additionalProperties}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParams.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParams.kt new file mode 100644 index 00000000..a7627f6b --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParams.kt @@ -0,0 +1,448 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +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.openlayer.api.core.ExcludeMissing +import com.openlayer.api.core.JsonField +import com.openlayer.api.core.JsonMissing +import com.openlayer.api.core.JsonValue +import com.openlayer.api.core.Params +import com.openlayer.api.core.checkRequired +import com.openlayer.api.core.http.Headers +import com.openlayer.api.core.http.QueryParams +import com.openlayer.api.errors.OpenlayerInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * Rename one of your API keys. A key's expiry can't be updated; rotate the key with a new + * `expiresAt` instead, so extending a key's life always issues a new secret. + */ +class ApiKeyUpdateParams +private constructor( + private val workspaceId: String, + private val apiKeyId: String?, + private val body: Body, + private val additionalHeaders: Headers, + private val additionalQueryParams: QueryParams, +) : Params { + + fun workspaceId(): String = workspaceId + + fun apiKeyId(): Optional = Optional.ofNullable(apiKeyId) + + /** + * The API key name. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun name(): Optional = body.name() + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + fun _name(): JsonField = body._name() + + fun _additionalBodyProperties(): Map = body._additionalProperties() + + /** Additional headers to send with the request. */ + fun _additionalHeaders(): Headers = additionalHeaders + + /** Additional query param to send with the request. */ + fun _additionalQueryParams(): QueryParams = additionalQueryParams + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ApiKeyUpdateParams]. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyUpdateParams]. */ + class Builder internal constructor() { + + private var workspaceId: String? = null + private var apiKeyId: String? = null + private var body: Body.Builder = Body.builder() + private var additionalHeaders: Headers.Builder = Headers.builder() + private var additionalQueryParams: QueryParams.Builder = QueryParams.builder() + + @JvmSynthetic + internal fun from(apiKeyUpdateParams: ApiKeyUpdateParams) = apply { + workspaceId = apiKeyUpdateParams.workspaceId + apiKeyId = apiKeyUpdateParams.apiKeyId + body = apiKeyUpdateParams.body.toBuilder() + additionalHeaders = apiKeyUpdateParams.additionalHeaders.toBuilder() + additionalQueryParams = apiKeyUpdateParams.additionalQueryParams.toBuilder() + } + + fun workspaceId(workspaceId: String) = apply { this.workspaceId = workspaceId } + + fun apiKeyId(apiKeyId: String?) = apply { this.apiKeyId = apiKeyId } + + /** Alias for calling [Builder.apiKeyId] with `apiKeyId.orElse(null)`. */ + fun apiKeyId(apiKeyId: Optional) = apiKeyId(apiKeyId.getOrNull()) + + /** + * Sets the entire request body. + * + * This is generally only useful if you are already constructing the body separately. + * Otherwise, it's more convenient to use the top-level setters instead: + * - [name] + */ + fun body(body: Body) = apply { this.body = body.toBuilder() } + + /** The API key name. */ + fun name(name: String?) = apply { body.name(name) } + + /** Alias for calling [Builder.name] with `name.orElse(null)`. */ + fun name(name: Optional) = name(name.getOrNull()) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { body.name(name) } + + fun additionalBodyProperties(additionalBodyProperties: Map) = apply { + body.additionalProperties(additionalBodyProperties) + } + + fun putAdditionalBodyProperty(key: String, value: JsonValue) = apply { + body.putAdditionalProperty(key, value) + } + + fun putAllAdditionalBodyProperties(additionalBodyProperties: Map) = + apply { + body.putAllAdditionalProperties(additionalBodyProperties) + } + + fun removeAdditionalBodyProperty(key: String) = apply { body.removeAdditionalProperty(key) } + + fun removeAllAdditionalBodyProperties(keys: Set) = apply { + body.removeAllAdditionalProperties(keys) + } + + fun additionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun additionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.clear() + putAllAdditionalHeaders(additionalHeaders) + } + + fun putAdditionalHeader(name: String, value: String) = apply { + additionalHeaders.put(name, value) + } + + fun putAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.put(name, values) + } + + fun putAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun putAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.putAll(additionalHeaders) + } + + fun replaceAdditionalHeaders(name: String, value: String) = apply { + additionalHeaders.replace(name, value) + } + + fun replaceAdditionalHeaders(name: String, values: Iterable) = apply { + additionalHeaders.replace(name, values) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Headers) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun replaceAllAdditionalHeaders(additionalHeaders: Map>) = apply { + this.additionalHeaders.replaceAll(additionalHeaders) + } + + fun removeAdditionalHeaders(name: String) = apply { additionalHeaders.remove(name) } + + fun removeAllAdditionalHeaders(names: Set) = apply { + additionalHeaders.removeAll(names) + } + + fun additionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun additionalQueryParams(additionalQueryParams: Map>) = apply { + this.additionalQueryParams.clear() + putAllAdditionalQueryParams(additionalQueryParams) + } + + fun putAdditionalQueryParam(key: String, value: String) = apply { + additionalQueryParams.put(key, value) + } + + fun putAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.put(key, values) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun putAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.putAll(additionalQueryParams) + } + + fun replaceAdditionalQueryParams(key: String, value: String) = apply { + additionalQueryParams.replace(key, value) + } + + fun replaceAdditionalQueryParams(key: String, values: Iterable) = apply { + additionalQueryParams.replace(key, values) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: QueryParams) = apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun replaceAllAdditionalQueryParams(additionalQueryParams: Map>) = + apply { + this.additionalQueryParams.replaceAll(additionalQueryParams) + } + + fun removeAdditionalQueryParams(key: String) = apply { additionalQueryParams.remove(key) } + + fun removeAllAdditionalQueryParams(keys: Set) = apply { + additionalQueryParams.removeAll(keys) + } + + /** + * Returns an immutable instance of [ApiKeyUpdateParams]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .workspaceId() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyUpdateParams = + ApiKeyUpdateParams( + checkRequired("workspaceId", workspaceId), + apiKeyId, + body.build(), + additionalHeaders.build(), + additionalQueryParams.build(), + ) + } + + fun _body(): Body = body + + fun _pathParam(index: Int): String = + when (index) { + 0 -> workspaceId + 1 -> apiKeyId ?: "" + else -> "" + } + + override fun _headers(): Headers = additionalHeaders + + override fun _queryParams(): QueryParams = additionalQueryParams + + class Body + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val name: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of() + ) : this(name, mutableMapOf()) + + /** + * The API key name. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if + * the server responded with an unexpected value). + */ + fun name(): Optional = name.getOptional("name") + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [Body]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [Body]. */ + class Builder internal constructor() { + + private var name: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(body: Body) = apply { + name = body.name + additionalProperties = body.additionalProperties.toMutableMap() + } + + /** The API key name. */ + fun name(name: String?) = name(JsonField.ofNullable(name)) + + /** Alias for calling [Builder.name] with `name.orElse(null)`. */ + fun name(name: Optional) = name(name.getOrNull()) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun name(name: JsonField) = apply { this.name = name } + + fun additionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.clear() + putAllAdditionalProperties(additionalProperties) + } + + fun putAdditionalProperty(key: String, value: JsonValue) = apply { + additionalProperties.put(key, value) + } + + fun putAllAdditionalProperties(additionalProperties: Map) = apply { + this.additionalProperties.putAll(additionalProperties) + } + + fun removeAdditionalProperty(key: String) = apply { additionalProperties.remove(key) } + + fun removeAllAdditionalProperties(keys: Set) = apply { + keys.forEach(::removeAdditionalProperty) + } + + /** + * Returns an immutable instance of [Body]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): Body = Body(name, 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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Body = apply { + if (validated) { + return@apply + } + + name() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + 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 (name.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is Body && + name == other.name && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(name, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = "Body{name=$name, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ApiKeyUpdateParams && + workspaceId == other.workspaceId && + apiKeyId == other.apiKeyId && + body == other.body && + additionalHeaders == other.additionalHeaders && + additionalQueryParams == other.additionalQueryParams + } + + override fun hashCode(): Int = + Objects.hash(workspaceId, apiKeyId, body, additionalHeaders, additionalQueryParams) + + override fun toString() = + "ApiKeyUpdateParams{workspaceId=$workspaceId, apiKeyId=$apiKeyId, body=$body, additionalHeaders=$additionalHeaders, additionalQueryParams=$additionalQueryParams}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponse.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponse.kt new file mode 100644 index 00000000..5f47558f --- /dev/null +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponse.kt @@ -0,0 +1,799 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +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.openlayer.api.core.Enum +import com.openlayer.api.core.ExcludeMissing +import com.openlayer.api.core.JsonField +import com.openlayer.api.core.JsonMissing +import com.openlayer.api.core.JsonValue +import com.openlayer.api.core.checkRequired +import com.openlayer.api.errors.OpenlayerInvalidDataException +import java.time.OffsetDateTime +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +class ApiKeyUpdateResponse +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val id: JsonField, + private val dateCreated: JsonField, + private val dateLastUsed: JsonField, + private val dateUpdated: JsonField, + private val secureKey: JsonField, + private val status: JsonField, + private val expiresAt: JsonField, + private val lastRotatedAt: JsonField, + private val name: JsonField, + private val previousKeyExpiresAt: JsonField, + private val secret: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), + @JsonProperty("dateCreated") + @ExcludeMissing + dateCreated: JsonField = JsonMissing.of(), + @JsonProperty("dateLastUsed") + @ExcludeMissing + dateLastUsed: JsonField = JsonMissing.of(), + @JsonProperty("dateUpdated") + @ExcludeMissing + dateUpdated: JsonField = JsonMissing.of(), + @JsonProperty("secureKey") @ExcludeMissing secureKey: JsonField = JsonMissing.of(), + @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), + @JsonProperty("expiresAt") + @ExcludeMissing + expiresAt: JsonField = JsonMissing.of(), + @JsonProperty("lastRotatedAt") + @ExcludeMissing + lastRotatedAt: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + previousKeyExpiresAt: JsonField = JsonMissing.of(), + @JsonProperty("secret") @ExcludeMissing secret: JsonField = JsonMissing.of(), + ) : this( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + mutableMapOf(), + ) + + /** + * The API key id. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun id(): String = id.getRequired("id") + + /** + * The API key creation date. + * + * @throws OpenlayerInvalidDataException 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 dateCreated(): OffsetDateTime = dateCreated.getRequired("dateCreated") + + /** + * The API key last use date. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun dateLastUsed(): Optional = dateLastUsed.getOptional("dateLastUsed") + + /** + * The API key last update date. + * + * @throws OpenlayerInvalidDataException 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 dateUpdated(): OffsetDateTime = dateUpdated.getRequired("dateUpdated") + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds the + * full secret, for backward compatibility; prefer `secret`. + * + * @throws OpenlayerInvalidDataException 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 secureKey(): String = secureKey.getRequired("secureKey") + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + * + * @throws OpenlayerInvalidDataException 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 status(): Status = status.getRequired("status") + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key is + * created or rotated, and must be in the future. When the request is authenticated with an API + * key that expires, the result can't be later than that key's expiry. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun expiresAt(): Optional = expiresAt.getOptional("expiresAt") + + /** + * When the key was last rotated. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun lastRotatedAt(): Optional = lastRotatedAt.getOptional("lastRotatedAt") + + /** + * The API key name. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun name(): Optional = name.getOptional("name") + + /** + * While `status` is `rotating`, when the previous secret stops authenticating. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun previousKeyExpiresAt(): Optional = + previousKeyExpiresAt.getOptional("previousKeyExpiresAt") + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + * + * @throws OpenlayerInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun secret(): Optional = secret.getOptional("secret") + + /** + * Returns the raw JSON value of [id]. + * + * Unlike [id], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("id") @ExcludeMissing fun _id(): JsonField = id + + /** + * Returns the raw JSON value of [dateCreated]. + * + * Unlike [dateCreated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateCreated") + @ExcludeMissing + fun _dateCreated(): JsonField = dateCreated + + /** + * Returns the raw JSON value of [dateLastUsed]. + * + * Unlike [dateLastUsed], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateLastUsed") + @ExcludeMissing + fun _dateLastUsed(): JsonField = dateLastUsed + + /** + * Returns the raw JSON value of [dateUpdated]. + * + * Unlike [dateUpdated], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("dateUpdated") + @ExcludeMissing + fun _dateUpdated(): JsonField = dateUpdated + + /** + * Returns the raw JSON value of [secureKey]. + * + * Unlike [secureKey], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secureKey") @ExcludeMissing fun _secureKey(): JsonField = secureKey + + /** + * Returns the raw JSON value of [status]. + * + * Unlike [status], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("status") @ExcludeMissing fun _status(): JsonField = status + + /** + * Returns the raw JSON value of [expiresAt]. + * + * Unlike [expiresAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("expiresAt") + @ExcludeMissing + fun _expiresAt(): JsonField = expiresAt + + /** + * Returns the raw JSON value of [lastRotatedAt]. + * + * Unlike [lastRotatedAt], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("lastRotatedAt") + @ExcludeMissing + fun _lastRotatedAt(): JsonField = lastRotatedAt + + /** + * Returns the raw JSON value of [name]. + * + * Unlike [name], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("name") @ExcludeMissing fun _name(): JsonField = name + + /** + * Returns the raw JSON value of [previousKeyExpiresAt]. + * + * Unlike [previousKeyExpiresAt], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("previousKeyExpiresAt") + @ExcludeMissing + fun _previousKeyExpiresAt(): JsonField = previousKeyExpiresAt + + /** + * Returns the raw JSON value of [secret]. + * + * Unlike [secret], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("secret") @ExcludeMissing fun _secret(): JsonField = secret + + @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 [ApiKeyUpdateResponse]. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ApiKeyUpdateResponse]. */ + class Builder internal constructor() { + + private var id: JsonField? = null + private var dateCreated: JsonField? = null + private var dateLastUsed: JsonField? = null + private var dateUpdated: JsonField? = null + private var secureKey: JsonField? = null + private var status: JsonField? = null + private var expiresAt: JsonField = JsonMissing.of() + private var lastRotatedAt: JsonField = JsonMissing.of() + private var name: JsonField = JsonMissing.of() + private var previousKeyExpiresAt: JsonField = JsonMissing.of() + private var secret: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(apiKeyUpdateResponse: ApiKeyUpdateResponse) = apply { + id = apiKeyUpdateResponse.id + dateCreated = apiKeyUpdateResponse.dateCreated + dateLastUsed = apiKeyUpdateResponse.dateLastUsed + dateUpdated = apiKeyUpdateResponse.dateUpdated + secureKey = apiKeyUpdateResponse.secureKey + status = apiKeyUpdateResponse.status + expiresAt = apiKeyUpdateResponse.expiresAt + lastRotatedAt = apiKeyUpdateResponse.lastRotatedAt + name = apiKeyUpdateResponse.name + previousKeyExpiresAt = apiKeyUpdateResponse.previousKeyExpiresAt + secret = apiKeyUpdateResponse.secret + additionalProperties = apiKeyUpdateResponse.additionalProperties.toMutableMap() + } + + /** The API key id. */ + fun id(id: String) = id(JsonField.of(id)) + + /** + * Sets [Builder.id] to an arbitrary JSON value. + * + * You should usually call [Builder.id] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun id(id: JsonField) = apply { this.id = id } + + /** The API key creation date. */ + fun dateCreated(dateCreated: OffsetDateTime) = dateCreated(JsonField.of(dateCreated)) + + /** + * Sets [Builder.dateCreated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateCreated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateCreated(dateCreated: JsonField) = apply { + this.dateCreated = dateCreated + } + + /** The API key last use date. */ + fun dateLastUsed(dateLastUsed: OffsetDateTime?) = + dateLastUsed(JsonField.ofNullable(dateLastUsed)) + + /** Alias for calling [Builder.dateLastUsed] with `dateLastUsed.orElse(null)`. */ + fun dateLastUsed(dateLastUsed: Optional) = + dateLastUsed(dateLastUsed.getOrNull()) + + /** + * Sets [Builder.dateLastUsed] to an arbitrary JSON value. + * + * You should usually call [Builder.dateLastUsed] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateLastUsed(dateLastUsed: JsonField) = apply { + this.dateLastUsed = dateLastUsed + } + + /** The API key last update date. */ + fun dateUpdated(dateUpdated: OffsetDateTime) = dateUpdated(JsonField.of(dateUpdated)) + + /** + * Sets [Builder.dateUpdated] to an arbitrary JSON value. + * + * You should usually call [Builder.dateUpdated] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun dateUpdated(dateUpdated: JsonField) = apply { + this.dateUpdated = dateUpdated + } + + /** + * An obfuscated hint of the API key value. When a key is created or rotated this also holds + * the full secret, for backward compatibility; prefer `secret`. + */ + fun secureKey(secureKey: String) = secureKey(JsonField.of(secureKey)) + + /** + * Sets [Builder.secureKey] to an arbitrary JSON value. + * + * You should usually call [Builder.secureKey] with a well-typed [String] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun secureKey(secureKey: JsonField) = apply { this.secureKey = secureKey } + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the + * key was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + fun status(status: Status) = status(JsonField.of(status)) + + /** + * Sets [Builder.status] to an arbitrary JSON value. + * + * You should usually call [Builder.status] with a well-typed [Status] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun status(status: JsonField) = apply { this.status = status } + + /** + * When the key stops authenticating. `null` means the key never expires. Set when the key + * is created or rotated, and must be in the future. When the request is authenticated with + * an API key that expires, the result can't be later than that key's expiry. + */ + fun expiresAt(expiresAt: OffsetDateTime?) = expiresAt(JsonField.ofNullable(expiresAt)) + + /** Alias for calling [Builder.expiresAt] with `expiresAt.orElse(null)`. */ + fun expiresAt(expiresAt: Optional) = expiresAt(expiresAt.getOrNull()) + + /** + * Sets [Builder.expiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.expiresAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun expiresAt(expiresAt: JsonField) = apply { this.expiresAt = expiresAt } + + /** When the key was last rotated. */ + fun lastRotatedAt(lastRotatedAt: OffsetDateTime?) = + lastRotatedAt(JsonField.ofNullable(lastRotatedAt)) + + /** Alias for calling [Builder.lastRotatedAt] with `lastRotatedAt.orElse(null)`. */ + fun lastRotatedAt(lastRotatedAt: Optional) = + lastRotatedAt(lastRotatedAt.getOrNull()) + + /** + * Sets [Builder.lastRotatedAt] to an arbitrary JSON value. + * + * You should usually call [Builder.lastRotatedAt] with a well-typed [OffsetDateTime] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun lastRotatedAt(lastRotatedAt: JsonField) = apply { + this.lastRotatedAt = lastRotatedAt + } + + /** The API key name. */ + fun name(name: String?) = name(JsonField.ofNullable(name)) + + /** Alias for calling [Builder.name] with `name.orElse(null)`. */ + fun name(name: Optional) = name(name.getOrNull()) + + /** + * Sets [Builder.name] to an arbitrary JSON value. + * + * You should usually call [Builder.name] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun name(name: JsonField) = apply { this.name = name } + + /** While `status` is `rotating`, when the previous secret stops authenticating. */ + fun previousKeyExpiresAt(previousKeyExpiresAt: OffsetDateTime?) = + previousKeyExpiresAt(JsonField.ofNullable(previousKeyExpiresAt)) + + /** + * Alias for calling [Builder.previousKeyExpiresAt] with + * `previousKeyExpiresAt.orElse(null)`. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: Optional) = + previousKeyExpiresAt(previousKeyExpiresAt.getOrNull()) + + /** + * Sets [Builder.previousKeyExpiresAt] to an arbitrary JSON value. + * + * You should usually call [Builder.previousKeyExpiresAt] with a well-typed [OffsetDateTime] + * value instead. This method is primarily for setting the field to an undocumented or not + * yet supported value. + */ + fun previousKeyExpiresAt(previousKeyExpiresAt: JsonField) = apply { + this.previousKeyExpiresAt = previousKeyExpiresAt + } + + /** + * The full API key. Only present in the response that creates or rotates the key, and never + * shown again. + */ + fun secret(secret: String) = secret(JsonField.of(secret)) + + /** + * Sets [Builder.secret] to an arbitrary JSON value. + * + * You should usually call [Builder.secret] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun secret(secret: JsonField) = apply { this.secret = secret } + + 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 [ApiKeyUpdateResponse]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .id() + * .dateCreated() + * .dateLastUsed() + * .dateUpdated() + * .secureKey() + * .status() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): ApiKeyUpdateResponse = + ApiKeyUpdateResponse( + checkRequired("id", id), + checkRequired("dateCreated", dateCreated), + checkRequired("dateLastUsed", dateLastUsed), + checkRequired("dateUpdated", dateUpdated), + checkRequired("secureKey", secureKey), + checkRequired("status", status), + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + 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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): ApiKeyUpdateResponse = apply { + if (validated) { + return@apply + } + + id() + dateCreated() + dateLastUsed() + dateUpdated() + secureKey() + status().validate() + expiresAt() + lastRotatedAt() + name() + previousKeyExpiresAt() + secret() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (id.asKnown().isPresent) 1 else 0) + + (if (dateCreated.asKnown().isPresent) 1 else 0) + + (if (dateLastUsed.asKnown().isPresent) 1 else 0) + + (if (dateUpdated.asKnown().isPresent) 1 else 0) + + (if (secureKey.asKnown().isPresent) 1 else 0) + + (status.asKnown().getOrNull()?.validity() ?: 0) + + (if (expiresAt.asKnown().isPresent) 1 else 0) + + (if (lastRotatedAt.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + + (if (previousKeyExpiresAt.asKnown().isPresent) 1 else 0) + + (if (secret.asKnown().isPresent) 1 else 0) + + /** + * The key's lifecycle state. `active`: the current secret authenticates. `rotating`: the key + * was rotated and the previous secret still authenticates until `previousKeyExpiresAt`. + * `expired`: `expiresAt` has passed and no secret authenticates. + */ + class Status @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 ACTIVE = of("active") + + @JvmField val ROTATING = of("rotating") + + @JvmField val EXPIRED = of("expired") + + @JvmStatic fun of(value: String) = Status(JsonField.of(value)) + } + + /** An enum containing [Status]'s known values. */ + enum class Known { + ACTIVE, + ROTATING, + EXPIRED, + } + + /** + * An enum containing [Status]'s known values, as well as an [_UNKNOWN] member. + * + * An instance of [Status] 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 { + ACTIVE, + ROTATING, + EXPIRED, + /** An enum member indicating that [Status] 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) { + ACTIVE -> Value.ACTIVE + ROTATING -> Value.ROTATING + EXPIRED -> Value.EXPIRED + 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 OpenlayerInvalidDataException if this class instance's value is a not a known + * member. + */ + fun known(): Known = + when (this) { + ACTIVE -> Known.ACTIVE + ROTATING -> Known.ROTATING + EXPIRED -> Known.EXPIRED + else -> throw OpenlayerInvalidDataException("Unknown Status: $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 OpenlayerInvalidDataException if this class instance's value does not have the + * expected primitive type. + */ + fun asString(): String = + _value().asString().orElseThrow { + OpenlayerInvalidDataException("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 OpenlayerInvalidDataException if any value type in this object doesn't match its + * expected type. + */ + fun validate(): Status = apply { + if (validated) { + return@apply + } + + known() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenlayerInvalidDataException) { + 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 Status && 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 ApiKeyUpdateResponse && + id == other.id && + dateCreated == other.dateCreated && + dateLastUsed == other.dateLastUsed && + dateUpdated == other.dateUpdated && + secureKey == other.secureKey && + status == other.status && + expiresAt == other.expiresAt && + lastRotatedAt == other.lastRotatedAt && + name == other.name && + previousKeyExpiresAt == other.previousKeyExpiresAt && + secret == other.secret && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + id, + dateCreated, + dateLastUsed, + dateUpdated, + secureKey, + status, + expiresAt, + lastRotatedAt, + name, + previousKeyExpiresAt, + secret, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ApiKeyUpdateResponse{id=$id, dateCreated=$dateCreated, dateLastUsed=$dateLastUsed, dateUpdated=$dateUpdated, secureKey=$secureKey, status=$status, expiresAt=$expiresAt, lastRotatedAt=$lastRotatedAt, name=$name, previousKeyExpiresAt=$previousKeyExpiresAt, secret=$secret, additionalProperties=$additionalProperties}" +} diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsync.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsync.kt index f771bd00..33ed5039 100644 --- a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsync.kt +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsync.kt @@ -4,9 +4,19 @@ package com.openlayer.api.services.async.workspaces import com.openlayer.api.core.ClientOptions import com.openlayer.api.core.RequestOptions +import com.openlayer.api.core.http.HttpResponse import com.openlayer.api.core.http.HttpResponseFor import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateParams import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyDeleteParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer @@ -24,7 +34,12 @@ interface ApiKeyServiceAsync { */ fun withOptions(modifier: Consumer): ApiKeyServiceAsync - /** Create a new API key in a workspace. */ + /** + * Create a new API key in a workspace. The full secret is returned in `secret`, only in this + * response. Optionally set `expiresAt`. When you authenticate with an API key that expires, the + * new key can't outlive it: omit `expiresAt` to inherit that expiry, and a later expiry (or + * `null`) is rejected with 400. + */ fun create(workspaceId: String): CompletableFuture = create(workspaceId, ApiKeyCreateParams.none()) @@ -59,6 +74,155 @@ interface ApiKeyServiceAsync { ): CompletableFuture = create(workspaceId, ApiKeyCreateParams.none(), requestOptions) + /** + * Retrieve one of your API keys, with its lifecycle status. The secret is never returned; + * `secureKey` is an obfuscated hint. + */ + fun retrieve( + apiKeyId: String, + params: ApiKeyRetrieveParams, + ): CompletableFuture = retrieve(apiKeyId, params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + apiKeyId: String, + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + retrieve(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve(params: ApiKeyRetrieveParams): CompletableFuture = + retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * Rename one of your API keys. A key's expiry can't be updated; rotate the key with a new + * `expiresAt` instead, so extending a key's life always issues a new secret. + */ + fun update( + apiKeyId: String, + params: ApiKeyUpdateParams, + ): CompletableFuture = update(apiKeyId, params, RequestOptions.none()) + + /** @see update */ + fun update( + apiKeyId: String, + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + update(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see update */ + fun update(params: ApiKeyUpdateParams): CompletableFuture = + update(params, RequestOptions.none()) + + /** @see update */ + fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * List the API keys you own in a workspace, with their lifecycle status. Secrets are never + * returned; `secureKey` is an obfuscated hint. + */ + fun list(workspaceId: String): CompletableFuture> = + list(workspaceId, ApiKeyListParams.none()) + + /** @see list */ + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + list(params.toBuilder().workspaceId(workspaceId).build(), requestOptions) + + /** @see list */ + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + ): CompletableFuture> = + list(workspaceId, params, RequestOptions.none()) + + /** @see list */ + fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** @see list */ + fun list(params: ApiKeyListParams): CompletableFuture> = + list(params, RequestOptions.none()) + + /** @see list */ + fun list( + workspaceId: String, + requestOptions: RequestOptions, + ): CompletableFuture> = + list(workspaceId, ApiKeyListParams.none(), requestOptions) + + /** + * Delete one of your API keys. Every secret for the key stops working immediately, including a + * previous secret still in its rotation grace period. + */ + fun delete(apiKeyId: String, params: ApiKeyDeleteParams): CompletableFuture = + delete(apiKeyId, params, RequestOptions.none()) + + /** @see delete */ + fun delete( + apiKeyId: String, + params: ApiKeyDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see delete */ + fun delete(params: ApiKeyDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see delete */ + fun delete( + params: ApiKeyDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * Replace an API key's secret now. The new secret is returned in `secret`, only in this + * response. Send `expiresAt` to change the key's expiry (`null` for never); omit it to keep the + * current one. The previous secret keeps authenticating for `gracePeriodHours` (default 0, so + * it stops working immediately), and never past `expiresAt`. The key keeps its id and name. + * Expired keys cannot be rotated. Only one previous secret is kept, so rotating again during a + * grace period retires the older one immediately. + */ + fun rotate( + apiKeyId: String, + params: ApiKeyRotateParams, + ): CompletableFuture = rotate(apiKeyId, params, RequestOptions.none()) + + /** @see rotate */ + fun rotate( + apiKeyId: String, + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + rotate(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see rotate */ + fun rotate(params: ApiKeyRotateParams): CompletableFuture = + rotate(params, RequestOptions.none()) + + /** @see rotate */ + fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + /** * A view of [ApiKeyServiceAsync] that provides access to raw HTTP responses for each method. */ @@ -113,5 +277,164 @@ interface ApiKeyServiceAsync { requestOptions: RequestOptions, ): CompletableFuture> = create(workspaceId, ApiKeyCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /workspaces/{workspaceId}/api-keys/{apiKeyId}`, but + * is otherwise the same as [ApiKeyServiceAsync.retrieve]. + */ + fun retrieve( + apiKeyId: String, + params: ApiKeyRetrieveParams, + ): CompletableFuture> = + retrieve(apiKeyId, params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + apiKeyId: String, + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + retrieve(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve( + params: ApiKeyRetrieveParams + ): CompletableFuture> = + retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `put /workspaces/{workspaceId}/api-keys/{apiKeyId}`, but + * is otherwise the same as [ApiKeyServiceAsync.update]. + */ + fun update( + apiKeyId: String, + params: ApiKeyUpdateParams, + ): CompletableFuture> = + update(apiKeyId, params, RequestOptions.none()) + + /** @see update */ + fun update( + apiKeyId: String, + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + update(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see update */ + fun update( + params: ApiKeyUpdateParams + ): CompletableFuture> = + update(params, RequestOptions.none()) + + /** @see update */ + fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> + + /** + * Returns a raw HTTP response for `get /workspaces/{workspaceId}/api-keys`, but is + * otherwise the same as [ApiKeyServiceAsync.list]. + */ + fun list( + workspaceId: String + ): CompletableFuture>> = + list(workspaceId, ApiKeyListParams.none()) + + /** @see list */ + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> = + list(params.toBuilder().workspaceId(workspaceId).build(), requestOptions) + + /** @see list */ + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + ): CompletableFuture>> = + list(workspaceId, params, RequestOptions.none()) + + /** @see list */ + fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture>> + + /** @see list */ + fun list( + params: ApiKeyListParams + ): CompletableFuture>> = + list(params, RequestOptions.none()) + + /** @see list */ + fun list( + workspaceId: String, + requestOptions: RequestOptions, + ): CompletableFuture>> = + list(workspaceId, ApiKeyListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /workspaces/{workspaceId}/api-keys/{apiKeyId}`, + * but is otherwise the same as [ApiKeyServiceAsync.delete]. + */ + fun delete(apiKeyId: String, params: ApiKeyDeleteParams): CompletableFuture = + delete(apiKeyId, params, RequestOptions.none()) + + /** @see delete */ + fun delete( + apiKeyId: String, + params: ApiKeyDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture = + delete(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see delete */ + fun delete(params: ApiKeyDeleteParams): CompletableFuture = + delete(params, RequestOptions.none()) + + /** @see delete */ + fun delete( + params: ApiKeyDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture + + /** + * Returns a raw HTTP response for `post + * /workspaces/{workspaceId}/api-keys/{apiKeyId}/rotate`, but is otherwise the same as + * [ApiKeyServiceAsync.rotate]. + */ + fun rotate( + apiKeyId: String, + params: ApiKeyRotateParams, + ): CompletableFuture> = + rotate(apiKeyId, params, RequestOptions.none()) + + /** @see rotate */ + fun rotate( + apiKeyId: String, + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> = + rotate(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see rotate */ + fun rotate( + params: ApiKeyRotateParams + ): CompletableFuture> = + rotate(params, RequestOptions.none()) + + /** @see rotate */ + fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): CompletableFuture> } } diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncImpl.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncImpl.kt index 4566f11c..2ec90d8e 100644 --- a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncImpl.kt +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncImpl.kt @@ -5,6 +5,7 @@ package com.openlayer.api.services.async.workspaces import com.openlayer.api.core.ClientOptions import com.openlayer.api.core.RequestOptions import com.openlayer.api.core.checkRequired +import com.openlayer.api.core.handlers.emptyHandler import com.openlayer.api.core.handlers.errorBodyHandler import com.openlayer.api.core.handlers.errorHandler import com.openlayer.api.core.handlers.jsonHandler @@ -18,6 +19,15 @@ import com.openlayer.api.core.http.parseable import com.openlayer.api.core.prepareAsync import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateParams import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyDeleteParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateResponse import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull @@ -41,6 +51,41 @@ class ApiKeyServiceAsyncImpl internal constructor(private val clientOptions: Cli // post /workspaces/{workspaceId}/api-keys withRawResponse().create(params, requestOptions).thenApply { it.parse() } + override fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // get /workspaces/{workspaceId}/api-keys/{apiKeyId} + withRawResponse().retrieve(params, requestOptions).thenApply { it.parse() } + + override fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // put /workspaces/{workspaceId}/api-keys/{apiKeyId} + withRawResponse().update(params, requestOptions).thenApply { it.parse() } + + override fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions, + ): CompletableFuture> = + // get /workspaces/{workspaceId}/api-keys + withRawResponse().list(params, requestOptions).thenApply { it.parse() } + + override fun delete( + params: ApiKeyDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // delete /workspaces/{workspaceId}/api-keys/{apiKeyId} + withRawResponse().delete(params, requestOptions).thenAccept {} + + override fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions, + ): CompletableFuture = + // post /workspaces/{workspaceId}/api-keys/{apiKeyId}/rotate + withRawResponse().rotate(params, requestOptions).thenApply { it.parse() } + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : ApiKeyServiceAsync.WithRawResponse { @@ -87,5 +132,187 @@ class ApiKeyServiceAsyncImpl internal constructor(private val clientOptions: Cli } } } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + ) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.PUT) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + ) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } + + private val listHandler: Handler> = + jsonHandler>(clientOptions.jsonMapper) + + override fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions, + ): CompletableFuture>> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("workspaceId", params.workspaceId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("workspaces", params._pathParam(0), "api-keys") + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.forEach { it.validate() } + } + } + } + } + } + + private val deleteHandler: Handler = emptyHandler() + + override fun delete( + params: ApiKeyDeleteParams, + requestOptions: RequestOptions, + ): CompletableFuture { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + ) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response.use { deleteHandler.handle(it) } + } + } + } + + private val rotateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions, + ): CompletableFuture> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + "rotate", + ) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepareAsync(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + return request + .thenComposeAsync { clientOptions.httpClient.executeAsync(it, requestOptions) } + .thenApply { response -> + errorHandler.handle(response).parseable { + response + .use { rotateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + } } } diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyService.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyService.kt index abc1eea2..b653a87b 100644 --- a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyService.kt +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyService.kt @@ -5,9 +5,19 @@ package com.openlayer.api.services.blocking.workspaces import com.google.errorprone.annotations.MustBeClosed import com.openlayer.api.core.ClientOptions import com.openlayer.api.core.RequestOptions +import com.openlayer.api.core.http.HttpResponse import com.openlayer.api.core.http.HttpResponseFor import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateParams import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyDeleteParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateResponse import java.util.function.Consumer interface ApiKeyService { @@ -24,7 +34,12 @@ interface ApiKeyService { */ fun withOptions(modifier: Consumer): ApiKeyService - /** Create a new API key in a workspace. */ + /** + * Create a new API key in a workspace. The full secret is returned in `secret`, only in this + * response. Optionally set `expiresAt`. When you authenticate with an API key that expires, the + * new key can't outlive it: omit `expiresAt` to inherit that expiry, and a later expiry (or + * `null`) is rejected with 400. + */ fun create(workspaceId: String): ApiKeyCreateResponse = create(workspaceId, ApiKeyCreateParams.none()) @@ -56,6 +71,138 @@ interface ApiKeyService { fun create(workspaceId: String, requestOptions: RequestOptions): ApiKeyCreateResponse = create(workspaceId, ApiKeyCreateParams.none(), requestOptions) + /** + * Retrieve one of your API keys, with its lifecycle status. The secret is never returned; + * `secureKey` is an obfuscated hint. + */ + fun retrieve(apiKeyId: String, params: ApiKeyRetrieveParams): ApiKeyRetrieveResponse = + retrieve(apiKeyId, params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + apiKeyId: String, + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiKeyRetrieveResponse = + retrieve(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see retrieve */ + fun retrieve(params: ApiKeyRetrieveParams): ApiKeyRetrieveResponse = + retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiKeyRetrieveResponse + + /** + * Rename one of your API keys. A key's expiry can't be updated; rotate the key with a new + * `expiresAt` instead, so extending a key's life always issues a new secret. + */ + fun update(apiKeyId: String, params: ApiKeyUpdateParams): ApiKeyUpdateResponse = + update(apiKeyId, params, RequestOptions.none()) + + /** @see update */ + fun update( + apiKeyId: String, + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiKeyUpdateResponse = update(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see update */ + fun update(params: ApiKeyUpdateParams): ApiKeyUpdateResponse = + update(params, RequestOptions.none()) + + /** @see update */ + fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiKeyUpdateResponse + + /** + * List the API keys you own in a workspace, with their lifecycle status. Secrets are never + * returned; `secureKey` is an obfuscated hint. + */ + fun list(workspaceId: String): List = + list(workspaceId, ApiKeyListParams.none()) + + /** @see list */ + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): List = + list(params.toBuilder().workspaceId(workspaceId).build(), requestOptions) + + /** @see list */ + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + ): List = list(workspaceId, params, RequestOptions.none()) + + /** @see list */ + fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): List + + /** @see list */ + fun list(params: ApiKeyListParams): List = + list(params, RequestOptions.none()) + + /** @see list */ + fun list(workspaceId: String, requestOptions: RequestOptions): List = + list(workspaceId, ApiKeyListParams.none(), requestOptions) + + /** + * Delete one of your API keys. Every secret for the key stops working immediately, including a + * previous secret still in its rotation grace period. + */ + fun delete(apiKeyId: String, params: ApiKeyDeleteParams) = + delete(apiKeyId, params, RequestOptions.none()) + + /** @see delete */ + fun delete( + apiKeyId: String, + params: ApiKeyDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ) = delete(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see delete */ + fun delete(params: ApiKeyDeleteParams) = delete(params, RequestOptions.none()) + + /** @see delete */ + fun delete(params: ApiKeyDeleteParams, requestOptions: RequestOptions = RequestOptions.none()) + + /** + * Replace an API key's secret now. The new secret is returned in `secret`, only in this + * response. Send `expiresAt` to change the key's expiry (`null` for never); omit it to keep the + * current one. The previous secret keeps authenticating for `gracePeriodHours` (default 0, so + * it stops working immediately), and never past `expiresAt`. The key keeps its id and name. + * Expired keys cannot be rotated. Only one previous secret is kept, so rotating again during a + * grace period retires the older one immediately. + */ + fun rotate(apiKeyId: String, params: ApiKeyRotateParams): ApiKeyRotateResponse = + rotate(apiKeyId, params, RequestOptions.none()) + + /** @see rotate */ + fun rotate( + apiKeyId: String, + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiKeyRotateResponse = rotate(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see rotate */ + fun rotate(params: ApiKeyRotateParams): ApiKeyRotateResponse = + rotate(params, RequestOptions.none()) + + /** @see rotate */ + fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): ApiKeyRotateResponse + /** A view of [ApiKeyService] that provides access to raw HTTP responses for each method. */ interface WithRawResponse { @@ -110,5 +257,172 @@ interface ApiKeyService { requestOptions: RequestOptions, ): HttpResponseFor = create(workspaceId, ApiKeyCreateParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `get /workspaces/{workspaceId}/api-keys/{apiKeyId}`, but + * is otherwise the same as [ApiKeyService.retrieve]. + */ + @MustBeClosed + fun retrieve( + apiKeyId: String, + params: ApiKeyRetrieveParams, + ): HttpResponseFor = + retrieve(apiKeyId, params, RequestOptions.none()) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + apiKeyId: String, + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + retrieve(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see retrieve */ + @MustBeClosed + fun retrieve(params: ApiKeyRetrieveParams): HttpResponseFor = + retrieve(params, RequestOptions.none()) + + /** @see retrieve */ + @MustBeClosed + fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `put /workspaces/{workspaceId}/api-keys/{apiKeyId}`, but + * is otherwise the same as [ApiKeyService.update]. + */ + @MustBeClosed + fun update( + apiKeyId: String, + params: ApiKeyUpdateParams, + ): HttpResponseFor = update(apiKeyId, params, RequestOptions.none()) + + /** @see update */ + @MustBeClosed + fun update( + apiKeyId: String, + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + update(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see update */ + @MustBeClosed + fun update(params: ApiKeyUpdateParams): HttpResponseFor = + update(params, RequestOptions.none()) + + /** @see update */ + @MustBeClosed + fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor + + /** + * Returns a raw HTTP response for `get /workspaces/{workspaceId}/api-keys`, but is + * otherwise the same as [ApiKeyService.list]. + */ + @MustBeClosed + fun list(workspaceId: String): HttpResponseFor> = + list(workspaceId, ApiKeyListParams.none()) + + /** @see list */ + @MustBeClosed + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> = + list(params.toBuilder().workspaceId(workspaceId).build(), requestOptions) + + /** @see list */ + @MustBeClosed + fun list( + workspaceId: String, + params: ApiKeyListParams = ApiKeyListParams.none(), + ): HttpResponseFor> = + list(workspaceId, params, RequestOptions.none()) + + /** @see list */ + @MustBeClosed + fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor> + + /** @see list */ + @MustBeClosed + fun list(params: ApiKeyListParams): HttpResponseFor> = + list(params, RequestOptions.none()) + + /** @see list */ + @MustBeClosed + fun list( + workspaceId: String, + requestOptions: RequestOptions, + ): HttpResponseFor> = + list(workspaceId, ApiKeyListParams.none(), requestOptions) + + /** + * Returns a raw HTTP response for `delete /workspaces/{workspaceId}/api-keys/{apiKeyId}`, + * but is otherwise the same as [ApiKeyService.delete]. + */ + @MustBeClosed + fun delete(apiKeyId: String, params: ApiKeyDeleteParams): HttpResponse = + delete(apiKeyId, params, RequestOptions.none()) + + /** @see delete */ + @MustBeClosed + fun delete( + apiKeyId: String, + params: ApiKeyDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse = delete(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see delete */ + @MustBeClosed + fun delete(params: ApiKeyDeleteParams): HttpResponse = delete(params, RequestOptions.none()) + + /** @see delete */ + @MustBeClosed + fun delete( + params: ApiKeyDeleteParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponse + + /** + * Returns a raw HTTP response for `post + * /workspaces/{workspaceId}/api-keys/{apiKeyId}/rotate`, but is otherwise the same as + * [ApiKeyService.rotate]. + */ + @MustBeClosed + fun rotate( + apiKeyId: String, + params: ApiKeyRotateParams, + ): HttpResponseFor = rotate(apiKeyId, params, RequestOptions.none()) + + /** @see rotate */ + @MustBeClosed + fun rotate( + apiKeyId: String, + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor = + rotate(params.toBuilder().apiKeyId(apiKeyId).build(), requestOptions) + + /** @see rotate */ + @MustBeClosed + fun rotate(params: ApiKeyRotateParams): HttpResponseFor = + rotate(params, RequestOptions.none()) + + /** @see rotate */ + @MustBeClosed + fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions = RequestOptions.none(), + ): HttpResponseFor } } diff --git a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceImpl.kt b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceImpl.kt index 70936566..377ad950 100644 --- a/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceImpl.kt +++ b/openlayer-java-core/src/main/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceImpl.kt @@ -5,6 +5,7 @@ package com.openlayer.api.services.blocking.workspaces import com.openlayer.api.core.ClientOptions import com.openlayer.api.core.RequestOptions import com.openlayer.api.core.checkRequired +import com.openlayer.api.core.handlers.emptyHandler import com.openlayer.api.core.handlers.errorBodyHandler import com.openlayer.api.core.handlers.errorHandler import com.openlayer.api.core.handlers.jsonHandler @@ -18,6 +19,15 @@ import com.openlayer.api.core.http.parseable import com.openlayer.api.core.prepare import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateParams import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyDeleteParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyListResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateResponse +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateResponse import java.util.function.Consumer import kotlin.jvm.optionals.getOrNull @@ -40,6 +50,39 @@ class ApiKeyServiceImpl internal constructor(private val clientOptions: ClientOp // post /workspaces/{workspaceId}/api-keys withRawResponse().create(params, requestOptions).parse() + override fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions, + ): ApiKeyRetrieveResponse = + // get /workspaces/{workspaceId}/api-keys/{apiKeyId} + withRawResponse().retrieve(params, requestOptions).parse() + + override fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions, + ): ApiKeyUpdateResponse = + // put /workspaces/{workspaceId}/api-keys/{apiKeyId} + withRawResponse().update(params, requestOptions).parse() + + override fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions, + ): List = + // get /workspaces/{workspaceId}/api-keys + withRawResponse().list(params, requestOptions).parse() + + override fun delete(params: ApiKeyDeleteParams, requestOptions: RequestOptions) { + // delete /workspaces/{workspaceId}/api-keys/{apiKeyId} + withRawResponse().delete(params, requestOptions) + } + + override fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions, + ): ApiKeyRotateResponse = + // post /workspaces/{workspaceId}/api-keys/{apiKeyId}/rotate + withRawResponse().rotate(params, requestOptions).parse() + class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : ApiKeyService.WithRawResponse { @@ -83,5 +126,172 @@ class ApiKeyServiceImpl internal constructor(private val clientOptions: ClientOp } } } + + private val retrieveHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun retrieve( + params: ApiKeyRetrieveParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + ) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { retrieveHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val updateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun update( + params: ApiKeyUpdateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.PUT) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + ) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { updateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } + + private val listHandler: Handler> = + jsonHandler>(clientOptions.jsonMapper) + + override fun list( + params: ApiKeyListParams, + requestOptions: RequestOptions, + ): HttpResponseFor> { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("workspaceId", params.workspaceId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.GET) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments("workspaces", params._pathParam(0), "api-keys") + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { listHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.forEach { it.validate() } + } + } + } + } + + private val deleteHandler: Handler = emptyHandler() + + override fun delete( + params: ApiKeyDeleteParams, + requestOptions: RequestOptions, + ): HttpResponse { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.DELETE) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + ) + .apply { params._body().ifPresent { body(json(clientOptions.jsonMapper, it)) } } + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response.use { deleteHandler.handle(it) } + } + } + + private val rotateHandler: Handler = + jsonHandler(clientOptions.jsonMapper) + + override fun rotate( + params: ApiKeyRotateParams, + requestOptions: RequestOptions, + ): HttpResponseFor { + // We check here instead of in the params builder because this can be specified + // positionally or in the params class. + checkRequired("apiKeyId", params.apiKeyId().getOrNull()) + val request = + HttpRequest.builder() + .method(HttpMethod.POST) + .baseUrl(clientOptions.baseUrl()) + .addPathSegments( + "workspaces", + params._pathParam(0), + "api-keys", + params._pathParam(1), + "rotate", + ) + .body(json(clientOptions.jsonMapper, params._body())) + .build() + .prepare(clientOptions, params) + val requestOptions = requestOptions.applyDefaults(RequestOptions.from(clientOptions)) + val response = clientOptions.httpClient.execute(request, requestOptions) + return errorHandler.handle(response).parseable { + response + .use { rotateHandler.handle(it) } + .also { + if (requestOptions.responseValidation!!) { + it.validate() + } + } + } + } } } diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParamsTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParamsTest.kt index 6a30e296..84f044d3 100644 --- a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParamsTest.kt +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateParamsTest.kt @@ -2,6 +2,7 @@ package com.openlayer.api.models.workspaces.apikeys +import java.time.OffsetDateTime import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test @@ -11,6 +12,7 @@ internal class ApiKeyCreateParamsTest { fun create() { ApiKeyCreateParams.builder() .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) .name("Secret Key") .build() } @@ -30,11 +32,13 @@ internal class ApiKeyCreateParamsTest { val params = ApiKeyCreateParams.builder() .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) .name("Secret Key") .build() val body = params._body() + assertThat(body.expiresAt()).contains(OffsetDateTime.parse("2027-01-01T00:00:00Z")) assertThat(body.name()).contains("Secret Key") } diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponseTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponseTest.kt index c6313a20..ae2c89a8 100644 --- a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponseTest.kt +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyCreateResponseTest.kt @@ -18,8 +18,13 @@ internal class ApiKeyCreateResponseTest { .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) - .secureKey("sk-ol-*************************5PW0") + .secureKey("sk-o...5PW0") + .status(ApiKeyCreateResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") .build() assertThat(apiKeyCreateResponse.id()).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") @@ -29,9 +34,16 @@ internal class ApiKeyCreateResponseTest { .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) assertThat(apiKeyCreateResponse.dateUpdated()) .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) - assertThat(apiKeyCreateResponse.secureKey()) - .isEqualTo("sk-ol-*************************5PW0") + assertThat(apiKeyCreateResponse.secureKey()).isEqualTo("sk-o...5PW0") + assertThat(apiKeyCreateResponse.status()).isEqualTo(ApiKeyCreateResponse.Status.ACTIVE) + assertThat(apiKeyCreateResponse.expiresAt()) + .contains(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + assertThat(apiKeyCreateResponse.lastRotatedAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) assertThat(apiKeyCreateResponse.name()).contains("Secret Key") + assertThat(apiKeyCreateResponse.previousKeyExpiresAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyCreateResponse.secret()).contains("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") } @Test @@ -43,8 +55,13 @@ internal class ApiKeyCreateResponseTest { .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) - .secureKey("sk-ol-*************************5PW0") + .secureKey("sk-o...5PW0") + .status(ApiKeyCreateResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") .build() val roundtrippedApiKeyCreateResponse = diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParamsTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParamsTest.kt new file mode 100644 index 00000000..101a0d67 --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyDeleteParamsTest.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyDeleteParamsTest { + + @Test + fun create() { + ApiKeyDeleteParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + } + + @Test + fun pathParams() { + val params = + ApiKeyDeleteParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + + assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(1)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + // out-of-bound path param + assertThat(params._pathParam(2)).isEqualTo("") + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParamsTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParamsTest.kt new file mode 100644 index 00000000..2aa7822f --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListParamsTest.kt @@ -0,0 +1,24 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyListParamsTest { + + @Test + fun create() { + ApiKeyListParams.builder().workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + } + + @Test + fun pathParams() { + val params = + ApiKeyListParams.builder().workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e").build() + + assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + // out-of-bound path param + assertThat(params._pathParam(1)).isEqualTo("") + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponseTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponseTest.kt new file mode 100644 index 00000000..d05001f8 --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyListResponseTest.kt @@ -0,0 +1,75 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openlayer.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyListResponseTest { + + @Test + fun create() { + val apiKeyListResponse = + ApiKeyListResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyListResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + assertThat(apiKeyListResponse.id()).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(apiKeyListResponse.dateCreated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyListResponse.dateLastUsed()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyListResponse.dateUpdated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyListResponse.secureKey()).isEqualTo("sk-o...5PW0") + assertThat(apiKeyListResponse.status()).isEqualTo(ApiKeyListResponse.Status.ACTIVE) + assertThat(apiKeyListResponse.expiresAt()) + .contains(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + assertThat(apiKeyListResponse.lastRotatedAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyListResponse.name()).contains("Secret Key") + assertThat(apiKeyListResponse.previousKeyExpiresAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyListResponse.secret()).contains("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val apiKeyListResponse = + ApiKeyListResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyListResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + val roundtrippedApiKeyListResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(apiKeyListResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedApiKeyListResponse).isEqualTo(apiKeyListResponse) + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParamsTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParamsTest.kt new file mode 100644 index 00000000..a5b58cb0 --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveParamsTest.kt @@ -0,0 +1,31 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyRetrieveParamsTest { + + @Test + fun create() { + ApiKeyRetrieveParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + } + + @Test + fun pathParams() { + val params = + ApiKeyRetrieveParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + + assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(1)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + // out-of-bound path param + assertThat(params._pathParam(2)).isEqualTo("") + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponseTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponseTest.kt new file mode 100644 index 00000000..14212676 --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRetrieveResponseTest.kt @@ -0,0 +1,75 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openlayer.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyRetrieveResponseTest { + + @Test + fun create() { + val apiKeyRetrieveResponse = + ApiKeyRetrieveResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyRetrieveResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + assertThat(apiKeyRetrieveResponse.id()).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(apiKeyRetrieveResponse.dateCreated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRetrieveResponse.dateLastUsed()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRetrieveResponse.dateUpdated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRetrieveResponse.secureKey()).isEqualTo("sk-o...5PW0") + assertThat(apiKeyRetrieveResponse.status()).isEqualTo(ApiKeyRetrieveResponse.Status.ACTIVE) + assertThat(apiKeyRetrieveResponse.expiresAt()) + .contains(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + assertThat(apiKeyRetrieveResponse.lastRotatedAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRetrieveResponse.name()).contains("Secret Key") + assertThat(apiKeyRetrieveResponse.previousKeyExpiresAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRetrieveResponse.secret()).contains("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val apiKeyRetrieveResponse = + ApiKeyRetrieveResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyRetrieveResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + val roundtrippedApiKeyRetrieveResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(apiKeyRetrieveResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedApiKeyRetrieveResponse).isEqualTo(apiKeyRetrieveResponse) + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParamsTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParamsTest.kt new file mode 100644 index 00000000..ac47c2d8 --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateParamsTest.kt @@ -0,0 +1,61 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyRotateParamsTest { + + @Test + fun create() { + ApiKeyRotateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .gracePeriodHours(24L) + .build() + } + + @Test + fun pathParams() { + val params = + ApiKeyRotateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + + assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(1)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + // out-of-bound path param + assertThat(params._pathParam(2)).isEqualTo("") + } + + @Test + fun body() { + val params = + ApiKeyRotateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .gracePeriodHours(24L) + .build() + + val body = params._body() + + assertThat(body.expiresAt()).contains(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + assertThat(body.gracePeriodHours()).contains(24L) + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + ApiKeyRotateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + + val body = params._body() + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponseTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponseTest.kt new file mode 100644 index 00000000..d290f569 --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyRotateResponseTest.kt @@ -0,0 +1,75 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openlayer.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyRotateResponseTest { + + @Test + fun create() { + val apiKeyRotateResponse = + ApiKeyRotateResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyRotateResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + assertThat(apiKeyRotateResponse.id()).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(apiKeyRotateResponse.dateCreated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRotateResponse.dateLastUsed()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRotateResponse.dateUpdated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRotateResponse.secureKey()).isEqualTo("sk-o...5PW0") + assertThat(apiKeyRotateResponse.status()).isEqualTo(ApiKeyRotateResponse.Status.ACTIVE) + assertThat(apiKeyRotateResponse.expiresAt()) + .contains(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + assertThat(apiKeyRotateResponse.lastRotatedAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRotateResponse.name()).contains("Secret Key") + assertThat(apiKeyRotateResponse.previousKeyExpiresAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyRotateResponse.secret()).contains("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val apiKeyRotateResponse = + ApiKeyRotateResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyRotateResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + val roundtrippedApiKeyRotateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(apiKeyRotateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedApiKeyRotateResponse).isEqualTo(apiKeyRotateResponse) + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParamsTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParamsTest.kt new file mode 100644 index 00000000..6940b1c5 --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateParamsTest.kt @@ -0,0 +1,57 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyUpdateParamsTest { + + @Test + fun create() { + ApiKeyUpdateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .name("Secret Key") + .build() + } + + @Test + fun pathParams() { + val params = + ApiKeyUpdateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + + assertThat(params._pathParam(0)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(params._pathParam(1)).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + // out-of-bound path param + assertThat(params._pathParam(2)).isEqualTo("") + } + + @Test + fun body() { + val params = + ApiKeyUpdateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .name("Secret Key") + .build() + + val body = params._body() + + assertThat(body.name()).contains("Secret Key") + } + + @Test + fun bodyWithoutOptionalFields() { + val params = + ApiKeyUpdateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + + val body = params._body() + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponseTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponseTest.kt new file mode 100644 index 00000000..c45ad9cb --- /dev/null +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/models/workspaces/apikeys/ApiKeyUpdateResponseTest.kt @@ -0,0 +1,75 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openlayer.api.models.workspaces.apikeys + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openlayer.api.core.jsonMapper +import java.time.OffsetDateTime +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ApiKeyUpdateResponseTest { + + @Test + fun create() { + val apiKeyUpdateResponse = + ApiKeyUpdateResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyUpdateResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + assertThat(apiKeyUpdateResponse.id()).isEqualTo("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + assertThat(apiKeyUpdateResponse.dateCreated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyUpdateResponse.dateLastUsed()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyUpdateResponse.dateUpdated()) + .isEqualTo(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyUpdateResponse.secureKey()).isEqualTo("sk-o...5PW0") + assertThat(apiKeyUpdateResponse.status()).isEqualTo(ApiKeyUpdateResponse.Status.ACTIVE) + assertThat(apiKeyUpdateResponse.expiresAt()) + .contains(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + assertThat(apiKeyUpdateResponse.lastRotatedAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyUpdateResponse.name()).contains("Secret Key") + assertThat(apiKeyUpdateResponse.previousKeyExpiresAt()) + .contains(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + assertThat(apiKeyUpdateResponse.secret()).contains("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val apiKeyUpdateResponse = + ApiKeyUpdateResponse.builder() + .id("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .dateCreated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateLastUsed(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .dateUpdated(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secureKey("sk-o...5PW0") + .status(ApiKeyUpdateResponse.Status.ACTIVE) + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .lastRotatedAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .name("Secret Key") + .previousKeyExpiresAt(OffsetDateTime.parse("2019-12-27T18:11:19.117Z")) + .secret("sk-ol-Xq3v9Rk2mPz8TnW4yL7bC1dF5hJ6") + .build() + + val roundtrippedApiKeyUpdateResponse = + jsonMapper.readValue( + jsonMapper.writeValueAsString(apiKeyUpdateResponse), + jacksonTypeRef(), + ) + + assertThat(roundtrippedApiKeyUpdateResponse).isEqualTo(apiKeyUpdateResponse) + } +} diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncTest.kt index c58cc3db..68ab13b1 100644 --- a/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncTest.kt +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/async/workspaces/ApiKeyServiceAsyncTest.kt @@ -5,6 +5,11 @@ package com.openlayer.api.services.async.workspaces import com.openlayer.api.TestServerExtension import com.openlayer.api.client.okhttp.OpenlayerOkHttpClientAsync import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyDeleteParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateParams +import java.time.OffsetDateTime import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -24,6 +29,7 @@ internal class ApiKeyServiceAsyncTest { apiKeyServiceAsync.create( ApiKeyCreateParams.builder() .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) .name("Secret Key") .build() ) @@ -31,4 +37,105 @@ internal class ApiKeyServiceAsyncTest { val apiKey = apiKeyFuture.get() apiKey.validate() } + + @Test + fun retrieve() { + val client = + OpenlayerOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyServiceAsync = client.workspaces().apiKeys() + + val apiKeyFuture = + apiKeyServiceAsync.retrieve( + ApiKeyRetrieveParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + ) + + val apiKey = apiKeyFuture.get() + apiKey.validate() + } + + @Test + fun update() { + val client = + OpenlayerOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyServiceAsync = client.workspaces().apiKeys() + + val apiKeyFuture = + apiKeyServiceAsync.update( + ApiKeyUpdateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .name("Secret Key") + .build() + ) + + val apiKey = apiKeyFuture.get() + apiKey.validate() + } + + @Test + fun list() { + val client = + OpenlayerOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyServiceAsync = client.workspaces().apiKeys() + + val apiKeysFuture = apiKeyServiceAsync.list("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + + val apiKeys = apiKeysFuture.get() + apiKeys.forEach { it.validate() } + } + + @Test + fun delete() { + val client = + OpenlayerOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyServiceAsync = client.workspaces().apiKeys() + + val future = + apiKeyServiceAsync.delete( + ApiKeyDeleteParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + ) + + val response = future.get() + } + + @Test + fun rotate() { + val client = + OpenlayerOkHttpClientAsync.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyServiceAsync = client.workspaces().apiKeys() + + val responseFuture = + apiKeyServiceAsync.rotate( + ApiKeyRotateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .gracePeriodHours(24L) + .build() + ) + + val response = responseFuture.get() + response.validate() + } } diff --git a/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceTest.kt b/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceTest.kt index f4e1506b..93e2df52 100644 --- a/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceTest.kt +++ b/openlayer-java-core/src/test/kotlin/com/openlayer/api/services/blocking/workspaces/ApiKeyServiceTest.kt @@ -5,6 +5,11 @@ package com.openlayer.api.services.blocking.workspaces import com.openlayer.api.TestServerExtension import com.openlayer.api.client.okhttp.OpenlayerOkHttpClient import com.openlayer.api.models.workspaces.apikeys.ApiKeyCreateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyDeleteParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRetrieveParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyRotateParams +import com.openlayer.api.models.workspaces.apikeys.ApiKeyUpdateParams +import java.time.OffsetDateTime import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -24,10 +29,105 @@ internal class ApiKeyServiceTest { apiKeyService.create( ApiKeyCreateParams.builder() .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) .name("Secret Key") .build() ) apiKey.validate() } + + @Test + fun retrieve() { + val client = + OpenlayerOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyService = client.workspaces().apiKeys() + + val apiKey = + apiKeyService.retrieve( + ApiKeyRetrieveParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + ) + + apiKey.validate() + } + + @Test + fun update() { + val client = + OpenlayerOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyService = client.workspaces().apiKeys() + + val apiKey = + apiKeyService.update( + ApiKeyUpdateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .name("Secret Key") + .build() + ) + + apiKey.validate() + } + + @Test + fun list() { + val client = + OpenlayerOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyService = client.workspaces().apiKeys() + + val apiKeys = apiKeyService.list("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + + apiKeys.forEach { it.validate() } + } + + @Test + fun delete() { + val client = + OpenlayerOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyService = client.workspaces().apiKeys() + + apiKeyService.delete( + ApiKeyDeleteParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .build() + ) + } + + @Test + fun rotate() { + val client = + OpenlayerOkHttpClient.builder() + .baseUrl(TestServerExtension.BASE_URL) + .apiKey("My API Key") + .build() + val apiKeyService = client.workspaces().apiKeys() + + val response = + apiKeyService.rotate( + ApiKeyRotateParams.builder() + .workspaceId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .apiKeyId("182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e") + .expiresAt(OffsetDateTime.parse("2027-01-01T00:00:00Z")) + .gracePeriodHours(24L) + .build() + ) + + response.validate() + } } diff --git a/scripts/mock b/scripts/mock index 86001174..c99c89ab 100755 --- a/scripts/mock +++ b/scripts/mock @@ -16,7 +16,7 @@ elif [ -n "$STAINLESS_OPENAPI_SPEC_URL" ]; then URL="$STAINLESS_OPENAPI_SPEC_URL" else # Embedded OpenAPI spec (base64-encoded, gzipped) - EMBEDDED_SPEC="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" + EMBEDDED_SPEC="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" SPEC_TMPFILE="$(mktemp)" trap 'rm -f "$SPEC_TMPFILE"' EXIT echo "$EMBEDDED_SPEC" | base64 --decode | gunzip > "$SPEC_TMPFILE"