2020-04-26 22:34:22 +02:00
|
|
|
use super::SECURITY_NAME;
|
2021-02-03 21:22:46 +00:00
|
|
|
|
2020-04-26 22:34:22 +02:00
|
|
|
use futures_util::{future, future::FutureExt};
|
|
|
|
use gotham::{
|
2020-09-17 12:25:58 +02:00
|
|
|
anyhow,
|
2020-04-26 22:34:22 +02:00
|
|
|
handler::{Handler, HandlerFuture, NewHandler},
|
|
|
|
helpers::http::response::create_response,
|
2021-02-03 21:22:46 +00:00
|
|
|
hyper::StatusCode,
|
2020-04-26 22:34:22 +02:00
|
|
|
state::State
|
|
|
|
};
|
|
|
|
use indexmap::IndexMap;
|
|
|
|
use mime::{APPLICATION_JSON, TEXT_PLAIN};
|
|
|
|
use openapiv3::{APIKeyLocation, OpenAPI, ReferenceOr, SecurityScheme};
|
|
|
|
use std::{
|
|
|
|
pin::Pin,
|
|
|
|
sync::{Arc, RwLock}
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2020-09-15 15:10:41 +02:00
|
|
|
pub struct OpenapiHandler {
|
|
|
|
openapi: Arc<RwLock<OpenAPI>>
|
2020-04-26 22:34:22 +02:00
|
|
|
}
|
|
|
|
|
2020-09-15 15:10:41 +02:00
|
|
|
impl OpenapiHandler {
|
|
|
|
pub fn new(openapi: Arc<RwLock<OpenAPI>>) -> Self {
|
2020-04-26 22:34:22 +02:00
|
|
|
Self { openapi }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-15 15:10:41 +02:00
|
|
|
impl NewHandler for OpenapiHandler {
|
2020-04-26 22:34:22 +02:00
|
|
|
type Instance = Self;
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-09-17 12:25:58 +02:00
|
|
|
fn new_handler(&self) -> anyhow::Result<Self> {
|
2020-04-26 22:34:22 +02:00
|
|
|
Ok(self.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "auth")]
|
2020-09-15 15:10:41 +02:00
|
|
|
fn get_security(state: &mut State) -> IndexMap<String, ReferenceOr<SecurityScheme>> {
|
2020-04-26 22:34:22 +02:00
|
|
|
use crate::AuthSource;
|
|
|
|
use gotham::state::FromState;
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-04-26 22:34:22 +02:00
|
|
|
let source = match AuthSource::try_borrow_from(state) {
|
|
|
|
Some(source) => source,
|
|
|
|
None => return Default::default()
|
|
|
|
};
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-04-26 22:34:22 +02:00
|
|
|
let security_scheme = match source {
|
|
|
|
AuthSource::Cookie(name) => SecurityScheme::APIKey {
|
|
|
|
location: APIKeyLocation::Cookie,
|
|
|
|
name: name.to_string()
|
|
|
|
},
|
|
|
|
AuthSource::Header(name) => SecurityScheme::APIKey {
|
|
|
|
location: APIKeyLocation::Header,
|
|
|
|
name: name.to_string()
|
|
|
|
},
|
|
|
|
AuthSource::AuthorizationHeader => SecurityScheme::HTTP {
|
|
|
|
scheme: "bearer".to_owned(),
|
|
|
|
bearer_format: Some("JWT".to_owned())
|
|
|
|
}
|
|
|
|
};
|
2020-09-15 15:10:41 +02:00
|
|
|
|
|
|
|
let mut security_schemes: IndexMap<String, ReferenceOr<SecurityScheme>> = Default::default();
|
2020-04-26 22:34:22 +02:00
|
|
|
security_schemes.insert(SECURITY_NAME.to_owned(), ReferenceOr::Item(security_scheme));
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-04-26 22:34:22 +02:00
|
|
|
security_schemes
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "auth"))]
|
2020-09-15 15:10:41 +02:00
|
|
|
fn get_security(state: &mut State) -> (Vec<SecurityRequirement>, IndexMap<String, ReferenceOr<SecurityScheme>>) {
|
2020-04-26 22:34:22 +02:00
|
|
|
Default::default()
|
|
|
|
}
|
|
|
|
|
2020-09-15 15:10:41 +02:00
|
|
|
impl Handler for OpenapiHandler {
|
|
|
|
fn handle(self, mut state: State) -> Pin<Box<HandlerFuture>> {
|
2020-04-26 22:34:22 +02:00
|
|
|
let openapi = match self.openapi.read() {
|
|
|
|
Ok(openapi) => openapi,
|
|
|
|
Err(e) => {
|
|
|
|
error!("Unable to acquire read lock for the OpenAPI specification: {}", e);
|
2021-02-03 21:22:46 +00:00
|
|
|
let res = create_response(&state, StatusCode::INTERNAL_SERVER_ERROR, TEXT_PLAIN, "");
|
2020-09-15 15:10:41 +02:00
|
|
|
return future::ok((state, res)).boxed();
|
2020-04-26 22:34:22 +02:00
|
|
|
}
|
|
|
|
};
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-04-26 22:34:22 +02:00
|
|
|
let mut openapi = openapi.clone();
|
|
|
|
let security_schemes = get_security(&mut state);
|
|
|
|
let mut components = openapi.components.unwrap_or_default();
|
|
|
|
components.security_schemes = security_schemes;
|
|
|
|
openapi.components = Some(components);
|
2020-09-15 15:10:41 +02:00
|
|
|
|
2020-04-26 22:34:22 +02:00
|
|
|
match serde_json::to_string(&openapi) {
|
|
|
|
Ok(body) => {
|
2021-02-03 21:22:46 +00:00
|
|
|
let res = create_response(&state, StatusCode::OK, APPLICATION_JSON, body);
|
2020-04-26 22:34:22 +02:00
|
|
|
future::ok((state, res)).boxed()
|
|
|
|
},
|
|
|
|
Err(e) => {
|
|
|
|
error!("Unable to handle OpenAPI request due to error: {}", e);
|
2021-02-03 21:22:46 +00:00
|
|
|
let res = create_response(&state, StatusCode::INTERNAL_SERVER_ERROR, TEXT_PLAIN, "");
|
2020-04-26 22:34:22 +02:00
|
|
|
future::ok((state, res)).boxed()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|