1
0
Fork 0
mirror of https://gitlab.com/msrd0/gotham-restful.git synced 2025-02-23 13:02:28 +00:00

add dependencies to schema

This commit is contained in:
Dominic 2019-10-01 16:13:13 +02:00
parent d1c7ac5887
commit 3427fb4c6f
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
4 changed files with 141 additions and 50 deletions

View file

@ -27,14 +27,14 @@ rest_struct!{User {
username : String username : String
}} }}
impl ResourceReadAll<Success<Vec<User>>> for Users impl ResourceReadAll<Success<Vec<Option<User>>>> for Users
{ {
fn read_all(_state : &mut State) -> Success<Vec<User>> fn read_all(_state : &mut State) -> Success<Vec<Option<User>>>
{ {
vec![Username().fake(), Username().fake()] vec![Username().fake(), Username().fake()]
.into_iter() .into_iter()
.map(|username| User { username }) .map(|username| Some(User { username }))
.collect::<Vec<User>>() .collect::<Vec<Option<User>>>()
.into() .into()
} }
} }

View file

@ -35,12 +35,32 @@ macro_rules! rest_struct {
let mut properties : IndexMap<String, ReferenceOr<Box<Schema>>> = IndexMap::new(); let mut properties : IndexMap<String, ReferenceOr<Box<Schema>>> = IndexMap::new();
let mut required : Vec<String> = Vec::new(); let mut required : Vec<String> = Vec::new();
let mut dependencies : IndexMap<String, OpenapiSchema> = IndexMap::new();
$( $(
{
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( properties.insert(
stringify!($field_id).to_string(), stringify!($field_id).to_string(),
ReferenceOr::Item(Box::new(<$field_ty>::to_schema().to_schema())) ReferenceOr::Item(Box::new(<$field_ty>::to_schema().to_schema()))
); );
}
}
)* )*
let schema = SchemaKind::Type(Type::Object(ObjectType { let schema = SchemaKind::Type(Type::Object(ObjectType {
@ -54,7 +74,8 @@ macro_rules! rest_struct {
OpenapiSchema { OpenapiSchema {
name: Some(stringify!($struct_name).to_string()), name: Some(stringify!($struct_name).to_string()),
nullable: false, nullable: false,
schema schema,
dependencies
} }
} }
} }

View file

@ -2,6 +2,7 @@ use crate::{
resource::*, resource::*,
result::*, result::*,
routing::*, routing::*,
OpenapiSchema,
OpenapiType, OpenapiType,
ResourceType ResourceType
}; };
@ -19,7 +20,7 @@ use mime::{APPLICATION_JSON, TEXT_PLAIN};
use openapiv3::{ use openapiv3::{
Components, MediaType, OpenAPI, Operation, Parameter, ParameterData, ParameterSchemaOrContent, PathItem, Components, MediaType, OpenAPI, Operation, Parameter, ParameterData, ParameterSchemaOrContent, PathItem,
PathStyle, Paths, ReferenceOr, ReferenceOr::Item, ReferenceOr::Reference, RequestBody, Response, Responses, PathStyle, Paths, ReferenceOr, ReferenceOr::Item, ReferenceOr::Reference, RequestBody, Response, Responses,
Server, StatusCode Schema, Server, StatusCode
}; };
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::panic::RefUnwindSafe; use std::panic::RefUnwindSafe;
@ -69,22 +70,49 @@ impl OpenapiRouter
self.0.paths.insert(path.to_string(), Item(item)); self.0.paths.insert(path.to_string(), Item(item));
} }
fn add_schema<T : OpenapiType>(&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(); self.add_schema_dependencies(&mut schema.dependencies);
let name = schema.name.clone().unwrap_or_else(|| format!("path_{}_{}_{}", path, method, desc));
let item = schema.to_schema();
match &mut self.0.components { match &mut self.0.components {
Some(comp) => { Some(comp) => {
comp.schemas.insert(name.to_string(), Item(item)); comp.schemas.insert(name, Item(schema.to_schema()));
}, },
None => { None => {
let mut comp = Components::default(); 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); self.0.components = Some(comp);
} }
}; };
name }
fn add_schema_dependencies(&mut self, dependencies : &mut IndexMap<String, OpenapiSchema>)
{
let keys : Vec<String> = 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<T : OpenapiType>(&mut self) -> ReferenceOr<Schema>
{
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 get_openapi(&mut self, path : &str);
} }
fn schema_to_content(schema : &str) -> IndexMap<String, MediaType> fn schema_to_content(schema : ReferenceOr<Schema>) -> IndexMap<String, MediaType>
{ {
let mut content : IndexMap<String, MediaType> = IndexMap::new(); let mut content : IndexMap<String, MediaType> = IndexMap::new();
content.insert(APPLICATION_JSON.to_string(), MediaType { content.insert(APPLICATION_JSON.to_string(), MediaType {
schema: Some(Reference { schema: Some(schema),
reference: format!("#/components/schemas/{}", schema)
}),
example: None, example: None,
examples: IndexMap::new(), examples: IndexMap::new(),
encoding: IndexMap::new() encoding: IndexMap::new()
@ -149,7 +175,7 @@ fn schema_to_content(schema : &str) -> IndexMap<String, MediaType>
content content
} }
fn new_operation(schema : &str, path_params : Vec<&str>, body_schema : Option<&str>) -> Operation fn new_operation(schema : ReferenceOr<Schema>, path_params : Vec<&str>, body_schema : Option<ReferenceOr<Schema>>) -> Operation
{ {
let mut responses : IndexMap<StatusCode, ReferenceOr<Response>> = IndexMap::new(); let mut responses : IndexMap<StatusCode, ReferenceOr<Response>> = IndexMap::new();
responses.insert(StatusCode::Code(200), Item(Response { responses.insert(StatusCode::Code(200), Item(Response {
@ -235,11 +261,11 @@ macro_rules! implOpenapiRouter {
Res : ResourceResult, Res : ResourceResult,
Handler : ResourceReadAll<Res> Handler : ResourceReadAll<Res>
{ {
let schema = (self.0).1.add_schema::<Res>(&self.1, "read_all", "result_body"); let schema = (self.0).1.add_schema::<Res>();
let path = format!("/{}", &self.1); let path = format!("/{}", &self.1);
let mut item = (self.0).1.remove_path(&path); 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); (self.0).1.add_path(path, item);
(&mut *(self.0).0, self.1.to_string()).read_all::<Handler, Res>() (&mut *(self.0).0, self.1.to_string()).read_all::<Handler, Res>()
@ -251,11 +277,11 @@ macro_rules! implOpenapiRouter {
Res : ResourceResult, Res : ResourceResult,
Handler : ResourceRead<ID, Res> Handler : ResourceRead<ID, Res>
{ {
let schema = (self.0).1.add_schema::<Res>(&self.1, "read", "result_body"); let schema = (self.0).1.add_schema::<Res>();
let path = format!("/{}/{{id}}", &self.1); let path = format!("/{}/{{id}}", &self.1);
let mut item = (self.0).1.remove_path(&path); 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); (self.0).1.add_path(path, item);
(&mut *(self.0).0, self.1.to_string()).read::<Handler, ID, Res>() (&mut *(self.0).0, self.1.to_string()).read::<Handler, ID, Res>()
@ -267,12 +293,12 @@ macro_rules! implOpenapiRouter {
Res : ResourceResult, Res : ResourceResult,
Handler : ResourceCreate<Body, Res> Handler : ResourceCreate<Body, Res>
{ {
let schema = (self.0).1.add_schema::<Res>(&self.1, "create", "result_body"); let schema = (self.0).1.add_schema::<Res>();
let body_schema = (self.0).1.add_schema::<Body>(&self.1, "create", "body"); let body_schema = (self.0).1.add_schema::<Body>();
let path = format!("/{}", &self.1); let path = format!("/{}", &self.1);
let mut item = (self.0).1.remove_path(&path); 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); (self.0).1.add_path(path, item);
(&mut *(self.0).0, self.1.to_string()).create::<Handler, Body, Res>() (&mut *(self.0).0, self.1.to_string()).create::<Handler, Body, Res>()
@ -284,12 +310,12 @@ macro_rules! implOpenapiRouter {
Res : ResourceResult, Res : ResourceResult,
Handler : ResourceUpdateAll<Body, Res> Handler : ResourceUpdateAll<Body, Res>
{ {
let schema = (self.0).1.add_schema::<Res>(&self.1, "update_all", "result_body"); let schema = (self.0).1.add_schema::<Res>();
let body_schema = (self.0).1.add_schema::<Body>(&self.1, "create", "body"); let body_schema = (self.0).1.add_schema::<Body>();
let path = format!("/{}", &self.1); let path = format!("/{}", &self.1);
let mut item = (self.0).1.remove_path(&path); 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); (self.0).1.add_path(path, item);
(&mut *(self.0).0, self.1.to_string()).update_all::<Handler, Body, Res>() (&mut *(self.0).0, self.1.to_string()).update_all::<Handler, Body, Res>()
@ -302,12 +328,12 @@ macro_rules! implOpenapiRouter {
Res : ResourceResult, Res : ResourceResult,
Handler : ResourceUpdate<ID, Body, Res> Handler : ResourceUpdate<ID, Body, Res>
{ {
let schema = (self.0).1.add_schema::<Res>(&self.1, "update", "result_body"); let schema = (self.0).1.add_schema::<Res>();
let body_schema = (self.0).1.add_schema::<Body>(&self.1, "create", "body"); let body_schema = (self.0).1.add_schema::<Body>();
let path = format!("/{}/{{id}}", &self.1); let path = format!("/{}/{{id}}", &self.1);
let mut item = (self.0).1.remove_path(&path); 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); (self.0).1.add_path(path, item);
(&mut *(self.0).0, self.1.to_string()).update::<Handler, ID, Body, Res>() (&mut *(self.0).0, self.1.to_string()).update::<Handler, ID, Body, Res>()
@ -318,11 +344,11 @@ macro_rules! implOpenapiRouter {
Res : ResourceResult, Res : ResourceResult,
Handler : ResourceDeleteAll<Res> Handler : ResourceDeleteAll<Res>
{ {
let schema = (self.0).1.add_schema::<Res>(&self.1, "delete_all", "result_body"); let schema = (self.0).1.add_schema::<Res>();
let path = format!("/{}", &self.1); let path = format!("/{}", &self.1);
let mut item = (self.0).1.remove_path(&path); 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); (self.0).1.add_path(path, item);
(&mut *(self.0).0, self.1.to_string()).delete_all::<Handler, Res>() (&mut *(self.0).0, self.1.to_string()).delete_all::<Handler, Res>()
@ -334,11 +360,11 @@ macro_rules! implOpenapiRouter {
Res : ResourceResult, Res : ResourceResult,
Handler : ResourceDelete<ID, Res> Handler : ResourceDelete<ID, Res>
{ {
let schema = (self.0).1.add_schema::<Res>(&self.1, "delete", "result_body"); let schema = (self.0).1.add_schema::<Res>();
let path = format!("/{}/{{id}}", &self.1); let path = format!("/{}/{{id}}", &self.1);
let mut item = (self.0).1.remove_path(&path); 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); (self.0).1.add_path(path, item);
(&mut *(self.0).0, self.1.to_string()).delete::<Handler, ID, Res>() (&mut *(self.0).0, self.1.to_string()).delete::<Handler, ID, Res>()

View file

@ -2,6 +2,7 @@
use chrono::{ use chrono::{
Date, DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, Utc Date, DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, Utc
}; };
use indexmap::IndexMap;
use openapiv3::{ use openapiv3::{
ArrayType, IntegerType, NumberType, ObjectType, ReferenceOr::Item, ReferenceOr::Reference, Schema, ArrayType, IntegerType, NumberType, ObjectType, ReferenceOr::Item, ReferenceOr::Reference, Schema,
SchemaData, SchemaKind, StringFormat, StringType, Type, VariantOrUnknownOrEmpty 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. /// The name of this schema. If it is None, the schema will be inlined.
pub name : Option<String>, pub name : Option<String>,
pub nullable : bool, pub nullable : bool,
pub schema : SchemaKind pub schema : SchemaKind,
pub dependencies : IndexMap<String, OpenapiSchema>
} }
impl OpenapiSchema impl OpenapiSchema
@ -23,7 +25,8 @@ impl OpenapiSchema
Self { Self {
name: None, name: None,
nullable: false, nullable: false,
schema schema,
dependencies: IndexMap::new()
} }
} }
@ -124,20 +127,61 @@ macro_rules! str_types {
str_types!(String, &str); str_types!(String, &str);
impl<T : OpenapiType> OpenapiType for Option<T>
{
fn to_schema() -> OpenapiSchema
{
let schema = T::to_schema();
let mut dependencies : IndexMap<String, OpenapiSchema> = 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<T : OpenapiType> OpenapiType for Vec<T> impl<T : OpenapiType> OpenapiType for Vec<T>
{ {
fn to_schema() -> OpenapiSchema fn to_schema() -> OpenapiSchema
{ {
let schema = T::to_schema(); let schema = T::to_schema();
OpenapiSchema::new(SchemaKind::Type(Type::Array(ArrayType { let mut dependencies : IndexMap<String, OpenapiSchema> = IndexMap::new();
items: match schema.name {
Some(name) => Reference { reference: format!("#/components/schemas/{}", name) }, let items = if let Some(name) = schema.name.clone()
None => Item(Box::new(schema.to_schema())) {
}, 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, min_items: None,
max_items: None, max_items: None,
unique_items: false unique_items: false
}))) })),
dependencies
}
} }
} }