From fb575c0f30842e4f1f1638621914aee1ef9adf0a Mon Sep 17 00:00:00 2001 From: Vincent Gerber Date: Sun, 8 Dec 2024 17:50:56 +0100 Subject: [PATCH 1/2] feat: Added support to parse discriminator --- crates/oas3/src/spec/discriminator.rs | 57 +++++++++++++++++++++++++++ crates/oas3/src/spec/mod.rs | 1 + crates/oas3/src/spec/schema.rs | 26 +++++++++++- 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 crates/oas3/src/spec/discriminator.rs diff --git a/crates/oas3/src/spec/discriminator.rs b/crates/oas3/src/spec/discriminator.rs new file mode 100644 index 0000000..24f2df5 --- /dev/null +++ b/crates/oas3/src/spec/discriminator.rs @@ -0,0 +1,57 @@ +//! Schema specification for [OpenAPI 3.1](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md) + +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +/// The discriminator is a specific object in a schema which is used to inform the consumer of +/// the document of an alternative schema based on the value associated with it. +/// +/// See +#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize)] +pub struct Discriminator { + /// The name of the property in the payload that will hold the discriminator value. + #[serde(rename = "propertyName")] + pub property_name: String, + + /// An object to hold mappings between payload values and schema names or references + /// + /// When using the discriminator, inline schemas will not be considered + #[serde(skip_serializing_if = "Option::is_none")] + pub mapping: Option>, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn discriminator_property_name_parsed_correctly() { + let spec = "propertyName: testName"; + let discriminator = serde_yml::from_str::(spec).unwrap(); + assert_eq!("testName", discriminator.property_name); + assert!(discriminator.mapping.is_none()); + } + + #[test] + fn discriminator_mapping_parsed_correctly() { + let spec = indoc::indoc! {" + propertyName: petType + mapping: + dog: '#/components/schemas/Dog' + cat: '#/components/schemas/Cat' + monster: 'https://gigantic-server.com/schemas/Monster/schema.json' + "}; + let discriminator = serde_yml::from_str::(spec).unwrap(); + + assert_eq!("petType", discriminator.property_name); + let mapping = discriminator.mapping.unwrap(); + + assert_eq!("#/components/schemas/Dog", mapping.get("dog").unwrap()); + assert_eq!("#/components/schemas/Cat", mapping.get("cat").unwrap()); + assert_eq!( + "https://gigantic-server.com/schemas/Monster/schema.json", + mapping.get("monster").unwrap() + ); + } +} diff --git a/crates/oas3/src/spec/mod.rs b/crates/oas3/src/spec/mod.rs index 2034931..8c00e15 100644 --- a/crates/oas3/src/spec/mod.rs +++ b/crates/oas3/src/spec/mod.rs @@ -13,6 +13,7 @@ mod components; mod contact; mod encoding; +mod discriminator; mod error; mod example; mod external_doc; diff --git a/crates/oas3/src/spec/schema.rs b/crates/oas3/src/spec/schema.rs index 0ca9a1e..fcb31a9 100644 --- a/crates/oas3/src/spec/schema.rs +++ b/crates/oas3/src/spec/schema.rs @@ -5,7 +5,10 @@ use std::{collections::BTreeMap, fmt}; use derive_more::derive::{Display, Error}; use serde::{Deserialize, Deserializer, Serialize}; -use super::{spec_extensions, FromRef, ObjectOrReference, Ref, RefError, RefType, Spec}; +use super::{ + discriminator::Discriminator, spec_extensions, FromRef, ObjectOrReference, Ref, RefError, + RefType, Spec, +}; /// Schema errors. #[derive(Debug, Clone, PartialEq, Display, Error)] @@ -526,6 +529,9 @@ pub struct ObjectSchema { /// See . #[serde(flatten, with = "spec_extensions")] pub extensions: BTreeMap, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub discriminator: Option, } impl ObjectSchema { @@ -628,4 +634,22 @@ mod tests { let schema = serde_yml::from_str::(spec).unwrap(); assert_eq!(schema.example, Some(serde_json::Value::Null)); } + + #[test] + fn discriminator_example_is_parsed_correctly() { + let spec = indoc::indoc! {" + oneOf: + - $ref: '#/components/schemas/Cat' + - $ref: '#/components/schemas/Dog' + - $ref: '#/components/schemas/Lizard' + - $ref: 'https://gigantic-server.com/schemas/Monster/schema.json' + discriminator: + propertyName: petType + mapping: + dog: '#/components/schemas/Dog' + monster: 'https://gigantic-server.com/schemas/Monster/schema.json' + "}; + let schema = serde_yml::from_str::(spec).unwrap(); + assert!(schema.discriminator.is_some()); + } } From 40706b4dec097086f03c4972634e167fbc4918b1 Mon Sep 17 00:00:00 2001 From: Vincent Gerber Date: Sun, 8 Dec 2024 17:56:15 +0100 Subject: [PATCH 2/2] doc: Added discriminator doc string --- crates/oas3/src/spec/schema.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/oas3/src/spec/schema.rs b/crates/oas3/src/spec/schema.rs index fcb31a9..23ba935 100644 --- a/crates/oas3/src/spec/schema.rs +++ b/crates/oas3/src/spec/schema.rs @@ -530,6 +530,9 @@ pub struct ObjectSchema { #[serde(flatten, with = "spec_extensions")] pub extensions: BTreeMap, + /// Discriminator for object selection based on propertyName + /// + /// See #[serde(default, skip_serializing_if = "Option::is_none")] pub discriminator: Option, }