From 804786cfac26b3f9161f067a1158be62bed63ae9 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Tue, 23 Sep 2025 17:06:20 -0700 Subject: [PATCH] chore(core): standardize api schema --- Cargo.lock | 1 + out/openapi.json | 349 +----------------- .../common/api-types/src/actors/create.rs | 1 - packages/common/api-types/src/actors/get.rs | 16 - packages/common/api-types/src/actors/list.rs | 2 +- packages/common/api-types/src/actors/mod.rs | 1 - packages/common/api-types/src/lib.rs | 1 + .../common/api-types/src/namespaces/list.rs | 21 ++ .../common/api-types/src/namespaces/mod.rs | 1 + packages/common/api-types/src/runners/get.rs | 16 - packages/common/api-types/src/runners/list.rs | 3 + packages/common/api-types/src/runners/mod.rs | 1 - packages/common/types/src/lib.rs | 1 + .../types/src/namespaces.rs} | 10 - packages/core/api-peer/src/actors/delete.rs | 2 +- packages/core/api-peer/src/actors/get.rs | 40 -- packages/core/api-peer/src/actors/mod.rs | 1 - packages/core/api-peer/src/namespaces.rs | 97 +---- packages/core/api-peer/src/router.rs | 7 - packages/core/api-peer/src/runner_configs.rs | 20 +- packages/core/api-peer/src/runners.rs | 92 ++--- packages/core/api-public/src/actors/get.rs | 70 ---- .../core/api-public/src/actors/get_by_id.rs | 74 ---- .../api-public/src/actors/get_or_create.rs | 5 +- .../src/actors/get_or_create_by_id.rs | 160 -------- packages/core/api-public/src/actors/list.rs | 5 +- packages/core/api-public/src/actors/mod.rs | 3 - packages/core/api-public/src/actors/utils.rs | 36 +- packages/core/api-public/src/namespaces.rs | 53 +-- packages/core/api-public/src/router.rs | 24 +- packages/core/api-public/src/runners.rs | 57 +-- packages/core/pegboard-serverless/src/lib.rs | 2 +- packages/services/namespace/Cargo.toml | 3 +- packages/services/namespace/src/keys.rs | 4 +- packages/services/namespace/src/lib.rs | 2 +- .../services/namespace/src/ops/get_global.rs | 15 +- .../services/namespace/src/ops/get_local.rs | 3 +- packages/services/namespace/src/ops/list.rs | 3 +- .../src/ops/resolve_for_name_global.rs | 41 +- .../src/ops/resolve_for_name_local.rs | 3 +- .../namespace/src/ops/runner_config/delete.rs | 4 +- .../src/ops/runner_config/get_global.rs | 6 +- .../src/ops/runner_config/get_local.rs | 2 +- .../namespace/src/ops/runner_config/list.rs | 3 +- .../namespace/src/ops/runner_config/upsert.rs | 7 +- packages/services/namespace/src/utils.rs | 9 + 46 files changed, 181 insertions(+), 1096 deletions(-) delete mode 100644 packages/common/api-types/src/actors/get.rs create mode 100644 packages/common/api-types/src/namespaces/list.rs create mode 100644 packages/common/api-types/src/namespaces/mod.rs delete mode 100644 packages/common/api-types/src/runners/get.rs rename packages/{services/namespace/src/types.rs => common/types/src/namespaces.rs} (89%) delete mode 100644 packages/core/api-peer/src/actors/get.rs delete mode 100644 packages/core/api-public/src/actors/get.rs delete mode 100644 packages/core/api-public/src/actors/get_by_id.rs delete mode 100644 packages/core/api-public/src/actors/get_or_create_by_id.rs create mode 100644 packages/services/namespace/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 0273e20ecb..9153bb9826 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2782,6 +2782,7 @@ dependencies = [ "gasoline", "internal", "rivet-api-builder", + "rivet-api-types", "rivet-api-util", "rivet-data", "rivet-error", diff --git a/out/openapi.json b/out/openapi.json index d6da9279e1..e7faf42314 100644 --- a/out/openapi.json +++ b/out/openapi.json @@ -20,7 +20,7 @@ "actors::list" ], "summary": " ## Datacenter Round Trips", - "description": " **If key is some & `include_destroyed` is false**\n\n 2 round trips:\n - namespace::ops::resolve_for_name_global\n - GET /actors/{} (multiple DCs based on actor IDs)\n\n\tThis path is optimized because we can read the actor IDs fro the key directly from Epoxy with\n\tstale consistency to determine which datacenter the actor lives in. Under most circumstances,\n\tthis means we don't need to fan out to all datacenters (like normal list does).\n\n\tThe reason `include_destroyed` has to be false is Epoxy only stores currently active actors. If\n\t`include_destroyed` is true, we show all previous iterations of actors with the same key.\n\n **Otherwise**\n\n 2 round trips:\n - namespace::ops::resolve_for_name_global\n - GET /actors (fanout)\n\n ## Optimized Alternative Routes\n\n For minimal round trips to check if an actor exists for a key, use `GET /actors/by-id`. This\n does not require fetching the actor's state, so it returns immediately.", + "description": " **If key is some & `include_destroyed` is false**\n\n 2 round trips:\n - namespace::ops::resolve_for_name_global\n - GET /actors (multiple DCs based on actor IDs)\n\n\tThis path is optimized because we can read the actor IDs fro the key directly from Epoxy with\n\tstale consistency to determine which datacenter the actor lives in. Under most circumstances,\n\tthis means we don't need to fan out to all datacenters (like normal list does).\n\n\tThe reason `include_destroyed` has to be false is Epoxy only stores currently active actors. If\n\t`include_destroyed` is true, we show all previous iterations of actors with the same key.\n\n **Otherwise**\n\n 2 round trips:\n - namespace::ops::resolve_for_name_global\n - GET /actors (fanout)\n\n ## Optimized Alternative Routes", "operationId": "actors_list", "parameters": [ { @@ -99,7 +99,7 @@ "actors::get_or_create" ], "summary": "## Datacenter Round Trips", - "description": "**If actor exists**\n\n2 round trips:\n- namespace::ops::resolve_for_name_global\n- GET /actors/{}\n\n**If actor does not exist and is created in the current datacenter:**\n\n2 round trips:\n- namespace::ops::resolve_for_name_global\n- [pegboard::workflows::actor] Create actor workflow (includes Epoxy key allocation)\n\n**If actor does not exist and is created in a different datacenter:**\n\n3 round trips:\n- namespace::ops::resolve_for_name_global\n- POST /actors to remote datacenter\n- [pegboard::workflows::actor] Create actor workflow (includes Epoxy key allocation)\n\nactor::get will always be in the same datacenter.\n\n## Optimized Alternative Routes\n\nFor minimal round trips to get or create an actor, use `PUT /actors/by-id`. This doesn't\nrequire fetching the actor's state from the other datacenter.", + "description": "**If actor exists**\n\n2 round trips:\n- namespace::ops::resolve_for_name_global\n- GET /actors/{}\n\n**If actor does not exist and is created in the current datacenter:**\n\n2 round trips:\n- namespace::ops::resolve_for_name_global\n- [pegboard::workflows::actor] Create actor workflow (includes Epoxy key allocation)\n\n**If actor does not exist and is created in a different datacenter:**\n\n3 round trips:\n- namespace::ops::resolve_for_name_global\n- POST /actors to remote datacenter\n- [pegboard::workflows::actor] Create actor workflow (includes Epoxy key allocation)\n\nactor::get will always be in the same datacenter.\n\n## Optimized Alternative Routes", "operationId": "actors_get_or_create", "parameters": [ { @@ -191,102 +191,6 @@ } } }, - "/actors/by-id": { - "get": { - "tags": [ - "actors::get_by_id" - ], - "summary": "## Datacenter Round Trips", - "description": "1 round trip:\n- namespace::ops::resolve_for_name_global\n\nThis does not require another round trip since we use stale consistency for the get_id_for_key.", - "operationId": "actors_get_by_id", - "parameters": [ - { - "name": "namespace", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "name", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "key", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ActorsGetByIdResponse" - } - } - } - } - } - }, - "put": { - "tags": [ - "actors::get_or_create_by_id" - ], - "summary": "## Datacenter Round Trips", - "description": "**If actor exists**\n\n1 round trip:\n- namespace::ops::resolve_for_name_global\n\n**If actor does not exist and is created in the current datacenter:**\n\n2 round trips:\n- namespace::ops::resolve_for_name_global\n- [pegboard::workflows::actors::keys::allocate_key] Reserve Epoxy key\n\n**If actor does not exist and is created in a different datacenter:**\n\n3 round trips:\n- namespace::ops::resolve_for_name_global\n- namespace::ops::get (to get namespace name for remote call)\n- POST /actors to remote datacenter", - "operationId": "actors_get_or_create_by_id", - "parameters": [ - { - "name": "namespace", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "datacenter", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ActorsGetOrCreateByIdRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ActorsGetOrCreateByIdResponse" - } - } - } - } - } - } - }, "/actors/names": { "get": { "tags": [ @@ -337,44 +241,6 @@ } }, "/actors/{actor_id}": { - "get": { - "tags": [ - "actors::get" - ], - "summary": "## Datacenter Round Trips", - "description": "2 round trip:\n- GET /actors/{}\n- [api-peer] namespace::ops::resolve_for_name_global", - "operationId": "actors_get", - "parameters": [ - { - "name": "actor_id", - "in": "path", - "required": true, - "schema": { - "$ref": "#/components/schemas/RivetId" - } - }, - { - "name": "namespace", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ActorsGetResponse" - } - } - } - } - } - }, "delete": { "tags": [ "actors::delete" @@ -467,14 +333,11 @@ } }, { - "name": "namespace_id", + "name": "namespace_ids", "in": "query", "required": false, "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RivetId" - } + "type": "string" } } ], @@ -484,7 +347,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NamespacesListResponse" + "$ref": "#/components/schemas/ActorsListResponse" } } } @@ -520,36 +383,6 @@ } } }, - "/namespaces/{namespace_id}": { - "get": { - "tags": [ - "namespaces" - ], - "operationId": "namespaces_get", - "parameters": [ - { - "name": "namespace_id", - "in": "path", - "required": true, - "schema": { - "$ref": "#/components/schemas/RivetId" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NamespacesGetResponse" - } - } - } - } - } - } - }, "/runner-configs": { "get": { "tags": [ @@ -591,14 +424,11 @@ } }, { - "name": "runner_name", + "name": "runner_names", "in": "query", "required": false, "schema": { - "type": "array", - "items": { - "type": "string" - } + "type": "string" } } ], @@ -723,6 +553,14 @@ "type": "string" } }, + { + "name": "runner_ids", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, { "name": "include_stopped", "in": "query", @@ -811,44 +649,6 @@ } } } - }, - "/runners/{runner_id}": { - "get": { - "tags": [ - "runners" - ], - "operationId": "runners_get", - "parameters": [ - { - "name": "runner_id", - "in": "path", - "required": true, - "schema": { - "$ref": "#/components/schemas/RivetId" - } - }, - { - "name": "namespace", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RunnersGetResponse" - } - } - } - } - } - } } }, "components": { @@ -992,66 +792,6 @@ "ActorsDeleteResponse": { "type": "object" }, - "ActorsGetByIdResponse": { - "type": "object", - "properties": { - "actor_id": { - "oneOf": [ - { - "type": "null" - }, - { - "$ref": "#/components/schemas/RivetId" - } - ] - } - } - }, - "ActorsGetOrCreateByIdRequest": { - "type": "object", - "required": [ - "name", - "key", - "runner_name_selector", - "crash_policy" - ], - "properties": { - "crash_policy": { - "$ref": "#/components/schemas/CrashPolicy" - }, - "input": { - "type": [ - "string", - "null" - ] - }, - "key": { - "type": "string" - }, - "name": { - "type": "string" - }, - "runner_name_selector": { - "type": "string" - } - }, - "additionalProperties": false - }, - "ActorsGetOrCreateByIdResponse": { - "type": "object", - "required": [ - "actor_id", - "created" - ], - "properties": { - "actor_id": { - "$ref": "#/components/schemas/RivetId" - }, - "created": { - "type": "boolean" - } - } - }, "ActorsGetOrCreateRequest": { "type": "object", "required": [ @@ -1097,17 +837,6 @@ } } }, - "ActorsGetResponse": { - "type": "object", - "required": [ - "actor" - ], - "properties": { - "actor": { - "$ref": "#/components/schemas/Actor" - } - } - }, "ActorsListNamesResponse": { "type": "object", "required": [ @@ -1133,14 +862,14 @@ "ActorsListResponse": { "type": "object", "required": [ - "actors", + "namespaces", "pagination" ], "properties": { - "actors": { + "namespaces": { "type": "array", "items": { - "$ref": "#/components/schemas/Actor" + "$ref": "#/components/schemas/Namespace" } }, "pagination": { @@ -1246,36 +975,6 @@ }, "additionalProperties": false }, - "NamespacesGetResponse": { - "type": "object", - "required": [ - "namespace" - ], - "properties": { - "namespace": { - "$ref": "#/components/schemas/Namespace" - } - } - }, - "NamespacesListResponse": { - "type": "object", - "required": [ - "namespaces", - "pagination" - ], - "properties": { - "namespaces": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Namespace" - } - }, - "pagination": { - "$ref": "#/components/schemas/Pagination" - } - }, - "additionalProperties": false - }, "Pagination": { "type": "object", "properties": { @@ -1526,18 +1225,6 @@ "RunnerConfigsUpsertResponse": { "type": "object" }, - "RunnersGetResponse": { - "type": "object", - "required": [ - "runner" - ], - "properties": { - "runner": { - "$ref": "#/components/schemas/Runner" - } - }, - "additionalProperties": false - }, "RunnersListNamesResponse": { "type": "object", "required": [ diff --git a/packages/common/api-types/src/actors/create.rs b/packages/common/api-types/src/actors/create.rs index 004058dc7f..6b138eccd7 100644 --- a/packages/common/api-types/src/actors/create.rs +++ b/packages/common/api-types/src/actors/create.rs @@ -1,4 +1,3 @@ -use rivet_util::Id; use serde::{Deserialize, Serialize}; use utoipa::{IntoParams, ToSchema}; diff --git a/packages/common/api-types/src/actors/get.rs b/packages/common/api-types/src/actors/get.rs deleted file mode 100644 index 4976e84a2e..0000000000 --- a/packages/common/api-types/src/actors/get.rs +++ /dev/null @@ -1,16 +0,0 @@ -use serde::{Deserialize, Serialize}; -use utoipa::{IntoParams, ToSchema}; - -#[derive(Debug, Serialize, Deserialize, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct GetQuery { - pub namespace: Option, -} - -#[derive(Serialize, Deserialize, ToSchema)] -#[serde(deny_unknown_fields)] -#[schema(as = ActorsGetResponse)] -pub struct GetResponse { - pub actor: rivet_types::actors::Actor, -} diff --git a/packages/common/api-types/src/actors/list.rs b/packages/common/api-types/src/actors/list.rs index dfa1f0d0ef..1efb53b4af 100644 --- a/packages/common/api-types/src/actors/list.rs +++ b/packages/common/api-types/src/actors/list.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use utoipa::{IntoParams, ToSchema}; -#[derive(Debug, Serialize, Deserialize, Clone, IntoParams)] +#[derive(Debug, Serialize, Deserialize, Clone, IntoParams, Default)] #[serde(deny_unknown_fields)] #[into_params(parameter_in = Query)] pub struct ListQuery { diff --git a/packages/common/api-types/src/actors/mod.rs b/packages/common/api-types/src/actors/mod.rs index 36bf390af0..29aaf4f89e 100644 --- a/packages/common/api-types/src/actors/mod.rs +++ b/packages/common/api-types/src/actors/mod.rs @@ -1,4 +1,3 @@ pub mod create; -pub mod get; pub mod list; pub mod list_names; diff --git a/packages/common/api-types/src/lib.rs b/packages/common/api-types/src/lib.rs index 018fad1d46..345bda71ec 100644 --- a/packages/common/api-types/src/lib.rs +++ b/packages/common/api-types/src/lib.rs @@ -1,4 +1,5 @@ pub mod actors; pub mod datacenters; +pub mod namespaces; pub mod pagination; pub mod runners; diff --git a/packages/common/api-types/src/namespaces/list.rs b/packages/common/api-types/src/namespaces/list.rs new file mode 100644 index 0000000000..bd84b83a45 --- /dev/null +++ b/packages/common/api-types/src/namespaces/list.rs @@ -0,0 +1,21 @@ +use serde::{Deserialize, Serialize}; +use utoipa::{IntoParams, ToSchema}; + +#[derive(Debug, Serialize, Deserialize, Clone, IntoParams)] +#[serde(deny_unknown_fields)] +#[into_params(parameter_in = Query)] +pub struct ListQuery { + pub limit: Option, + pub cursor: Option, + pub name: Option, + #[serde(default)] + pub namespace_ids: Option, +} + +#[derive(Serialize, Deserialize, ToSchema)] +#[serde(deny_unknown_fields)] +#[schema(as = NamespaceListResponse)] +pub struct ListResponse { + pub namespaces: Vec, + pub pagination: crate::pagination::Pagination, +} diff --git a/packages/common/api-types/src/namespaces/mod.rs b/packages/common/api-types/src/namespaces/mod.rs new file mode 100644 index 0000000000..d17e233fbf --- /dev/null +++ b/packages/common/api-types/src/namespaces/mod.rs @@ -0,0 +1 @@ +pub mod list; diff --git a/packages/common/api-types/src/runners/get.rs b/packages/common/api-types/src/runners/get.rs deleted file mode 100644 index 9db4c8ed31..0000000000 --- a/packages/common/api-types/src/runners/get.rs +++ /dev/null @@ -1,16 +0,0 @@ -use serde::{Deserialize, Serialize}; -use utoipa::{IntoParams, ToSchema}; - -#[derive(Debug, Serialize, Deserialize, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct GetQuery { - pub namespace: Option, -} - -#[derive(Serialize, ToSchema)] -#[serde(deny_unknown_fields)] -#[schema(as = RunnersGetResponse)] -pub struct GetResponse { - pub runner: rivet_types::runners::Runner, -} diff --git a/packages/common/api-types/src/runners/list.rs b/packages/common/api-types/src/runners/list.rs index 573f6a71f1..1dc80a3c63 100644 --- a/packages/common/api-types/src/runners/list.rs +++ b/packages/common/api-types/src/runners/list.rs @@ -1,3 +1,4 @@ +use rivet_util::Id; use serde::{Deserialize, Serialize}; use utoipa::{IntoParams, ToSchema}; @@ -9,6 +10,8 @@ use crate::pagination::Pagination; pub struct ListQuery { pub namespace: String, pub name: Option, + #[serde(default)] + pub runner_ids: Option, pub include_stopped: Option, pub limit: Option, pub cursor: Option, diff --git a/packages/common/api-types/src/runners/mod.rs b/packages/common/api-types/src/runners/mod.rs index 89d961bc20..d17e233fbf 100644 --- a/packages/common/api-types/src/runners/mod.rs +++ b/packages/common/api-types/src/runners/mod.rs @@ -1,2 +1 @@ -pub mod get; pub mod list; diff --git a/packages/common/types/src/lib.rs b/packages/common/types/src/lib.rs index 19ca637eaf..37904cce2e 100644 --- a/packages/common/types/src/lib.rs +++ b/packages/common/types/src/lib.rs @@ -2,4 +2,5 @@ pub mod actors; pub mod datacenters; pub mod keys; pub mod msgs; +pub mod namespaces; pub mod runners; diff --git a/packages/services/namespace/src/types.rs b/packages/common/types/src/namespaces.rs similarity index 89% rename from packages/services/namespace/src/types.rs rename to packages/common/types/src/namespaces.rs index b2034accd4..70c2dfc8e4 100644 --- a/packages/services/namespace/src/types.rs +++ b/packages/common/types/src/namespaces.rs @@ -1,8 +1,6 @@ use gas::prelude::*; use utoipa::ToSchema; -use crate::keys; - #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct Namespace { pub namespace_id: Id, @@ -25,14 +23,6 @@ pub enum RunnerConfig { }, } -impl RunnerConfig { - pub fn variant(&self) -> keys::RunnerConfigVariant { - match self { - RunnerConfig::Serverless { .. } => keys::RunnerConfigVariant::Serverless, - } - } -} - impl From for rivet_data::generated::namespace_runner_config_v1::Data { fn from(value: RunnerConfig) -> Self { match value { diff --git a/packages/core/api-peer/src/actors/delete.rs b/packages/core/api-peer/src/actors/delete.rs index de66ced6cd..770d7dbb8d 100644 --- a/packages/core/api-peer/src/actors/delete.rs +++ b/packages/core/api-peer/src/actors/delete.rs @@ -23,7 +23,7 @@ pub struct DeletePath { #[utoipa::path( delete, - operation_id = "runners_delete", + operation_id = "actors_delete", path = "/actors/{actor_id}", params( ("actor_id" = Id, Path), diff --git a/packages/core/api-peer/src/actors/get.rs b/packages/core/api-peer/src/actors/get.rs deleted file mode 100644 index 954bab3ff0..0000000000 --- a/packages/core/api-peer/src/actors/get.rs +++ /dev/null @@ -1,40 +0,0 @@ -use anyhow::Result; -use rivet_api_builder::ApiCtx; -use rivet_api_types::actors::get::{GetQuery, GetResponse}; -use rivet_util::Id; -use serde::Deserialize; - -#[derive(Deserialize)] -pub struct GetPath { - pub actor_id: Id, -} - -pub async fn get(ctx: ApiCtx, path: GetPath, query: GetQuery) -> Result { - let actors_res = ctx - .op(pegboard::ops::actor::get::Input { - actor_ids: vec![path.actor_id], - }) - .await?; - - let actor = actors_res - .actors - .into_iter() - .next() - .ok_or_else(|| pegboard::errors::Actor::NotFound.build())?; - - // If namespace is provided, verify the actor belongs to it - if let Some(namespace_name) = query.namespace { - let namespace = ctx - .op(namespace::ops::resolve_for_name_global::Input { - name: namespace_name, - }) - .await? - .ok_or_else(|| namespace::errors::Namespace::NotFound.build())?; - - if actor.namespace_id != namespace.namespace_id { - return Err(pegboard::errors::Actor::NotFound.build()); - } - } - - Ok(GetResponse { actor }) -} diff --git a/packages/core/api-peer/src/actors/mod.rs b/packages/core/api-peer/src/actors/mod.rs index 0c36f2af11..ce36036d8f 100644 --- a/packages/core/api-peer/src/actors/mod.rs +++ b/packages/core/api-peer/src/actors/mod.rs @@ -1,5 +1,4 @@ pub mod create; pub mod delete; -pub mod get; pub mod list; pub mod list_names; diff --git a/packages/core/api-peer/src/namespaces.rs b/packages/core/api-peer/src/namespaces.rs index 931db762fb..16c3e88a00 100644 --- a/packages/core/api-peer/src/namespaces.rs +++ b/packages/core/api-peer/src/namespaces.rs @@ -1,91 +1,18 @@ use anyhow::Result; use gas::prelude::*; use rivet_api_builder::ApiCtx; -use rivet_api_types::pagination::Pagination; +use rivet_api_types::{namespaces::list::*, pagination::Pagination}; use rivet_util::Id; use serde::{Deserialize, Serialize}; -use utoipa::{IntoParams, ToSchema}; - -#[derive(Debug, Serialize, Deserialize, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct GetQuery {} - -#[derive(Serialize, ToSchema)] -#[schema(as = NamespacesGetResponse)] -pub struct GetResponse { - pub namespace: namespace::types::Namespace, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct GetPath { - pub namespace_id: Id, -} - -pub async fn get(ctx: ApiCtx, path: GetPath, _query: GetQuery) -> Result { - let namespace = ctx - .op(namespace::ops::get_local::Input { - namespace_ids: vec![path.namespace_id], - }) - .await? - .into_iter() - .next() - .ok_or_else(|| namespace::errors::Namespace::NotFound.build())?; - - Ok(GetResponse { namespace }) -} - -#[derive(Debug, Serialize, Deserialize, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct ResolveForNameQuery {} - -#[derive(Serialize, ToSchema)] -#[schema(as = NamespacesResolveForNameResponse)] -pub struct ResolveForNameResponse { - pub namespace: namespace::types::Namespace, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct ResolveForNamePath { - pub name: String, -} - -pub async fn resolve_for_name( - ctx: ApiCtx, - path: ResolveForNamePath, - _query: ResolveForNameQuery, -) -> Result { - let namespace = ctx - .op(namespace::ops::resolve_for_name_local::Input { name: path.name }) - .await? - .ok_or_else(|| namespace::errors::Namespace::NotFound.build())?; - - Ok(ResolveForNameResponse { namespace }) -} - -#[derive(Debug, Serialize, Deserialize, Clone, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct ListQuery { - pub limit: Option, - pub cursor: Option, - pub name: Option, - #[serde(default)] - pub namespace_id: Vec, -} - -#[derive(Serialize, Deserialize, ToSchema)] -#[serde(deny_unknown_fields)] -#[schema(as = NamespacesListResponse)] -pub struct ListResponse { - pub namespaces: Vec, - pub pagination: Pagination, -} +use utoipa::ToSchema; pub async fn list(ctx: ApiCtx, _path: (), query: ListQuery) -> Result { + let namespace_ids = query.namespace_ids.as_ref().map(|x| { + x.split(',') + .filter_map(|s| s.trim().parse::().ok()) + .collect::>() + }); + // If name filter is provided, resolve and return only that namespace if let Some(name) = query.name { let namespace = ctx @@ -102,11 +29,9 @@ pub async fn list(ctx: ApiCtx, _path: (), query: ListQuery) -> Result, pub variant: Option, #[serde(default)] - pub runner_name: Vec, + pub runner_names: Option, } #[derive(Deserialize)] @@ -27,7 +28,7 @@ pub struct ListPath {} #[serde(deny_unknown_fields)] #[schema(as = RunnerConfigsListResponse)] pub struct ListResponse { - pub runner_configs: HashMap, + pub runner_configs: HashMap, pub pagination: Pagination, } @@ -39,13 +40,12 @@ pub async fn list(ctx: ApiCtx, _path: ListPath, query: ListQuery) -> Result Result Result { - let runners_res = ctx - .op(pegboard::ops::runner::get::Input { - runner_ids: vec![path.runner_id], - }) - .await?; - - let runner = runners_res - .runners - .into_iter() - .next() - .ok_or_else(|| pegboard::errors::Runner::NotFound.build())?; - - // If namespace is provided, verify the runner has actors from that namespace - if let Some(namespace_name) = query.namespace { - let namespace = ctx - .op(namespace::ops::resolve_for_name_global::Input { - name: namespace_name, - }) - .await? - .ok_or_else(|| namespace::errors::Namespace::NotFound.build())?; - - if runner.namespace_id != namespace.namespace_id { - return Err(pegboard::errors::Runner::NotFound.build()); - } - } - - Ok(GetResponse { runner }) -} - #[utoipa::path( get, operation_id = "runners_list", @@ -61,26 +21,42 @@ pub async fn list(ctx: ApiCtx, _path: (), query: ListQuery) -> Result()) - .transpose()?, - limit: query.limit.unwrap_or(100), + if let Some(runner_ids) = query.runner_ids { + let runner_ids = runner_ids + .split(',') + .filter_map(|s| s.trim().parse::().ok()) + .collect::>(); + let runners = ctx + .op(pegboard::ops::runner::get::Input { runner_ids }) + .await? + .runners; + + Ok(ListResponse { + runners, + pagination: Pagination { cursor: None }, }) - .await?; + } else { + let list_res = ctx + .op(pegboard::ops::runner::list_for_ns::Input { + namespace_id: namespace.namespace_id, + name: query.name, + include_stopped: query.include_stopped.unwrap_or(false), + created_before: query + .cursor + .as_deref() + .map(|c| c.parse::()) + .transpose()?, + limit: query.limit.unwrap_or(100), + }) + .await?; - let cursor = list_res.runners.last().map(|x| x.create_ts.to_string()); + let cursor = list_res.runners.last().map(|x| x.create_ts.to_string()); - Ok(ListResponse { - runners: list_res.runners, - pagination: Pagination { cursor }, - }) + Ok(ListResponse { + runners: list_res.runners, + pagination: Pagination { cursor }, + }) + } } #[derive(Debug, Serialize, Deserialize, Clone, IntoParams)] diff --git a/packages/core/api-public/src/actors/get.rs b/packages/core/api-public/src/actors/get.rs deleted file mode 100644 index 1c6b57053e..0000000000 --- a/packages/core/api-public/src/actors/get.rs +++ /dev/null @@ -1,70 +0,0 @@ -use anyhow::Result; -use axum::{ - extract::{Extension, Path, Query}, - http::HeaderMap, - response::{IntoResponse, Json, Response}, -}; -use rivet_api_builder::{ApiCtx, ApiError}; -use rivet_util::Id; -use serde::{Deserialize, Serialize}; -use utoipa::{IntoParams, ToSchema}; - -use crate::actors::utils; - -#[derive(Debug, Serialize, Deserialize, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct GetQuery { - pub namespace: Option, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct GetPath { - pub actor_id: Id, -} - -#[derive(Serialize, ToSchema)] -#[schema(as = ActorsGetResponse)] -pub struct GetResponse { - pub actor: rivet_types::actors::Actor, -} - -/// ## Datacenter Round Trips -/// -/// 2 round trip: -/// - GET /actors/{} -/// - [api-peer] namespace::ops::resolve_for_name_global -#[utoipa::path( - get, - operation_id = "actors_get", - path = "/actors/{actor_id}", - params( - ("actor_id" = Id, Path), - GetQuery, - ), - responses( - (status = 200, body = GetResponse), - ), -)] -pub async fn get( - Extension(ctx): Extension, - headers: HeaderMap, - Path(path): Path, - Query(query): Query, -) -> Response { - match get_inner(ctx, headers, path, query).await { - Ok(response) => response, - Err(err) => ApiError::from(err).into_response(), - } -} - -async fn get_inner( - ctx: ApiCtx, - headers: HeaderMap, - path: GetPath, - query: GetQuery, -) -> Result { - let actor = utils::fetch_actor_by_id(&ctx, headers, path.actor_id, query.namespace).await?; - Ok(Json(GetResponse { actor }).into_response()) -} diff --git a/packages/core/api-public/src/actors/get_by_id.rs b/packages/core/api-public/src/actors/get_by_id.rs deleted file mode 100644 index da898edaa8..0000000000 --- a/packages/core/api-public/src/actors/get_by_id.rs +++ /dev/null @@ -1,74 +0,0 @@ -use anyhow::Result; -use axum::{ - extract::{Extension, Query}, - response::{IntoResponse, Json, Response}, -}; -use rivet_api_builder::{ApiCtx, ApiError}; -use rivet_util::Id; -use serde::{Deserialize, Serialize}; -use utoipa::{IntoParams, ToSchema}; - -#[derive(Debug, Deserialize, Serialize, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct GetByIdQuery { - pub namespace: String, - pub name: String, - pub key: String, -} - -#[derive(Serialize, ToSchema)] -#[schema(as = ActorsGetByIdResponse)] -pub struct GetByIdResponse { - pub actor_id: Option, -} - -/// ## Datacenter Round Trips -/// -/// 1 round trip: -/// - namespace::ops::resolve_for_name_global -/// -/// This does not require another round trip since we use stale consistency for the get_id_for_key. -#[utoipa::path( - get, - operation_id = "actors_get_by_id", - path = "/actors/by-id", - params(GetByIdQuery), - responses( - (status = 200, body = GetByIdResponse), - ), -)] -pub async fn get_by_id( - Extension(ctx): Extension, - Query(query): Query, -) -> Response { - match get_by_id_inner(ctx, query).await { - Ok(response) => Json(response).into_response(), - Err(err) => ApiError::from(err).into_response(), - } -} - -async fn get_by_id_inner(ctx: ApiCtx, query: GetByIdQuery) -> Result { - // Resolve namespace - let namespace = ctx - .op(namespace::ops::resolve_for_name_global::Input { - name: query.namespace.clone(), - }) - .await? - .ok_or_else(|| namespace::errors::Namespace::NotFound.build())?; - - // Get the actor for the key - // This operation uses global consistency and handles datacenter routing - let actor = ctx - .op(pegboard::ops::actor::get_for_key::Input { - namespace_id: namespace.namespace_id, - name: query.name, - key: query.key, - }) - .await? - .actor; - - Ok(GetByIdResponse { - actor_id: actor.map(|x| x.actor_id), - }) -} diff --git a/packages/core/api-public/src/actors/get_or_create.rs b/packages/core/api-public/src/actors/get_or_create.rs index cda86de774..290e69a019 100644 --- a/packages/core/api-public/src/actors/get_or_create.rs +++ b/packages/core/api-public/src/actors/get_or_create.rs @@ -63,9 +63,6 @@ pub struct GetOrCreateResponse { /// actor::get will always be in the same datacenter. /// /// ## Optimized Alternative Routes -/// -/// For minimal round trips to get or create an actor, use `PUT /actors/by-id`. This doesn't -/// require fetching the actor's state from the other datacenter. #[utoipa::path( put, operation_id = "actors_get_or_create", @@ -162,7 +159,7 @@ async fn get_or_create_inner( &ctx, headers.clone(), existing_actor_id, - Some(query.namespace.clone()), + query.namespace.clone(), ) .await?; return Ok(GetOrCreateResponse { diff --git a/packages/core/api-public/src/actors/get_or_create_by_id.rs b/packages/core/api-public/src/actors/get_or_create_by_id.rs deleted file mode 100644 index ec6453a7de..0000000000 --- a/packages/core/api-public/src/actors/get_or_create_by_id.rs +++ /dev/null @@ -1,160 +0,0 @@ -use anyhow::Result; -use axum::{ - extract::{Extension, Query}, - response::{IntoResponse, Json, Response}, -}; -use rivet_api_builder::{ApiCtx, ApiError}; -use rivet_types::actors::CrashPolicy; -use rivet_util::Id; -use serde::{Deserialize, Serialize}; -use utoipa::{IntoParams, ToSchema}; - -use crate::actors::utils; -use crate::errors; - -#[derive(Debug, Deserialize, IntoParams)] -#[serde(deny_unknown_fields)] -#[into_params(parameter_in = Query)] -pub struct GetOrCreateByIdQuery { - pub namespace: String, - pub datacenter: Option, -} - -#[derive(Deserialize, ToSchema)] -#[serde(deny_unknown_fields)] -#[schema(as = ActorsGetOrCreateByIdRequest)] -pub struct GetOrCreateByIdRequest { - pub name: String, - pub key: String, - pub input: Option, - pub runner_name_selector: String, - pub crash_policy: CrashPolicy, -} - -#[derive(Serialize, ToSchema)] -#[schema(as = ActorsGetOrCreateByIdResponse)] -pub struct GetOrCreateByIdResponse { - pub actor_id: Id, - pub created: bool, -} - -/// ## Datacenter Round Trips -/// -/// **If actor exists** -/// -/// 1 round trip: -/// - namespace::ops::resolve_for_name_global -/// -/// **If actor does not exist and is created in the current datacenter:** -/// -/// 2 round trips: -/// - namespace::ops::resolve_for_name_global -/// - [pegboard::workflows::actors::keys::allocate_key] Reserve Epoxy key -/// -/// **If actor does not exist and is created in a different datacenter:** -/// -/// 3 round trips: -/// - namespace::ops::resolve_for_name_global -/// - namespace::ops::get (to get namespace name for remote call) -/// - POST /actors to remote datacenter -#[utoipa::path( - put, - operation_id = "actors_get_or_create_by_id", - path = "/actors/by-id", - params(GetOrCreateByIdQuery), - request_body(content = GetOrCreateByIdRequest, content_type = "application/json"), - responses( - (status = 200, body = GetOrCreateByIdResponse), - ), -)] -pub async fn get_or_create_by_id( - Extension(ctx): Extension, - Query(query): Query, - Json(body): Json, -) -> Response { - match get_or_create_by_id_inner(ctx, query, body).await { - Ok(response) => Json(response).into_response(), - Err(err) => ApiError::from(err).into_response(), - } -} - -async fn get_or_create_by_id_inner( - ctx: ApiCtx, - query: GetOrCreateByIdQuery, - body: GetOrCreateByIdRequest, -) -> Result { - // Resolve namespace - let namespace = ctx - .op(namespace::ops::resolve_for_name_global::Input { - name: query.namespace.clone(), - }) - .await? - .ok_or_else(|| namespace::errors::Namespace::NotFound.build())?; - - // Check if actor already exists for the key - // The get_for_key op uses global consistency and handles datacenter routing - let existing = ctx - .op(pegboard::ops::actor::get_for_key::Input { - namespace_id: namespace.namespace_id, - name: body.name.clone(), - key: body.key.clone(), - }) - .await?; - - if let Some(actor) = existing.actor { - // Actor exists, just return the ID (no round trip needed) - return Ok(GetOrCreateByIdResponse { - actor_id: actor.actor_id, - created: false, - }); - } - - // Actor doesn't exist for any key, create it - // Determine which datacenter to create the actor in - let target_dc_label = if let Some(dc_name) = &query.datacenter { - ctx.config() - .dc_for_name(dc_name) - .ok_or_else(|| errors::Datacenter::NotFound.build())? - .datacenter_label - } else { - ctx.config().dc_label() - }; - - let actor_id = Id::new_v1(target_dc_label); - - match ctx - .op(pegboard::ops::actor::create::Input { - actor_id, - namespace_id: namespace.namespace_id, - name: body.name.clone(), - key: Some(body.key.clone()), - runner_name_selector: body.runner_name_selector, - input: body.input.clone(), - crash_policy: body.crash_policy, - forward_request: true, - datacenter_name: query.datacenter.clone(), - }) - .await - { - Ok(_) => Ok(GetOrCreateByIdResponse { - actor_id, - created: true, - }), - Err(err) => { - // Check if this is a DuplicateKey error and extract the existing actor ID - if let Some(existing_actor_id) = utils::extract_duplicate_key_error(&err) { - tracing::info!( - ?existing_actor_id, - "received duplicate key error, returning existing actor id" - ); - return Ok(GetOrCreateByIdResponse { - actor_id: existing_actor_id, - created: false, - }); - } - - // Re-throw the original error if it's not a DuplicateKey - Err(err) - } - } -} diff --git a/packages/core/api-public/src/actors/list.rs b/packages/core/api-public/src/actors/list.rs index 93d7aca504..e597bbc959 100644 --- a/packages/core/api-public/src/actors/list.rs +++ b/packages/core/api-public/src/actors/list.rs @@ -39,7 +39,7 @@ pub struct ListResponse { /// /// 2 round trips: /// - namespace::ops::resolve_for_name_global -/// - GET /actors/{} (multiple DCs based on actor IDs) +/// - GET /actors (multiple DCs based on actor IDs) /// /// This path is optimized because we can read the actor IDs fro the key directly from Epoxy with /// stale consistency to determine which datacenter the actor lives in. Under most circumstances, @@ -55,9 +55,6 @@ pub struct ListResponse { /// - GET /actors (fanout) /// /// ## Optimized Alternative Routes -/// -/// For minimal round trips to check if an actor exists for a key, use `GET /actors/by-id`. This -/// does not require fetching the actor's state, so it returns immediately. #[utoipa::path( get, operation_id = "actors_list", diff --git a/packages/core/api-public/src/actors/mod.rs b/packages/core/api-public/src/actors/mod.rs index 1223d528db..9a563baae5 100644 --- a/packages/core/api-public/src/actors/mod.rs +++ b/packages/core/api-public/src/actors/mod.rs @@ -1,9 +1,6 @@ pub mod create; pub mod delete; -pub mod get; -pub mod get_by_id; pub mod get_or_create; -pub mod get_or_create_by_id; pub mod list; pub mod list_names; pub mod utils; diff --git a/packages/core/api-public/src/actors/utils.rs b/packages/core/api-public/src/actors/utils.rs index 4576a12e74..e98bd4815a 100644 --- a/packages/core/api-public/src/actors/utils.rs +++ b/packages/core/api-public/src/actors/utils.rs @@ -13,31 +13,43 @@ pub async fn fetch_actor_by_id( ctx: &ApiCtx, headers: HeaderMap, actor_id: Id, - namespace: Option, + namespace: String, ) -> Result { + let list_query = rivet_api_types::actors::list::ListQuery { + namespace, + actor_ids: Some(actor_id.to_string()), + ..Default::default() + }; + if actor_id.label() == ctx.config().dc_label() { // Local datacenter - use peer API directly - let res = rivet_api_peer::actors::get::get( - ctx.clone(), - rivet_api_peer::actors::get::GetPath { actor_id }, - rivet_api_types::actors::get::GetQuery { namespace }, - ) - .await?; + let res = rivet_api_peer::actors::list::list(ctx.clone(), (), list_query).await?; + let actor = res + .actors + .into_iter() + .next() + .ok_or_else(|| pegboard::errors::Actor::NotFound.build())?; - Ok(res.actor) + Ok(actor) } else { // Remote datacenter - make HTTP request - let res = request_remote_datacenter::( + let res = request_remote_datacenter::( ctx.config(), actor_id.label(), - &format!("/actors/{}", actor_id), + "/actors", Method::GET, headers, - Some(&rivet_api_types::actors::get::GetQuery { namespace }), + Some(&list_query), Option::<&()>::None, ) .await?; - Ok(res.actor) + let actor = res + .actors + .into_iter() + .next() + .ok_or_else(|| pegboard::errors::Actor::NotFound.build())?; + + Ok(actor) } } diff --git a/packages/core/api-public/src/namespaces.rs b/packages/core/api-public/src/namespaces.rs index c3818993b6..62d8276724 100644 --- a/packages/core/api-public/src/namespaces.rs +++ b/packages/core/api-public/src/namespaces.rs @@ -5,10 +5,9 @@ use axum::{ response::{IntoResponse, Json, Response}, }; use rivet_api_builder::{ApiCtx, ApiError}; -use rivet_util::Id; - use rivet_api_peer::namespaces::*; -use rivet_api_util::{request_remote_datacenter, request_remote_datacenter_raw}; +use rivet_api_types::namespaces::list::*; +use rivet_api_util::request_remote_datacenter; #[utoipa::path( get, @@ -48,54 +47,6 @@ async fn list_inner(ctx: ApiCtx, headers: HeaderMap, query: ListQuery) -> Result } } -#[utoipa::path( - get, - operation_id = "namespaces_get", - path = "/namespaces/{namespace_id}", - params( - ("namespace_id" = Id, Path), - GetQuery, - ), - responses( - (status = 200, body = GetResponse), - ), -)] -pub async fn get( - Extension(ctx): Extension, - headers: HeaderMap, - Path(path): Path, - Query(query): Query, -) -> Response { - match get_inner(ctx, headers, path, query).await { - Ok(response) => response, - Err(err) => ApiError::from(err).into_response(), - } -} - -async fn get_inner( - ctx: ApiCtx, - headers: HeaderMap, - path: GetPath, - query: GetQuery, -) -> Result { - if ctx.config().is_leader() { - let res = rivet_api_peer::namespaces::get(ctx, path, query).await?; - Ok(Json(res).into_response()) - } else { - let leader_dc = ctx.config().leader_dc()?; - request_remote_datacenter_raw( - &ctx, - leader_dc.datacenter_label, - &format!("/namespaces/{}", path.namespace_id), - axum::http::Method::GET, - headers, - Some(&query), - Option::<&()>::None, - ) - .await - } -} - #[utoipa::path( post, operation_id = "namespaces_create", diff --git a/packages/core/api-public/src/router.rs b/packages/core/api-public/src/router.rs index 2b7d5e2e52..85c45af3d2 100644 --- a/packages/core/api-public/src/router.rs +++ b/packages/core/api-public/src/router.rs @@ -1,8 +1,5 @@ use axum::response::Redirect; -use rivet_api_builder::{ - create_router, - wrappers::{get, post}, -}; +use rivet_api_builder::{create_router, wrappers::get}; use utoipa::OpenApi; use crate::{actors, datacenters, namespaces, runner_configs, runners, ui}; @@ -10,18 +7,13 @@ use crate::{actors, datacenters, namespaces, runner_configs, runners, ui}; #[derive(OpenApi)] #[openapi(paths( actors::list::list, - actors::get::get, actors::create::create, actors::delete::delete, actors::list_names::list_names, actors::get_or_create::get_or_create, - actors::get_by_id::get_by_id, - actors::get_or_create_by_id::get_or_create_by_id, runners::list, - runners::get, runners::list_names, namespaces::list, - namespaces::get, namespaces::create, runner_configs::list, runner_configs::upsert, @@ -46,10 +38,6 @@ pub async fn router( // MARK: Namespaces .route("/namespaces", axum::routing::get(namespaces::list)) .route("/namespaces", axum::routing::post(namespaces::create)) - .route( - "/namespaces/{namespace_id}", - axum::routing::get(namespaces::get), - ) .route("/runner-configs", axum::routing::get(runner_configs::list)) .route( "/runner-configs/{runner_name}", @@ -74,18 +62,8 @@ pub async fn router( "/actors/names", axum::routing::get(actors::list_names::list_names), ) - .route( - "/actors/by-id", - axum::routing::get(actors::get_by_id::get_by_id), - ) - .route( - "/actors/by-id", - axum::routing::put(actors::get_or_create_by_id::get_or_create_by_id), - ) - .route("/actors/{actor_id}", axum::routing::get(actors::get::get)) // MARK: Runners .route("/runners", axum::routing::get(runners::list)) - .route("/runners/{runner_id}", axum::routing::get(runners::get)) .route("/runners/names", axum::routing::get(runners::list_names)) // MARK: Datacenters .route("/datacenters", get(datacenters::list)) diff --git a/packages/core/api-public/src/runners.rs b/packages/core/api-public/src/runners.rs index 3b5a9f85ca..364860fbe4 100644 --- a/packages/core/api-public/src/runners.rs +++ b/packages/core/api-public/src/runners.rs @@ -1,66 +1,15 @@ use anyhow::Result; use axum::{ - extract::{Extension, Path, Query}, + extract::{Extension, Query}, http::HeaderMap, response::{IntoResponse, Json, Response}, }; use rivet_api_builder::{ApiCtx, ApiError}; -use rivet_api_types::{ - pagination::Pagination, - runners::{get::*, list::*}, -}; -use rivet_api_util::{fanout_to_datacenters, request_remote_datacenter_raw}; -use rivet_util::Id; +use rivet_api_types::{pagination::Pagination, runners::list::*}; +use rivet_api_util::fanout_to_datacenters; use serde::{Deserialize, Serialize}; use utoipa::{IntoParams, ToSchema}; -#[utoipa::path( - get, - operation_id = "runners_get", - path = "/runners/{runner_id}", - params( - ("runner_id" = Id, Path), - GetQuery, - ), - responses( - (status = 200, body = GetResponse), - ), -)] -pub async fn get( - Extension(ctx): Extension, - headers: HeaderMap, - Path(path): Path, - Query(query): Query, -) -> Response { - match get_inner(ctx, headers, path, query).await { - Ok(response) => response, - Err(err) => ApiError::from(err).into_response(), - } -} - -async fn get_inner( - ctx: ApiCtx, - headers: HeaderMap, - path: rivet_api_peer::runners::GetPath, - query: GetQuery, -) -> Result { - if path.runner_id.label() == ctx.config().dc_label() { - let res = rivet_api_peer::runners::get(ctx, path, query).await?; - Ok(Json(res).into_response()) - } else { - request_remote_datacenter_raw( - &ctx, - path.runner_id.label(), - &format!("/runners/{}", path.runner_id), - axum::http::Method::GET, - headers, - Some(&query), - Option::<&()>::None, - ) - .await - } -} - #[utoipa::path( get, operation_id = "runners_list", diff --git a/packages/core/pegboard-serverless/src/lib.rs b/packages/core/pegboard-serverless/src/lib.rs index 397d1d694f..baf1ff1671 100644 --- a/packages/core/pegboard-serverless/src/lib.rs +++ b/packages/core/pegboard-serverless/src/lib.rs @@ -9,10 +9,10 @@ use std::{ use anyhow::Result; use futures_util::{StreamExt, TryStreamExt}; use gas::prelude::*; -use namespace::types::RunnerConfig; use pegboard::keys; use reqwest_eventsource as sse; use rivet_runner_protocol as protocol; +use rivet_types::namespaces::RunnerConfig; use tokio::{sync::oneshot, task::JoinHandle, time::Duration}; use universaldb::options::StreamingMode; use universaldb::utils::IsolationLevel::*; diff --git a/packages/services/namespace/Cargo.toml b/packages/services/namespace/Cargo.toml index efd8da845f..66bcc71131 100644 --- a/packages/services/namespace/Cargo.toml +++ b/packages/services/namespace/Cargo.toml @@ -11,6 +11,7 @@ gas.workspace = true internal.workspace = true rivet-api-builder.workspace = true rivet-api-util.workspace = true +rivet-api-types.workspace = true rivet-data.workspace = true rivet-error.workspace = true rivet-types.workspace = true @@ -21,4 +22,4 @@ tracing.workspace = true universaldb.workspace = true url.workspace = true utoipa.workspace = true -versioned-data-util.workspace = true \ No newline at end of file +versioned-data-util.workspace = true diff --git a/packages/services/namespace/src/keys.rs b/packages/services/namespace/src/keys.rs index caa49d1005..bb55eef205 100644 --- a/packages/services/namespace/src/keys.rs +++ b/packages/services/namespace/src/keys.rs @@ -232,7 +232,7 @@ impl RunnerConfigKey { } impl FormalKey for RunnerConfigKey { - type Value = crate::types::RunnerConfig; + type Value = rivet_types::namespaces::RunnerConfig; fn deserialize(&self, raw: &[u8]) -> Result { Ok( @@ -325,7 +325,7 @@ impl RunnerConfigByVariantKey { } impl FormalKey for RunnerConfigByVariantKey { - type Value = crate::types::RunnerConfig; + type Value = rivet_types::namespaces::RunnerConfig; fn deserialize(&self, raw: &[u8]) -> Result { Ok( diff --git a/packages/services/namespace/src/lib.rs b/packages/services/namespace/src/lib.rs index 9a44983b50..804955644d 100644 --- a/packages/services/namespace/src/lib.rs +++ b/packages/services/namespace/src/lib.rs @@ -3,7 +3,7 @@ use gas::prelude::*; pub mod errors; pub mod keys; pub mod ops; -pub mod types; +pub mod utils; pub mod workflows; pub fn registry() -> WorkflowResult { diff --git a/packages/services/namespace/src/ops/get_global.rs b/packages/services/namespace/src/ops/get_global.rs index 613f8030ee..574579b52c 100644 --- a/packages/services/namespace/src/ops/get_global.rs +++ b/packages/services/namespace/src/ops/get_global.rs @@ -1,6 +1,5 @@ use gas::prelude::*; - -use crate::types::Namespace; +use rivet_types::namespaces::Namespace; #[derive(Debug)] pub struct Input { @@ -40,8 +39,10 @@ pub async fn namespace_get_global(ctx: &OperationCtx, input: &Input) -> Result(res).await?; + let res = rivet_api_util::parse_response::< + rivet_api_types::namespaces::list::ListResponse, + >(res) + .await?; for ns in res.namespaces { let namespace_id = ns.namespace_id; @@ -55,9 +56,3 @@ pub async fn namespace_get_global(ctx: &OperationCtx, input: &Input) -> Result, -} diff --git a/packages/services/namespace/src/ops/get_local.rs b/packages/services/namespace/src/ops/get_local.rs index 156632f384..82ec33b84b 100644 --- a/packages/services/namespace/src/ops/get_local.rs +++ b/packages/services/namespace/src/ops/get_local.rs @@ -1,8 +1,9 @@ use futures_util::{StreamExt, TryStreamExt}; use gas::prelude::*; +use rivet_types::namespaces::Namespace; use universaldb::utils::IsolationLevel::*; -use crate::{errors, keys, types::Namespace}; +use crate::{errors, keys}; #[derive(Debug)] pub struct Input { diff --git a/packages/services/namespace/src/ops/list.rs b/packages/services/namespace/src/ops/list.rs index 57955718b7..c4055ff55c 100644 --- a/packages/services/namespace/src/ops/list.rs +++ b/packages/services/namespace/src/ops/list.rs @@ -1,10 +1,11 @@ use anyhow::Result; use futures_util::TryStreamExt; use gas::prelude::*; +use rivet_types::namespaces::Namespace; use universaldb::options::StreamingMode; use universaldb::utils::IsolationLevel::*; -use crate::{errors, keys, types::Namespace}; +use crate::{errors, keys}; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Input { diff --git a/packages/services/namespace/src/ops/resolve_for_name_global.rs b/packages/services/namespace/src/ops/resolve_for_name_global.rs index d132a255e0..039290a065 100644 --- a/packages/services/namespace/src/ops/resolve_for_name_global.rs +++ b/packages/services/namespace/src/ops/resolve_for_name_global.rs @@ -1,6 +1,5 @@ use gas::prelude::*; - -use crate::types::Namespace; +use rivet_types::namespaces::Namespace; #[derive(Debug)] pub struct Input { @@ -31,33 +30,17 @@ pub async fn namespace_resolve_for_name_global( let leader_dc = leader_dc.clone(); let client = client.clone(); async move { - let url = leader_dc - .api_peer_url - .join(&format!("/namespaces/resolve/{}", input.name))?; - let res = client.get(url).send().await?; + let url = leader_dc.api_peer_url.join(&format!("/namespaces"))?; + let res = client.get(url).query(&("name", &input.name)).send().await?; - let res = - rivet_api_util::parse_response::(res).await; + let res = rivet_api_util::parse_response::< + rivet_api_types::namespaces::list::ListResponse, + >(res) + .await?; - let res = match res { - Ok(res) => Ok(Some(res.namespace)), - Err(err) => { - // Explicitly handle namespace not found error - if let Some(error) = err.chain().find_map(|x| { - x.downcast_ref::() - }) { - if error.1.group == "namespace" && error.1.code == "not_found" { - Ok(None) - } else { - Err(err) - } - } else { - Err(err) - } - } - }; + let ns = res.namespaces.into_iter().next(); - cache.resolve(&key, res?); + cache.resolve(&key, ns); Ok(cache) } @@ -67,9 +50,3 @@ pub async fn namespace_resolve_for_name_global( .map(|x| x.flatten()) } } - -// TODO: Cyclical dependency with api_peer -#[derive(Deserialize)] -struct ResolveForNameResponse { - namespace: Namespace, -} diff --git a/packages/services/namespace/src/ops/resolve_for_name_local.rs b/packages/services/namespace/src/ops/resolve_for_name_local.rs index b02617a35d..cd212abb82 100644 --- a/packages/services/namespace/src/ops/resolve_for_name_local.rs +++ b/packages/services/namespace/src/ops/resolve_for_name_local.rs @@ -1,7 +1,8 @@ use gas::prelude::*; +use rivet_types::namespaces::Namespace; use universaldb::utils::IsolationLevel::*; -use crate::{errors, keys, ops::get_local::get_inner, types::Namespace}; +use crate::{errors, keys, ops::get_local::get_inner}; #[derive(Debug)] pub struct Input { diff --git a/packages/services/namespace/src/ops/runner_config/delete.rs b/packages/services/namespace/src/ops/runner_config/delete.rs index 1b6f68cc3a..6f169b48c8 100644 --- a/packages/services/namespace/src/ops/runner_config/delete.rs +++ b/packages/services/namespace/src/ops/runner_config/delete.rs @@ -2,7 +2,7 @@ use gas::prelude::*; use rivet_cache::CacheKey; use universaldb::utils::IsolationLevel::*; -use crate::{errors, keys}; +use crate::{errors, keys, utils::runner_config_variant}; #[derive(Debug)] pub struct Input { @@ -30,7 +30,7 @@ pub async fn namespace_runner_config_delete(ctx: &OperationCtx, input: &Input) - // Clear secondary idx tx.delete(&keys::RunnerConfigByVariantKey::new( input.namespace_id, - config.variant(), + runner_config_variant(&config), input.name.clone(), )); } diff --git a/packages/services/namespace/src/ops/runner_config/get_global.rs b/packages/services/namespace/src/ops/runner_config/get_global.rs index e7207fec43..af634e4c20 100644 --- a/packages/services/namespace/src/ops/runner_config/get_global.rs +++ b/packages/services/namespace/src/ops/runner_config/get_global.rs @@ -1,8 +1,6 @@ -use std::collections::HashMap; - use gas::prelude::*; - -use crate::types::RunnerConfig; +use rivet_types::namespaces::RunnerConfig; +use std::collections::HashMap; #[derive(Debug)] pub struct Input { diff --git a/packages/services/namespace/src/ops/runner_config/get_local.rs b/packages/services/namespace/src/ops/runner_config/get_local.rs index 35a00aca94..a03c64ee99 100644 --- a/packages/services/namespace/src/ops/runner_config/get_local.rs +++ b/packages/services/namespace/src/ops/runner_config/get_local.rs @@ -14,7 +14,7 @@ pub struct Input { pub struct RunnerConfig { pub namespace_id: Id, pub name: String, - pub config: crate::types::RunnerConfig, + pub config: rivet_types::namespaces::RunnerConfig, } #[operation] diff --git a/packages/services/namespace/src/ops/runner_config/list.rs b/packages/services/namespace/src/ops/runner_config/list.rs index 45f414304d..7344b88de3 100644 --- a/packages/services/namespace/src/ops/runner_config/list.rs +++ b/packages/services/namespace/src/ops/runner_config/list.rs @@ -1,9 +1,10 @@ use futures_util::{StreamExt, TryStreamExt}; use gas::prelude::*; +use rivet_types::namespaces::RunnerConfig; use universaldb::options::StreamingMode; use universaldb::utils::IsolationLevel::*; -use crate::{errors, keys, types::RunnerConfig}; +use crate::{errors, keys}; #[derive(Debug)] pub struct Input { diff --git a/packages/services/namespace/src/ops/runner_config/upsert.rs b/packages/services/namespace/src/ops/runner_config/upsert.rs index 5c88b74f62..8c52057f56 100644 --- a/packages/services/namespace/src/ops/runner_config/upsert.rs +++ b/packages/services/namespace/src/ops/runner_config/upsert.rs @@ -1,8 +1,9 @@ use gas::prelude::*; use rivet_cache::CacheKey; +use rivet_types::namespaces::RunnerConfig; use universaldb::options::MutationType; -use crate::{errors, keys, types::RunnerConfig}; +use crate::{errors, keys, utils::runner_config_variant}; #[derive(Debug)] pub struct Input { @@ -31,7 +32,7 @@ pub async fn namespace_runner_config_upsert(ctx: &OperationCtx, input: &Input) - tx.write( &keys::RunnerConfigByVariantKey::new( input.namespace_id, - input.config.variant(), + runner_config_variant(&input.config), input.name.clone(), ), input.config.clone(), @@ -77,7 +78,7 @@ pub async fn namespace_runner_config_upsert(ctx: &OperationCtx, input: &Input) - .map_err(|err| err.build())?; // Purge cache in all dcs - let variant_str = serde_json::to_string(&input.config.variant())?; + let variant_str = serde_json::to_string(&runner_config_variant(&input.config))?; ctx.op(internal::ops::cache::purge_global::Input { base_key: format!("namespace.runner_config.{variant_str}.get_global"), keys: vec![(input.namespace_id, input.name.as_str()).cache_key().into()], diff --git a/packages/services/namespace/src/utils.rs b/packages/services/namespace/src/utils.rs new file mode 100644 index 0000000000..296ef4805f --- /dev/null +++ b/packages/services/namespace/src/utils.rs @@ -0,0 +1,9 @@ +use rivet_types::namespaces::RunnerConfig; + +use crate::keys; + +pub fn runner_config_variant(runner_config: &RunnerConfig) -> keys::RunnerConfigVariant { + match runner_config { + RunnerConfig::Serverless { .. } => keys::RunnerConfigVariant::Serverless, + } +}