From 2082e12660d67f2adc39569ea742cbfa11c25233 Mon Sep 17 00:00:00 2001 From: NexVeridian Date: Thu, 21 Nov 2024 05:00:18 -0800 Subject: [PATCH] SecurityAddon --- src/app.rs | 43 ++++---------- src/auth/mod.rs | 2 + src/auth/openapi.rs | 57 +++++++++++++++++++ src/tests_cfg/db.rs | 17 ++++-- tests/controller/openapi.rs | 51 ++++++++++++++++- .../openapi_security_[Cookie]@openapi.snap | 9 +++ .../openapi_security_[Query]@openapi.snap | 9 +++ ...spec_[api-docs__openapi.json]@openapi.snap | 2 +- ...spec_[api-docs__openapi.yaml]@openapi.snap | 2 +- ...pi_spec_[redoc__openapi.json]@openapi.snap | 2 +- ...pi_spec_[redoc__openapi.yaml]@openapi.snap | 2 +- ...i_spec_[scalar__openapi.json]@openapi.snap | 2 +- ...i_spec_[scalar__openapi.yaml]@openapi.snap | 2 +- 13 files changed, 156 insertions(+), 44 deletions(-) create mode 100644 src/auth/openapi.rs create mode 100644 tests/controller/snapshots/openapi_security_[Cookie]@openapi.snap create mode 100644 tests/controller/snapshots/openapi_security_[Query]@openapi.snap diff --git a/src/app.rs b/src/app.rs index 6ae7790c6..41d956fc7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -223,7 +223,7 @@ pub trait Hooks: Send { /// Modify the OpenAPI spec before the routes are added, allowing you to edit (openapi::info)[https://docs.rs/utoipa/latest/utoipa/openapi/info/struct.Info.html] /// # Examples /// ```rust ignore - /// fn inital_openapi_spec() { + /// fn inital_openapi_spec(_ctx: &AppContext) -> utoipa::openapi::OpenApi { /// #[derive(OpenApi)] /// #[openapi(info( /// title = "Loco Demo", @@ -236,39 +236,18 @@ pub trait Hooks: Send { /// /// With SecurityAddon /// ```rust ignore - /// fn inital_openapi_spec() { + /// fn inital_openapi_spec(ctx: &AppContext) -> utoipa::openapi::OpenApi { + /// set_jwt_location(ctx); + /// /// #[derive(OpenApi)] - /// #[openapi(modifiers(&SecurityAddon), info( - /// title = "Loco Demo", - /// description = "This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project." - /// ))] + /// #[openapi( + /// modifiers(&SecurityAddon), + /// info( + /// title = "Loco Demo", + /// description = "This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project." + /// ) + /// )] /// struct ApiDoc; - /// - /// // TODO set the jwt token location - /// // let auth_location = ctx.config.auth.as_ref(); - /// - /// 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_schemes_from_iter([ - /// ( - /// "jwt_token", - /// SecurityScheme::Http( - /// HttpBuilder::new() - /// .scheme(HttpAuthScheme::Bearer) - /// .bearer_format("JWT") - /// .build(), - /// ), - /// ), - /// ( - /// "api_key", - /// SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("apikey"))), - /// ), - /// ]); - /// } - /// } - /// } /// ApiDoc::openapi() /// } /// ``` diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 3114f8423..ffcce5a29 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature = "auth_jwt")] pub mod jwt; +#[cfg(feature = "openapi")] +pub mod openapi; diff --git a/src/auth/openapi.rs b/src/auth/openapi.rs new file mode 100644 index 000000000..0d4ff992d --- /dev/null +++ b/src/auth/openapi.rs @@ -0,0 +1,57 @@ +use std::sync::OnceLock; +use utoipa::{ + openapi::security::{ApiKey, ApiKeyValue, HttpAuthScheme, HttpBuilder, SecurityScheme}, + Modify, +}; + +use crate::{app::AppContext, config::JWTLocation}; + +static JWT_LOCATION: OnceLock = OnceLock::new(); + +pub fn set_jwt_location(ctx: &AppContext) -> &'static JWTLocation { + JWT_LOCATION.get_or_init(|| { + ctx.config + .auth + .as_ref() + .and_then(|auth| auth.jwt.as_ref()) + .and_then(|jwt| jwt.location.as_ref()) + .unwrap_or(&JWTLocation::Bearer) + .clone() + }) +} + +fn get_jwt_location() -> &'static JWTLocation { + JWT_LOCATION.get().unwrap() +} + +pub 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_schemes_from_iter([ + ( + "jwt_token", + match get_jwt_location() { + JWTLocation::Bearer => SecurityScheme::Http( + HttpBuilder::new() + .scheme(HttpAuthScheme::Bearer) + .bearer_format("JWT") + .build(), + ), + JWTLocation::Query { name } => { + SecurityScheme::ApiKey(ApiKey::Query(ApiKeyValue::new(name))) + } + JWTLocation::Cookie { name } => { + SecurityScheme::ApiKey(ApiKey::Cookie(ApiKeyValue::new(name))) + } + }, + ), + ( + "api_key", + SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("apikey"))), + ), + ]); + } + } +} diff --git a/src/tests_cfg/db.rs b/src/tests_cfg/db.rs index b63def806..fab784d8d 100644 --- a/src/tests_cfg/db.rs +++ b/src/tests_cfg/db.rs @@ -7,6 +7,8 @@ pub use sea_orm_migration::prelude::*; #[cfg(feature = "openapi")] use utoipa::OpenApi; +#[cfg(feature = "openapi")] +use crate::auth::openapi::{set_jwt_location, SecurityAddon}; #[cfg(feature = "channels")] use crate::controller::channels::AppChannels; use crate::{ @@ -131,12 +133,17 @@ impl Hooks for AppHook { } #[cfg(feature = "openapi")] - fn inital_openapi_spec(_ctx: &AppContext) -> utoipa::openapi::OpenApi { + fn inital_openapi_spec(ctx: &AppContext) -> utoipa::openapi::OpenApi { + set_jwt_location(ctx); + #[derive(OpenApi)] - #[openapi(info( - title = "Loco Demo", - description = "This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project." - ))] + #[openapi( + modifiers(&SecurityAddon), + info( + title = "Loco Demo", + description = "This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project." + ) + )] struct ApiDoc; ApiDoc::openapi() } diff --git a/tests/controller/openapi.rs b/tests/controller/openapi.rs index 7d7b6da44..677b56320 100644 --- a/tests/controller/openapi.rs +++ b/tests/controller/openapi.rs @@ -1,5 +1,9 @@ use insta::assert_debug_snapshot; -use loco_rs::{prelude::*, tests_cfg}; +use loco_rs::{ + config::{Auth, JWTLocation, JWT}, + prelude::*, + tests_cfg, +}; use rstest::rstest; use serial_test::serial; @@ -98,3 +102,48 @@ async fn openapi_spec(#[case] test_name: &str) { handle.abort(); } + +#[rstest] +#[case(JWTLocation::Query { name: "JWT".to_string() })] +#[case(JWTLocation::Cookie { name: "JWT".to_string() })] +#[tokio::test] +#[serial] +async fn openapi_security(#[case] location: JWTLocation) { + configure_insta!(); + + let mut ctx: AppContext = tests_cfg::app::get_app_context().await; + ctx.config.auth = Some(Auth { + jwt: Some(JWT { + location: Some(location.clone()), + secret: "PqRwLF2rhHe8J22oBeHy".to_string(), + expiration: 604800, + }), + }); + + let handle = infra_cfg::server::start_from_ctx(ctx).await; + + let res = reqwest::Client::new() + .request( + reqwest::Method::GET, + infra_cfg::server::get_base_url() + "api-docs/openapi.json", + ) + .send() + .await + .expect("valid response"); + + let test_name = match location { + JWTLocation::Query { .. } => "Query", + JWTLocation::Cookie { .. } => "Cookie", + _ => "Bearer", + }; + assert_debug_snapshot!( + format!("openapi_security_[{test_name}]"), + ( + res.status().to_string(), + res.url().to_string(), + res.text().await.unwrap(), + ) + ); + + handle.abort(); +} diff --git a/tests/controller/snapshots/openapi_security_[Cookie]@openapi.snap b/tests/controller/snapshots/openapi_security_[Cookie]@openapi.snap new file mode 100644 index 000000000..a7da4694e --- /dev/null +++ b/tests/controller/snapshots/openapi_security_[Cookie]@openapi.snap @@ -0,0 +1,9 @@ +--- +source: tests/controller/openapi.rs +expression: "(res.status().to_string(), res.url().to_string(), res.text().await.unwrap(),)" +--- +( + "200 OK", + "http://localhost:5555/api-docs/openapi.json", + "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.1\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}},\"securitySchemes\":{\"api_key\":{\"type\":\"apiKey\",\"in\":\"header\",\"name\":\"apikey\"},\"jwt_token\":{\"type\":\"apiKey\",\"in\":\"cookie\",\"name\":\"JWT\"}}}}", +) diff --git a/tests/controller/snapshots/openapi_security_[Query]@openapi.snap b/tests/controller/snapshots/openapi_security_[Query]@openapi.snap new file mode 100644 index 000000000..ba7a7f6d6 --- /dev/null +++ b/tests/controller/snapshots/openapi_security_[Query]@openapi.snap @@ -0,0 +1,9 @@ +--- +source: tests/controller/openapi.rs +expression: "(res.status().to_string(), res.url().to_string(), res.text().await.unwrap(),)" +--- +( + "200 OK", + "http://localhost:5555/api-docs/openapi.json", + "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.1\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}},\"securitySchemes\":{\"api_key\":{\"type\":\"apiKey\",\"in\":\"header\",\"name\":\"apikey\"},\"jwt_token\":{\"type\":\"apiKey\",\"in\":\"query\",\"name\":\"JWT\"}}}}", +) diff --git a/tests/controller/snapshots/openapi_spec_[api-docs__openapi.json]@openapi.snap b/tests/controller/snapshots/openapi_spec_[api-docs__openapi.json]@openapi.snap index 2e94c0cb7..7c8d04153 100644 --- a/tests/controller/snapshots/openapi_spec_[api-docs__openapi.json]@openapi.snap +++ b/tests/controller/snapshots/openapi_spec_[api-docs__openapi.json]@openapi.snap @@ -5,5 +5,5 @@ expression: "(res.status().to_string(), res.url().to_string(), res.text().await. ( "200 OK", "http://localhost:5555/api-docs/openapi.json", - "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.0\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}}}}", + "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.1\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}},\"securitySchemes\":{\"api_key\":{\"type\":\"apiKey\",\"in\":\"header\",\"name\":\"apikey\"},\"jwt_token\":{\"type\":\"http\",\"scheme\":\"bearer\",\"bearerFormat\":\"JWT\"}}}}", ) diff --git a/tests/controller/snapshots/openapi_spec_[api-docs__openapi.yaml]@openapi.snap b/tests/controller/snapshots/openapi_spec_[api-docs__openapi.yaml]@openapi.snap index d4c5b70b6..d1545ccb0 100644 --- a/tests/controller/snapshots/openapi_spec_[api-docs__openapi.yaml]@openapi.snap +++ b/tests/controller/snapshots/openapi_spec_[api-docs__openapi.yaml]@openapi.snap @@ -5,5 +5,5 @@ expression: "(res.status().to_string(), res.url().to_string(), res.text().await. ( "200 OK", "http://localhost:5555/api-docs/openapi.yaml", - "openapi: 3.1.0\ninfo:\n title: Loco Demo\n description: This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\n contact:\n name: Dotan Nahum\n email: dotan@rng0.io\n license:\n name: Apache-2.0\n version: 0.13.0\npaths:\n /album:\n get:\n operationId: get_action_openapi\n responses:\n '200':\n description: Album found\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Album'\ncomponents:\n schemas:\n Album:\n type: object\n required:\n - title\n - rating\n properties:\n rating:\n type: integer\n format: int32\n minimum: 0\n title:\n type: string\n", + "openapi: 3.1.0\ninfo:\n title: Loco Demo\n description: This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\n contact:\n name: Dotan Nahum\n email: dotan@rng0.io\n license:\n name: Apache-2.0\n version: 0.13.1\npaths:\n /album:\n get:\n operationId: get_action_openapi\n responses:\n '200':\n description: Album found\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Album'\ncomponents:\n schemas:\n Album:\n type: object\n required:\n - title\n - rating\n properties:\n rating:\n type: integer\n format: int32\n minimum: 0\n title:\n type: string\n securitySchemes:\n api_key:\n type: apiKey\n in: header\n name: apikey\n jwt_token:\n type: http\n scheme: bearer\n bearerFormat: JWT\n", ) diff --git a/tests/controller/snapshots/openapi_spec_[redoc__openapi.json]@openapi.snap b/tests/controller/snapshots/openapi_spec_[redoc__openapi.json]@openapi.snap index 63b9b2020..887ccaf11 100644 --- a/tests/controller/snapshots/openapi_spec_[redoc__openapi.json]@openapi.snap +++ b/tests/controller/snapshots/openapi_spec_[redoc__openapi.json]@openapi.snap @@ -5,5 +5,5 @@ expression: "(res.status().to_string(), res.url().to_string(), res.text().await. ( "200 OK", "http://localhost:5555/redoc/openapi.json", - "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.0\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}}}}", + "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.1\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}},\"securitySchemes\":{\"api_key\":{\"type\":\"apiKey\",\"in\":\"header\",\"name\":\"apikey\"},\"jwt_token\":{\"type\":\"http\",\"scheme\":\"bearer\",\"bearerFormat\":\"JWT\"}}}}", ) diff --git a/tests/controller/snapshots/openapi_spec_[redoc__openapi.yaml]@openapi.snap b/tests/controller/snapshots/openapi_spec_[redoc__openapi.yaml]@openapi.snap index 882eb0f97..349bf7210 100644 --- a/tests/controller/snapshots/openapi_spec_[redoc__openapi.yaml]@openapi.snap +++ b/tests/controller/snapshots/openapi_spec_[redoc__openapi.yaml]@openapi.snap @@ -5,5 +5,5 @@ expression: "(res.status().to_string(), res.url().to_string(), res.text().await. ( "200 OK", "http://localhost:5555/redoc/openapi.yaml", - "openapi: 3.1.0\ninfo:\n title: Loco Demo\n description: This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\n contact:\n name: Dotan Nahum\n email: dotan@rng0.io\n license:\n name: Apache-2.0\n version: 0.13.0\npaths:\n /album:\n get:\n operationId: get_action_openapi\n responses:\n '200':\n description: Album found\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Album'\ncomponents:\n schemas:\n Album:\n type: object\n required:\n - title\n - rating\n properties:\n rating:\n type: integer\n format: int32\n minimum: 0\n title:\n type: string\n", + "openapi: 3.1.0\ninfo:\n title: Loco Demo\n description: This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\n contact:\n name: Dotan Nahum\n email: dotan@rng0.io\n license:\n name: Apache-2.0\n version: 0.13.1\npaths:\n /album:\n get:\n operationId: get_action_openapi\n responses:\n '200':\n description: Album found\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Album'\ncomponents:\n schemas:\n Album:\n type: object\n required:\n - title\n - rating\n properties:\n rating:\n type: integer\n format: int32\n minimum: 0\n title:\n type: string\n securitySchemes:\n api_key:\n type: apiKey\n in: header\n name: apikey\n jwt_token:\n type: http\n scheme: bearer\n bearerFormat: JWT\n", ) diff --git a/tests/controller/snapshots/openapi_spec_[scalar__openapi.json]@openapi.snap b/tests/controller/snapshots/openapi_spec_[scalar__openapi.json]@openapi.snap index 1ce12df84..4243cbd18 100644 --- a/tests/controller/snapshots/openapi_spec_[scalar__openapi.json]@openapi.snap +++ b/tests/controller/snapshots/openapi_spec_[scalar__openapi.json]@openapi.snap @@ -5,5 +5,5 @@ expression: "(res.status().to_string(), res.url().to_string(), res.text().await. ( "200 OK", "http://localhost:5555/scalar/openapi.json", - "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.0\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}}}}", + "{\"openapi\":\"3.1.0\",\"info\":{\"title\":\"Loco Demo\",\"description\":\"This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\",\"contact\":{\"name\":\"Dotan Nahum\",\"email\":\"dotan@rng0.io\"},\"license\":{\"name\":\"Apache-2.0\"},\"version\":\"0.13.1\"},\"paths\":{\"/album\":{\"get\":{\"operationId\":\"get_action_openapi\",\"responses\":{\"200\":{\"description\":\"Album found\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/components/schemas/Album\"}}}}}}}},\"components\":{\"schemas\":{\"Album\":{\"type\":\"object\",\"required\":[\"title\",\"rating\"],\"properties\":{\"rating\":{\"type\":\"integer\",\"format\":\"int32\",\"minimum\":0},\"title\":{\"type\":\"string\"}}}},\"securitySchemes\":{\"api_key\":{\"type\":\"apiKey\",\"in\":\"header\",\"name\":\"apikey\"},\"jwt_token\":{\"type\":\"http\",\"scheme\":\"bearer\",\"bearerFormat\":\"JWT\"}}}}", ) diff --git a/tests/controller/snapshots/openapi_spec_[scalar__openapi.yaml]@openapi.snap b/tests/controller/snapshots/openapi_spec_[scalar__openapi.yaml]@openapi.snap index 26d65b74b..ffe1847cd 100644 --- a/tests/controller/snapshots/openapi_spec_[scalar__openapi.yaml]@openapi.snap +++ b/tests/controller/snapshots/openapi_spec_[scalar__openapi.yaml]@openapi.snap @@ -5,5 +5,5 @@ expression: "(res.status().to_string(), res.url().to_string(), res.text().await. ( "200 OK", "http://localhost:5555/scalar/openapi.yaml", - "openapi: 3.1.0\ninfo:\n title: Loco Demo\n description: This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\n contact:\n name: Dotan Nahum\n email: dotan@rng0.io\n license:\n name: Apache-2.0\n version: 0.13.0\npaths:\n /album:\n get:\n operationId: get_action_openapi\n responses:\n '200':\n description: Album found\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Album'\ncomponents:\n schemas:\n Album:\n type: object\n required:\n - title\n - rating\n properties:\n rating:\n type: integer\n format: int32\n minimum: 0\n title:\n type: string\n", + "openapi: 3.1.0\ninfo:\n title: Loco Demo\n description: This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.\n contact:\n name: Dotan Nahum\n email: dotan@rng0.io\n license:\n name: Apache-2.0\n version: 0.13.1\npaths:\n /album:\n get:\n operationId: get_action_openapi\n responses:\n '200':\n description: Album found\n content:\n application/json:\n schema:\n $ref: '#/components/schemas/Album'\ncomponents:\n schemas:\n Album:\n type: object\n required:\n - title\n - rating\n properties:\n rating:\n type: integer\n format: int32\n minimum: 0\n title:\n type: string\n securitySchemes:\n api_key:\n type: apiKey\n in: header\n name: apikey\n jwt_token:\n type: http\n scheme: bearer\n bearerFormat: JWT\n", )