From 3427fb4c6fdb15a607f6fa0f64d7016005391dea Mon Sep 17 00:00:00 2001 From: Dominic Date: Tue, 1 Oct 2019 16:13:13 +0200 Subject: [PATCH] add dependencies to schema --- examples/users.rs | 8 ++-- src/helper.rs | 31 +++++++++++++--- src/openapi/router.rs | 86 ++++++++++++++++++++++++++++--------------- src/openapi/types.rs | 66 +++++++++++++++++++++++++++------ 4 files changed, 141 insertions(+), 50 deletions(-) diff --git a/examples/users.rs b/examples/users.rs index 1f169b8..25c6fb9 100644 --- a/examples/users.rs +++ b/examples/users.rs @@ -27,14 +27,14 @@ rest_struct!{User { username : String }} -impl ResourceReadAll>> for Users +impl ResourceReadAll>>> for Users { - fn read_all(_state : &mut State) -> Success> + fn read_all(_state : &mut State) -> Success>> { vec![Username().fake(), Username().fake()] .into_iter() - .map(|username| User { username }) - .collect::>() + .map(|username| Some(User { username })) + .collect::>>() .into() } } diff --git a/src/helper.rs b/src/helper.rs index 4b2789e..0a5fb1e 100644 --- a/src/helper.rs +++ b/src/helper.rs @@ -35,12 +35,32 @@ macro_rules! rest_struct { let mut properties : IndexMap>> = IndexMap::new(); let mut required : Vec = Vec::new(); + let mut dependencies : IndexMap = IndexMap::new(); $( - properties.insert( - stringify!($field_id).to_string(), - ReferenceOr::Item(Box::new(<$field_ty>::to_schema().to_schema())) - ); + { + let mut schema = <$field_ty>::to_schema(); + if let Some(name) = schema.name.clone() + { + properties.insert( + stringify!($field_id).to_string(), + ReferenceOr::Reference { reference: format!("#/components/schemas/{}", name) } + ); + if schema.nullable + { + required.push(stringify!($field_id).to_string()); + schema.nullable = false; + } + dependencies.insert(name, schema); + } + else + { + properties.insert( + stringify!($field_id).to_string(), + ReferenceOr::Item(Box::new(<$field_ty>::to_schema().to_schema())) + ); + } + } )* let schema = SchemaKind::Type(Type::Object(ObjectType { @@ -54,7 +74,8 @@ macro_rules! rest_struct { OpenapiSchema { name: Some(stringify!($struct_name).to_string()), nullable: false, - schema + schema, + dependencies } } } diff --git a/src/openapi/router.rs b/src/openapi/router.rs index 1bade2e..bc9cb60 100644 --- a/src/openapi/router.rs +++ b/src/openapi/router.rs @@ -2,6 +2,7 @@ use crate::{ resource::*, result::*, routing::*, + OpenapiSchema, OpenapiType, ResourceType }; @@ -19,7 +20,7 @@ use mime::{APPLICATION_JSON, TEXT_PLAIN}; use openapiv3::{ Components, MediaType, OpenAPI, Operation, Parameter, ParameterData, ParameterSchemaOrContent, PathItem, PathStyle, Paths, ReferenceOr, ReferenceOr::Item, ReferenceOr::Reference, RequestBody, Response, Responses, - Server, StatusCode + Schema, Server, StatusCode }; use serde::de::DeserializeOwned; use std::panic::RefUnwindSafe; @@ -69,22 +70,49 @@ impl OpenapiRouter self.0.paths.insert(path.to_string(), Item(item)); } - fn add_schema(&mut self, path : &str, method : &str, desc : &str) -> String + fn add_schema_impl(&mut self, name : String, mut schema : OpenapiSchema) { - let schema = T::to_schema(); - let name = schema.name.clone().unwrap_or_else(|| format!("path_{}_{}_{}", path, method, desc)); - let item = schema.to_schema(); + self.add_schema_dependencies(&mut schema.dependencies); + match &mut self.0.components { Some(comp) => { - comp.schemas.insert(name.to_string(), Item(item)); + comp.schemas.insert(name, Item(schema.to_schema())); }, None => { let mut comp = Components::default(); - comp.schemas.insert(name.to_string(), Item(item)); + comp.schemas.insert(name, Item(schema.to_schema())); self.0.components = Some(comp); } }; - name + } + + fn add_schema_dependencies(&mut self, dependencies : &mut IndexMap) + { + let keys : Vec = dependencies.keys().map(|k| k.to_string()).collect(); + for dep in keys + { + let dep_schema = dependencies.swap_remove(&dep); + if let Some(dep_schema) = dep_schema + { + self.add_schema_impl(dep, dep_schema); + } + } + } + + fn add_schema(&mut self) -> ReferenceOr + { + let mut schema = T::to_schema(); + if let Some(name) = schema.name.clone() + { + let reference = Reference { reference: format!("#/components/schemas/{}", name) }; + self.add_schema_impl(name, schema); + reference + } + else + { + self.add_schema_dependencies(&mut schema.dependencies); + Item(schema.to_schema()) + } } } @@ -135,13 +163,11 @@ pub trait GetOpenapi fn get_openapi(&mut self, path : &str); } -fn schema_to_content(schema : &str) -> IndexMap +fn schema_to_content(schema : ReferenceOr) -> IndexMap { let mut content : IndexMap = IndexMap::new(); content.insert(APPLICATION_JSON.to_string(), MediaType { - schema: Some(Reference { - reference: format!("#/components/schemas/{}", schema) - }), + schema: Some(schema), example: None, examples: IndexMap::new(), encoding: IndexMap::new() @@ -149,7 +175,7 @@ fn schema_to_content(schema : &str) -> IndexMap content } -fn new_operation(schema : &str, path_params : Vec<&str>, body_schema : Option<&str>) -> Operation +fn new_operation(schema : ReferenceOr, path_params : Vec<&str>, body_schema : Option>) -> Operation { let mut responses : IndexMap> = IndexMap::new(); responses.insert(StatusCode::Code(200), Item(Response { @@ -235,11 +261,11 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceReadAll { - let schema = (self.0).1.add_schema::(&self.1, "read_all", "result_body"); + let schema = (self.0).1.add_schema::(); let path = format!("/{}", &self.1); let mut item = (self.0).1.remove_path(&path); - item.get = Some(new_operation(&schema, vec![], None)); + item.get = Some(new_operation(schema, vec![], None)); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).read_all::() @@ -251,11 +277,11 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceRead { - let schema = (self.0).1.add_schema::(&self.1, "read", "result_body"); + let schema = (self.0).1.add_schema::(); let path = format!("/{}/{{id}}", &self.1); let mut item = (self.0).1.remove_path(&path); - item.get = Some(new_operation(&schema, vec!["id"], None)); + item.get = Some(new_operation(schema, vec!["id"], None)); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).read::() @@ -267,12 +293,12 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceCreate { - let schema = (self.0).1.add_schema::(&self.1, "create", "result_body"); - let body_schema = (self.0).1.add_schema::(&self.1, "create", "body"); + let schema = (self.0).1.add_schema::(); + let body_schema = (self.0).1.add_schema::(); let path = format!("/{}", &self.1); let mut item = (self.0).1.remove_path(&path); - item.post = Some(new_operation(&schema, vec![], Some(&body_schema))); + item.post = Some(new_operation(schema, vec![], Some(body_schema))); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).create::() @@ -284,12 +310,12 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceUpdateAll { - let schema = (self.0).1.add_schema::(&self.1, "update_all", "result_body"); - let body_schema = (self.0).1.add_schema::(&self.1, "create", "body"); + let schema = (self.0).1.add_schema::(); + let body_schema = (self.0).1.add_schema::(); let path = format!("/{}", &self.1); let mut item = (self.0).1.remove_path(&path); - item.put = Some(new_operation(&schema, vec![], Some(&body_schema))); + item.put = Some(new_operation(schema, vec![], Some(body_schema))); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).update_all::() @@ -302,12 +328,12 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceUpdate { - let schema = (self.0).1.add_schema::(&self.1, "update", "result_body"); - let body_schema = (self.0).1.add_schema::(&self.1, "create", "body"); + let schema = (self.0).1.add_schema::(); + let body_schema = (self.0).1.add_schema::(); let path = format!("/{}/{{id}}", &self.1); let mut item = (self.0).1.remove_path(&path); - item.put = Some(new_operation(&schema, vec!["id"], Some(&body_schema))); + item.put = Some(new_operation(schema, vec!["id"], Some(body_schema))); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).update::() @@ -318,11 +344,11 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceDeleteAll { - let schema = (self.0).1.add_schema::(&self.1, "delete_all", "result_body"); + let schema = (self.0).1.add_schema::(); let path = format!("/{}", &self.1); let mut item = (self.0).1.remove_path(&path); - item.delete = Some(new_operation(&schema, vec![], None)); + item.delete = Some(new_operation(schema, vec![], None)); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).delete_all::() @@ -334,11 +360,11 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceDelete { - let schema = (self.0).1.add_schema::(&self.1, "delete", "result_body"); + let schema = (self.0).1.add_schema::(); let path = format!("/{}/{{id}}", &self.1); let mut item = (self.0).1.remove_path(&path); - item.delete = Some(new_operation(&schema, vec!["id"], None)); + item.delete = Some(new_operation(schema, vec!["id"], None)); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).delete::() diff --git a/src/openapi/types.rs b/src/openapi/types.rs index cce241d..5e08d60 100644 --- a/src/openapi/types.rs +++ b/src/openapi/types.rs @@ -2,6 +2,7 @@ use chrono::{ Date, DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, Utc }; +use indexmap::IndexMap; use openapiv3::{ ArrayType, IntegerType, NumberType, ObjectType, ReferenceOr::Item, ReferenceOr::Reference, Schema, SchemaData, SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty @@ -13,7 +14,8 @@ pub struct OpenapiSchema /// The name of this schema. If it is None, the schema will be inlined. pub name : Option, pub nullable : bool, - pub schema : SchemaKind + pub schema : SchemaKind, + pub dependencies : IndexMap } impl OpenapiSchema @@ -23,7 +25,8 @@ impl OpenapiSchema Self { name: None, nullable: false, - schema + schema, + dependencies: IndexMap::new() } } @@ -124,20 +127,61 @@ macro_rules! str_types { str_types!(String, &str); +impl OpenapiType for Option +{ + fn to_schema() -> OpenapiSchema + { + let schema = T::to_schema(); + let mut dependencies : IndexMap = IndexMap::new(); + let refor = if let Some(name) = schema.name.clone() + { + let reference = Reference { reference: format!("#/components/schemas/{}", name) }; + dependencies.insert(name, schema); + reference + } + else + { + Item(schema.to_schema()) + }; + + OpenapiSchema { + nullable: true, + name: None, + schema: SchemaKind::AllOf { all_of: vec![refor] }, + dependencies + } + } +} + impl OpenapiType for Vec { fn to_schema() -> OpenapiSchema { let schema = T::to_schema(); - OpenapiSchema::new(SchemaKind::Type(Type::Array(ArrayType { - items: match schema.name { - Some(name) => Reference { reference: format!("#/components/schemas/{}", name) }, - None => Item(Box::new(schema.to_schema())) - }, - min_items: None, - max_items: None, - unique_items: false - }))) + let mut dependencies : IndexMap = IndexMap::new(); + + let items = if let Some(name) = schema.name.clone() + { + let reference = Reference { reference: format!("#/components/schemas/{}", name) }; + dependencies.insert(name, schema); + reference + } + else + { + Item(Box::new(schema.to_schema())) + }; + + OpenapiSchema { + nullable: false, + name: None, + schema: SchemaKind::Type(Type::Array(ArrayType { + items, + min_items: None, + max_items: None, + unique_items: false + })), + dependencies + } } }