From 5317e50961bdc6bb57fde7db665ebe36b6a2cf33 Mon Sep 17 00:00:00 2001 From: Dominic Date: Wed, 27 May 2020 10:22:13 +0200 Subject: [PATCH] improve feature documentation --- README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4de2465..643174c 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,14 @@ fn create(body : RawImage) -> Raw> { ## Features To make life easier for common use-cases, this create offers a few features that might be helpful -when you implement your web server. +when you implement your web server. The complete feature list is + - [`auth`](#authentication-feature) Advanced JWT middleware + - `chrono` openapi support for chrono types + - [`cors`](#cors-feature) CORS handling for all method handlers + - [`database`](#database-feature) diesel middleware support + - `errorlog` log errors returned from method handlers + - [`openapi`](#openapi-feature) router additions to generate an openapi spec + - `uuid` openapi support for uuid ### Authentication Feature @@ -241,6 +248,70 @@ fn main() { } ``` +### OpenAPI Feature + +The OpenAPI feature is probably the most powerful one of this crate. Definitely read this section +carefully both as a binary as well as a library author to avoid unwanted suprises. + +In order to automatically create an openapi specification, gotham-restful needs knowledge over +all routes and the types returned. `serde` does a great job at serialization but doesn't give +enough type information, so all types used in the router need to implement `OpenapiType`. This +can be derived for almoust any type and there should be no need to implement it manually. A simple +example could look like this: + +```rust +#[derive(Resource)] +#[resource(read_all)] +struct FooResource; + +#[derive(OpenapiType, Serialize)] +struct Foo { + bar: String +} + +#[read_all(FooResource)] +fn read_all() -> Success { + Foo { bar: "Hello World".to_owned() }.into() +} + +fn main() { + gotham::start("127.0.0.1:8080", build_simple_router(|route| { + let info = OpenapiInfo { + title: "My Foo API".to_owned(), + version: "0.1.0".to_owned(), + urls: vec!["https://example.org/foo/api/v1".to_owned()] + }; + route.with_openapi(info, |mut route| { + route.resource::("foo"); + route.get_openapi("openapi"); + }); + })); +} +``` + +Above example adds the resource as before, but adds another endpoint that we specified as `/openapi` +that will return the generated openapi specification. This allows you to easily write clients +in different languages without worying to exactly replicate your api in each of those languages. + +However, as of right now there is one caveat. If you wrote code before enabling the openapi feature, +it is likely to break. This is because of the new requirement of `OpenapiType` for all types used +with resources, even outside of the `with_openapi` scope. This issue will eventually be resolved. +If you are writing a library that uses gotham-restful, make sure that you expose an openapi feature. +In other words, put + +```toml +[features] +openapi = ["gotham-restful/openapi"] +``` + +into your libraries `Cargo.toml` and use the following for all types used with handlers: + +```rust +#[derive(Deserialize, Serialize)] +#[cfg_attr(feature = "openapi", derive(OpenapiType))] +struct Foo; +``` + ## Examples There is a lack of good examples, but there is currently a collection of code in the [example] diff --git a/src/lib.rs b/src/lib.rs index d7aa095..8a9ea87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,7 +110,14 @@ fn create(body : RawImage) -> Raw> { # Features To make life easier for common use-cases, this create offers a few features that might be helpful -when you implement your web server. +when you implement your web server. The complete feature list is + - [`auth`](#authentication-feature) Advanced JWT middleware + - `chrono` openapi support for chrono types + - [`cors`](#cors-feature) CORS handling for all method handlers + - [`database`](#database-feature) diesel middleware support + - `errorlog` log errors returned from method handlers + - [`openapi`](#openapi-feature) router additions to generate an openapi spec + - `uuid` openapi support for uuid ## Authentication Feature @@ -265,6 +272,79 @@ fn main() { # } ``` +## OpenAPI Feature + +The OpenAPI feature is probably the most powerful one of this crate. Definitely read this section +carefully both as a binary as well as a library author to avoid unwanted suprises. + +In order to automatically create an openapi specification, gotham-restful needs knowledge over +all routes and the types returned. `serde` does a great job at serialization but doesn't give +enough type information, so all types used in the router need to implement `OpenapiType`. This +can be derived for almoust any type and there should be no need to implement it manually. A simple +example could look like this: + +```rust,no_run +# #[macro_use] extern crate gotham_restful_derive; +# #[cfg(feature = "openapi")] +# mod openapi_feature_enabled { +# use gotham::{router::builder::*, state::State}; +# use gotham_restful::*; +# use serde::{Deserialize, Serialize}; +#[derive(Resource)] +#[resource(read_all)] +struct FooResource; + +#[derive(OpenapiType, Serialize)] +struct Foo { + bar: String +} + +#[read_all(FooResource)] +fn read_all() -> Success { + Foo { bar: "Hello World".to_owned() }.into() +} + +fn main() { + gotham::start("127.0.0.1:8080", build_simple_router(|route| { + let info = OpenapiInfo { + title: "My Foo API".to_owned(), + version: "0.1.0".to_owned(), + urls: vec!["https://example.org/foo/api/v1".to_owned()] + }; + route.with_openapi(info, |mut route| { + route.resource::("foo"); + route.get_openapi("openapi"); + }); + })); +} +# } +``` + +Above example adds the resource as before, but adds another endpoint that we specified as `/openapi` +that will return the generated openapi specification. This allows you to easily write clients +in different languages without worying to exactly replicate your api in each of those languages. + +However, as of right now there is one caveat. If you wrote code before enabling the openapi feature, +it is likely to break. This is because of the new requirement of `OpenapiType` for all types used +with resources, even outside of the `with_openapi` scope. This issue will eventually be resolved. +If you are writing a library that uses gotham-restful, make sure that you expose an openapi feature. +In other words, put + +```toml +[features] +openapi = ["gotham-restful/openapi"] +``` + +into your libraries `Cargo.toml` and use the following for all types used with handlers: + +``` +# use gotham_restful::OpenapiType; +# use serde::{Deserialize, Serialize}; +#[derive(Deserialize, Serialize)] +#[cfg_attr(feature = "openapi", derive(OpenapiType))] +struct Foo; +``` + # Examples There is a lack of good examples, but there is currently a collection of code in the [example]