diff --git a/Cargo.lock b/Cargo.lock index 3aa0008..54cb2fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 6ce8c6a..dd0aee2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/progenitor-impl/Cargo.toml b/progenitor-impl/Cargo.toml index 906a3e5..a12fd24 100644 --- a/progenitor-impl/Cargo.toml +++ b/progenitor-impl/Cargo.toml @@ -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" diff --git a/progenitor-impl/src/to_schema.rs b/progenitor-impl/src/to_schema.rs index 23a1bf1..81e4b85 100644 --- a/progenitor-impl/src/to_schema.rs +++ b/progenitor-impl/src/to_schema.rs @@ -105,6 +105,7 @@ impl Convert for openapiv3::Schema { read_only, write_only, example, + extensions, } = self.schema_data.clone(); let metadata = schemars::schema::Metadata { @@ -143,6 +144,7 @@ impl Convert 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 for openapiv3::Schema { }, )) .reduce(), + extensions: extensions.into_iter().collect(), ..Default::default() } } @@ -233,6 +236,7 @@ impl Convert for openapiv3::Schema { }, )) .reduce(), + extensions: extensions.into_iter().collect(), ..Default::default() } } @@ -261,6 +265,7 @@ impl Convert for openapiv3::Schema { property_names: None, })) .reduce(), + extensions: extensions.into_iter().collect(), ..Default::default() }, @@ -290,6 +295,7 @@ impl Convert for openapiv3::Schema { contains: None, })) .reduce(), + extensions: extensions.into_iter().collect(), ..Default::default() }, @@ -300,6 +306,7 @@ impl Convert for openapiv3::Schema { schemars::schema::InstanceType::Boolean, nullable, ), + extensions: extensions.into_iter().collect(), ..Default::default() } } @@ -312,6 +319,7 @@ impl Convert for openapiv3::Schema { ..Default::default() }, )), + extensions: extensions.into_iter().collect(), ..Default::default() } } @@ -324,6 +332,7 @@ impl Convert for openapiv3::Schema { ..Default::default() }, )), + extensions: extensions.into_iter().collect(), ..Default::default() } } @@ -336,12 +345,27 @@ impl Convert 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 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 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 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 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 for Option { } } -impl Convert for f64 { +impl Convert for Option { 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 for i64 { +impl Convert for Option { 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> { 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::(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 + ))) + ); + } +} diff --git a/progenitor-impl/tests/output/buildomat.out b/progenitor-impl/tests/output/buildomat.out index 5aec799..d1e799e 100644 --- a/progenitor-impl/tests/output/buildomat.out +++ b/progenitor-impl/tests/output/buildomat.out @@ -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"] diff --git a/sample_openapi/buildomat.json b/sample_openapi/buildomat.json index 2e40580..65a918f 100644 --- a/sample_openapi/buildomat.json +++ b/sample_openapi/buildomat.json @@ -10,7 +10,16 @@ "operationId": "control_hold", "responses": { "200": { - "description": "successful operation" + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "enum": [ + null + ] + } + } + } } } }