Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] [Rust-Axum] Unable to use oneOf generated structures #20101

Open
5 of 6 tasks
Victoria-Casasampere-BeeTheData opened this issue Nov 14, 2024 · 3 comments · May be fixed by #20336
Open
5 of 6 tasks

[BUG] [Rust-Axum] Unable to use oneOf generated structures #20101

Victoria-Casasampere-BeeTheData opened this issue Nov 14, 2024 · 3 comments · May be fixed by #20336

Comments

@Victoria-Casasampere-BeeTheData
Copy link
Contributor

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)? // Dead URL
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

The structure generated by models using oneOf cannot be easily or efficiently converted to the specific structure type.

Using the provided declaration, with the current implementation of oneOf and anyOf handling, generates a Message struct with a private field of the RawValue type, with 2 additional structures named Hello and Goodbye that have usable fields and traits. The generated endpoint Default/foo gets Message provided as one of its arguments, but this type is mostly useless. There is no enumeration or information available within the structure to facilitate obtaining the adequate model programmatically, with no guarantee that further modifications to the model (ones that add or remove fields to the list of variants) will have explicit handling within the user code. The only way to currently use the Message structure semi-reasonably is done by attempting to deserialize Hello and Goodbye from Message and seeing which one does not fail, or deserializing Value from Message, manually checking the tagged field, and generating the adequate type from there. Both of these options complicate the error handling and the use of the values, as they have separate types and cannot be assigned to the same variable.

openapi-generator version

7.10.0-SNAPSHOT

OpenAPI declaration file content or url

https://gist.github.com/Victoria-Casasampere-BeeTheData/e6c856d1dad01e64e21fa0f7701759d4

Generation Details

openapi-generator-cli generate -g rust-axum -o outoutrs -i base.yaml

Steps to reproduce

Use the rust-axum generator with the provided declaration or any declaration using oneOf as a model content.

Related issues/PRs

Possibly #15, but most likely none.

Suggest a fix

I would replace the current implementation with one that uses enum serde representations instead.

Untagged would be used when discriminator is not defined:

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum Message {
    Hello(Hello),
    Goodbye(Goodbye),
}

#[derive(Serialize, Deserialize)]
pub struct Hello {
    pub op: String,
    pub welcome_message: String,
}

#[derive(Serialize, Deserialize)]
pub struct Goodbye {
    pub op: String,
    pub goodbye_message: String,
}

Internally tagged enums are used when discriminator.propertyName is defined:

#[derive(Serialize, Deserialize)]
#[serde(tag = "op")]
pub enum Message {
    // ` mapping`s would lead to aliases
    #[serde(alias = "Hello", alias = "Greetings")]
    Hello(Hello),
    #[serde(alias = "Goodbye")]
    Goodbye(Goodbye),
}

// Note that tagged enums do not deserialize the tag to a field. If it were to be necessary, a possible
// solution to this would be to generate a method with the tag field name that returns the value.
// eg. `fn op() -> &'static str { "Hello" }`
#[derive(Serialize, Deserialize)]
pub struct Hello {
    pub welcome_message: String,
}

#[derive(Serialize, Deserialize)]
pub struct Goodbye {
    pub goodbye_message: String,
}

This solution could also be reused to support inline enums in the future, and oneOf enum mappings from OpenAPI 3.1 (see this stack overflow)

@wing328
Copy link
Member

wing328 commented Nov 17, 2024

if i'm not mistaken, the rust client generator supports oneOf.

can you please give it a try to see if you like the implementation?

@Victoria-Casasampere-BeeTheData
Copy link
Contributor Author

The rust generator does create useful models, very similar to my suggested solution. It however does not do content validation and type matching (integers are always i32), so getting it ported will not be simple, but should be doable.

@Victoria-Casasampere-BeeTheData
Copy link
Contributor Author

I've been attempting to implement the updated model with adequate oneOf support and noticed that the models generated by the rust client are wrong. Hello and Goodbye in the example declaration generate with the op field, but this makes the structure unuseable, as serde doesn't serialize the field specified in the tag on the structures, and will always lead to a missing field "op", line: 0, column: 0 error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants