Handle operations whose response is a `null`

This commit is contained in:
Adam H. Leventhal 2021-11-09 23:27:07 -08:00
parent a3fbbc06ba
commit 75b224c047
6 changed files with 171 additions and 57 deletions

40
Cargo.lock generated
View File

@ -101,12 +101,6 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]]
name = "dtoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "dyn-clone"
version = "1.0.4"
@ -410,12 +404,6 @@ version = "0.2.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "log"
version = "0.4.14"
@ -525,14 +513,13 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
[[package]]
name = "openapiv3"
version = "1.0.0-beta.2"
version = "1.0.0-beta.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791f5f66415ca5d11fb29e8dcf0b923adff5b2c9bd1cf54621777494c8f8f688"
checksum = "d45518fa48878a21efa793483d3c5a3dd5f8f98026fc3dade65104d8b78bb535"
dependencies = [
"indexmap",
"serde",
"serde_json",
"serde_yaml",
]
[[package]]
@ -944,18 +931,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
dependencies = [
"dtoa",
"indexmap",
"serde",
"yaml-rust",
]
[[package]]
name = "slab"
version = "0.4.5"
@ -1366,11 +1341,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[patch.unused]]
name = "typify"
version = "0.0.4"

View File

@ -15,5 +15,5 @@ default-members = [
"progenitor-macro",
]
#[patch."https://github.com/oxidecomputer/typify"]
#typify = { path = "../typify/typify" }
[patch."https://github.com/oxidecomputer/typify"]
typify = { path = "../typify/typify" }

View File

@ -10,7 +10,7 @@ description = "An OpenAPI client generator - core implementation"
anyhow = "1.0"
getopts = "0.2"
indexmap = "1.7"
openapiv3 = "1.0.0-beta.2"
openapiv3 = "1.0.0-beta.5"
proc-macro2 = "1.0"
quote = "1.0"
regex = "1.5"

View File

@ -105,6 +105,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
read_only,
write_only,
example,
extensions,
} = self.schema_data.clone();
let metadata = schemars::schema::Metadata {
@ -143,6 +144,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
pattern: pattern.clone(),
}))
.reduce(),
extensions: extensions.into_iter().collect(),
..Default::default()
},
openapiv3::SchemaKind::Type(openapiv3::Type::Number(
@ -186,6 +188,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
},
))
.reduce(),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
@ -233,6 +236,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
},
))
.reduce(),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
@ -261,6 +265,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
property_names: None,
}))
.reduce(),
extensions: extensions.into_iter().collect(),
..Default::default()
},
@ -290,6 +295,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
contains: None,
}))
.reduce(),
extensions: extensions.into_iter().collect(),
..Default::default()
},
@ -300,6 +306,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
schemars::schema::InstanceType::Boolean,
nullable,
),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
@ -312,6 +319,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
..Default::default()
},
)),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
@ -324,6 +332,7 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
..Default::default()
},
)),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
@ -336,12 +345,27 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
..Default::default()
},
)),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
openapiv3::SchemaKind::Not { not } => {
schemars::schema::SchemaObject {
subschemas: Some(Box::new(
schemars::schema::SubschemaValidation {
not: Some(Box::new(not.convert())),
..Default::default()
},
)),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
// This is the permissive schema that allows anything to match.
openapiv3::SchemaKind::Any(AnySchema {
typ: None,
pattern: None,
multiple_of: None,
exclusive_minimum: None,
@ -358,12 +382,73 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
max_items: None,
unique_items: None,
format: None,
}) if properties.is_empty() && required.is_empty() => {
schemars::schema::Schema::Bool(true).into_object()
enumeration,
min_length: None,
max_length: None,
one_of,
all_of,
any_of,
not: None,
}) if properties.is_empty()
&& required.is_empty()
&& enumeration.is_empty()
&& one_of.is_empty()
&& all_of.is_empty()
&& any_of.is_empty() =>
{
let mut schema =
schemars::schema::Schema::Bool(true).into_object();
schema.extensions = extensions.into_iter().collect();
schema
}
// Malformed object.
// A simple null value.
openapiv3::SchemaKind::Any(AnySchema {
typ: None,
pattern: None,
multiple_of: None,
exclusive_minimum: None,
exclusive_maximum: None,
minimum: None,
maximum: None,
properties,
required,
additional_properties: None,
min_properties: None,
max_properties: None,
items: None,
min_items: None,
max_items: None,
unique_items: None,
format: None,
enumeration,
min_length: None,
max_length: None,
one_of,
all_of,
any_of,
not: None,
}) if properties.is_empty()
&& required.is_empty()
&& enumeration.len() == 1
&& enumeration[0] == serde_json::Value::Null
&& one_of.is_empty()
&& all_of.is_empty()
&& any_of.is_empty() =>
{
schemars::schema::SchemaObject {
metadata,
instance_type: Some(
schemars::schema::InstanceType::Null.into(),
),
extensions: extensions.into_iter().collect(),
..Default::default()
}
}
// Malformed object with 'type' not set.
openapiv3::SchemaKind::Any(AnySchema {
typ: None,
pattern: None,
multiple_of: None,
exclusive_minimum: None,
@ -380,7 +465,18 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
max_items: None,
unique_items: None,
format: None,
}) => {
enumeration,
min_length: None,
max_length: None,
one_of,
all_of,
any_of,
not: None,
}) if enumeration.is_empty()
&& one_of.is_empty()
&& all_of.is_empty()
&& any_of.is_empty() =>
{
let object = openapiv3::Schema {
schema_data: self.schema_data.clone(),
schema_kind: openapiv3::SchemaKind::Type(
@ -397,8 +493,9 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
object.convert().into()
}
// Malformed array.
// Malformed array with 'type' not set.
openapiv3::SchemaKind::Any(AnySchema {
typ: None,
pattern: None,
multiple_of: None,
exclusive_minimum: None,
@ -415,7 +512,20 @@ impl Convert<schemars::schema::Schema> for openapiv3::Schema {
max_items,
unique_items,
format: None,
}) if properties.is_empty() && required.is_empty() => {
enumeration,
min_length: None,
max_length: None,
one_of,
all_of,
any_of,
not: None,
}) if properties.is_empty()
&& required.is_empty()
&& enumeration.is_empty()
&& one_of.is_empty()
&& all_of.is_empty()
&& any_of.is_empty() =>
{
let array = openapiv3::Schema {
schema_data: self.schema_data.clone(),
schema_kind: openapiv3::SchemaKind::Type(
@ -493,14 +603,22 @@ impl Convert<Value> for Option<String> {
}
}
impl Convert<Value> for f64 {
impl Convert<Value> for Option<f64> {
fn convert(&self) -> Value {
Value::Number(serde_json::Number::from_f64(*self).unwrap())
match self {
Some(value) => {
Value::Number(serde_json::Number::from_f64(*value).unwrap())
}
None => Value::Null,
}
}
}
impl Convert<Value> for i64 {
impl Convert<Value> for Option<i64> {
fn convert(&self) -> Value {
Value::Number(serde_json::Number::from(*self))
match self {
Some(value) => Value::Number(serde_json::Number::from(*value)),
None => Value::Null,
}
}
}
@ -509,14 +627,9 @@ fn instance_type(
nullable: bool,
) -> Option<schemars::schema::SingleOrVec<schemars::schema::InstanceType>> {
if nullable {
Some(schemars::schema::SingleOrVec::Vec(vec![
instance_type,
schemars::schema::InstanceType::Null,
]))
Some(vec![instance_type, schemars::schema::InstanceType::Null].into())
} else {
Some(schemars::schema::SingleOrVec::Single(Box::new(
instance_type,
)))
Some(instance_type.into())
}
}
@ -591,3 +704,25 @@ where
}
}
}
#[cfg(test)]
mod tests {
use serde_json::json;
use crate::to_schema::Convert;
#[test]
fn test_null() {
let schema_value = json!({ "enum": [null] });
let oa_schema =
serde_json::from_value::<openapiv3::Schema>(schema_value).unwrap();
let schema = oa_schema.convert();
assert_eq!(
schema.into_object().instance_type,
Some(schemars::schema::SingleOrVec::Single(Box::new(
schemars::schema::InstanceType::Null
)))
);
}
}

View File

@ -187,7 +187,7 @@ impl Client {
let request = self.client.post(url).build()?;
let result = self.client.execute(request).await;
let res = result?.error_for_status()?;
Ok(())
Ok(res.json().await?)
}
#[doc = "control_resume: POST /v1/control/resume"]

View File

@ -10,7 +10,16 @@
"operationId": "control_hold",
"responses": {
"200": {
"description": "successful operation"
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"enum": [
null
]
}
}
}
}
}
}