apistos_models/
lib.rs

1//! [OAS 3.0](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md) models over [schemars](https://github.com/GREsau/schemars)'s [`Schema`](https://docs.rs/schemars/latest/schemars/schema/enum.Schema.html).
2//!
3//! These models are not linked to any web framework.
4
5// schemars::Schema does not implement Eq
6#![allow(clippy::derive_partial_eq_without_eq)]
7
8use crate::components::Components;
9use crate::info::Info;
10use crate::paths::{ExternalDocumentation, Paths};
11use crate::security::SecurityRequirement;
12use crate::server::Server;
13use crate::tag::Tag;
14use indexmap::IndexMap;
15use serde::Serialize;
16use serde_json::Value;
17
18pub mod components;
19pub mod info;
20pub mod paths;
21pub mod reference_or;
22pub mod security;
23pub mod server;
24pub mod tag;
25
26pub use schemars::schema::*;
27
28#[derive(Serialize, Clone, Debug)]
29#[cfg_attr(any(test, feature = "deserialize"), derive(serde::Deserialize, PartialEq))]
30pub enum OpenApiVersion {
31  #[serde(rename = "3.0.3")]
32  OAS3_0,
33}
34
35impl Default for OpenApiVersion {
36  fn default() -> Self {
37    Self::OAS3_0
38  }
39}
40
41/// This is the root document object of the [OpenAPI document](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#openapi-document).
42#[derive(Serialize, Clone, Debug, Default)]
43#[cfg_attr(any(test, feature = "deserialize"), derive(serde::Deserialize, PartialEq))]
44#[serde(rename_all = "camelCase")]
45pub struct OpenApi {
46  /// This string MUST be the [semantic version number](https://semver.org/spec/v2.0.0.html) of the [OpenAPI Specification version](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#versions) that the OpenAPI document uses. The `openapi` field SHOULD be used by tooling specifications and clients to interpret the OpenAPI document. This is not related to the API [**`info.version`**](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#infoVersion) string.
47  pub openapi: OpenApiVersion,
48  /// Provides metadata about the API. The metadata MAY be used by tooling as required.
49  pub info: Info,
50  /// An array of Server Objects, which provide connectivity information to a target server. If the `servers` property is not provided, or is an empty array, the default value would be a [Server Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-object) with a [url](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#serverUrl) value of `/`.
51  pub servers: Vec<Server>,
52  /// The available paths and operations for the API.
53  pub paths: Paths,
54  /// An element to hold various schemas for the specification.
55  #[serde(skip_serializing_if = "Option::is_none")]
56  pub components: Option<Components>,
57  /// A declaration of which security mechanisms can be used across the API. The list of values includes alternative security requirement objects that can be used. Only one of the security requirement objects need to be satisfied to authorize a request. Individual operations can override this definition. To make security optional, an empty security requirement (`{}`) can be included in the array.
58  #[serde(skip_serializing_if = "Vec::is_empty", default)]
59  pub security: Vec<SecurityRequirement>,
60  /// A list of tags used by the specification with additional metadata. The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used by the [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object) must be declared. The tags that are not declared MAY be organized randomly or based on the tools' logic. Each tag name in the list MUST be unique.
61  #[serde(skip_serializing_if = "Vec::is_empty", default)]
62  pub tags: Vec<Tag>,
63  /// Additional external documentation.
64  #[serde(skip_serializing_if = "Option::is_none")]
65  pub external_docs: Option<ExternalDocumentation>,
66  /// This object MAY be extended with [Specification Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specification-extensions).
67  #[serde(flatten, skip_serializing_if = "IndexMap::is_empty", skip_deserializing)]
68  pub extensions: IndexMap<String, Value>,
69}
70
71#[cfg(test)]
72mod test {
73  #![allow(clippy::expect_used)]
74
75  use crate::info::Info;
76  use crate::paths::{Operation, OperationType, PathItem, Paths, Response, Responses};
77  use crate::reference_or::ReferenceOr;
78  use crate::server::Server;
79  use crate::tag::Tag;
80  use crate::{OpenApi, OpenApiVersion};
81  use indexmap::IndexMap;
82  use std::collections::BTreeMap;
83
84  #[test]
85  fn empty_openapi_properly_generated() {
86    let oas = OpenApi {
87      openapi: OpenApiVersion::OAS3_0,
88      info: Info {
89        title: "Test".to_string(),
90        description: Some("Description".to_string()),
91        version: "1.0.0".to_string(),
92        ..Default::default()
93      },
94      paths: Paths::default(),
95      ..Default::default()
96    };
97
98    let oas_json = serde_json::to_string_pretty(&oas).expect("Error generating json for oas");
99    assert_eq!(oas_json, include_str!("../test-assets/empty-openapi.json"));
100  }
101
102  #[test]
103  fn openapi_properly_generated() {
104    let oas = OpenApi {
105      openapi: OpenApiVersion::OAS3_0,
106      info: Info {
107        title: "Test".to_string(),
108        description: Some("Description".to_string()),
109        version: "1.0.0".to_string(),
110        ..Default::default()
111      },
112      servers: vec![Server {
113        url: "https://google.com".to_string(),
114        description: Some("A big search server".to_string()),
115        ..Default::default()
116      }],
117      paths: Paths {
118        paths: IndexMap::from_iter(vec![(
119          "/search".to_string(),
120          PathItem {
121            operations: IndexMap::from_iter(vec![(
122              OperationType::Get,
123              Operation {
124                tags: vec!["Search".to_string()],
125                summary: Some("I don't know what this do".to_string()),
126                operation_id: Some("get_search".to_string()),
127                responses: Responses {
128                  responses: BTreeMap::from_iter(vec![(
129                    "200".to_string(),
130                    ReferenceOr::Object(Response {
131                      description: "A search thingy".to_string(),
132                      ..Default::default()
133                    }),
134                  )]),
135                  ..Default::default()
136                },
137                ..Default::default()
138              },
139            )]),
140            ..Default::default()
141          },
142        )]),
143        ..Default::default()
144      },
145      tags: vec![Tag {
146        name: "Search".to_string(),
147        ..Default::default()
148      }],
149      ..Default::default()
150    };
151
152    let oas_json = serde_json::to_string_pretty(&oas).expect("Error generating json for oas");
153    assert_eq!(oas_json, include_str!("../test-assets/openapi.json"));
154  }
155}