Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore schema and path builders #48

Merged
merged 3 commits into from
Mar 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 37 additions & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,26 +249,28 @@ pub trait OpenApi {
/// impl utoipa::Component for Pet {
/// fn component() -> utoipa::openapi::schema::Component {
/// use utoipa::openapi::ToArray;
/// utoipa::openapi::Object::new()
/// .with_property(
/// utoipa::openapi::ObjectBuilder::new()
/// .property(
/// "id",
/// utoipa::openapi::Property::new(utoipa::openapi::ComponentType::Integer)
/// .with_format(utoipa::openapi::ComponentFormat::Int64),
/// utoipa::openapi::PropertyBuilder::new()
/// .component_type(utoipa::openapi::ComponentType::Integer)
/// .format(Some(utoipa::openapi::ComponentFormat::Int64)),
/// )
/// .with_required("id")
/// .with_property(
/// .required("id")
/// .property(
/// "name",
/// utoipa::openapi::Property::new(utoipa::openapi::ComponentType::String),
/// )
/// .with_required("name")
/// .with_property(
/// .required("name")
/// .property(
/// "age",
/// utoipa::openapi::Property::new(utoipa::openapi::ComponentType::Integer)
/// .with_format(utoipa::openapi::ComponentFormat::Int32),
/// utoipa::openapi::PropertyBuilder::new()
/// .component_type(utoipa::openapi::ComponentType::Integer)
/// .format(Some(utoipa::openapi::ComponentFormat::Int32)),
/// )
/// .with_example(serde_json::json!({
/// "name":"bob the cat","id":1
/// }))
/// .example(Some(serde_json::json!({
/// "name": "bob the cat", "id": 1
/// })))
/// .into()
/// }
/// }
Expand Down Expand Up @@ -316,12 +318,12 @@ pub trait Component {
///
/// Example of what would manual implementation roughly look like of above `#[utoipa::path(...)]` macro.
/// ```rust
/// utoipa::openapi::Paths::new().append(
/// utoipa::openapi::PathsBuilder::new().path(
/// "/pets/{id}",
/// utoipa::openapi::PathItem::new(
/// utoipa::openapi::PathItemType::Get,
/// utoipa::openapi::path::Operation::new()
/// .with_responses(
/// utoipa::openapi::path::OperationBuilder::new()
/// .responses(
/// utoipa::openapi::ResponsesBuilder::new()
/// .response(
/// "200",
Expand All @@ -331,26 +333,28 @@ pub trait Component {
/// utoipa::openapi::Content::new(
/// utoipa::openapi::Ref::from_component_name("Pet"),
/// ),
/// ).build(),
/// ),
/// )
/// .response("404", utoipa::openapi::Response::new("Pet was not found")).build(),
/// .response("404", utoipa::openapi::Response::new("Pet was not found")),
/// )
/// .with_operation_id("get_pet_by_id")
/// .with_deprecated(utoipa::openapi::Deprecated::False)
/// .with_summary("Get pet by id")
/// .with_description("Get pet by id\n\nGet pet from database by pet database id\n")
/// .with_parameter(
/// utoipa::openapi::path::Parameter::new("id")
/// .with_in(utoipa::openapi::path::ParameterIn::Path)
/// .with_deprecated(utoipa::openapi::Deprecated::False)
/// .with_description("Pet database id to get Pet for")
/// .with_schema(
/// utoipa::openapi::Property::new(utoipa::openapi::ComponentType::Integer)
/// .with_format(utoipa::openapi::ComponentFormat::Int64),
/// )
/// .with_required(utoipa::openapi::Required::True),
/// .operation_id(Some("get_pet_by_id"))
/// .deprecated(Some(utoipa::openapi::Deprecated::False))
/// .summary(Some("Get pet by id"))
/// .description(Some("Get pet by id\n\nGet pet from database by pet database id\n"))
/// .parameter(
/// utoipa::openapi::path::ParameterBuilder::new()
/// .name("id")
/// .parameter_in(utoipa::openapi::path::ParameterIn::Path)
/// .required(utoipa::openapi::Required::True)
/// .deprecated(Some(utoipa::openapi::Deprecated::False))
/// .description(Some("Pet database id to get Pet for"))
/// .schema(
/// Some(utoipa::openapi::PropertyBuilder::new()
/// .component_type(utoipa::openapi::ComponentType::Integer)
/// .format(Some(utoipa::openapi::ComponentFormat::Int64))),
/// ),
/// )
/// .with_tag("pet_api"),
/// .tag("pet_api"),
/// ),
/// );
/// ```
Expand Down
56 changes: 29 additions & 27 deletions src/openapi.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use std::collections::BTreeMap;

use serde::{de::Visitor, Deserialize, Serialize, Serializer};

pub use self::{
content::{Content, ContentBuilder},
external_docs::ExternalDocs,
header::{Header, HeaderBuilder},
info::{Contact, ContactBuilder, Info, InfoBuilder, License, LicenseBuilder},
path::{PathItem, PathItemType, Paths},
path::{PathItem, PathItemType, Paths, PathsBuilder},
response::{Response, ResponseBuilder, Responses, ResponsesBuilder},
schema::{
Array, Component, ComponentFormat, ComponentType, Components, Object, OneOf, Property, Ref,
ToArray,
Array, ArrayBuilder, Component, ComponentFormat, ComponentType, Components,
ComponentsBuilder, Object, ObjectBuilder, OneOf, OneOfBuilder, Property, PropertyBuilder,
Ref, ToArray,
},
security::SecurityRequirement,
server::Server,
Expand Down Expand Up @@ -79,7 +78,8 @@ builder! {
/// Available paths and operations for the API.
///
/// See more details at <https://spec.openapis.org/oas/latest.html#paths-object>.
pub paths: BTreeMap<String, PathItem>,
#[serde(flatten)]
pub paths: Paths,

/// Holds various reusable schemas for the OpenAPI document.
///
Expand Down Expand Up @@ -124,10 +124,10 @@ impl OpenApi {
/// #
/// let openapi = OpenApi::new(Info::new("pet api", "0.1.0"), Paths::new());
/// ```
pub fn new(info: Info, paths: Paths) -> Self {
pub fn new<P: Into<Paths>>(info: Info, paths: P) -> Self {
Self {
info,
paths: paths.to_map(),
paths: paths.into(),
..Default::default()
}
}
Expand All @@ -152,40 +152,40 @@ impl OpenApi {
impl OpenApiBuilder {
/// Add [`Info`] metadata of the API.
pub fn info(mut self, info: Info) -> Self {
add_value!(self info info)
set_value!(self info info)
}

/// Add iterator of [`Server`]s to configure target servers.
pub fn servers<I: IntoIterator<Item = Server>>(mut self, servers: Option<I>) -> Self {
add_value!(self servers servers.map(|servers| servers.into_iter().collect()))
set_value!(self servers servers.map(|servers| servers.into_iter().collect()))
}

/// Add [`Paths`] to configure operations and endpoints of the API.
pub fn paths(mut self, paths: Paths) -> Self {
add_value!(self paths paths.to_map())
pub fn paths<P: Into<Paths>>(mut self, paths: P) -> Self {
set_value!(self paths paths.into())
}

/// Add [`Components`] to configure reusable schemas.
pub fn components(mut self, components: Option<Components>) -> Self {
add_value!(self components components)
set_value!(self components components)
}

/// Add iterator of [`SecurityRequirement`]s that are globally available for all operations.
pub fn security<I: IntoIterator<Item = SecurityRequirement>>(
mut self,
security: Option<I>,
) -> Self {
add_value!(self security security.map(|security| security.into_iter().collect()))
set_value!(self security security.map(|security| security.into_iter().collect()))
}

/// Add iterator of [`Tag`]s to add additional documentation for **operations** tags.
pub fn tags<I: IntoIterator<Item = Tag>>(mut self, tags: Option<I>) -> Self {
add_value!(self tags tags.map(|tags| tags.into_iter().collect()))
set_value!(self tags tags.map(|tags| tags.into_iter().collect()))
}

/// Add [`ExternalDocs`] for refering additional documentation.
pub fn external_docs(mut self, external_docs: Option<ExternalDocs>) -> Self {
add_value!(self external_docs external_docs)
set_value!(self external_docs external_docs)
}
}

Expand Down Expand Up @@ -318,21 +318,20 @@ macro_rules! build_fn {
$(
$field: self.$field,
)*
..Default::default()
}
}
};
}
pub(crate) use build_fn;

macro_rules! add_value {
macro_rules! set_value {
( $self:ident $field:ident $value:expr ) => {{
$self.$field = $value;

$self
}};
}
pub(crate) use add_value;
pub(crate) use set_value;

macro_rules! new {
( $vis:vis $name:ident ) => {
Expand Down Expand Up @@ -406,7 +405,10 @@ pub(crate) use builder;
#[cfg(test)]
#[cfg(feature = "serde_json")]
mod tests {
use crate::openapi::info::InfoBuilder;
use crate::openapi::{
info::InfoBuilder,
path::{OperationBuilder, PathsBuilder},
};

use super::{path::Operation, response::Response, *};

Expand Down Expand Up @@ -447,26 +449,26 @@ mod tests {
fn serialize_openapi_json_with_paths_success() -> Result<(), serde_json::Error> {
let openapi = OpenApi::new(
Info::new("My big api", "1.1.0"),
Paths::new()
.append(
PathsBuilder::new()
.path(
"/api/v1/users",
PathItem::new(
PathItemType::Get,
Operation::new().with_response("200", Response::new("Get users list")),
OperationBuilder::new().response("200", Response::new("Get users list")),
),
)
.append(
.path(
"/api/v1/users",
PathItem::new(
PathItemType::Post,
Operation::new().with_response("200", Response::new("Post new user")),
OperationBuilder::new().response("200", Response::new("Post new user")),
),
)
.append(
.path(
"/api/v1/users/{id}",
PathItem::new(
PathItemType::Get,
Operation::new().with_response("200", Response::new("Get user by id")),
OperationBuilder::new().response("200", Response::new("Get user by id")),
),
),
);
Expand Down
8 changes: 4 additions & 4 deletions src/openapi/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "serde_json")]
use serde_json::Value;

use super::{add_value, build_fn, from, new, Component};
use super::{build_fn, from, new, set_value, Component};

/// Content holds request body content or response content.
#[derive(Serialize, Deserialize, Default, Clone)]
Expand Down Expand Up @@ -53,19 +53,19 @@ impl ContentBuilder {

/// Add schema.
pub fn schema<I: Into<Component>>(mut self, component: I) -> Self {
add_value!(self schema component.into())
set_value!(self schema component.into())
}

/// Add example of schema.
#[cfg(feature = "serde_json")]
pub fn example(mut self, example: Option<Value>) -> Self {
add_value!(self example example)
set_value!(self example example)
}

/// Add example of schema.
#[cfg(not(feature = "serde_json"))]
pub fn example<S: Into<String>>(mut self, example: Option<S>) -> Self {
add_value!(self example example.map(|example| example.into()))
set_value!(self example example.map(|example| example.into()))
}

build_fn!(pub Content schema, example);
Expand Down
6 changes: 3 additions & 3 deletions src/openapi/external_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! [external_docs]: https://spec.openapis.org/oas/latest.html#xml-object
use serde::{Deserialize, Serialize};

use super::{add_value, build_fn, builder, from, new};
use super::{build_fn, builder, from, new, set_value};

builder! {
ExternalDocsBuilder;
Expand Down Expand Up @@ -43,11 +43,11 @@ impl ExternalDocs {
impl ExternalDocsBuilder {
/// Add target url for external documentation location.
pub fn url<I: Into<String>>(mut self, url: I) -> Self {
add_value!(self url url.into())
set_value!(self url url.into())
}

/// Add additional description of external documentation.
pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
add_value!(self description description.map(|description| description.into()))
set_value!(self description description.map(|description| description.into()))
}
}
6 changes: 3 additions & 3 deletions src/openapi/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use serde::{Deserialize, Serialize};

use super::{add_value, build_fn, builder, from, new, Component, ComponentType, Property};
use super::{build_fn, builder, from, new, set_value, Component, ComponentType, Property};

builder! {
HeaderBuilder;
Expand Down Expand Up @@ -64,11 +64,11 @@ impl Default for Header {
impl HeaderBuilder {
/// Add schema of header.
pub fn schema<I: Into<Component>>(mut self, component: I) -> Self {
add_value!(self schema component.into())
set_value!(self schema component.into())
}

/// Add additional description for header.
pub fn description<S: Into<String>>(mut self, description: Option<S>) -> Self {
add_value!(self description description.map(|description| description.into()))
set_value!(self description description.map(|description| description.into()))
}
}
Loading