From ac6bdbaafba3319850bb061a95e7f6b49510532a Mon Sep 17 00:00:00 2001 From: Raminder Singh Date: Wed, 11 Sep 2024 12:35:18 +0530 Subject: [PATCH] add openapi docs for all endpoints --- api/src/db/publications.rs | 3 +- api/src/routes/health_check.rs | 5 ++ api/src/routes/images.rs | 59 +++++++++++-- api/src/routes/pipelines.rs | 57 +++++++++++-- api/src/routes/sinks.rs | 59 +++++++++++-- api/src/routes/sources.rs | 60 +++++++++++-- api/src/routes/sources/publications.rs | 113 ++++++++++++++++++------- api/src/routes/sources/tables.rs | 9 ++ api/src/routes/tenants.rs | 11 ++- api/src/startup.rs | 66 +++++++++++++-- 10 files changed, 376 insertions(+), 66 deletions(-) diff --git a/api/src/db/publications.rs b/api/src/db/publications.rs index 428c1c4..9bda419 100644 --- a/api/src/db/publications.rs +++ b/api/src/db/publications.rs @@ -2,6 +2,7 @@ use std::{borrow::Cow, collections::HashMap}; use serde::Serialize; use sqlx::{postgres::PgConnectOptions, Connection, Executor, PgConnection, Row}; +use utoipa::ToSchema; use super::tables::Table; @@ -48,7 +49,7 @@ pub fn quote_literal(literal: &str) -> String { quoted_literal } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct Publication { pub name: String, pub tables: Vec, diff --git a/api/src/routes/health_check.rs b/api/src/routes/health_check.rs index 6f7d3f1..2431967 100644 --- a/api/src/routes/health_check.rs +++ b/api/src/routes/health_check.rs @@ -1,5 +1,10 @@ use actix_web::{get, HttpResponse, Responder}; +#[utoipa::path( + responses( + (status = 200, description = "Api is healthy"), + ) +)] #[get("/health_check")] pub async fn health_check() -> impl Responder { HttpResponse::Ok().body("ok") diff --git a/api/src/routes/images.rs b/api/src/routes/images.rs index 903716d..8dd5f90 100644 --- a/api/src/routes/images.rs +++ b/api/src/routes/images.rs @@ -8,6 +8,7 @@ use actix_web::{ use serde::{Deserialize, Serialize}; use sqlx::PgPool; use thiserror::Error; +use utoipa::ToSchema; use crate::db; @@ -53,24 +54,33 @@ impl ResponseError for ImageError { } } -#[derive(Deserialize)] -struct PostImageRequest { +#[derive(Deserialize, ToSchema)] +pub struct PostImageRequest { + #[schema(example = "supabase/replicator:1.2.3")] pub name: String, + #[schema(example = true)] pub is_default: bool, } -#[derive(Serialize)] -struct PostImageResponse { +#[derive(Serialize, ToSchema)] +pub struct PostImageResponse { id: i64, } -#[derive(Serialize)] -struct GetImageResponse { +#[derive(Serialize, ToSchema)] +pub struct GetImageResponse { id: i64, name: String, is_default: bool, } +#[utoipa::path( + request_body = PostImageRequest, + responses( + (status = 200, description = "Create new image", body = PostImageResponse), + (status = 500, description = "Internal server error") + ) +)] #[post("/images")] pub async fn create_image( pool: Data, @@ -82,6 +92,16 @@ pub async fn create_image( Ok(Json(response)) } +#[utoipa::path( + params( + ("image_id" = i64, Path, description = "Id of the image"), + ), + responses( + (status = 200, description = "Return image with id = image_id", body = GetImageResponse), + (status = 404, description = "Image not found"), + (status = 500, description = "Internal server error") + ) +)] #[get("/images/{image_id}")] pub async fn read_image( pool: Data, @@ -99,6 +119,17 @@ pub async fn read_image( Ok(Json(response)) } +#[utoipa::path( + request_body = PostImageRequest, + params( + ("image_id" = i64, Path, description = "Id of the image"), + ), + responses( + (status = 200, description = "Update image with id = image_id"), + (status = 404, description = "Image not found"), + (status = 500, description = "Internal server error") + ) +)] #[post("/images/{image_id}")] pub async fn update_image( pool: Data, @@ -112,6 +143,16 @@ pub async fn update_image( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + params( + ("image_id" = i64, Path, description = "Id of the image"), + ), + responses( + (status = 200, description = "Delete image with id = image_id"), + (status = 404, description = "Image not found"), + (status = 500, description = "Internal server error") + ) +)] #[delete("/images/{image_id}")] pub async fn delete_image( pool: Data, @@ -124,6 +165,12 @@ pub async fn delete_image( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + responses( + (status = 200, description = "Return all images"), + (status = 500, description = "Internal server error") + ) +)] #[get("/images")] pub async fn read_all_images(pool: Data) -> Result { let mut sources = vec![]; diff --git a/api/src/routes/pipelines.rs b/api/src/routes/pipelines.rs index 9525ced..eaced78 100644 --- a/api/src/routes/pipelines.rs +++ b/api/src/routes/pipelines.rs @@ -10,6 +10,7 @@ use actix_web::{ use serde::{Deserialize, Serialize}; use sqlx::PgPool; use thiserror::Error; +use utoipa::ToSchema; use crate::{ db::{ @@ -112,21 +113,21 @@ impl ResponseError for PipelineError { } } -#[derive(Deserialize)] -struct PostPipelineRequest { +#[derive(Deserialize, ToSchema)] +pub struct PostPipelineRequest { pub source_id: i64, pub sink_id: i64, pub publication_name: String, pub config: PipelineConfig, } -#[derive(Serialize)] -struct PostPipelineResponse { +#[derive(Serialize, ToSchema)] +pub struct PostPipelineResponse { id: i64, } -#[derive(Serialize)] -struct GetPipelineResponse { +#[derive(Serialize, ToSchema)] +pub struct GetPipelineResponse { id: i64, tenant_id: i64, source_id: i64, @@ -151,6 +152,13 @@ fn extract_tenant_id(req: &HttpRequest) -> Result { Ok(tenant_id) } +#[utoipa::path( + request_body = PostPipelineRequest, + responses( + (status = 200, description = "Create new pipeline", body = PostPipelineResponse), + (status = 500, description = "Internal server error") + ) +)] #[post("/pipelines")] pub async fn create_pipeline( req: HttpRequest, @@ -188,6 +196,16 @@ pub async fn create_pipeline( Ok(Json(response)) } +#[utoipa::path( + params( + ("pipeline_id" = i64, Path, description = "Id of the pipeline"), + ), + responses( + (status = 200, description = "Return pipeline with id = pipeline_id", body = GetPipelineResponse), + (status = 404, description = "Pipeline not found"), + (status = 500, description = "Internal server error") + ) +)] #[get("/pipelines/{pipeline_id}")] pub async fn read_pipeline( req: HttpRequest, @@ -217,6 +235,17 @@ pub async fn read_pipeline( Ok(Json(response)) } +#[utoipa::path( + request_body = PostSinkRequest, + params( + ("pipeline_id" = i64, Path, description = "Id of the pipeline"), + ), + responses( + (status = 200, description = "Update pipeline with id = pipeline_id"), + (status = 404, description = "Pipeline not found"), + (status = 500, description = "Internal server error") + ) +)] #[post("/pipelines/{pipeline_id}")] pub async fn update_pipeline( req: HttpRequest, @@ -255,6 +284,16 @@ pub async fn update_pipeline( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + params( + ("pipeline_id" = i64, Path, description = "Id of the pipeline"), + ), + responses( + (status = 200, description = "Delete pipeline with id = pipeline_id"), + (status = 404, description = "Pipeline not found"), + (status = 500, description = "Internal server error") + ) +)] #[delete("/pipelines/{pipeline_id}")] pub async fn delete_pipeline( req: HttpRequest, @@ -269,6 +308,12 @@ pub async fn delete_pipeline( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + responses( + (status = 200, description = "Return all pipelines"), + (status = 500, description = "Internal server error") + ) +)] #[get("/pipelines")] pub async fn read_all_pipelines( req: HttpRequest, diff --git a/api/src/routes/sinks.rs b/api/src/routes/sinks.rs index 43967f7..d1254a5 100644 --- a/api/src/routes/sinks.rs +++ b/api/src/routes/sinks.rs @@ -8,6 +8,7 @@ use actix_web::{ use serde::{Deserialize, Serialize}; use sqlx::PgPool; use thiserror::Error; +use utoipa::ToSchema; use crate::{ db::{ @@ -71,19 +72,21 @@ impl ResponseError for SinkError { } } -#[derive(Deserialize)] -struct PostSinkRequest { +#[derive(Deserialize, ToSchema)] +pub struct PostSinkRequest { pub config: SinkConfig, } -#[derive(Serialize)] -struct PostSinkResponse { +#[derive(Serialize, ToSchema)] +pub struct PostSinkResponse { id: i64, } -#[derive(Serialize)] -struct GetSinkResponse { +#[derive(Serialize, ToSchema)] +pub struct GetSinkResponse { + #[schema(example = 1)] id: i64, + #[schema(example = 1)] tenant_id: i64, config: SinkConfig, } @@ -101,6 +104,13 @@ fn extract_tenant_id(req: &HttpRequest) -> Result { Ok(tenant_id) } +#[utoipa::path( + request_body = PostSinkRequest, + responses( + (status = 200, description = "Create new sink", body = PostSinkResponse), + (status = 500, description = "Internal server error") + ) +)] #[post("/sinks")] pub async fn create_sink( req: HttpRequest, @@ -116,6 +126,16 @@ pub async fn create_sink( Ok(Json(response)) } +#[utoipa::path( + params( + ("sink_id" = i64, Path, description = "Id of the sink"), + ), + responses( + (status = 200, description = "Return sink with id = sink_id", body = GetSourceResponse), + (status = 404, description = "Sink not found"), + (status = 500, description = "Internal server error") + ) +)] #[get("/sinks/{sink_id}")] pub async fn read_sink( req: HttpRequest, @@ -136,6 +156,17 @@ pub async fn read_sink( Ok(Json(response)) } +#[utoipa::path( + request_body = PostSinkRequest, + params( + ("sink_id" = i64, Path, description = "Id of the sink"), + ), + responses( + (status = 200, description = "Update sink with id = sink_id"), + (status = 404, description = "Sink not found"), + (status = 500, description = "Internal server error") + ) +)] #[post("/sinks/{sink_id}")] pub async fn update_sink( req: HttpRequest, @@ -154,6 +185,16 @@ pub async fn update_sink( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + params( + ("sink_id" = i64, Path, description = "Id of the sink"), + ), + responses( + (status = 200, description = "Delete sink with id = sink_id"), + (status = 404, description = "Sink not found"), + (status = 500, description = "Internal server error") + ) +)] #[delete("/sinks/{sink_id}")] pub async fn delete_sink( req: HttpRequest, @@ -168,6 +209,12 @@ pub async fn delete_sink( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + responses( + (status = 200, description = "Return all sinks"), + (status = 500, description = "Internal server error") + ) +)] #[get("/sinks")] pub async fn read_all_sinks( req: HttpRequest, diff --git a/api/src/routes/sources.rs b/api/src/routes/sources.rs index bf7a479..e8c31d1 100644 --- a/api/src/routes/sources.rs +++ b/api/src/routes/sources.rs @@ -8,6 +8,7 @@ use actix_web::{ use serde::{Deserialize, Serialize}; use sqlx::PgPool; use thiserror::Error; +use utoipa::ToSchema; use super::ErrorMessage; use crate::{ @@ -75,19 +76,22 @@ impl ResponseError for SourceError { } } -#[derive(Deserialize)] -struct PostSourceRequest { +#[derive(Deserialize, ToSchema)] +pub struct PostSourceRequest { + #[schema(required = true)] pub config: SourceConfig, } -#[derive(Serialize)] -struct PostSourceResponse { +#[derive(Serialize, ToSchema)] +pub struct PostSourceResponse { id: i64, } -#[derive(Serialize)] -struct GetSourceResponse { +#[derive(Serialize, ToSchema)] +pub struct GetSourceResponse { + #[schema(example = 1)] id: i64, + #[schema(example = 1)] tenant_id: i64, config: SourceConfig, } @@ -107,6 +111,13 @@ fn extract_tenant_id(req: &HttpRequest) -> Result { Ok(tenant_id) } +#[utoipa::path( + request_body = PostSourceRequest, + responses( + (status = 200, description = "Create new source", body = PostSourceResponse), + (status = 500, description = "Internal server error") + ) +)] #[post("/sources")] pub async fn create_source( req: HttpRequest, @@ -122,6 +133,16 @@ pub async fn create_source( Ok(Json(response)) } +#[utoipa::path( + params( + ("source_id" = i64, Path, description = "Id of the source"), + ), + responses( + (status = 200, description = "Return source with id = source_id", body = GetSourceResponse), + (status = 404, description = "Source not found"), + (status = 500, description = "Internal server error") + ) +)] #[get("/sources/{source_id}")] pub async fn read_source( req: HttpRequest, @@ -142,6 +163,17 @@ pub async fn read_source( Ok(Json(response)) } +#[utoipa::path( + request_body = PostSourceRequest, + params( + ("source_id" = i64, Path, description = "Id of the source"), + ), + responses( + (status = 200, description = "Update source with id = source_id"), + (status = 404, description = "Source not found"), + (status = 500, description = "Internal server error") + ) +)] #[post("/sources/{source_id}")] pub async fn update_source( req: HttpRequest, @@ -160,6 +192,16 @@ pub async fn update_source( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + params( + ("source_id" = i64, Path, description = "Id of the source"), + ), + responses( + (status = 200, description = "Delete source with id = source_id"), + (status = 404, description = "Source not found"), + (status = 500, description = "Internal server error") + ) +)] #[delete("/sources/{source_id}")] pub async fn delete_source( req: HttpRequest, @@ -174,6 +216,12 @@ pub async fn delete_source( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + responses( + (status = 200, description = "Return all sources"), + (status = 500, description = "Internal server error") + ) +)] #[get("/sources")] pub async fn read_all_sources( req: HttpRequest, diff --git a/api/src/routes/sources/publications.rs b/api/src/routes/sources/publications.rs index 3c09c5d..264590a 100644 --- a/api/src/routes/sources/publications.rs +++ b/api/src/routes/sources/publications.rs @@ -8,6 +8,7 @@ use actix_web::{ use serde::Deserialize; use sqlx::PgPool; use thiserror::Error; +use utoipa::ToSchema; use crate::{ db::{self, publications::Publication, sources::SourcesDbError, tables::Table}, @@ -89,17 +90,24 @@ fn extract_tenant_id(req: &HttpRequest) -> Result { Ok(tenant_id) } -#[derive(Deserialize)] -struct CreatePublicationRequest { +#[derive(Deserialize, ToSchema)] +pub struct CreatePublicationRequest { name: String, tables: Vec
, } -#[derive(Deserialize)] -struct UpdatePublicationRequest { +#[derive(Deserialize, ToSchema)] +pub struct UpdatePublicationRequest { tables: Vec
, } +#[utoipa::path( + request_body = CreatePublicationRequest, + responses( + (status = 200, description = "Create new publication"), + (status = 500, description = "Internal server error") + ) +)] #[post("/sources/{source_id}/publications")] pub async fn create_publication( req: HttpRequest, @@ -127,6 +135,52 @@ pub async fn create_publication( Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + params( + ("source_id" = i64, Path, description = "Id of the source"), + ("publication_name" = i64, Path, description = "Name of the publication"), + ), + responses( + (status = 200, description = "Return publication with name = publication_name from source with id = source_id", body = Publication), + (status = 404, description = "Publication not found"), + (status = 500, description = "Internal server error") + ) +)] +#[get("/sources/{source_id}/publications/{publication_name}")] +pub async fn read_publication( + req: HttpRequest, + pool: Data, + encryption_key: Data, + source_id_and_pub_name: Path<(i64, String)>, +) -> Result { + let tenant_id = extract_tenant_id(&req)?; + let (source_id, publication_name) = source_id_and_pub_name.into_inner(); + + let config = db::sources::read_source(&pool, tenant_id, source_id, &encryption_key) + .await? + .map(|s| s.config) + .ok_or(PublicationError::SourceNotFound(source_id))?; + + let options = config.connect_options(); + let publications = db::publications::read_publication(&publication_name, &options) + .await? + .ok_or(PublicationError::PublicationNotFound(publication_name))?; + + Ok(Json(publications)) +} + +#[utoipa::path( + request_body = UpdatePublicationRequest, + params( + ("source_id" = i64, Path, description = "Id of the source"), + ("publication_name" = i64, Path, description = "Name of the publication"), + ), + responses( + (status = 200, description = "Update publication with name = publication_name from source with id = source_id"), + (status = 404, description = "Publication not found"), + (status = 500, description = "Internal server error") + ) +)] #[post("/sources/{source_id}/publications/{publication_name}")] pub async fn update_publication( req: HttpRequest, @@ -154,8 +208,19 @@ pub async fn update_publication( Ok(HttpResponse::Ok().finish()) } -#[get("/sources/{source_id}/publications/{publication_name}")] -pub async fn read_publication( +#[utoipa::path( + params( + ("source_id" = i64, Path, description = "Id of the source"), + ("publication_name" = i64, Path, description = "Name of the publication"), + ), + responses( + (status = 200, description = "Delete publication with name = publication_name from source with id = source_id"), + (status = 404, description = "Publication not found"), + (status = 500, description = "Internal server error") + ) +)] +#[delete("/sources/{source_id}/publications/{publication_name}")] +pub async fn delete_publication( req: HttpRequest, pool: Data, encryption_key: Data, @@ -170,13 +235,20 @@ pub async fn read_publication( .ok_or(PublicationError::SourceNotFound(source_id))?; let options = config.connect_options(); - let publications = db::publications::read_publication(&publication_name, &options) - .await? - .ok_or(PublicationError::PublicationNotFound(publication_name))?; + db::publications::drop_publication(&publication_name, &options).await?; - Ok(Json(publications)) + Ok(HttpResponse::Ok().finish()) } +#[utoipa::path( + params( + ("source_id" = i64, Path, description = "Id of the source"), + ), + responses( + (status = 200, description = "Return all publications"), + (status = 500, description = "Internal server error") + ) +)] #[get("/sources/{source_id}/publications")] pub async fn read_all_publications( req: HttpRequest, @@ -197,24 +269,3 @@ pub async fn read_all_publications( Ok(Json(publications)) } - -#[delete("/sources/{source_id}/publications/{publication_name}")] -pub async fn delete_publication( - req: HttpRequest, - pool: Data, - encryption_key: Data, - source_id_and_pub_name: Path<(i64, String)>, -) -> Result { - let tenant_id = extract_tenant_id(&req)?; - let (source_id, publication_name) = source_id_and_pub_name.into_inner(); - - let config = db::sources::read_source(&pool, tenant_id, source_id, &encryption_key) - .await? - .map(|s| s.config) - .ok_or(PublicationError::SourceNotFound(source_id))?; - - let options = config.connect_options(); - db::publications::drop_publication(&publication_name, &options).await?; - - Ok(HttpResponse::Ok().finish()) -} diff --git a/api/src/routes/sources/tables.rs b/api/src/routes/sources/tables.rs index 4cb82db..e7db0f8 100644 --- a/api/src/routes/sources/tables.rs +++ b/api/src/routes/sources/tables.rs @@ -80,6 +80,15 @@ fn extract_tenant_id(req: &HttpRequest) -> Result { Ok(tenant_id) } +#[utoipa::path( + params( + ("source_id" = i64, Path, description = "Id of the source"), + ), + responses( + (status = 200, description = "Return all tables from source with id = source_id", body = Vec
), + (status = 500, description = "Internal server error") + ) +)] #[get("/sources/{source_id}/tables")] pub async fn read_table_names( req: HttpRequest, diff --git a/api/src/routes/tenants.rs b/api/src/routes/tenants.rs index 19ddc0f..1509978 100644 --- a/api/src/routes/tenants.rs +++ b/api/src/routes/tenants.rs @@ -76,7 +76,7 @@ pub struct GetTenantResponse { #[utoipa::path( request_body = PostTenantRequest, responses( - (status = 200, description = "New tenant created", body = PostTenantResponse), + (status = 200, description = "Create new tenant", body = PostTenantResponse), (status = 500, description = "Internal server error") ) )] @@ -97,7 +97,7 @@ pub async fn create_tenant( ("tenant_id" = i64, Path, description = "Id of the tenant"), ), responses( - (status = 200, description = "Returned tenant with id = tenant_id", body = GetTenantResponse), + (status = 200, description = "Return tenant with id = tenant_id", body = GetTenantResponse), (status = 404, description = "Tenant not found"), (status = 500, description = "Internal server error") ) @@ -120,8 +120,11 @@ pub async fn read_tenant( #[utoipa::path( request_body = PostTenantRequest, + params( + ("tenant_id" = i64, Path, description = "Id of the tenant"), + ), responses( - (status = 200, description = "Updated a tenant"), + (status = 200, description = "Update tenant with id = tenant_id"), (status = 404, description = "Tenant not found"), (status = 500, description = "Internal server error") ) @@ -163,7 +166,7 @@ pub async fn delete_tenant( #[utoipa::path( responses( - (status = 200, description = "Returned all tenants"), + (status = 200, description = "Return all tenants"), (status = 500, description = "Internal server error") ) )] diff --git a/api/src/startup.rs b/api/src/startup.rs index 92b68f9..3313246 100644 --- a/api/src/startup.rs +++ b/api/src/startup.rs @@ -10,25 +10,33 @@ use utoipa_swagger_ui::SwaggerUi; use crate::{ configuration::{DatabaseSettings, Settings}, + db::publications::Publication, encryption, k8s_client::HttpK8sClient, routes::{ health_check::health_check, - images::{create_image, delete_image, read_all_images, read_image, update_image}, + images::{ + create_image, delete_image, read_all_images, read_image, update_image, + GetImageResponse, PostImageRequest, PostImageResponse, + }, pipelines::{ create_pipeline, delete_pipeline, read_all_pipelines, read_pipeline, start_pipeline, - stop_pipeline, update_pipeline, + stop_pipeline, update_pipeline, GetPipelineResponse, PostPipelineRequest, + PostPipelineResponse, + }, + sinks::{ + create_sink, delete_sink, read_all_sinks, read_sink, update_sink, GetSinkResponse, + PostSinkRequest, PostSinkResponse, }, - sinks::{create_sink, delete_sink, read_all_sinks, read_sink, update_sink}, sources::{ create_source, delete_source, publications::{ create_publication, delete_publication, read_all_publications, read_publication, - update_publication, + update_publication, CreatePublicationRequest, UpdatePublicationRequest, }, read_all_sources, read_source, tables::read_table_names, - update_source, + update_source, GetSourceResponse, PostSourceRequest, PostSourceResponse, }, tenants::{ create_tenant, delete_tenant, read_all_tenants, read_tenant, update_tenant, @@ -93,13 +101,59 @@ pub async fn run( #[derive(OpenApi)] #[openapi( paths( + crate::routes::health_check::health_check, + crate::routes::images::create_image, + crate::routes::images::read_image, + crate::routes::images::update_image, + crate::routes::images::delete_image, + crate::routes::images::read_all_images, + crate::routes::pipelines::create_pipeline, + crate::routes::pipelines::read_pipeline, + crate::routes::pipelines::update_pipeline, + crate::routes::pipelines::delete_pipeline, + crate::routes::pipelines::read_all_pipelines, crate::routes::tenants::create_tenant, crate::routes::tenants::read_tenant, crate::routes::tenants::update_tenant, crate::routes::tenants::delete_tenant, crate::routes::tenants::read_all_tenants, + crate::routes::sources::create_source, + crate::routes::sources::read_source, + crate::routes::sources::update_source, + crate::routes::sources::delete_source, + crate::routes::sources::read_all_sources, + crate::routes::sources::publications::create_publication, + crate::routes::sources::publications::read_publication, + crate::routes::sources::publications::update_publication, + crate::routes::sources::publications::delete_publication, + crate::routes::sources::publications::read_all_publications, + crate::routes::sources::tables::read_table_names, + crate::routes::sinks::create_sink, + crate::routes::sinks::read_sink, + crate::routes::sinks::update_sink, + crate::routes::sinks::delete_sink, + crate::routes::sinks::read_all_sinks, ), - components(schemas(PostTenantRequest, PostTenantResponse, GetTenantResponse)) + components(schemas( + PostImageRequest, + PostImageResponse, + GetImageResponse, + PostPipelineRequest, + PostPipelineResponse, + GetPipelineResponse, + PostTenantRequest, + PostTenantResponse, + GetTenantResponse, + PostSourceRequest, + PostSourceResponse, + GetSourceResponse, + CreatePublicationRequest, + UpdatePublicationRequest, + Publication, + PostSinkRequest, + PostSinkResponse, + GetSinkResponse, + )) )] struct ApiDoc;