2020-04-27 02:12:51 +02:00
|
|
|
use crate::{
|
|
|
|
resource::*,
|
|
|
|
result::*,
|
|
|
|
OpenapiSchema,
|
|
|
|
RequestBody
|
|
|
|
};
|
|
|
|
use super::SECURITY_NAME;
|
|
|
|
use indexmap::IndexMap;
|
2020-05-03 18:48:55 +02:00
|
|
|
use mime::Mime;
|
2020-04-27 02:12:51 +02:00
|
|
|
use openapiv3::{
|
|
|
|
MediaType, Operation, Parameter, ParameterData, ParameterSchemaOrContent, ReferenceOr,
|
|
|
|
ReferenceOr::Item, RequestBody as OARequestBody, Response, Responses, Schema, SchemaKind,
|
|
|
|
StatusCode, Type
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct OperationParams<'a>
|
|
|
|
{
|
2020-04-29 19:10:11 +02:00
|
|
|
path_params : Vec<(&'a str, ReferenceOr<Schema>)>,
|
2020-04-27 02:12:51 +02:00
|
|
|
query_params : Option<OpenapiSchema>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> OperationParams<'a>
|
|
|
|
{
|
|
|
|
fn add_path_params(&self, params : &mut Vec<ReferenceOr<Parameter>>)
|
|
|
|
{
|
|
|
|
for param in &self.path_params
|
|
|
|
{
|
|
|
|
params.push(Item(Parameter::Path {
|
|
|
|
parameter_data: ParameterData {
|
2020-04-29 19:10:11 +02:00
|
|
|
name: (*param).0.to_string(),
|
2020-04-27 02:12:51 +02:00
|
|
|
description: None,
|
|
|
|
required: true,
|
|
|
|
deprecated: None,
|
2020-04-29 19:10:11 +02:00
|
|
|
format: ParameterSchemaOrContent::Schema((*param).1.clone()),
|
2020-04-27 02:12:51 +02:00
|
|
|
example: None,
|
|
|
|
examples: IndexMap::new()
|
|
|
|
},
|
|
|
|
style: Default::default(),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_query_params(self, params : &mut Vec<ReferenceOr<Parameter>>)
|
|
|
|
{
|
|
|
|
let query_params = match self.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(&mut params);
|
|
|
|
self.add_query_params(&mut params);
|
|
|
|
params
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct OperationDescription<'a>
|
|
|
|
{
|
|
|
|
operation_id : Option<String>,
|
|
|
|
default_status : crate::StatusCode,
|
|
|
|
accepted_types : Option<Vec<Mime>>,
|
|
|
|
schema : ReferenceOr<Schema>,
|
|
|
|
params : OperationParams<'a>,
|
|
|
|
body_schema : Option<ReferenceOr<Schema>>,
|
|
|
|
supported_types : Option<Vec<Mime>>,
|
|
|
|
requires_auth : bool
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> OperationDescription<'a>
|
|
|
|
{
|
|
|
|
pub fn new<Handler : ResourceMethod>(schema : ReferenceOr<Schema>) -> Self
|
|
|
|
{
|
|
|
|
Self {
|
|
|
|
operation_id: Handler::operation_id(),
|
|
|
|
default_status: Handler::Res::default_status(),
|
|
|
|
accepted_types: Handler::Res::accepted_types(),
|
|
|
|
schema,
|
|
|
|
params: Default::default(),
|
|
|
|
body_schema: None,
|
|
|
|
supported_types: None,
|
|
|
|
requires_auth: Handler::wants_auth()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-29 19:10:11 +02:00
|
|
|
pub fn add_path_param(mut self, name : &'a str, schema : ReferenceOr<Schema>) -> Self
|
2020-04-27 02:12:51 +02:00
|
|
|
{
|
2020-04-29 19:10:11 +02:00
|
|
|
self.params.path_params.push((name, schema));
|
2020-04-27 02:12:51 +02:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_query_params(mut self, params : OpenapiSchema) -> Self
|
|
|
|
{
|
|
|
|
self.params.query_params = Some(params);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_body<Body : RequestBody>(mut self, schema : ReferenceOr<Schema>) -> Self
|
|
|
|
{
|
|
|
|
self.body_schema = Some(schema);
|
|
|
|
self.supported_types = Body::supported_types();
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-05-03 18:48:55 +02:00
|
|
|
let content = Self::schema_to_content(accepted_types.or_all_types(), schema);
|
2020-04-27 02:12:51 +02:00
|
|
|
|
|
|
|
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,
|
2020-05-03 18:48:55 +02:00
|
|
|
content: Self::schema_to_content(supported_types.or_all_types(), schema),
|
2020-04-27 02:12:51 +02:00
|
|
|
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
|
|
|
|
{
|
2020-04-29 19:10:11 +02:00
|
|
|
use crate::{OpenapiType, ResourceResult};
|
2020-04-27 02:12:51 +02:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_content_schema_to_content()
|
|
|
|
{
|
|
|
|
let types = NoContent::accepted_types();
|
|
|
|
let schema = <NoContent as OpenapiType>::schema();
|
2020-05-03 18:48:55 +02:00
|
|
|
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
|
2020-04-27 02:12:51 +02:00
|
|
|
assert!(content.is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn raw_schema_to_content()
|
|
|
|
{
|
|
|
|
let types = Raw::<&str>::accepted_types();
|
|
|
|
let schema = <Raw<&str> as OpenapiType>::schema();
|
2020-05-03 18:48:55 +02:00
|
|
|
let content = OperationDescription::schema_to_content(types.or_all_types(), Item(schema.into_schema()));
|
2020-04-27 02:12:51 +02:00
|
|
|
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"}}"#);
|
|
|
|
}
|
|
|
|
}
|