diff --git a/common/src/models/project.rs b/common/src/models/project.rs index 458175983..2286f3abb 100644 --- a/common/src/models/project.rs +++ b/common/src/models/project.rs @@ -15,12 +15,13 @@ pub const IDLE_MINUTES: u64 = 30; #[schema(as = shuttle_common::models::project::Response)] pub struct Response { pub name: String, + #[schema(value_type = shuttle_common::models::project::State)] pub state: State, } #[derive(Clone, Debug, Deserialize, Serialize, EnumString, ToSchema)] -#[schema(as = shuttle_common::models::project::State)] #[serde(rename_all = "lowercase")] +#[schema(as = shuttle_common::models::project::State)] pub enum State { Creating { recreate_count: usize }, Attaching { recreate_count: usize }, diff --git a/common/src/models/secret.rs b/common/src/models/secret.rs index b7d13700e..fdb4c8dcf 100644 --- a/common/src/models/secret.rs +++ b/common/src/models/secret.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use utoipa::ToSchema; #[derive(Deserialize, Serialize, ToSchema)] +#[schema(as = shuttle_common::models::secret::Response)] pub struct Response { pub key: String, #[schema(value_type = KnownFormat::DateTime)] diff --git a/common/src/models/stats.rs b/common/src/models/stats.rs index 24556be2b..41d0852a8 100644 --- a/common/src/models/stats.rs +++ b/common/src/models/stats.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use utoipa::{ToSchema}; +use utoipa::ToSchema; use uuid::Uuid; #[derive(Deserialize, Serialize, ToSchema)] diff --git a/common/src/resource.rs b/common/src/resource.rs index f4287ccfc..6da342354 100644 --- a/common/src/resource.rs +++ b/common/src/resource.rs @@ -15,11 +15,11 @@ pub struct Response { pub r#type: Type, /// The config used when creating this resource. Use the [Self::r#type] to know how to parse this data. - #[schema(value_type = object)] + #[schema(value_type = Object)] pub config: Value, /// The data associated with this resource. Use the [Self::r#type] to know how to parse this data. - #[schema(value_type = object)] + #[schema(value_type = Object)] pub data: Value, } diff --git a/deployer/src/handlers/mod.rs b/deployer/src/handlers/mod.rs index 1344d7e33..4082715da 100644 --- a/deployer/src/handlers/mod.rs +++ b/deployer/src/handlers/mod.rs @@ -70,7 +70,7 @@ mod project; shuttle_common::deployment::State )) )] -struct ApiDoc; +pub struct ApiDoc; pub async fn make_router( persistence: Persistence, @@ -81,6 +81,8 @@ pub async fn make_router( project_name: ProjectName, ) -> Router { Router::new() + // TODO: The `/swagger-ui` responds with a 303 See Other response which is followed in + // browsers but leads to 404 Not Found. This must be investigated. .merge(SwaggerUi::new("/projects/:project_name/swagger-ui").url( "/projects/:project_name/api-docs/openapi.json", ApiDoc::openapi(), diff --git a/deployer/src/lib.rs b/deployer/src/lib.rs index 73912cb76..39bede711 100644 --- a/deployer/src/lib.rs +++ b/deployer/src/lib.rs @@ -19,7 +19,7 @@ use crate::deployment::gateway_client::GatewayClient; mod args; mod deployment; mod error; -mod handlers; +pub mod handlers; mod persistence; mod proxy; mod runtime_manager; diff --git a/gateway/src/api/latest.rs b/gateway/src/api/latest.rs index 348b9cf52..76d4b59b2 100644 --- a/gateway/src/api/latest.rs +++ b/gateway/src/api/latest.rs @@ -91,6 +91,9 @@ impl StatusResponse { responses( (status = 200, description = "Sucesffuly got a specific project information.", body = shuttle_common::models::project::Response), (status = 500, description = "Server internal error.") + ), + params( + ("project_name" = String, Path, description = "The name of the project."), ) )] async fn get_project( @@ -155,6 +158,9 @@ async fn get_projects_list( responses( (status = 200, description = "Sucesffuly created a specific project.", body = shuttle_common::models::project::Response), (status = 500, description = "Server internal error.") + ), + params( + ("project_name" = String, Path, description = "The name of the project."), ) )] async fn create_project( @@ -192,6 +198,9 @@ async fn create_project( responses( (status = 200, description = "Sucesffuly destroyed a specific project.", body = shuttle_common::models::project::Response), (status = 500, description = "Server internal error.") + ), + params( + ("project_name" = String, Path, description = "The name of the project."), ) )] async fn destroy_project( @@ -272,7 +281,7 @@ async fn get_status(State(RouterState { sender, .. }): State) -> Re post, path = "/stats/load", responses( - (status = 200, description = "Successfully fetched the build queue load.", body = shttle_common::models::stats::LoadResponse), + (status = 200, description = "Successfully fetched the build queue load.", body = shuttle_common::models::stats::LoadResponse), (status = 500, description = "Server internal error.") ) )] @@ -412,12 +421,13 @@ async fn destroy_projects( post, path = "/admin/acme/{email}", responses( - (status = 200, description = "Created an acme account.", body = String), + (status = 200, description = "Created an acme account.", content_type = "application/json", body = String), (status = 500, description = "Server internal error.") ), params( ("email" = String, Path, description = "An email the acme account binds to."), - ) + ), + )] async fn create_acme_account( Extension(acme_client): Extension, @@ -616,6 +626,19 @@ async fn get_projects( Ok(AxumJson(projects)) } +struct SecurityAddon; + +impl Modify for SecurityAddon { + fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { + if let Some(components) = openapi.components.as_mut() { + components.add_security_scheme( + "Gateway API Key", + SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("Bearer"))), + ) + } + } +} + #[derive(OpenApi)] #[openapi( paths( @@ -636,26 +659,16 @@ async fn get_projects( get_load_admin, delete_load_admin ), + modifiers(&SecurityAddon), components(schemas( shuttle_common::models::project::Response, shuttle_common::models::stats::LoadResponse, - shuttle_common::models::project::AdminResponse + shuttle_common::models::project::AdminResponse, + shuttle_common::models::stats::LoadResponse, + shuttle_common::models::project::State )) )] -struct ApiDoc; - -struct SecurityAddon; - -impl Modify for SecurityAddon { - fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { - if let Some(components) = openapi.components.as_mut() { - components.add_security_scheme( - "api_key", - SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("todo_apikey"))), - ) - } - } -} +pub struct ApiDoc; #[derive(Clone)] pub(crate) struct RouterState { @@ -789,6 +802,8 @@ impl ApiBuilder { .delete(delete_load_admin) .layer(ScopedLayer::new(vec![Scope::Admin])), ) + // TODO: The `/admin/swagger-ui` responds with a 303 See Other response which is followed in + // browsers but leads to 404 Not Found. This must be investigated. .merge( SwaggerUi::new("/admin/swagger-ui") .url("/admin/api-docs/openapi.json", ApiDoc::openapi()),