From 7286054a2fae89159a0774497535c17e0bc6282d Mon Sep 17 00:00:00 2001 From: Dominic Date: Tue, 1 Oct 2019 00:23:34 +0200 Subject: [PATCH] openapi for read method --- examples/users.rs | 2 +- src/openapi/router.rs | 133 ++++++++++++++++++++++++++++-------------- src/result.rs | 1 + 3 files changed, 92 insertions(+), 44 deletions(-) diff --git a/examples/users.rs b/examples/users.rs index 6ba9b55..1f169b8 100644 --- a/examples/users.rs +++ b/examples/users.rs @@ -118,7 +118,7 @@ fn main() ); gotham::start(ADDR, build_router(chain, pipelines, |route| { - route.with_openapi("Users Example", "0.0.1", ADDR, |mut route| { + route.with_openapi("Users Example", "0.0.1", format!("http://{}", ADDR), |mut route| { route.resource::("users"); route.get_openapi("openapi"); }); diff --git a/src/openapi/router.rs b/src/openapi/router.rs index 76d637c..678ad5f 100644 --- a/src/openapi/router.rs +++ b/src/openapi/router.rs @@ -1,7 +1,8 @@ use crate::{ resource::*, result::*, - routing::* + routing::*, + OpenapiType }; use futures::future::ok; use gotham::{ @@ -15,8 +16,9 @@ use indexmap::IndexMap; use log::error; use mime::{APPLICATION_JSON, TEXT_PLAIN}; use openapiv3::{ - Components, MediaType, OpenAPI, Operation, PathItem, Paths, ReferenceOr, ReferenceOr::Item, ReferenceOr::Reference, - Response, Responses, Schema, SchemaData, Server, StatusCode + Components, MediaType, OpenAPI, Operation, Parameter, ParameterData, ParameterSchemaOrContent, PathItem, + PathStyle, Paths, ReferenceOr, ReferenceOr::Item, ReferenceOr::Reference, Response, Responses, Schema, + SchemaData, Server, StatusCode }; use serde::de::DeserializeOwned; use std::panic::RefUnwindSafe; @@ -66,8 +68,24 @@ impl OpenapiRouter self.0.paths.insert(path.to_string(), Item(item)); } - fn add_schema(&mut self, name : Name, item : Schema) + fn add_schema(&mut self, path : &str, method : &str, desc : &str) -> String { + let name = T::schema_name().unwrap_or_else(|| format!("path_{}_{}_{}", path, method, desc)); + let item = Schema { + schema_data: SchemaData { + nullable: false, + read_only: false, + write_only: false, + deprecated: false, + external_docs: None, + example: None, + title: Some(name.to_string()), + description: None, + discriminator: None, + default: None + }, + schema_kind: T::to_schema() + }; match &mut self.0.components { Some(comp) => { comp.schemas.insert(name.to_string(), Item(item)); @@ -78,6 +96,7 @@ impl OpenapiRouter self.0.components = Some(comp); } }; + name } } @@ -128,6 +147,64 @@ pub trait GetOpenapi fn get_openapi(&mut self, path : &str); } +fn new_operation(schema : &str, path_params : Vec<&str>) -> Operation +{ + let mut content : IndexMap = IndexMap::new(); + content.insert(APPLICATION_JSON.to_string(), MediaType { + schema: Some(Reference { + reference: format!("#/components/schemas/{}", schema) + }), + example: None, + examples: IndexMap::new(), + encoding: IndexMap::new() + }); + + let mut responses : IndexMap> = IndexMap::new(); + responses.insert(StatusCode::Code(200), Item(Response { + description: "OK".to_string(), + headers: IndexMap::new(), + content, + links: IndexMap::new() + })); + + let mut params : Vec> = Vec::new(); + for param in path_params + { + params.push(Item(Parameter::Path { + parameter_data: ParameterData { + name: param.to_string(), + description: None, + required: true, + deprecated: None, + format: ParameterSchemaOrContent::Schema(Item(Schema { + schema_data: SchemaData::default(), + schema_kind: String::to_schema() + })), + example: None, + examples: IndexMap::new() + }, + style: PathStyle::default(), + })); + } + + Operation { + tags: Vec::new(), + summary: None, + description: None, + external_documentation: None, + operation_id: None, // TODO + parameters: params, + request_body: None, + responses: Responses { + default: None, + responses + }, + deprecated: false, + security: Vec::new(), + servers: Vec::new() + } +} + macro_rules! implOpenapiRouter { ($implType:ident) => { @@ -163,48 +240,11 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceReadAll { - let schema = Res::schema_name().unwrap_or_else(|| { - format!("Resource_{}_ReadAllResult", self.1) - }); - (self.0).1.add_schema(&schema, Schema { - schema_data: SchemaData::default(), - schema_kind: Res::to_schema() - }); + let schema = (self.0).1.add_schema::(&self.1, "read_all", "result_body"); let path = format!("/{}", &self.1); let mut item = (self.0).1.remove_path(&path); - let mut content : IndexMap = IndexMap::new(); - content.insert(APPLICATION_JSON.to_string(), MediaType { - schema: Some(Reference { - reference: format!("#/components/schemas/{}", schema) - }), - example: None, - examples: IndexMap::new(), - encoding: IndexMap::new() - }); - let mut responses : IndexMap> = IndexMap::new(); - responses.insert(StatusCode::Code(200), Item(Response { - description: "OK".to_string(), - headers: IndexMap::new(), - content, - links: IndexMap::new() - })); - item.get = Some(Operation { - tags: Vec::new(), - summary: None, - description: None, - external_documentation: None, - operation_id: None, // TODO - parameters: Vec::new(), - request_body: None, - responses: Responses { - default: None, - responses - }, - deprecated: false, - security: Vec::new(), - servers: Vec::new() - }); + item.get = Some(new_operation(&schema, vec![])); (self.0).1.add_path(path, item); (&mut *(self.0).0, self.1.to_string()).read_all::() @@ -216,6 +256,13 @@ macro_rules! implOpenapiRouter { Res : ResourceResult, Handler : ResourceRead { + let schema = (self.0).1.add_schema::(&self.1, "read", "result_body"); + + let path = format!("/{}/{{id}}", &self.1); + let mut item = (self.0).1.remove_path(&path); + item.get = Some(new_operation(&schema, vec!["id"])); + (self.0).1.add_path(path, item); + (&mut *(self.0).0, self.1.to_string()).read::() } diff --git a/src/result.rs b/src/result.rs index 97d3f26..b67333d 100644 --- a/src/result.rs +++ b/src/result.rs @@ -1,4 +1,5 @@ use crate::{ResourceType, StatusCode}; +#[cfg(feature = "openapi")] use openapiv3::SchemaKind; use serde::Serialize; use serde_json::error::Error as SerdeJsonError;