Skip to content

Instantly share code, notes, and snippets.

@mirosval
Created November 28, 2023 14:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mirosval/433308deb22727e3b1102fb58f3a6652 to your computer and use it in GitHub Desktop.
Save mirosval/433308deb22727e3b1102fb58f3a6652 to your computer and use it in GitHub Desktop.
OpenAPI Codegen Bug

There seems to be a bug in how oneOf code gets generated in OpenAPI >6.0.

The type gets generated as an empty struct instead of swagger::OneOf<A, B> as it was in 5.4

generate-540:
docker run --rm \
-v $(PWD):/local openapitools/openapi-generator-cli:v5.4.0 generate \
-i /local/test.yaml \
--generator-name rust-server \
--output /local/openapi \
--global-property models,modelDocs=false,supportingFiles
mv openapi/src/models.rs test-540.rs
rm -rf openapi
generate-710:
docker run --rm \
-v $(PWD):/local openapitools/openapi-generator-cli:v7.1.0 generate \
-i /local/test.yaml \
--generator-name rust-server \
--output /local/openapi \
--global-property models,modelDocs=false,supportingFiles
mv openapi/src/models.rs test-710.rs
rm -rf openapi
generate-latest:
docker run --rm \
-v $(PWD):/local openapitools/openapi-generator-cli:latest generate \
-i /local/test.yaml \
--generator-name rust-server \
--output /local/openapi \
--global-property models,modelDocs=false,supportingFiles
mv openapi/src/models.rs test-latest.rs
rm -rf openapi
#![allow(unused_qualifications)]
use crate::models;
#[cfg(any(feature = "client", feature = "server"))]
use crate::header;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct Test {
#[serde(rename = "something")]
#[serde(skip_serializing_if="Option::is_none")]
pub something: Option<swagger::OneOf2<String,i32>>,
#[serde(rename = "something_else")]
#[serde(skip_serializing_if="Option::is_none")]
pub something_else: Option<String>,
}
impl Test {
pub fn new() -> Test {
Test {
something: None,
something_else: None,
}
}
}
/// Converts the Test value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for Test {
fn to_string(&self) -> String {
let mut params: Vec<String> = vec![];
// Skipping something in query parameter serialization
if let Some(ref something_else) = self.something_else {
params.push("something_else".to_string());
params.push(something_else.to_string());
}
params.join(",").to_string()
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a Test value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for Test {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
#[derive(Default)]
// An intermediate representation of the struct to use for parsing.
struct IntermediateRep {
pub something: Vec<swagger::OneOf2<String,i32>>,
pub something_else: Vec<String>,
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',').into_iter();
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => return std::result::Result::Err("Missing value while parsing Test".to_string())
};
if let Some(key) = key_result {
match key {
"something" => intermediate_rep.something.push(<swagger::OneOf2<String,i32> as std::str::FromStr>::from_str(val).map_err(|x| format!("{}", x))?),
"something_else" => intermediate_rep.something_else.push(<String as std::str::FromStr>::from_str(val).map_err(|x| format!("{}", x))?),
_ => return std::result::Result::Err("Unexpected key while parsing Test".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(Test {
something: intermediate_rep.something.into_iter().next(),
something_else: intermediate_rep.something_else.into_iter().next(),
})
}
}
// Methods for converting between header::IntoHeaderValue<Test> and hyper::header::HeaderValue
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Test>> for hyper::header::HeaderValue {
type Error = String;
fn try_from(hdr_value: header::IntoHeaderValue<Test>) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match hyper::header::HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(
format!("Invalid header value for Test - value: {} is invalid {}",
hdr_value, e))
}
}
}
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Test> {
type Error = String;
fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <Test as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)),
std::result::Result::Err(err) => std::result::Result::Err(
format!("Unable to convert header value '{}' into Test - {}",
value, err))
}
},
std::result::Result::Err(e) => std::result::Result::Err(
format!("Unable to convert header: {:?} to string: {}",
hdr_value, e))
}
}
}
#![allow(unused_qualifications)]
use validator::Validate;
use crate::models;
#[cfg(any(feature = "client", feature = "server"))]
use crate::header;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct Test {
#[serde(rename = "something")]
#[serde(skip_serializing_if="Option::is_none")]
pub something: Option<models::TestSomething>,
#[serde(rename = "something_else")]
#[serde(skip_serializing_if="Option::is_none")]
pub something_else: Option<String>,
}
impl Test {
#[allow(clippy::new_without_default)]
pub fn new() -> Test {
Test {
something: None,
something_else: None,
}
}
}
/// Converts the Test value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for Test {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
// Skipping something in query parameter serialization
self.something_else.as_ref().map(|something_else| {
vec![
"something_else".to_string(),
something_else.to_string(),
].join(",")
}),
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a Test value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for Test {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
pub something: Vec<models::TestSomething>,
pub something_else: Vec<String>,
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => return std::result::Result::Err("Missing value while parsing Test".to_string())
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
#[allow(clippy::redundant_clone)]
"something" => intermediate_rep.something.push(<models::TestSomething as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?),
#[allow(clippy::redundant_clone)]
"something_else" => intermediate_rep.something_else.push(<String as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?),
_ => return std::result::Result::Err("Unexpected key while parsing Test".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(Test {
something: intermediate_rep.something.into_iter().next(),
something_else: intermediate_rep.something_else.into_iter().next(),
})
}
}
// Methods for converting between header::IntoHeaderValue<Test> and hyper::header::HeaderValue
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Test>> for hyper::header::HeaderValue {
type Error = String;
fn try_from(hdr_value: header::IntoHeaderValue<Test>) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match hyper::header::HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(
format!("Invalid header value for Test - value: {} is invalid {}",
hdr_value, e))
}
}
}
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Test> {
type Error = String;
fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <Test as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)),
std::result::Result::Err(err) => std::result::Result::Err(
format!("Unable to convert header value '{}' into Test - {}",
value, err))
}
},
std::result::Result::Err(e) => std::result::Result::Err(
format!("Unable to convert header: {:?} to string: {}",
hdr_value, e))
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct TestSomething {
}
impl TestSomething {
#[allow(clippy::new_without_default)]
pub fn new() -> TestSomething {
TestSomething {
}
}
}
/// Converts the TestSomething value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for TestSomething {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a TestSomething value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for TestSomething {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => return std::result::Result::Err("Missing value while parsing TestSomething".to_string())
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
_ => return std::result::Result::Err("Unexpected key while parsing TestSomething".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(TestSomething {
})
}
}
// Methods for converting between header::IntoHeaderValue<TestSomething> and hyper::header::HeaderValue
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<TestSomething>> for hyper::header::HeaderValue {
type Error = String;
fn try_from(hdr_value: header::IntoHeaderValue<TestSomething>) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match hyper::header::HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(
format!("Invalid header value for TestSomething - value: {} is invalid {}",
hdr_value, e))
}
}
}
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<TestSomething> {
type Error = String;
fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <TestSomething as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)),
std::result::Result::Err(err) => std::result::Result::Err(
format!("Unable to convert header value '{}' into TestSomething - {}",
value, err))
}
},
std::result::Result::Err(e) => std::result::Result::Err(
format!("Unable to convert header: {:?} to string: {}",
hdr_value, e))
}
}
}
#![allow(unused_qualifications)]
use validator::Validate;
use crate::models;
#[cfg(any(feature = "client", feature = "server"))]
use crate::header;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct Test {
#[serde(rename = "something")]
#[serde(skip_serializing_if="Option::is_none")]
pub something: Option<models::TestSomething>,
#[serde(rename = "something_else")]
#[serde(skip_serializing_if="Option::is_none")]
pub something_else: Option<String>,
}
impl Test {
#[allow(clippy::new_without_default)]
pub fn new() -> Test {
Test {
something: None,
something_else: None,
}
}
}
/// Converts the Test value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for Test {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
// Skipping something in query parameter serialization
self.something_else.as_ref().map(|something_else| {
[
"something_else".to_string(),
something_else.to_string(),
].join(",")
}),
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a Test value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for Test {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
pub something: Vec<models::TestSomething>,
pub something_else: Vec<String>,
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => return std::result::Result::Err("Missing value while parsing Test".to_string())
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
#[allow(clippy::redundant_clone)]
"something" => intermediate_rep.something.push(<models::TestSomething as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?),
#[allow(clippy::redundant_clone)]
"something_else" => intermediate_rep.something_else.push(<String as std::str::FromStr>::from_str(val).map_err(|x| x.to_string())?),
_ => return std::result::Result::Err("Unexpected key while parsing Test".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(Test {
something: intermediate_rep.something.into_iter().next(),
something_else: intermediate_rep.something_else.into_iter().next(),
})
}
}
// Methods for converting between header::IntoHeaderValue<Test> and hyper::header::HeaderValue
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<Test>> for hyper::header::HeaderValue {
type Error = String;
fn try_from(hdr_value: header::IntoHeaderValue<Test>) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match hyper::header::HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(
format!("Invalid header value for Test - value: {} is invalid {}",
hdr_value, e))
}
}
}
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<Test> {
type Error = String;
fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <Test as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)),
std::result::Result::Err(err) => std::result::Result::Err(
format!("Unable to convert header value '{}' into Test - {}",
value, err))
}
},
std::result::Result::Err(e) => std::result::Result::Err(
format!("Unable to convert header: {:?} to string: {}",
hdr_value, e))
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize, validator::Validate)]
#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))]
pub struct TestSomething {
}
impl TestSomething {
#[allow(clippy::new_without_default)]
pub fn new() -> TestSomething {
TestSomething {
}
}
}
/// Converts the TestSomething value to the Query Parameters representation (style=form, explode=false)
/// specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde serializer
impl std::string::ToString for TestSomething {
fn to_string(&self) -> String {
let params: Vec<Option<String>> = vec![
];
params.into_iter().flatten().collect::<Vec<_>>().join(",")
}
}
/// Converts Query Parameters representation (style=form, explode=false) to a TestSomething value
/// as specified in https://swagger.io/docs/specification/serialization/
/// Should be implemented in a serde deserializer
impl std::str::FromStr for TestSomething {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
/// An intermediate representation of the struct to use for parsing.
#[derive(Default)]
#[allow(dead_code)]
struct IntermediateRep {
}
let mut intermediate_rep = IntermediateRep::default();
// Parse into intermediate representation
let mut string_iter = s.split(',');
let mut key_result = string_iter.next();
while key_result.is_some() {
let val = match string_iter.next() {
Some(x) => x,
None => return std::result::Result::Err("Missing value while parsing TestSomething".to_string())
};
if let Some(key) = key_result {
#[allow(clippy::match_single_binding)]
match key {
_ => return std::result::Result::Err("Unexpected key while parsing TestSomething".to_string())
}
}
// Get the next key
key_result = string_iter.next();
}
// Use the intermediate representation to return the struct
std::result::Result::Ok(TestSomething {
})
}
}
// Methods for converting between header::IntoHeaderValue<TestSomething> and hyper::header::HeaderValue
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<header::IntoHeaderValue<TestSomething>> for hyper::header::HeaderValue {
type Error = String;
fn try_from(hdr_value: header::IntoHeaderValue<TestSomething>) -> std::result::Result<Self, Self::Error> {
let hdr_value = hdr_value.to_string();
match hyper::header::HeaderValue::from_str(&hdr_value) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(e) => std::result::Result::Err(
format!("Invalid header value for TestSomething - value: {} is invalid {}",
hdr_value, e))
}
}
}
#[cfg(any(feature = "client", feature = "server"))]
impl std::convert::TryFrom<hyper::header::HeaderValue> for header::IntoHeaderValue<TestSomething> {
type Error = String;
fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result<Self, Self::Error> {
match hdr_value.to_str() {
std::result::Result::Ok(value) => {
match <TestSomething as std::str::FromStr>::from_str(value) {
std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)),
std::result::Result::Err(err) => std::result::Result::Err(
format!("Unable to convert header value '{}' into TestSomething - {}",
value, err))
}
},
std::result::Result::Err(e) => std::result::Result::Err(
format!("Unable to convert header: {:?} to string: {}",
hdr_value, e))
}
}
}
openapi: 3.0.3
info:
title: Test
version: "0.2"
paths:
/a:
post:
summary: Test
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Test'
responses:
200:
description: Ok
components:
schemas:
Test:
type: object
properties:
something:
oneOf:
- type: string
- type: integer
something_else:
type: string
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment