1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-04-19 22:44:38 +00:00
deprecated-gotham-restful/src/openapi/operation.rs

206 lines
5.8 KiB
Rust
Raw Normal View History

use super::SECURITY_NAME;
use crate::{result::*, EndpointWithSchema, OpenapiSchema, RequestBody};
use indexmap::IndexMap;
use mime::Mime;
use openapiv3::{
MediaType, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr, ReferenceOr::Item,
RequestBody as OARequestBody, Response, Responses, Schema, SchemaKind, StatusCode, Type
};
#[derive(Default)]
struct OperationParams {
path_params: Option<OpenapiSchema>,
query_params: Option<OpenapiSchema>
}
impl OperationParams {
fn add_path_params(path_params: Option<OpenapiSchema>, params: &mut Vec<ReferenceOr<Parameter>>) {
let path_params = match path_params {
Some(pp) => pp.schema,
None => return
};
let path_params = match path_params {
SchemaKind::Type(Type::Object(ty)) => ty,
_ => panic!("Path Parameters needs to be a plain struct")
};
for (name, schema) in path_params.properties {
let required = path_params.required.contains(&name);
params.push(Item(Parameter::Path {
parameter_data: ParameterData {
name,
description: None,
required,
deprecated: None,
format: ParameterSchemaOrContent::Schema(schema.unbox()),
example: None,
examples: IndexMap::new()
},
style: Default::default()
}))
}
}
fn add_query_params(query_params: Option<OpenapiSchema>, params: &mut Vec<ReferenceOr<Parameter>>) {
let query_params = match query_params {
Some(qp) => qp.schema,
None => return
};
let query_params = match query_params {
SchemaKind::Type(Type::Object(ty)) => ty,
_ => panic!("Query Parameters needs to be a plain struct")
};
for (name, schema) in query_params.properties {
let required = query_params.required.contains(&name);
params.push(Item(Parameter::Query {
parameter_data: ParameterData {
name,
description: None,
required,
deprecated: None,
format: ParameterSchemaOrContent::Schema(schema.unbox()),
example: None,
examples: IndexMap::new()
},
allow_reserved: false,
style: Default::default(),
allow_empty_value: None
}))
}
}
fn into_params(self) -> Vec<ReferenceOr<Parameter>> {
let mut params: Vec<ReferenceOr<Parameter>> = Vec::new();
Self::add_path_params(self.path_params, &mut params);
Self::add_query_params(self.query_params, &mut params);
params
}
}
pub struct OperationDescription {
operation_id: Option<String>,
2021-02-03 21:22:46 +00:00
default_status: gotham::hyper::StatusCode,
accepted_types: Option<Vec<Mime>>,
schema: ReferenceOr<Schema>,
params: OperationParams,
body_schema: Option<ReferenceOr<Schema>>,
supported_types: Option<Vec<Mime>>,
requires_auth: bool
}
impl OperationDescription {
pub fn new<E: EndpointWithSchema>(schema: ReferenceOr<Schema>) -> Self {
Self {
operation_id: E::operation_id(),
default_status: E::Output::default_status(),
accepted_types: E::Output::accepted_types(),
schema,
params: Default::default(),
body_schema: None,
supported_types: None,
requires_auth: E::wants_auth()
}
}
pub fn set_path_params(&mut self, params: OpenapiSchema) {
self.params.path_params = Some(params);
}
pub fn set_query_params(&mut self, params: OpenapiSchema) {
self.params.query_params = Some(params);
}
pub fn set_body<Body: RequestBody>(&mut self, schema: ReferenceOr<Schema>) {
self.body_schema = Some(schema);
self.supported_types = Body::supported_types();
}
fn schema_to_content(types: Vec<Mime>, schema: ReferenceOr<Schema>) -> IndexMap<String, MediaType> {
let mut content: IndexMap<String, MediaType> = IndexMap::new();
for ty in types {
content.insert(ty.to_string(), MediaType {
schema: Some(schema.clone()),
..Default::default()
});
}
content
}
pub fn into_operation(self) -> Operation {
// this is unfortunately neccessary to prevent rust from complaining about partially moving self
let (operation_id, default_status, accepted_types, schema, params, body_schema, supported_types, requires_auth) = (
self.operation_id,
self.default_status,
self.accepted_types,
self.schema,
self.params,
self.body_schema,
self.supported_types,
self.requires_auth
);
let content = Self::schema_to_content(accepted_types.or_all_types(), schema);
let mut responses: IndexMap<StatusCode, ReferenceOr<Response>> = IndexMap::new();
responses.insert(
StatusCode::Code(default_status.as_u16()),
Item(Response {
description: default_status.canonical_reason().map(|d| d.to_string()).unwrap_or_default(),
content,
..Default::default()
})
);
let request_body = body_schema.map(|schema| {
Item(OARequestBody {
description: None,
content: Self::schema_to_content(supported_types.or_all_types(), schema),
required: true
})
});
let mut security = Vec::new();
if requires_auth {
let mut sec = IndexMap::new();
sec.insert(SECURITY_NAME.to_owned(), Vec::new());
security.push(sec);
}
Operation {
tags: Vec::new(),
operation_id,
parameters: params.into_params(),
request_body,
responses: Responses {
default: None,
responses
},
deprecated: false,
security,
..Default::default()
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn no_content_schema_to_content() {
let types = NoContent::accepted_types();
2021-02-21 18:31:44 +01:00
let schema = <NoContent as ResourceResult>::schema();
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
assert!(content.is_empty());
}
#[test]
fn raw_schema_to_content() {
let types = Raw::<&str>::accepted_types();
2021-02-21 18:31:44 +01:00
let schema = <Raw<&str> as ResourceResult>::schema();
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
assert_eq!(content.len(), 1);
let json = serde_json::to_string(&content.values().nth(0).unwrap()).unwrap();
assert_eq!(json, r#"{"schema":{"type":"string","format":"binary"}}"#);
}
}