From 2ed345ee7ea79eaeea8de99c781df048865a760b Mon Sep 17 00:00:00 2001 From: Mendy Man Date: Tue, 11 Mar 2025 11:01:44 -0400 Subject: [PATCH 1/4] Copy the openapi-codegen after we build all of the other formatters This helps with docker layer cache --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index c366cab..2c0182a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -86,9 +86,6 @@ RUN echo "${BIOME_HASH} biome" > biome.sha256 && \ mv biome /usr/bin/ && \ chmod +x /usr/bin/biome -# openapi-codegen -COPY --from=openapi-codegen-builder /app/target/${RUST_TARGET}/release/openapi-codegen /usr/bin/ - # Ruby COPY --from=rubyfmt-builder /app/target/release/rubyfmt-main /usr/bin/rubyfmt @@ -117,3 +114,6 @@ RUN apk add --no-cache binutils && \ rm -rf /root/.rustup/toolchains/nightly-*/share && \ strip /root/.rustup/toolchains/nightly-*/lib/librustc_driver-*.so && \ apk del binutils + +# openapi-codegen +COPY --from=openapi-codegen-builder /app/target/${RUST_TARGET}/release/openapi-codegen /usr/bin/ From 5fc1d2cf52e7990c0b59ca50926976ab74701bcc Mon Sep 17 00:00:00 2001 From: Mendy Man Date: Tue, 11 Mar 2025 11:02:00 -0400 Subject: [PATCH 2/4] Add op webhooks body schema to generated models --- src/api.rs | 10 ++++++++-- src/main.rs | 29 +++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/api.rs b/src/api.rs index 0ac4feb..4ac242c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -57,8 +57,14 @@ impl Api { .flat_map(Resource::referenced_components) } - pub(crate) fn types(&self, schemas: &mut IndexMap) -> Types { - Types::from_referenced_components(schemas, self.referenced_components()) + pub(crate) fn types( + &self, + schemas: &mut IndexMap, + webhooks: Vec, + ) -> Types { + let mut referenced_components: Vec<&str> = webhooks.iter().map(|s| &**s).collect(); + referenced_components.extend(self.referenced_components()); + Types::from_referenced_components(schemas, referenced_components.into_iter()) } } diff --git a/src/main.rs b/src/main.rs index a338357..5527fd4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -127,11 +127,11 @@ fn analyze_and_generate( path: &Utf8Path, flags: GenerateFlags, ) -> anyhow::Result<()> { + let webhooks = get_webhooks(&spec); let mut components = spec.components.unwrap_or_default(); - if let Some(paths) = spec.paths { let api = Api::new(paths, &components.schemas, flags.include_hidden).unwrap(); - let types = api.types(&mut components.schemas); + let types = api.types(&mut components.schemas, webhooks); if flags.debug { let mut api_file = BufWriter::new(File::create("api.ron")?); @@ -161,3 +161,28 @@ fn write_codegen_metadata(input_sha256sum: String, output_dir: &Utf8Path) -> any std::fs::write(metadata_path, &encoded_metadata)?; Ok(()) } + +fn get_webhooks(spec: &OpenApi) -> Vec { + let empty_obj = serde_json::json!({}); + let empty_obj = empty_obj.as_object().unwrap(); + let mut referenced_components = std::collections::BTreeSet::::new(); + if let Some(webhooks) = spec.extensions.get("x-webhooks") { + for req in webhooks.as_object().unwrap_or(empty_obj).values() { + for method in req.as_object().unwrap_or(empty_obj).values() { + if let Some(schema_ref) = method + .get("requestBody") + .and_then(|v| v.get("content")) + .and_then(|v| v.get("application/json")) + .and_then(|v| v.get("schema")) + .and_then(|v| v.get("$ref")) + .and_then(|v| v.as_str()) + { + if let Some(schema_name) = schema_ref.split('/').next_back() { + referenced_components.insert(schema_name.to_string()); + } + } + } + } + } + referenced_components.into_iter().collect::>() +} From 7bace9c3279ef40908b12929793f9ba3a9865a82 Mon Sep 17 00:00:00 2001 From: Mendy Man Date: Tue, 11 Mar 2025 11:03:55 -0400 Subject: [PATCH 3/4] Add workaround for `BackgroundTaskFinishedEvent2` --- src/types.rs | 75 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/src/types.rs b/src/types.rs index 877474b..c8e5557 100644 --- a/src/types.rs +++ b/src/types.rs @@ -614,10 +614,8 @@ impl FieldType { Self::List(field_type) | Self::Set(field_type) => { format!("List<{}>", field_type.to_csharp_typename()).into() } - Self::SchemaRef(name) => name.clone().into(), - Self::StringConst(_) => { - unreachable!("FieldType::const should never be exposed to template code") - } + Self::SchemaRef(name) => filter_schema_ref(name, "Object"), + Self::StringConst(_) => "string".into(), } } @@ -636,10 +634,8 @@ impl FieldType { Self::List(field_type) | Self::Set(field_type) => { format!("[]{}", field_type.to_go_typename()).into() } - Self::SchemaRef(name) => name.clone().into(), - Self::StringConst(_) => { - unreachable!("FieldType::const should never be exposed to template code") - } + Self::SchemaRef(name) => filter_schema_ref(name, "map[string]any"), + Self::StringConst(_) => "string".into(), } } @@ -659,10 +655,8 @@ impl FieldType { Self::JsonObject => "Map".into(), Self::List(field_type) => format!("List<{}>", field_type.to_kotlin_typename()).into(), Self::Set(field_type) => format!("Set<{}>", field_type.to_kotlin_typename()).into(), - Self::SchemaRef(name) => name.clone().into(), - Self::StringConst(_) => { - unreachable!("FieldType::const should never be exposed to template code") - } + Self::SchemaRef(name) => filter_schema_ref(name, "Map"), + Self::StringConst(_) => "String".into(), } } @@ -681,10 +675,8 @@ impl FieldType { Self::Map { value_ty } => { format!("{{ [key: string]: {} }}", value_ty.to_js_typename()).into() } - Self::SchemaRef(name) => name.clone().into(), - Self::StringConst(_) => { - unreachable!("FieldType::const should never be exposed to template code") - } + Self::SchemaRef(name) => filter_schema_ref(name, "any"), + Self::StringConst(_) => "string".into(), } } @@ -710,14 +702,23 @@ impl FieldType { value_ty.to_rust_typename(), ) .into(), - Self::SchemaRef(name) => name.clone().into(), - Self::StringConst(_) => unreachable!("FieldType::const should never be exposed to template code"), + Self::SchemaRef(name) => filter_schema_ref(name, "serde_json::Value"), + Self::StringConst(_) => "String".into() } } pub(crate) fn referenced_schema(&self) -> Option<&str> { match self { - Self::SchemaRef(v) => Some(v), + Self::SchemaRef(v) => { + // TODO: the `BackgroundTaskFinishedEvent2` struct has a field with type of `Data` + // this corresponds to a `#[serde(untagged)]` enum `svix_server::v1::endpoints::background_tasks::Data` + // we should change this server side, but for now I am changing it here + if v == "Data" { + None + } else { + Some(v) + } + } Self::List(ty) | Self::Set(ty) | Self::Map { value_ty: ty } => ty.referenced_schema(), _ => None, } @@ -729,7 +730,7 @@ impl FieldType { Self::Int16 | Self::UInt16 | Self::Int32 | Self::Int64 | Self::UInt64 => "int".into(), Self::String => "str".into(), Self::DateTime => "datetime".into(), - Self::SchemaRef(name) => name.clone().into(), + Self::SchemaRef(name) => filter_schema_ref(name, "t.Dict[str, t.Any]"), Self::Uri => "str".into(), Self::JsonObject => "t.Dict[str, t.Any]".into(), Self::Set(field_type) | Self::List(field_type) => { @@ -738,9 +739,7 @@ impl FieldType { Self::Map { value_ty } => { format!("t.Dict[str, {}]", value_ty.to_python_typename()).into() } - Self::StringConst(_) => { - unreachable!("FieldType::const should never be exposed to template code") - } + Self::StringConst(_) => "str".into(), } } @@ -762,10 +761,9 @@ impl FieldType { FieldType::Map { value_ty } => { format!("Map", value_ty.to_java_typename()).into() } - FieldType::SchemaRef(name) => name.clone().into(), - FieldType::StringConst(_) => { - unreachable!("FieldType::const should never be exposed to template code") - } + FieldType::SchemaRef(name) => filter_schema_ref(name, "Object"), + // backwards compat + FieldType::StringConst(_) => "TypeEnum".into(), } } @@ -853,6 +851,10 @@ impl minijinja::value::Object for FieldType { ensure_no_args(args, "is_json_object")?; Ok(matches!(**self, Self::JsonObject).into()) } + "is_string_const" => { + ensure_no_args(args, "is_string_const")?; + Ok(matches!(**self, Self::StringConst(_)).into()) + } // Returns the inner type of a list or set "inner_type" => { @@ -878,6 +880,14 @@ impl minijinja::value::Object for FieldType { }; Ok(ty.into()) } + "string_const_val" => { + ensure_no_args(args, "string_const_val")?; + let val = match &**self { + Self::StringConst(val) => Some(minijinja::Value::from_safe_string(val.clone())), + _ => None, + }; + Ok(val.into()) + } _ => Err(minijinja::Error::from(minijinja::ErrorKind::UnknownMethod)), } } @@ -901,3 +911,14 @@ impl serde::Serialize for FieldType { minijinja::Value::from_object(self.clone()).serialize(serializer) } } + +fn filter_schema_ref<'a>(name: &'a String, json_obj_typename: &'a str) -> Cow<'a, str> { + // TODO: the `BackgroundTaskFinishedEvent2` struct has a field with type of `Data` + // this corresponds to a `#[serde(untagged)]` enum `svix_server::v1::endpoints::background_tasks::Data` + // we should change this server side, but for now I am changing it here + if name == "Data" { + json_obj_typename.into() + } else { + name.clone().into() + } +} From 6532e23e61500d1b46fede653369d08d376a0969 Mon Sep 17 00:00:00 2001 From: Mendy Man Date: Tue, 11 Mar 2025 11:18:31 -0400 Subject: [PATCH 4/4] Add issue number --- src/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.rs b/src/types.rs index c8e5557..ce8f184 100644 --- a/src/types.rs +++ b/src/types.rs @@ -710,7 +710,7 @@ impl FieldType { pub(crate) fn referenced_schema(&self) -> Option<&str> { match self { Self::SchemaRef(v) => { - // TODO: the `BackgroundTaskFinishedEvent2` struct has a field with type of `Data` + // TODO(10055): the `BackgroundTaskFinishedEvent2` struct has a field with type of `Data` // this corresponds to a `#[serde(untagged)]` enum `svix_server::v1::endpoints::background_tasks::Data` // we should change this server side, but for now I am changing it here if v == "Data" { @@ -913,7 +913,7 @@ impl serde::Serialize for FieldType { } fn filter_schema_ref<'a>(name: &'a String, json_obj_typename: &'a str) -> Cow<'a, str> { - // TODO: the `BackgroundTaskFinishedEvent2` struct has a field with type of `Data` + // TODO(10055): the `BackgroundTaskFinishedEvent2` struct has a field with type of `Data` // this corresponds to a `#[serde(untagged)]` enum `svix_server::v1::endpoints::background_tasks::Data` // we should change this server side, but for now I am changing it here if name == "Data" {