diff --git a/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt b/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt index 85f8fc65..fb586c0e 100644 --- a/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt +++ b/src/main/kotlin/dev/restate/sdktesting/infra/RestateDeployer.kt @@ -13,7 +13,7 @@ import dev.restate.admin.api.DeploymentApi import dev.restate.admin.client.ApiClient import dev.restate.admin.client.ApiException import dev.restate.admin.model.RegisterDeploymentRequest -import dev.restate.admin.model.RegisterDeploymentRequestAnyOf +import dev.restate.admin.model.RegisterHttpDeploymentRequest import dev.restate.sdk.endpoint.Endpoint import dev.restate.sdk.http.vertx.RestateHttpServer import dev.restate.sdktesting.infra.runtimeconfig.IngressOptions @@ -391,7 +391,7 @@ private constructor( } private fun discoverDeployment(client: DeploymentApi, uri: String) { - val request = RegisterDeploymentRequest(RegisterDeploymentRequestAnyOf().uri(uri).force(false)) + val request = RegisterDeploymentRequest(RegisterHttpDeploymentRequest().uri(uri).force(false)) val response = Unreliables.retryUntilSuccess(20, TimeUnit.SECONDS) { diff --git a/src/main/kotlin/dev/restate/sdktesting/tests/FrontCompatibilityTest.kt b/src/main/kotlin/dev/restate/sdktesting/tests/FrontCompatibilityTest.kt index 5911aecb..5dd1e226 100644 --- a/src/main/kotlin/dev/restate/sdktesting/tests/FrontCompatibilityTest.kt +++ b/src/main/kotlin/dev/restate/sdktesting/tests/FrontCompatibilityTest.kt @@ -254,11 +254,14 @@ class FrontCompatibilityTest { .dryRun(false)) try { - adminApi.updateDeployment(deployment.id, updateRequest) + adminApi.updateDeployment(deployment.httpDeploymentResponse.id, updateRequest) LOG.info( - "Successfully updated deployment {} to use URI {}", deployment.id, localEndpointURI) + "Successfully updated deployment {} to use URI {}", + deployment.httpDeploymentResponse.id, + localEndpointURI) } catch (e: Exception) { - LOG.error("Failed to update deployment {}: {}", deployment.id, e.message) + LOG.error( + "Failed to update deployment {}: {}", deployment.httpDeploymentResponse.id, e.message) throw e } } diff --git a/src/main/kotlin/dev/restate/sdktesting/tests/StatePatchingTest.kt b/src/main/kotlin/dev/restate/sdktesting/tests/StatePatchingTest.kt index c2cc22a8..5de7dacc 100644 --- a/src/main/kotlin/dev/restate/sdktesting/tests/StatePatchingTest.kt +++ b/src/main/kotlin/dev/restate/sdktesting/tests/StatePatchingTest.kt @@ -22,8 +22,8 @@ import dev.restate.sdk.kotlin.set import dev.restate.sdktesting.infra.InjectAdminURI import dev.restate.sdktesting.infra.InjectClient import dev.restate.sdktesting.infra.RestateDeployerExtension -import kotlinx.serialization.json.Json import java.net.URI +import kotlinx.serialization.json.Json import org.assertj.core.api.Assertions.assertThat import org.awaitility.kotlin.await import org.awaitility.kotlin.withAlias @@ -73,9 +73,11 @@ class StatePatchingTest { // Patch the state val newState = "patched-state" - val request = ModifyServiceStateRequest() - .objectKey("test-key") - .newState(mapOf("state" to Json.encodeToString(newState).toByteArray().map { it.toInt() })) + val request = + ModifyServiceStateRequest() + .objectKey("test-key") + .newState( + mapOf("state" to Json.encodeToString(newState).toByteArray().map { it.toInt() })) serviceApi.modifyServiceState("StateObject", request) diff --git a/src/main/kotlin/dev/restate/sdktesting/tests/TimeTravelTest.kt b/src/main/kotlin/dev/restate/sdktesting/tests/TimeTravelTest.kt new file mode 100644 index 00000000..41694b7f --- /dev/null +++ b/src/main/kotlin/dev/restate/sdktesting/tests/TimeTravelTest.kt @@ -0,0 +1,157 @@ +// Copyright (c) 2023 - Restate Software, Inc., Restate GmbH +// +// This file is part of the Restate SDK Test suite tool, +// which is released under the MIT license. +// +// You can find a copy of the license in file LICENSE in the root +// directory of this repository or package, or at +// https://github.com/restatedev/sdk-test-suite/blob/main/LICENSE +package dev.restate.sdktesting.tests + +import dev.restate.admin.api.InvocationApi +import dev.restate.admin.client.ApiClient +import dev.restate.client.Client +import dev.restate.client.kotlin.attachSuspend +import dev.restate.sdk.annotation.Handler +import dev.restate.sdk.annotation.Name +import dev.restate.sdk.annotation.Service +import dev.restate.sdk.annotation.VirtualObject +import dev.restate.sdk.endpoint.Endpoint +import dev.restate.sdk.kotlin.* +import dev.restate.sdktesting.infra.InjectAdminURI +import dev.restate.sdktesting.infra.InjectClient +import dev.restate.sdktesting.infra.RestateDeployerExtension +import java.net.URI +import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.AtomicInteger +import kotlin.time.Duration.Companion.seconds +import org.assertj.core.api.Assertions.assertThat +import org.awaitility.kotlin.await +import org.awaitility.kotlin.withAlias +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.RegisterExtension + +class TimeTravelTest { + + @VirtualObject + @Name("TimeObject") + class TimeObject { + companion object { + val shouldFail = AtomicBoolean(true) + } + + @Handler + suspend fun getState(ctx: ObjectContext): String? { + return ctx.get("state") + } + + @Handler + suspend fun testHandler(ctx: ObjectContext): String { + // Call another service, but don't await the response yet + val firstMethodResponse = TimeTravelTestCalleeServiceClient.fromContext(ctx).firstMethod() + + // Load if we should fail + val shouldFail = shouldFail.get() + + // Set a different state + ctx.set("state", if (shouldFail) "a" else "b") + + // Execute a different context run + val runResult = ctx.runAsync("my-run") { if (shouldFail) "a" else "b" } + + // Call the second method + val secondMethodResponse = + TimeTravelTestCalleeServiceClient.fromContext(ctx).secondMethod(shouldFail) + + // Await all calls and return + return listOf(firstMethodResponse, secondMethodResponse, runResult) + .awaitAll() + .joinToString("-") + } + } + + @Service + @Name("CalleeService") + class CalleeService { + companion object { + val firstMethod = AtomicInteger(0) + } + + @Handler + suspend fun firstMethod(ctx: Context): String { + ctx.sleep(2.seconds) + // Assert it's called just once + assertThat(firstMethod.incrementAndGet()).isEqualTo(1) + return "first" + } + + @Handler + fun secondMethod(ctx: Context, shouldFail: Boolean): String { + if (shouldFail) { + throw RuntimeException("Failing!") + } + return "second" + } + } + + companion object { + @RegisterExtension + val deployerExt: RestateDeployerExtension = RestateDeployerExtension { + withEndpoint(Endpoint.bind(TimeObject()).bind(CalleeService())) + } + } + + @Test + fun testTimeTravel( + @InjectClient ingressClient: Client, + @InjectAdminURI adminURI: URI, + ) = runTest { + // Create clients for the services + val timeClient = TimeTravelTestTimeObjectClient.fromClient(ingressClient, "test-key") + + // Send request + val sendResult = timeClient.send().testHandler(init = idempotentCallOptions) + val invocationId = sendResult.invocationId() + + // Wait for the invocation to reach the error state + await withAlias + "invocation has all the journal entries" untilAsserted + { + assertThat(getJournal(adminURI, invocationId).rows) + .map { it.entryType } + .containsExactlyInAnyOrder( + "Command: Input", + "Command: Call", + "Notification: CallInvocationId", + "Notification: Call", + "Command: SetState", + "Command: Run", + "Notification: Run", + "Command: Call", + "Notification: CallInvocationId", + ) + } + + // Find the trim index + val trimIndex = + getJournal(adminURI, invocationId).rows.find { it.entryType == "Command: SetState" }!!.index + + // Set shouldFail to false so the handler will succeed after time travel + TimeObject.shouldFail.set(false) + + // Use the time travel API to trim entry index 2 + val adminClient = ApiClient().setHost(adminURI.host).setPort(adminURI.port) + val invocationApi = InvocationApi(adminClient) + invocationApi.timeTravelInvocation(invocationId, trimIndex) + + // Wait for the response to be sent back + assertThat(sendResult.attachSuspend().response()).isEqualTo("first-second-b") + + // Wait for state to be b + await withAlias + "response is sent back" untilAsserted + { + assertThat(timeClient.getState()).isEqualTo("b") + } + } +} diff --git a/src/main/kotlin/dev/restate/sdktesting/tests/utils.kt b/src/main/kotlin/dev/restate/sdktesting/tests/utils.kt index fc7a1ce5..40a90f5e 100644 --- a/src/main/kotlin/dev/restate/sdktesting/tests/utils.kt +++ b/src/main/kotlin/dev/restate/sdktesting/tests/utils.kt @@ -9,15 +9,23 @@ package dev.restate.sdktesting.tests import dev.restate.common.RequestBuilder +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse import java.util.UUID import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.future.await import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withContext +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.kotlin.additionalLoggingContext import org.awaitility.core.ConditionFactory @@ -40,3 +48,42 @@ fun runTest(timeout: Duration = 60.seconds, testBody: suspend TestScope.() -> Un val idempotentCallOptions: RequestBuilder<*, *>.() -> Unit = { idempotencyKey = UUID.randomUUID().toString() } + +/** Data classes for sys_invocation query result */ +@Serializable data class JournalQueryResult(val rows: List = emptyList()) + +@Serializable +data class SysInvocationEntry(val index: Int, @SerialName("entry_type") val entryType: String) + +/** JSON parser with configuration for sys_invocation query result */ +private val sysJournalJson = Json { + ignoreUnknownKeys = true + coerceInputValues = true +} + +/** + * Queries the sys_journal table for a given invocation ID and returns the parsed result. + * + * @param invocationId The ID of the invocation to query + * @param adminURI The URI of the Restate admin API + * @return The parsed result of the query + */ +suspend fun getJournal(adminURI: URI, invocationId: String): JournalQueryResult { + // Create the HTTP request to query sys_invocation + val request = + HttpRequest.newBuilder() + .uri(URI.create("http://${adminURI.host}:${adminURI.port}/query")) + .header("accept", "application/json") + .header("content-type", "application/json") + .POST( + HttpRequest.BodyPublishers.ofString( + """{"query": "SELECT index, entry_type FROM sys_journal WHERE id = '$invocationId'"}""")) + .build() + + // Send the request and get the response + val response = + HttpClient.newHttpClient().sendAsync(request, HttpResponse.BodyHandlers.ofString()).await() + + // Parse the response using Kotlin serialization + return sysJournalJson.decodeFromString(response.body()) +} diff --git a/src/main/openapi/admin.json b/src/main/openapi/admin.json index 79d02c00..c9f8573e 100644 --- a/src/main/openapi/admin.json +++ b/src/main/openapi/admin.json @@ -1,2381 +1 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Admin API", - "version": "1.3.0-rc.1" - }, - "paths": { - "/cluster-health": { - "get": { - "tags": [ - "cluster_health" - ], - "summary": "Cluster health", - "description": "Get the cluster health.", - "operationId": "cluster_health", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ClusterHealthResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/deployments": { - "get": { - "tags": [ - "deployment" - ], - "summary": "List deployments", - "description": "List all registered deployments.", - "operationId": "list_deployments", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListDeploymentsResponse" - } - } - } - } - } - }, - "post": { - "tags": [ - "deployment" - ], - "summary": "Create deployment", - "description": "Create deployment. Restate will invoke the endpoint to gather additional information required for registration, such as the services exposed by the deployment. If the deployment is already registered, this method will fail unless `force` is set to `true`.", - "operationId": "create_deployment", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterDeploymentRequest" - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "Created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterDeploymentResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/deployments/{deployment}": { - "get": { - "tags": [ - "deployment" - ], - "summary": "Get deployment", - "description": "Get deployment metadata", - "operationId": "get_deployment", - "parameters": [ - { - "name": "deployment", - "in": "path", - "description": "Deployment identifier", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DetailedDeploymentResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - }, - "put": { - "tags": [ - "deployment" - ], - "summary": "Update deployment", - "description": "Update deployment. Invokes the endpoint and replaces the existing deployment metadata with the discovered information. This is a dangerous operation that should be used only when there are failing invocations on the deployment that cannot be resolved any other way. Sense checks are applied to test that the new deployment is sufficiently similar to the old one.", - "operationId": "update_deployment", - "parameters": [ - { - "name": "deployment", - "in": "path", - "description": "Deployment identifier", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateDeploymentRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DetailedDeploymentResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - }, - "delete": { - "tags": [ - "deployment" - ], - "summary": "Delete deployment", - "description": "Delete deployment. Currently it's supported to remove a deployment only using the force flag", - "operationId": "delete_deployment", - "parameters": [ - { - "name": "deployment", - "in": "path", - "description": "Deployment identifier", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "force", - "in": "query", - "description": "If true, the deployment will be forcefully deleted. This might break in-flight invocations, use with caution.", - "style": "simple", - "schema": { - "type": "boolean" - } - } - ], - "responses": { - "202": { - "description": "Accepted" - }, - "501": { - "description": "Not implemented. Only using the force flag is supported at the moment." - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/health": { - "get": { - "tags": [ - "health" - ], - "summary": "Health check", - "description": "Check REST API Health.", - "operationId": "health", - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/invocations/{invocation_id}": { - "delete": { - "tags": [ - "invocation" - ], - "summary": "Delete an invocation", - "description": "Delete the given invocation. By default, an invocation is terminated by gracefully cancelling it. This ensures virtual object state consistency. Alternatively, an invocation can be killed which does not guarantee consistency for virtual object instance state, in-flight invocations to other services, etc. A stored completed invocation can also be purged", - "operationId": "delete_invocation", - "parameters": [ - { - "name": "invocation_id", - "in": "path", - "description": "Invocation identifier.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "mode", - "in": "query", - "description": "If cancel, it will gracefully terminate the invocation. If kill, it will terminate the invocation with a hard stop. If purge, it will only cleanup the response for completed invocations, and leave unaffected an in-flight invocation.", - "style": "simple", - "schema": { - "$ref": "#/components/schemas/DeletionMode" - } - } - ], - "responses": { - "202": { - "description": "Accepted" - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/openapi": { - "get": { - "tags": [ - "openapi" - ], - "summary": "OpenAPI specification", - "externalDocs": { - "url": "https://swagger.io/specification/" - }, - "operationId": "openapi_spec", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - } - } - }, - "/services": { - "get": { - "tags": [ - "service" - ], - "summary": "List services", - "description": "List all registered services.", - "operationId": "list_services", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListServicesResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/services/{service}": { - "get": { - "tags": [ - "service" - ], - "summary": "Get service", - "description": "Get a registered service.", - "operationId": "get_service", - "parameters": [ - { - "name": "service", - "in": "path", - "description": "Fully qualified service name.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceMetadata" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - }, - "patch": { - "tags": [ - "service" - ], - "summary": "Modify a service", - "description": "Modify a registered service.", - "operationId": "modify_service", - "parameters": [ - { - "name": "service", - "in": "path", - "description": "Fully qualified service name.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModifyServiceRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceMetadata" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/services/{service}/handlers": { - "get": { - "tags": [ - "service_handler" - ], - "summary": "List service handlers", - "description": "List all the handlers of the given service.", - "operationId": "list_service_handlers", - "parameters": [ - { - "name": "service", - "in": "path", - "description": "Fully qualified service name.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListServiceHandlersResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/services/{service}/handlers/{handler}": { - "get": { - "tags": [ - "service_handler" - ], - "summary": "Get service handler", - "description": "Get the handler of a service", - "operationId": "get_service_handler", - "parameters": [ - { - "name": "service", - "in": "path", - "description": "Fully qualified service name.", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "handler", - "in": "path", - "description": "Handler name.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HandlerMetadata" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/services/{service}/openapi": { - "get": { - "tags": [ - "service" - ], - "summary": "Get service OpenAPI", - "description": "Get the service OpenAPI 3.1 contract.", - "operationId": "get_service_openapi", - "parameters": [ - { - "name": "service", - "in": "path", - "description": "Fully qualified service name.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "OpenAPI 3.1 of the service", - "content": { - "application/json": { - "schema": {} - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/services/{service}/state": { - "post": { - "tags": [ - "service" - ], - "summary": "Modify a service state", - "description": "Modify service state", - "operationId": "modify_service_state", - "parameters": [ - { - "name": "service", - "in": "path", - "description": "Fully qualified service name.", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModifyServiceStateRequest" - } - } - }, - "required": true - }, - "responses": { - "202": { - "description": "Accepted" - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/subscriptions": { - "get": { - "tags": [ - "subscription" - ], - "summary": "List subscriptions", - "description": "List all subscriptions.", - "operationId": "list_subscriptions", - "parameters": [ - { - "name": "sink", - "in": "query", - "description": "Filter by the exact specified sink.", - "style": "simple", - "schema": { - "type": "string" - } - }, - { - "name": "source", - "in": "query", - "description": "Filter by the exact specified source.", - "style": "simple", - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListSubscriptionsResponse" - } - } - } - } - } - }, - "post": { - "tags": [ - "subscription" - ], - "summary": "Create subscription", - "description": "Create subscription.", - "operationId": "create_subscription", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateSubscriptionRequest" - } - } - }, - "required": true - }, - "responses": { - "201": { - "description": "Created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubscriptionResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/subscriptions/{subscription}": { - "get": { - "tags": [ - "subscription" - ], - "summary": "Get subscription", - "description": "Get subscription", - "operationId": "get_subscription", - "parameters": [ - { - "name": "subscription", - "in": "path", - "description": "Subscription identifier", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubscriptionResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - }, - "delete": { - "tags": [ - "subscription" - ], - "summary": "Delete subscription", - "description": "Delete subscription.", - "operationId": "delete_subscription", - "parameters": [ - { - "name": "subscription", - "in": "path", - "description": "Subscription identifier", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "202": { - "description": "Accepted" - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "409": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "500": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - }, - "503": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorDescriptionResponse" - } - } - } - } - } - } - }, - "/version": { - "get": { - "tags": [ - "version" - ], - "summary": "Admin version information", - "description": "Obtain admin version information.", - "operationId": "version", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VersionInformation" - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "ClusterHealthResponse": { - "type": "object", - "required": [ - "cluster_name" - ], - "properties": { - "cluster_name": { - "description": "Cluster name", - "type": "string" - }, - "metadata_cluster_health": { - "description": "Embedded metadata cluster health if it was enabled", - "allOf": [ - { - "$ref": "#/components/schemas/EmbeddedMetadataClusterHealth" - } - ], - "nullable": true - } - } - }, - "EmbeddedMetadataClusterHealth": { - "type": "object", - "required": [ - "members" - ], - "properties": { - "members": { - "description": "Current members of the embedded metadata cluster", - "type": "array", - "items": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - } - } - }, - "ErrorDescriptionResponse": { - "title": "Error description response", - "description": "Error details of the response", - "type": "object", - "required": [ - "message" - ], - "properties": { - "message": { - "type": "string" - }, - "restate_code": { - "title": "Restate code", - "description": "Restate error code describing this error", - "type": "string", - "nullable": true - } - } - }, - "ListDeploymentsResponse": { - "type": "object", - "required": [ - "deployments" - ], - "properties": { - "deployments": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DeploymentResponse" - } - } - } - }, - "DeploymentResponse": { - "type": "object", - "required": [ - "id", - "services" - ], - "properties": { - "id": { - "$ref": "#/components/schemas/String" - }, - "services": { - "title": "Services", - "description": "List of services exposed by this deployment.", - "type": "array", - "items": { - "$ref": "#/components/schemas/ServiceNameRevPair" - } - } - } - }, - "String": { - "type": "string" - }, - "ServiceNameRevPair": { - "type": "object", - "required": [ - "name", - "revision" - ], - "properties": { - "name": { - "type": "string" - }, - "revision": { - "type": "integer", - "format": "uint32", - "minimum": 0.0 - } - } - }, - "ProtocolType": { - "type": "string", - "enum": [ - "RequestResponse", - "BidiStream" - ] - }, - "LambdaARN": { - "type": "string", - "format": "arn" - }, - "RegisterDeploymentRequest": { - "anyOf": [ - { - "type": "object", - "required": [ - "uri" - ], - "properties": { - "uri": { - "title": "Uri", - "description": "Uri to use to discover/invoke the http deployment.", - "type": "string" - }, - "additional_headers": { - "title": "Additional headers", - "description": "Additional headers added to the discover/invoke requests to the deployment.", - "type": "object", - "additionalProperties": { - "type": "string" - }, - "nullable": true - }, - "use_http_11": { - "title": "Use http1.1", - "description": "If `true`, discovery will be attempted using a client that defaults to HTTP1.1 instead of a prior-knowledge HTTP2 client. HTTP2 may still be used for TLS servers that advertise HTTP2 support via ALPN. HTTP1.1 deployments will only work in request-response mode.", - "default": false, - "type": "boolean" - }, - "force": { - "title": "Force", - "description": "If `true`, it will override, if existing, any deployment using the same `uri`. Beware that this can lead in-flight invocations to an unrecoverable error state.\n\nBy default, this is `true` but it might change in future to `false`.\n\nSee the [versioning documentation](https://docs.restate.dev/operate/versioning) for more information.", - "default": true, - "type": "boolean" - }, - "dry_run": { - "title": "Dry-run mode", - "description": "If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.", - "default": false, - "type": "boolean" - } - } - }, - { - "type": "object", - "required": [ - "arn" - ], - "properties": { - "arn": { - "title": "ARN", - "description": "ARN to use to discover/invoke the lambda deployment.", - "type": "string" - }, - "assume_role_arn": { - "title": "Assume role ARN", - "description": "Optional ARN of a role to assume when invoking the addressed Lambda, to support role chaining", - "type": "string", - "nullable": true - }, - "additional_headers": { - "title": "Additional headers", - "description": "Additional headers added to the discover/invoke requests to the deployment.", - "type": "object", - "additionalProperties": { - "type": "string" - }, - "nullable": true - }, - "force": { - "title": "Force", - "description": "If `true`, it will override, if existing, any deployment using the same `uri`. Beware that this can lead in-flight invocations to an unrecoverable error state.\n\nBy default, this is `true` but it might change in future to `false`.\n\nSee the [versioning documentation](https://docs.restate.dev/operate/versioning) for more information.", - "default": true, - "type": "boolean" - }, - "dry_run": { - "title": "Dry-run mode", - "description": "If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.", - "default": false, - "type": "boolean" - } - } - } - ] - }, - "RegisterDeploymentResponse": { - "type": "object", - "required": [ - "id", - "services" - ], - "properties": { - "id": { - "$ref": "#/components/schemas/String" - }, - "services": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ServiceMetadata" - } - } - } - }, - "ServiceMetadata": { - "type": "object", - "required": [ - "deployment_id", - "handlers", - "idempotency_retention", - "name", - "public", - "revision", - "ty" - ], - "properties": { - "name": { - "title": "Name", - "description": "Fully qualified name of the service", - "type": "string" - }, - "handlers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/HandlerMetadata" - } - }, - "ty": { - "$ref": "#/components/schemas/ServiceType" - }, - "documentation": { - "title": "Documentation", - "description": "Documentation of the service, as propagated by the SDKs.", - "type": "string", - "nullable": true - }, - "metadata": { - "title": "Metadata", - "description": "Additional service metadata, as propagated by the SDKs.", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "deployment_id": { - "title": "Deployment Id", - "description": "Deployment exposing the latest revision of the service.", - "type": "string" - }, - "revision": { - "title": "Revision", - "description": "Latest revision of the service.", - "type": "integer", - "format": "uint32", - "minimum": 0.0 - }, - "public": { - "title": "Public", - "description": "If true, the service can be invoked through the ingress. If false, the service can be invoked only from another Restate service.", - "type": "boolean" - }, - "idempotency_retention": { - "title": "Idempotency retention", - "description": "The retention duration of idempotent requests for this service.", - "type": "string" - }, - "workflow_completion_retention": { - "title": "Workflow completion retention", - "description": "The retention duration of workflows. Only available on workflow services.", - "type": "string", - "nullable": true - }, - "inactivity_timeout": { - "title": "Inactivity timeout", - "description": "This timer guards against stalled service/handler invocations. Once it expires, Restate triggers a graceful termination by asking the service invocation to suspend (which preserves intermediate progress).\n\nThe 'abort timeout' is used to abort the invocation, in case it doesn't react to the request to suspend.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format.\n\nThis overrides the default inactivity timeout set in invoker options.", - "type": "string", - "nullable": true - }, - "abort_timeout": { - "title": "Abort timeout", - "description": "This timer guards against stalled service/handler invocations that are supposed to terminate. The abort timeout is started after the 'inactivity timeout' has expired and the service/handler invocation has been asked to gracefully terminate. Once the timer expires, it will abort the service/handler invocation.\n\nThis timer potentially **interrupts** user code. If the user code needs longer to gracefully terminate, then this value needs to be set accordingly.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format.\n\nThis overrides the default abort timeout set in invoker options.", - "type": "string", - "nullable": true - } - } - }, - "HandlerMetadata": { - "type": "object", - "required": [ - "input_description", - "name", - "output_description" - ], - "properties": { - "name": { - "type": "string" - }, - "ty": { - "allOf": [ - { - "$ref": "#/components/schemas/HandlerMetadataType" - } - ], - "nullable": true - }, - "documentation": { - "title": "Documentation", - "description": "Documentation of the handler, as propagated by the SDKs.", - "type": "string", - "nullable": true - }, - "metadata": { - "title": "Metadata", - "description": "Additional handler metadata, as propagated by the SDKs.", - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "input_description": { - "title": "Human readable input description", - "description": "If empty, no schema was provided by the user at discovery time.", - "type": "string" - }, - "output_description": { - "title": "Human readable output description", - "description": "If empty, no schema was provided by the user at discovery time.", - "type": "string" - }, - "input_json_schema": { - "title": "Input JSON Schema", - "description": "JSON Schema of the handler input", - "nullable": true - }, - "output_json_schema": { - "title": "Output JSON Schema", - "description": "JSON Schema of the handler output", - "nullable": true - } - } - }, - "HandlerMetadataType": { - "type": "string", - "enum": [ - "Exclusive", - "Shared", - "Workflow" - ] - }, - "ServiceType": { - "type": "string", - "enum": [ - "Service", - "VirtualObject", - "Workflow" - ] - }, - "DetailedDeploymentResponse": { - "type": "object", - "anyOf": [ - { - "type": "object", - "required": [ - "created_at", - "http_version", - "max_protocol_version", - "min_protocol_version", - "protocol_type", - "uri" - ], - "properties": { - "uri": { - "type": "string" - }, - "protocol_type": { - "$ref": "#/components/schemas/ProtocolType" - }, - "http_version": { - "type": "string" - }, - "additional_headers": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "created_at": { - "type": "string" - }, - "min_protocol_version": { - "type": "integer", - "format": "int32" - }, - "max_protocol_version": { - "type": "integer", - "format": "int32" - } - } - }, - { - "type": "object", - "required": [ - "arn", - "created_at", - "max_protocol_version", - "min_protocol_version" - ], - "properties": { - "arn": { - "$ref": "#/components/schemas/LambdaARN" - }, - "assume_role_arn": { - "type": "string", - "nullable": true - }, - "additional_headers": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "created_at": { - "type": "string" - }, - "min_protocol_version": { - "type": "integer", - "format": "int32" - }, - "max_protocol_version": { - "type": "integer", - "format": "int32" - } - } - } - ], - "required": [ - "id", - "services" - ], - "properties": { - "id": { - "$ref": "#/components/schemas/String" - }, - "services": { - "title": "Services", - "description": "List of services exposed by this deployment.", - "type": "array", - "items": { - "$ref": "#/components/schemas/ServiceMetadata" - } - } - } - }, - "UpdateDeploymentRequest": { - "anyOf": [ - { - "type": "object", - "required": [ - "uri" - ], - "properties": { - "uri": { - "title": "Uri", - "description": "Uri to use to discover/invoke the http deployment.", - "type": "string" - }, - "additional_headers": { - "title": "Additional headers", - "description": "Additional headers added to the discover/invoke requests to the deployment.", - "type": "object", - "additionalProperties": { - "type": "string" - }, - "nullable": true - }, - "use_http_11": { - "title": "Use http1.1", - "description": "If `true`, discovery will be attempted using a client that defaults to HTTP1.1 instead of a prior-knowledge HTTP2 client. HTTP2 may still be used for TLS servers that advertise HTTP2 support via ALPN. HTTP1.1 deployments will only work in request-response mode.", - "default": false, - "type": "boolean" - }, - "dry_run": { - "title": "Dry-run mode", - "description": "If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.", - "default": false, - "type": "boolean" - } - } - }, - { - "type": "object", - "required": [ - "arn" - ], - "properties": { - "arn": { - "title": "ARN", - "description": "ARN to use to discover/invoke the lambda deployment.", - "type": "string" - }, - "assume_role_arn": { - "title": "Assume role ARN", - "description": "Optional ARN of a role to assume when invoking the addressed Lambda, to support role chaining", - "type": "string", - "nullable": true - }, - "additional_headers": { - "title": "Additional headers", - "description": "Additional headers added to the discover/invoke requests to the deployment.", - "type": "object", - "additionalProperties": { - "type": "string" - }, - "nullable": true - }, - "dry_run": { - "title": "Dry-run mode", - "description": "If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.", - "default": false, - "type": "boolean" - } - } - } - ] - }, - "DeletionMode": { - "type": "string", - "enum": [ - "Cancel", - "Kill", - "Purge" - ] - }, - "ListServicesResponse": { - "type": "object", - "required": [ - "services" - ], - "properties": { - "services": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ServiceMetadata" - } - } - } - }, - "ModifyServiceRequest": { - "type": "object", - "properties": { - "public": { - "title": "Public", - "description": "If true, the service can be invoked through the ingress. If false, the service can be invoked only from another Restate service.", - "default": null, - "type": "boolean", - "nullable": true - }, - "idempotency_retention": { - "title": "Idempotency retention", - "description": "Modify the retention of idempotent requests for this service.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.", - "default": null, - "type": "string", - "nullable": true - }, - "workflow_completion_retention": { - "title": "Workflow completion retention", - "description": "Modify the retention of the workflow completion. This can be modified only for workflow services!\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.", - "default": null, - "type": "string", - "nullable": true - }, - "inactivity_timeout": { - "title": "Inactivity timeout", - "description": "This timer guards against stalled service/handler invocations. Once it expires, Restate triggers a graceful termination by asking the service invocation to suspend (which preserves intermediate progress).\n\nThe 'abort timeout' is used to abort the invocation, in case it doesn't react to the request to suspend.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.\n\nThis overrides the default inactivity timeout set in invoker options.", - "default": null, - "type": "string", - "nullable": true - }, - "abort_timeout": { - "title": "Abort timeout", - "description": "This timer guards against stalled service/handler invocations that are supposed to terminate. The abort timeout is started after the 'inactivity timeout' has expired and the service/handler invocation has been asked to gracefully terminate. Once the timer expires, it will abort the service/handler invocation.\n\nThis timer potentially **interrupts** user code. If the user code needs longer to gracefully terminate, then this value needs to be set accordingly.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.\n\nThis overrides the default abort timeout set in invoker options.", - "default": null, - "type": "string", - "nullable": true - } - } - }, - "ListServiceHandlersResponse": { - "type": "object", - "required": [ - "handlers" - ], - "properties": { - "handlers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/HandlerMetadata" - } - } - } - }, - "ModifyServiceStateRequest": { - "type": "object", - "required": [ - "new_state", - "object_key" - ], - "properties": { - "version": { - "title": "Version", - "description": "If set, the latest version of the state is compared with this value and the operation will fail when the versions differ.", - "type": "string", - "nullable": true - }, - "object_key": { - "title": "Service key", - "description": "To what virtual object key to apply this change", - "type": "string" - }, - "new_state": { - "title": "New State", - "description": "The new state to replace the previous state with", - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "integer", - "format": "uint8", - "minimum": 0.0 - } - } - } - } - }, - "ListSubscriptionsResponse": { - "type": "object", - "required": [ - "subscriptions" - ], - "properties": { - "subscriptions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SubscriptionResponse" - } - } - } - }, - "SubscriptionResponse": { - "type": "object", - "required": [ - "id", - "options", - "sink", - "source" - ], - "properties": { - "id": { - "$ref": "#/components/schemas/String" - }, - "source": { - "type": "string" - }, - "sink": { - "type": "string" - }, - "options": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - }, - "CreateSubscriptionRequest": { - "type": "object", - "required": [ - "sink", - "source" - ], - "properties": { - "source": { - "title": "Source", - "description": "Source uri. Accepted forms:\n\n* `kafka:///`, e.g. `kafka://my-cluster/my-topic`", - "type": "string" - }, - "sink": { - "title": "Sink", - "description": "Sink uri. Accepted forms:\n\n* `service:///`, e.g. `service://Counter/count`", - "type": "string" - }, - "options": { - "title": "Options", - "description": "Additional options to apply to the subscription.", - "type": "object", - "additionalProperties": { - "type": "string" - }, - "nullable": true - } - } - }, - "VersionInformation": { - "type": "object", - "required": [ - "ingress_endpoint", - "max_admin_api_version", - "min_admin_api_version", - "version" - ], - "properties": { - "version": { - "title": "Admin server version", - "description": "Version of the admin server", - "type": "string" - }, - "min_admin_api_version": { - "title": "Min admin API version", - "description": "Minimum supported admin API version by the admin server", - "type": "integer", - "format": "uint16", - "minimum": 0.0 - }, - "max_admin_api_version": { - "title": "Max admin API version", - "description": "Maximum supported admin API version by the admin server", - "type": "integer", - "format": "uint16", - "minimum": 0.0 - }, - "ingress_endpoint": { - "title": "Ingress endpoint", - "description": "Ingress endpoint that the Web UI should use to interact with.", - "type": "string", - "format": "uri" - } - } - } - } - } -} +{"openapi":"3.0.0","info":{"title":"Admin API","description":"This API exposes the admin operations of a Restate cluster, such as registering new service deployments, interacting with running invocations, register Kafka subscriptions, retrieve service metadata. For an overview, check out the [Operate documentation](https://docs.restate.dev/operate/). If you're looking for how to call your services, check out the [Ingress HTTP API](https://docs.restate.dev/invoke/http) instead.","version":"1.3.3-dev"},"paths":{"/cluster-health":{"get":{"tags":["cluster_health"],"summary":"Cluster health","description":"Get the cluster health.","operationId":"cluster_health","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterHealthResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/deployments":{"get":{"tags":["deployment"],"summary":"List deployments","description":"List all registered deployments.","operationId":"list_deployments","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListDeploymentsResponse"}}}}}},"post":{"tags":["deployment"],"summary":"Create deployment","description":"Create deployment. Restate will invoke the endpoint to gather additional information required for registration, such as the services exposed by the deployment. If the deployment is already registered, this method will fail unless `force` is set to `true`.","externalDocs":{"url":"https://docs.restate.dev/operate/registration"},"operationId":"create_deployment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeploymentRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeploymentResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/deployments/{deployment}":{"get":{"tags":["deployment"],"summary":"Get deployment","description":"Get deployment metadata","operationId":"get_deployment","parameters":[{"name":"deployment","in":"path","description":"Deployment identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetailedDeploymentResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}},"put":{"tags":["deployment"],"summary":"Update deployment","description":"Update deployment. Invokes the endpoint and replaces the existing deployment metadata with the discovered information. This is a dangerous operation that should be used only when there are failing invocations on the deployment that cannot be resolved any other way. Sense checks are applied to test that the new deployment is sufficiently similar to the old one.","externalDocs":{"url":"https://docs.restate.dev/operate/versioning"},"operationId":"update_deployment","parameters":[{"name":"deployment","in":"path","description":"Deployment identifier","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDeploymentRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DetailedDeploymentResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}},"delete":{"tags":["deployment"],"summary":"Delete deployment","description":"Delete deployment. Currently it's supported to remove a deployment only using the force flag","operationId":"delete_deployment","parameters":[{"name":"deployment","in":"path","description":"Deployment identifier","required":true,"schema":{"type":"string"}},{"name":"force","in":"query","description":"If true, the deployment will be forcefully deleted. This might break in-flight invocations, use with caution.","style":"simple","schema":{"type":"boolean"}}],"responses":{"202":{"description":"Accepted"},"501":{"description":"Not implemented. Only using the force flag is supported at the moment."},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/health":{"get":{"tags":["health"],"summary":"Health check","description":"Check REST API Health.","operationId":"health","responses":{"200":{"description":"OK"}}}},"/invocations/{invocation_id}":{"delete":{"tags":["invocation"],"summary":"Delete an invocation","description":"Delete the given invocation. By default, an invocation is terminated by gracefully cancelling it. This ensures virtual object state consistency. Alternatively, an invocation can be killed which does not guarantee consistency for virtual object instance state, in-flight invocations to other services, etc. A stored completed invocation can also be purged","operationId":"delete_invocation","parameters":[{"name":"invocation_id","in":"path","description":"Invocation identifier.","required":true,"schema":{"type":"string"}},{"name":"mode","in":"query","description":"If cancel, it will gracefully terminate the invocation. If kill, it will terminate the invocation with a hard stop. If purge, it will only cleanup the response for completed invocations, and leave unaffected an in-flight invocation.","style":"simple","schema":{"$ref":"#/components/schemas/DeletionMode"}}],"responses":{"202":{"description":"Accepted"},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/invocations/{invocation_id}/time_travel/{trim_index}":{"patch":{"tags":["invocation"],"summary":"Time travel an invocation","description":"Time travel the given invocation. Time travel trims the invocation journal from the given index (inclusive) onward and resumes the invocation afterwards.","operationId":"time_travel_invocation","parameters":[{"name":"invocation_id","in":"path","description":"Invocation identifier.","required":true,"schema":{"type":"string"}},{"name":"trim_index","in":"path","description":"Trim entry index, inclusive. The index MUST correspond to a command entry, otherwise this operation will be ignored.","required":true,"schema":{"type":"integer","format":"uint32","minimum":0.0}}],"responses":{"202":{"description":"Accepted"},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/openapi":{"get":{"tags":["openapi"],"summary":"OpenAPI specification","externalDocs":{"url":"https://swagger.io/specification/"},"operationId":"openapi_spec","responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}},"/services":{"get":{"tags":["service"],"summary":"List services","description":"List all registered services.","operationId":"list_services","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServicesResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/services/{service}":{"get":{"tags":["service"],"summary":"Get service","description":"Get a registered service.","operationId":"get_service","parameters":[{"name":"service","in":"path","description":"Fully qualified service name.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceMetadata"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}},"patch":{"tags":["service"],"summary":"Modify a service","description":"Modify a registered service.","operationId":"modify_service","parameters":[{"name":"service","in":"path","description":"Fully qualified service name.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModifyServiceRequest"}}},"required":true},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceMetadata"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/services/{service}/handlers":{"get":{"tags":["service_handler"],"summary":"List service handlers","description":"List all the handlers of the given service.","operationId":"list_service_handlers","parameters":[{"name":"service","in":"path","description":"Fully qualified service name.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServiceHandlersResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/services/{service}/handlers/{handler}":{"get":{"tags":["service_handler"],"summary":"Get service handler","description":"Get the handler of a service","operationId":"get_service_handler","parameters":[{"name":"service","in":"path","description":"Fully qualified service name.","required":true,"schema":{"type":"string"}},{"name":"handler","in":"path","description":"Handler name.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandlerMetadata"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/services/{service}/openapi":{"get":{"tags":["service"],"summary":"Get service OpenAPI","description":"Get the service OpenAPI 3.1 contract.","operationId":"get_service_openapi","parameters":[{"name":"service","in":"path","description":"Fully qualified service name.","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OpenAPI 3.1 of the service","content":{"application/json":{"schema":{}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/services/{service}/state":{"post":{"tags":["service"],"summary":"Modify a service state","description":"Modify service state","operationId":"modify_service_state","parameters":[{"name":"service","in":"path","description":"Fully qualified service name.","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModifyServiceStateRequest"}}},"required":true},"responses":{"202":{"description":"Accepted"},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/subscriptions":{"get":{"tags":["subscription"],"summary":"List subscriptions","description":"List all subscriptions.","operationId":"list_subscriptions","parameters":[{"name":"sink","in":"query","description":"Filter by the exact specified sink.","style":"simple","schema":{"type":"string"}},{"name":"source","in":"query","description":"Filter by the exact specified source.","style":"simple","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSubscriptionsResponse"}}}}}},"post":{"tags":["subscription"],"summary":"Create subscription","description":"Create subscription.","externalDocs":{"url":"https://docs.restate.dev/operate/invocation#managing-kafka-subscriptions"},"operationId":"create_subscription","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionRequest"}}},"required":true},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/subscriptions/{subscription}":{"get":{"tags":["subscription"],"summary":"Get subscription","description":"Get subscription","operationId":"get_subscription","parameters":[{"name":"subscription","in":"path","description":"Subscription identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionResponse"}}}},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}},"delete":{"tags":["subscription"],"summary":"Delete subscription","description":"Delete subscription.","operationId":"delete_subscription","parameters":[{"name":"subscription","in":"path","description":"Subscription identifier","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Accepted"},"400":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"403":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"404":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"409":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"500":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}},"503":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorDescriptionResponse"}}}}}}},"/version":{"get":{"tags":["version"],"summary":"Admin version information","description":"Obtain admin version information.","operationId":"version","responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VersionInformation"}}}}}}}},"components":{"schemas":{"ClusterHealthResponse":{"type":"object","required":["cluster_name"],"properties":{"cluster_name":{"description":"Cluster name","type":"string"},"metadata_cluster_health":{"description":"Embedded metadata cluster health if it was enabled","allOf":[{"$ref":"#/components/schemas/EmbeddedMetadataClusterHealth"}],"nullable":true}}},"EmbeddedMetadataClusterHealth":{"type":"object","required":["members"],"properties":{"members":{"description":"Current members of the embedded metadata cluster","type":"array","items":{"type":"integer","format":"uint32","minimum":0.0}}}},"ErrorDescriptionResponse":{"title":"Error description response","description":"Error details of the response","type":"object","required":["message"],"properties":{"message":{"type":"string"},"restate_code":{"title":"Restate code","description":"Restate error code describing this error","type":"string","nullable":true}}},"ListDeploymentsResponse":{"type":"object","required":["deployments"],"properties":{"deployments":{"type":"array","items":{"$ref":"#/components/schemas/DeploymentResponse"}}}},"DeploymentResponse":{"anyOf":[{"title":"HttpDeploymentResponse","description":"Deployment response for HTTP deployments","type":"object","required":["created_at","http_version","id","max_protocol_version","min_protocol_version","protocol_type","services","uri"],"properties":{"id":{"title":"Deployment ID","allOf":[{"$ref":"#/components/schemas/String"}]},"uri":{"title":"Deployment URI","description":"URI used to invoke this service deployment.","type":"string"},"protocol_type":{"title":"Protocol Type","description":"Protocol type used to invoke this service deployment.","allOf":[{"$ref":"#/components/schemas/ProtocolType"}]},"http_version":{"title":"HTTP Version","description":"HTTP Version used to invoke this service deployment.","type":"string"},"additional_headers":{"title":"Additional headers","description":"Additional headers used to invoke this service deployment.","type":"object","additionalProperties":{"type":"string"}},"created_at":{"type":"string"},"min_protocol_version":{"type":"integer","format":"int32"},"max_protocol_version":{"type":"integer","format":"int32"},"services":{"title":"Services","description":"List of services exposed by this deployment.","type":"array","items":{"$ref":"#/components/schemas/ServiceNameRevPair"}}}},{"title":"LambdaDeploymentResponse","description":"Deployment response for Lambda deployments","type":"object","required":["arn","created_at","id","max_protocol_version","min_protocol_version","services"],"properties":{"id":{"title":"Deployment ID","allOf":[{"$ref":"#/components/schemas/String"}]},"arn":{"title":"Lambda ARN","description":"Lambda ARN used to invoke this service deployment.","allOf":[{"$ref":"#/components/schemas/LambdaARN"}]},"assume_role_arn":{"title":"Assume role ARN","description":"Assume role ARN used to invoke this deployment. Check https://docs.restate.dev/category/aws-lambda for more details.","type":"string","nullable":true},"additional_headers":{"title":"Additional headers","description":"Additional headers used to invoke this service deployment.","type":"object","additionalProperties":{"type":"string"}},"created_at":{"type":"string"},"min_protocol_version":{"type":"integer","format":"int32"},"max_protocol_version":{"type":"integer","format":"int32"},"services":{"title":"Services","description":"List of services exposed by this deployment.","type":"array","items":{"$ref":"#/components/schemas/ServiceNameRevPair"}}}}]},"String":{"type":"string"},"ProtocolType":{"type":"string","enum":["RequestResponse","BidiStream"]},"ServiceNameRevPair":{"type":"object","required":["name","revision"],"properties":{"name":{"type":"string"},"revision":{"type":"integer","format":"uint32","minimum":0.0}}},"LambdaARN":{"type":"string","format":"arn"},"RegisterDeploymentRequest":{"anyOf":[{"title":"RegisterHttpDeploymentRequest","description":"Register HTTP deployment request","type":"object","required":["uri"],"properties":{"uri":{"title":"Uri","description":"Uri to use to discover/invoke the http deployment.","type":"string"},"additional_headers":{"title":"Additional headers","description":"Additional headers added to the discover/invoke requests to the deployment.","type":"object","additionalProperties":{"type":"string"},"nullable":true},"use_http_11":{"title":"Use http1.1","description":"If `true`, discovery will be attempted using a client that defaults to HTTP1.1 instead of a prior-knowledge HTTP2 client. HTTP2 may still be used for TLS servers that advertise HTTP2 support via ALPN. HTTP1.1 deployments will only work in request-response mode.","default":false,"type":"boolean"},"force":{"title":"Force","description":"If `true`, it will override, if existing, any deployment using the same `uri`. Beware that this can lead in-flight invocations to an unrecoverable error state.\n\nBy default, this is `true` but it might change in future to `false`.\n\nSee the [versioning documentation](https://docs.restate.dev/operate/versioning) for more information.","default":true,"type":"boolean"},"dry_run":{"title":"Dry-run mode","description":"If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.","default":false,"type":"boolean"}}},{"title":"RegisterLambdaDeploymentRequest","description":"Register Lambda deployment request","type":"object","required":["arn"],"properties":{"arn":{"title":"ARN","description":"ARN to use to discover/invoke the lambda deployment.","type":"string"},"assume_role_arn":{"title":"Assume role ARN","description":"Optional ARN of a role to assume when invoking the addressed Lambda, to support role chaining","type":"string","nullable":true},"additional_headers":{"title":"Additional headers","description":"Additional headers added to the discover/invoke requests to the deployment.","type":"object","additionalProperties":{"type":"string"},"nullable":true},"force":{"title":"Force","description":"If `true`, it will override, if existing, any deployment using the same `uri`. Beware that this can lead in-flight invocations to an unrecoverable error state.\n\nBy default, this is `true` but it might change in future to `false`.\n\nSee the [versioning documentation](https://docs.restate.dev/operate/versioning) for more information.","default":true,"type":"boolean"},"dry_run":{"title":"Dry-run mode","description":"If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.","default":false,"type":"boolean"}}}]},"RegisterDeploymentResponse":{"type":"object","required":["id","services"],"properties":{"id":{"$ref":"#/components/schemas/String"},"services":{"type":"array","items":{"$ref":"#/components/schemas/ServiceMetadata"}}}},"ServiceMetadata":{"type":"object","required":["deployment_id","handlers","idempotency_retention","name","public","revision","ty"],"properties":{"name":{"title":"Name","description":"Fully qualified name of the service","type":"string"},"handlers":{"type":"array","items":{"$ref":"#/components/schemas/HandlerMetadata"}},"ty":{"$ref":"#/components/schemas/ServiceType"},"documentation":{"title":"Documentation","description":"Documentation of the service, as propagated by the SDKs.","type":"string","nullable":true},"metadata":{"title":"Metadata","description":"Additional service metadata, as propagated by the SDKs.","type":"object","additionalProperties":{"type":"string"}},"deployment_id":{"title":"Deployment Id","description":"Deployment exposing the latest revision of the service.","type":"string"},"revision":{"title":"Revision","description":"Latest revision of the service.","type":"integer","format":"uint32","minimum":0.0},"public":{"title":"Public","description":"If true, the service can be invoked through the ingress. If false, the service can be invoked only from another Restate service.","type":"boolean"},"idempotency_retention":{"title":"Idempotency retention","description":"The retention duration of idempotent requests for this service.","type":"string"},"workflow_completion_retention":{"title":"Workflow completion retention","description":"The retention duration of workflows. Only available on workflow services.","type":"string","nullable":true},"inactivity_timeout":{"title":"Inactivity timeout","description":"This timer guards against stalled service/handler invocations. Once it expires, Restate triggers a graceful termination by asking the service invocation to suspend (which preserves intermediate progress).\n\nThe 'abort timeout' is used to abort the invocation, in case it doesn't react to the request to suspend.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format.\n\nThis overrides the default inactivity timeout set in invoker options.","type":"string","nullable":true},"abort_timeout":{"title":"Abort timeout","description":"This timer guards against stalled service/handler invocations that are supposed to terminate. The abort timeout is started after the 'inactivity timeout' has expired and the service/handler invocation has been asked to gracefully terminate. Once the timer expires, it will abort the service/handler invocation.\n\nThis timer potentially **interrupts** user code. If the user code needs longer to gracefully terminate, then this value needs to be set accordingly.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format.\n\nThis overrides the default abort timeout set in invoker options.","type":"string","nullable":true}}},"HandlerMetadata":{"type":"object","required":["input_description","name","output_description"],"properties":{"name":{"type":"string"},"ty":{"allOf":[{"$ref":"#/components/schemas/HandlerMetadataType"}],"nullable":true},"documentation":{"title":"Documentation","description":"Documentation of the handler, as propagated by the SDKs.","type":"string","nullable":true},"metadata":{"title":"Metadata","description":"Additional handler metadata, as propagated by the SDKs.","type":"object","additionalProperties":{"type":"string"}},"input_description":{"title":"Human readable input description","description":"If empty, no schema was provided by the user at discovery time.","type":"string"},"output_description":{"title":"Human readable output description","description":"If empty, no schema was provided by the user at discovery time.","type":"string"},"input_json_schema":{"title":"Input JSON Schema","description":"JSON Schema of the handler input","nullable":true},"output_json_schema":{"title":"Output JSON Schema","description":"JSON Schema of the handler output","nullable":true}}},"HandlerMetadataType":{"type":"string","enum":["Exclusive","Shared","Workflow"]},"ServiceType":{"type":"string","enum":["Service","VirtualObject","Workflow"]},"DetailedDeploymentResponse":{"anyOf":[{"title":"HttpDetailedDeploymentResponse","description":"Detailed deployment response for HTTP deployments","type":"object","required":["created_at","http_version","id","max_protocol_version","min_protocol_version","protocol_type","services","uri"],"properties":{"id":{"title":"Deployment ID","allOf":[{"$ref":"#/components/schemas/String"}]},"uri":{"title":"Deployment URI","description":"URI used to invoke this service deployment.","type":"string"},"protocol_type":{"title":"Protocol Type","description":"Protocol type used to invoke this service deployment.","allOf":[{"$ref":"#/components/schemas/ProtocolType"}]},"http_version":{"title":"HTTP Version","description":"HTTP Version used to invoke this service deployment.","type":"string"},"additional_headers":{"title":"Additional headers","description":"Additional headers used to invoke this service deployment.","type":"object","additionalProperties":{"type":"string"}},"created_at":{"type":"string"},"min_protocol_version":{"type":"integer","format":"int32"},"max_protocol_version":{"type":"integer","format":"int32"},"services":{"title":"Services","description":"List of services exposed by this deployment.","type":"array","items":{"$ref":"#/components/schemas/ServiceMetadata"}}}},{"title":"LambdaDetailedDeploymentResponse","description":"Detailed deployment response for Lambda deployments","type":"object","required":["arn","created_at","id","max_protocol_version","min_protocol_version","services"],"properties":{"id":{"title":"Deployment ID","allOf":[{"$ref":"#/components/schemas/String"}]},"arn":{"title":"Lambda ARN","description":"Lambda ARN used to invoke this service deployment.","allOf":[{"$ref":"#/components/schemas/LambdaARN"}]},"assume_role_arn":{"title":"Assume role ARN","description":"Assume role ARN used to invoke this deployment. Check https://docs.restate.dev/category/aws-lambda for more details.","type":"string","nullable":true},"additional_headers":{"title":"Additional headers","description":"Additional headers used to invoke this service deployment.","type":"object","additionalProperties":{"type":"string"}},"created_at":{"type":"string"},"min_protocol_version":{"type":"integer","format":"int32"},"max_protocol_version":{"type":"integer","format":"int32"},"services":{"title":"Services","description":"List of services exposed by this deployment.","type":"array","items":{"$ref":"#/components/schemas/ServiceMetadata"}}}}]},"UpdateDeploymentRequest":{"anyOf":[{"type":"object","required":["uri"],"properties":{"uri":{"title":"Uri","description":"Uri to use to discover/invoke the http deployment.","type":"string"},"additional_headers":{"title":"Additional headers","description":"Additional headers added to the discover/invoke requests to the deployment.","type":"object","additionalProperties":{"type":"string"},"nullable":true},"use_http_11":{"title":"Use http1.1","description":"If `true`, discovery will be attempted using a client that defaults to HTTP1.1 instead of a prior-knowledge HTTP2 client. HTTP2 may still be used for TLS servers that advertise HTTP2 support via ALPN. HTTP1.1 deployments will only work in request-response mode.","default":false,"type":"boolean"},"dry_run":{"title":"Dry-run mode","description":"If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.","default":false,"type":"boolean"}}},{"type":"object","required":["arn"],"properties":{"arn":{"title":"ARN","description":"ARN to use to discover/invoke the lambda deployment.","type":"string"},"assume_role_arn":{"title":"Assume role ARN","description":"Optional ARN of a role to assume when invoking the addressed Lambda, to support role chaining","type":"string","nullable":true},"additional_headers":{"title":"Additional headers","description":"Additional headers added to the discover/invoke requests to the deployment.","type":"object","additionalProperties":{"type":"string"},"nullable":true},"dry_run":{"title":"Dry-run mode","description":"If `true`, discovery will run but the deployment will not be registered. This is useful to see the impact of a new deployment before registering it.","default":false,"type":"boolean"}}}]},"DeletionMode":{"type":"string","enum":["Cancel","Kill","Purge"]},"ListServicesResponse":{"type":"object","required":["services"],"properties":{"services":{"type":"array","items":{"$ref":"#/components/schemas/ServiceMetadata"}}}},"ModifyServiceRequest":{"type":"object","properties":{"public":{"title":"Public","description":"If true, the service can be invoked through the ingress. If false, the service can be invoked only from another Restate service.","default":null,"type":"boolean","nullable":true},"idempotency_retention":{"title":"Idempotency retention","description":"Modify the retention of idempotent requests for this service.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.","default":null,"type":"string","nullable":true},"workflow_completion_retention":{"title":"Workflow completion retention","description":"Modify the retention of the workflow completion. This can be modified only for workflow services!\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.","default":null,"type":"string","nullable":true},"inactivity_timeout":{"title":"Inactivity timeout","description":"This timer guards against stalled service/handler invocations. Once it expires, Restate triggers a graceful termination by asking the service invocation to suspend (which preserves intermediate progress).\n\nThe 'abort timeout' is used to abort the invocation, in case it doesn't react to the request to suspend.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.\n\nThis overrides the default inactivity timeout set in invoker options.","default":null,"type":"string","nullable":true},"abort_timeout":{"title":"Abort timeout","description":"This timer guards against stalled service/handler invocations that are supposed to terminate. The abort timeout is started after the 'inactivity timeout' has expired and the service/handler invocation has been asked to gracefully terminate. Once the timer expires, it will abort the service/handler invocation.\n\nThis timer potentially **interrupts** user code. If the user code needs longer to gracefully terminate, then this value needs to be set accordingly.\n\nCan be configured using the [`humantime`](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) format or the ISO8601.\n\nThis overrides the default abort timeout set in invoker options.","default":null,"type":"string","nullable":true}}},"ListServiceHandlersResponse":{"type":"object","required":["handlers"],"properties":{"handlers":{"type":"array","items":{"$ref":"#/components/schemas/HandlerMetadata"}}}},"ModifyServiceStateRequest":{"type":"object","required":["new_state","object_key"],"properties":{"version":{"title":"Version","description":"If set, the latest version of the state is compared with this value and the operation will fail when the versions differ.","type":"string","nullable":true},"object_key":{"title":"Service key","description":"To what virtual object key to apply this change","type":"string"},"new_state":{"title":"New State","description":"The new state to replace the previous state with","type":"object","additionalProperties":{"type":"array","items":{"type":"integer","format":"uint8","minimum":0.0}}}}},"ListSubscriptionsResponse":{"type":"object","required":["subscriptions"],"properties":{"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionResponse"}}}},"SubscriptionResponse":{"type":"object","required":["id","options","sink","source"],"properties":{"id":{"$ref":"#/components/schemas/String"},"source":{"type":"string"},"sink":{"type":"string"},"options":{"type":"object","additionalProperties":{"type":"string"}}}},"CreateSubscriptionRequest":{"type":"object","required":["sink","source"],"properties":{"source":{"title":"Source","description":"Source uri. Accepted forms:\n\n* `kafka:///`, e.g. `kafka://my-cluster/my-topic`","type":"string"},"sink":{"title":"Sink","description":"Sink uri. Accepted forms:\n\n* `service:///`, e.g. `service://Counter/count`","type":"string"},"options":{"title":"Options","description":"Additional options to apply to the subscription.","type":"object","additionalProperties":{"type":"string"},"nullable":true}}},"VersionInformation":{"type":"object","required":["ingress_endpoint","max_admin_api_version","min_admin_api_version","version"],"properties":{"version":{"title":"Admin server version","description":"Version of the admin server","type":"string"},"min_admin_api_version":{"title":"Min admin API version","description":"Minimum supported admin API version by the admin server","type":"integer","format":"uint16","minimum":0.0},"max_admin_api_version":{"title":"Max admin API version","description":"Maximum supported admin API version by the admin server","type":"integer","format":"uint16","minimum":0.0},"ingress_endpoint":{"title":"Ingress endpoint","description":"Ingress endpoint that the Web UI should use to interact with.","type":"string","format":"uri"}}}}},"tags":[{"name":"deployment","description":"Service Deployment management"},{"name":"invocation","description":"Invocation management","externalDocs":{"url":"https://docs.restate.dev/operate/invocation"}},{"name":"subscription","description":"Subscription management","externalDocs":{"url":"https://docs.restate.dev/operate/invocation#managing-kafka-subscriptions"}},{"name":"service","description":"Service management"},{"name":"service_handler","description":"Service handlers metadata"},{"name":"cluster_health","description":"Cluster health"},{"name":"health","description":"Admin API health"},{"name":"version","description":"API Version"}],"externalDocs":{"url":"https://docs.restate.dev/operate/"}}