Repository: softprops/openapi (language: rust)

Issue:
fix tests on master
It seems since the initial work on openapi v3 was introduced there has not been any clean up of failing tests. Some work was done in https://github.com/softprops/openapi/pull/25#issuecomment-464380205 to help surface errors more clearly to iterate on fixing them but it was out of scope of that pull to fix all the failing tests. lets open a separate pull that focuses mainly on get tests to pass so we can ship https://github.com/softprops/openapi/issues/22


Discussion / hints:

Fix patch:
diff --git a/data/v3.0/petstore-expanded.yaml b/data/v3.0/petstore-expanded.yaml
index e8c0fe5..af3ee2a 100644
--- a/data/v3.0/petstore-expanded.yaml
+++ b/data/v3.0/petstore-expanded.yaml
@@ -7,7 +7,7 @@ info:
   contact:
     name: Swagger API Team
     email: foo@example.com
-    url: http://madskristensen.net
+    url: http://madskristensen.net/
   license:
     name: MIT
     url: http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT
@@ -136,12 +136,12 @@ components:
 
     NewPet:
       required:
-        - name  
+        - name
       properties:
         name:
           type: string
         tag:
-          type: string    
+          type: string
 
     Error:
       required:
diff --git a/data/v3.0/uspto.yaml b/data/v3.0/uspto.yaml
index 774ca52..cdb9130 100644
--- a/data/v3.0/uspto.yaml
+++ b/data/v3.0/uspto.yaml
@@ -23,7 +23,7 @@ info:
   title: USPTO Data Set API
   contact:
     name: Open Data Portal
-    url: 'https://developer.uspto.gov'
+    url: 'https://developer.uspto.gov/'
     email: developer@uspto.gov
 tags:
   - name: metadata
diff --git a/src/v2/schema.rs b/src/v2/schema.rs
index 75d332a..374b19c 100644
--- a/src/v2/schema.rs
+++ b/src/v2/schema.rs
@@ -162,13 +162,18 @@ pub struct Operation {
     pub schemes: Option<Vec<String>>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub tags: Option<Vec<String>>,
-    #[serde(rename="operationId", skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "operationId", skip_serializing_if = "Option::is_none")]
     pub operation_id: Option<String>,
     pub responses: BTreeMap<String, Response>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub parameters: Option<Vec<ParameterOrRef>>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub security: Option<Vec<SecurityRequirement>>,
 }
 
+/// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#securityRequirementObject
+pub type SecurityRequirement = BTreeMap<String, Vec<String>>;
+
 #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
 #[serde(rename_all = "camelCase")]
 pub struct Parameter {
@@ -188,6 +193,10 @@ pub struct Parameter {
     pub format: Option<String>,
     #[serde(skip_serializing_if = "Option::is_none")]
     pub description: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub items: Option<Schema>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    default: Option<serde_json::Value>,
 }
 
 #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
@@ -203,7 +212,6 @@ pub struct Response {
 #[serde(untagged)]
 pub enum ParameterOrRef {
     /// both bodyParameter and nonBodyParameter in one for now
-    #[derive(Default)]
     Parameter {
         /// The name of the parameter.
         name: String,
@@ -229,8 +237,9 @@ pub enum ParameterOrRef {
         #[serde(skip_serializing_if = "Option::is_none")]
         description: Option<String>,
         #[serde(rename = "collectionFormat", skip_serializing_if = "Option::is_none")]
-        collection_format: Option<String>
-        // default: ???
+        collection_format: Option<String>,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        default: Option<serde_json::Value>,
         // maximum ?
         // exclusiveMaximum ??
         // minimum ??
@@ -243,6 +252,13 @@ pub enum ParameterOrRef {
         // enum ??
         // multipleOf ??
         // allowEmptyValue ( for query / body params )
+        #[serde(skip_serializing_if = "Option::is_none")]
+        items: Option<Schema>,
+        #[serde(
+            rename = "additionalProperties",
+            skip_serializing_if = "Option::is_none"
+        )]
+        additional_properties: Option<Schema>,
     },
     Ref {
         #[serde(rename = "$ref")]
@@ -257,19 +273,35 @@ pub enum Security {
         name: String,
         #[serde(rename = "in")]
         location: String,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        description: Option<String>,
     },
     #[serde(rename = "oauth2")]
     Oauth2 {
-        flow: String,
+        flow: Flow,
         #[serde(rename = "authorizationUrl")]
         authorization_url: String,
         #[serde(rename = "tokenUrl")]
         #[serde(skip_serializing_if = "Option::is_none")]
         token_url: Option<String>,
         scopes: BTreeMap<String, String>,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        description: Option<String>,
     },
     #[serde(rename = "basic")]
-    Basic,
+    Basic {
+        #[serde(skip_serializing_if = "Option::is_none")]
+        description: Option<String>,
+    },
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+pub enum Flow {
+    Implicit,
+    Password,
+    Application,
+    AccessCode,
 }
 
 /// A [JSON schema](http://json-schema.org/) definition describing
@@ -300,6 +332,13 @@ pub struct Schema {
     // implies object
     #[serde(skip_serializing_if = "Option::is_none")]
     pub properties: Option<BTreeMap<String, Schema>>,
+    // composition
+    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "allOf")]
+    pub all_of: Option<Vec<Box<Schema>>>,
+    // TODO: we need a validation step that we only collect x-* properties here.
+    #[serde(flatten)]
+    pub other: BTreeMap<String, serde_json::Value>,
 }
 
 #[cfg(test)]
@@ -317,6 +356,7 @@ mod tests {
             Security::ApiKey {
                 name: "foo".into(),
                 location: "query".into(),
+                description: None,
             }
         );
     }
@@ -328,6 +368,7 @@ mod tests {
             serde_json::to_string(&Security::ApiKey {
                 name: "foo".into(),
                 location: "query".into(),
+                description: None,
             })
             .unwrap(),
             json
@@ -339,14 +380,17 @@ mod tests {
         let json = r#"{"type":"basic"}"#;
         assert_eq!(
             serde_yaml::from_str::<Security>(&json).unwrap(),
-            Security::Basic
+            Security::Basic { description: None }
         );
     }
 
     #[test]
     fn security_basic_serializes() {
         let json = r#"{"type":"basic"}"#;
-        assert_eq!(json, serde_json::to_string(&Security::Basic).unwrap());
+        assert_eq!(
+            json,
+            serde_json::to_string(&Security::Basic { description: None }).unwrap()
+        );
     }
 
     #[test]
@@ -357,10 +401,11 @@ mod tests {
         assert_eq!(
             serde_yaml::from_str::<Security>(&json).unwrap(),
             Security::Oauth2 {
-                flow: "implicit".into(),
+                flow: Flow::Implicit,
                 authorization_url: "foo/bar".into(),
                 token_url: None,
                 scopes: scopes,
+                description: None,
             }
         );
     }
@@ -373,10 +418,11 @@ mod tests {
         assert_eq!(
             json,
             serde_json::to_string(&Security::Oauth2 {
-                flow: "implicit".into(),
+                flow: Flow::Implicit,
                 authorization_url: "foo/bar".into(),
                 token_url: None,
                 scopes: scopes,
+                description: None,
             })
             .unwrap()
         );
diff --git a/src/v3_0/schema.rs b/src/v3_0/schema.rs
index 7e0df62..caabbd5 100644
--- a/src/v3_0/schema.rs
+++ b/src/v3_0/schema.rs
@@ -1,5 +1,5 @@
 //! Schema specification for [OpenAPI 3.0.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
-//! 
+
 use semver;
 use serde::{Deserialize, Serialize};
 use serde_json;
@@ -96,8 +96,8 @@ pub struct Info {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub description: Option<String>,
     /// A URL to the Terms of Service for the API. MUST be in the format of a URL.
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub terms_of_service: Option<String>,
+    #[serde(rename = "termsOfService", skip_serializing_if = "Option::is_none")]
+    pub terms_of_service: Option<Url>,
     /// REQUIRED. The version of the OpenAPI document (which is distinct from the [OpenAPI Specification version](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#oasVersion)
     /// or the API implementation version).
     pub version: String,
@@ -156,10 +156,31 @@ pub struct Server {
     /// An optional string describing the host designated by the URL. CommonMark syntax MAY be used for rich text representation.
     #[serde(skip_serializing_if = "Option::is_none")]
     pub description: Option<String>,
-    // // FIXME: Add this. See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#server-object
-    // /// A map between a variable name and its value. The value is used for substitution in
-    // /// the server's URL template.
-    // pub variables: ,
+    /// A map between a variable name and its value. The value is used for substitution in
+    /// the server's URL template.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub variables: Option<BTreeMap<String, ServerVariable>>,
+}
+
+/// An object representing a Server Variable for server URL template substitution.
+///
+/// See [link]
+/// [link][https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#serverVariableObject]
+#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
+pub struct ServerVariable {
+    /// The default value to use for substitution, and to send, if an alternate value is not
+    /// supplied. Unlike the Schema Object's default, this value MUST be provided by the consumer.
+    pub default: String,
+    /// An enumeration of string values to be used if the substitution options are from a limited
+    /// set.
+    #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
+    pub substitutions_enum: Option<Vec<String>>,
+    /// An optional description for the server variable. [CommonMark] syntax MAY be used for rich
+    /// text representation.
+    ///
+    /// [CommonMark]: https://spec.commonmark.org/
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub description: Option<String>,
 }
 
 /// Describes the operations available on a single path.
@@ -295,7 +316,6 @@ pub struct Operation {
     /// [link][https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#responsesObject]
     pub responses: BTreeMap<String, Response>,
 
-    // FIXME: Implement
     /// A map of possible out-of band callbacks related to the parent operation. The key is
     /// a unique identifier for the Callback Object. Each value in the map is a
     /// [Callback Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#callbackObject)
@@ -303,7 +323,8 @@ pub struct Operation {
     /// expected responses. The key value used to identify the callback object is
     /// an expression, evaluated at runtime, that identifies a URL to use for the
     /// callback operation.
-    // pub callbacks: Option<???>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub callbacks: Option<BTreeMap<String, Callback>>,
 
     /// Declares this operation to be deprecated. Consumers SHOULD refrain from usage
     /// of the declared operation. Default value is `false`.
@@ -371,6 +392,18 @@ pub struct Parameter {
     // enum ??
     // multipleOf ??
     // allowEmptyValue ( for query / body params )
+    /// Describes how the parameter value will be serialized depending on the type of the parameter
+    /// value. Default values (based on value of in): for `query` - `form`; for `path` - `simple`; for
+    /// `header` - `simple`; for cookie - `form`.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    style: Option<ParameterStyle>,
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
+#[serde(rename_all = "camelCase")]
+enum ParameterStyle {
+    Form,
+    Simple,
 }
 
 // FIXME: Verify against OpenAPI 3.0
@@ -440,6 +473,7 @@ pub struct Schema {
     ///       [`serde_json::Value`](https://docs.serde.rs/serde_json/value/enum.Value.html). [spec][https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#data-types]
     #[serde(skip_serializing_if = "Option::is_none")]
     pub example: Option<serde_json::value::Value>,
+
     // The following properties are taken directly from the JSON Schema definition and
     // follow the same specifications:
     // title
@@ -472,6 +506,17 @@ pub struct Schema {
     // - description - [CommonMark syntax](http://spec.commonmark.org/) MAY be used for rich text representation.
     // - format - See [Data Type Formats](#dataTypeFormat) for further details. While relying on JSON Schema's defined formats, the OAS offers a few additional predefined formats.
     // - default - The default value represents what would be assumed by the consumer of the input as the value of the schema if one is not provided. Unlike JSON Schema, the value MUST conform to the defined type for the Schema Object defined at the same level. For example, if `type` is `string`, then `default` can be `"foo"` but cannot be `1`.
+    /// The default value represents what would be assumed by the consumer of the input as the value
+    /// of the schema if one is not provided. Unlike JSON Schema, the value MUST conform to the
+    /// defined type for the Schema Object defined at the same level. For example, if type is
+    /// `string`, then `default` can be `"foo"` but cannot be `1`.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    default: Option<serde_json::Value>,
+
+    /// Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard
+    /// JSON Schema.
+    #[serde(rename = "allOf", skip_serializing_if = "Option::is_none")]
+    pub all_of: Option<Vec<ObjectOrReference<Schema>>>,
 }
 
 /// Describes a single response from an API Operation, including design-time, static `links`
@@ -611,6 +656,8 @@ pub enum Link {
         // /// `[{in}.]{name}` for operations that use the same parameter name in different
         // /// locations (e.g. path.id).
         // parameters: BTreeMap<String, Any | {expression}>,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        parameters: Option<BTreeMap<String, String>>,
 
         // FIXME: Implement
         // /// A literal value or
@@ -643,6 +690,8 @@ pub enum Link {
         // /// `[{in}.]{name}` for operations that use the same parameter name in different
         // /// locations (e.g. path.id).
         // parameters: BTreeMap<String, Any | {expression}>,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        parameters: Option<BTreeMap<String, String>>,
 
         // FIXME: Implement
         // /// A literal value or
@@ -672,7 +721,7 @@ pub struct MediaType {
     pub schema: Option<ObjectOrReference<Schema>>,
 
     /// Example of the media type.
-    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(flatten, skip_serializing_if = "Option::is_none")]
     pub examples: Option<MediaTypeExample>,
 
     /// A map between a property name and its encoding information. The key, being the
@@ -690,13 +739,13 @@ pub enum MediaTypeExample {
     /// specified by the media type. The `example` field is mutually exclusive of the
     /// `examples` field. Furthermore, if referencing a `schema` which contains an example,
     /// the `example` value SHALL override the example provided by the schema.
-    Example { example: String },
+    Example { example: serde_json::Value },
     /// Examples of the media type. Each example object SHOULD match the media type and
     /// specified schema if present. The `examples` field is mutually exclusive of
     /// the `example` field. Furthermore, if referencing a `schema` which contains an
     /// example, the `examples` value SHALL override the example provided by the schema.
     Examples {
-        examples: Option<BTreeMap<String, ObjectOrReference<Example>>>,
+        examples: BTreeMap<String, ObjectOrReference<Example>>,
     },
 }
 
@@ -762,12 +811,11 @@ pub struct Example {
     #[serde(skip_serializing_if = "Option::is_none")]
     pub description: Option<String>,
     // FIXME: Implement (merge with externalValue as enum)
-    // /// Embedded literal example. The `value` field and `externalValue` field are mutually
-    // /// exclusive. To represent examples of media types that cannot naturally represented
-    // /// in JSON or YAML, use a string value to contain the example, escaping where necessary.
-    // #[serde(skip_serializing_if = "Option::is_none")]
-    // pub value: Option<????>,
-
+    /// Embedded literal example. The `value` field and `externalValue` field are mutually
+    /// exclusive. To represent examples of media types that cannot naturally represented
+    /// in JSON or YAML, use a string value to contain the example, escaping where necessary.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub value: Option<serde_json::Value>,
     // FIXME: Implement (merge with value as enum)
     // /// A URL that points to the literal example. This provides the capability to reference
     // /// examples that cannot easily be included in JSON or YAML documents. The `value` field
@@ -818,6 +866,7 @@ pub enum SecurityScheme {
     },
 }
 
+// TODO: Implement
 /// A map of possible out-of band callbacks related to the parent operation. Each value in
 /// the map is a Path Item Object that describes a set of requests that may be initiated by
 /// the API provider and the expected responses. The key value used to identify the callback
@@ -828,7 +877,7 @@ pub enum SecurityScheme {
 #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
 pub struct Callback(
     /// A Path Item Object used to define a callback request and expected responses.
-    String, // TODO: Add "Specification Extensions" https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#specificationExtensions}
+    serde_json::Value, // TODO: Add "Specification Extensions" https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#specificationExtensions}
 );
 
 // FIXME: Implement


Test patch: