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

Manually specify Discriminator Mapping #738

Closed
1lutz opened this issue Aug 16, 2023 · 6 comments · Fixed by #752
Closed

Manually specify Discriminator Mapping #738

1lutz opened this issue Aug 16, 2023 · 6 comments · Fixed by #752

Comments

@1lutz
Copy link
Contributor

1lutz commented Aug 16, 2023

I need to define a schema which contains a one of with a discriminator. The discrimantor must have a custom mapping.

As I saw in the docs the discriminator struct does not have a field for the mapping. But when I let utoipa to automatically generate a schema for a struct which has a serde tag, a mapping will be output. So code for mappings must be there.

Can it be made possible to manually specify a mapping?

@jayvdb
Copy link
Contributor

jayvdb commented Sep 11, 2023

Could you explain more what you are wanting? Ideally giving a code example.
https://github.com/juhaku/utoipa/blob/b1ce2d0/utoipa-gen/tests/schema_derive_test.rs#L1138 gives an example of how to build the Rust structs to construct a oneOf.

If you search that file for discriminator you will find about another 8 examples.

@juhaku
Copy link
Owner

juhaku commented Sep 12, 2023

I believe there is a need to declare something like described here https://swagger.io/specification/#discriminator-object as an example. With this we expect each object to have petType field that for Dog matches to dog and monster for external schema of the monster. What comes for Cat and Lizard the petType field must match to the name it is defined in #/components/schemas/....

    MyResponseType:
      oneOf:
      - $ref: '#/components/schemas/Cat'
      - $ref: '#/components/schemas/Dog'
      - $ref: '#/components/schemas/Lizard'
      - $ref: 'https://gigantic-server.com/schemas/Monster/schema.json'
      discriminator:
        propertyName: petType
        mapping:
          dog: '#/components/schemas/Dog'
          monster: 'https://gigantic-server.com/schemas/Monster/schema.json'

The expected goal would be to allow defining this manual mapping as a key value map between custom name and the actual schema by reference or an external reference. Secondly there should be a tests for this that the correct JSON is being output and support for macro syntax to enter the mapping. However that might be more evolved to actually complete because we suppose to not to allow defining mapping in cases where that does not make sense.

@jayvdb
Copy link
Contributor

jayvdb commented Sep 13, 2023

Does utoipa support external schemas? i.e. is this part already not supported?

$ref: 'https://gigantic-server.com/schemas/Monster/schema.json'

I don't see test cases which show this in action. Did I miss some?

Now looking at the other subset

MyResponseType:
      oneOf:
      - $ref: '#/components/schemas/Cat'
      - $ref: '#/components/schemas/Dog'
      discriminator:
        propertyName: petType
        mapping:
          dog: '#/components/schemas/Dog'

What is the benefit here? Does that mean that Dog doesnt need the field petType inside it?

Or does it just mean that petType can be dog instead of Dog?

If it is only that the latter, this could be described in Rust by using serde aliases. c.f. https://serde.rs/variant-attrs.html

i.e.

#[derive(....)]
enum PetType {
    Cat,
    #[serde(alias = "dog")]
    Dog,
}

Then utoipa would pick up those aliases and dynamically create the mapping.

@1lutz
Copy link
Contributor Author

1lutz commented Sep 14, 2023

I am trying to create OneOfs with a discriminator and a mapping using code. I need that something like this is possible:

let mut discriminator = Discriminator::new("type");
discriminator.mapping = [("other".to_string(), "#/components/schemas/OtherSchema".to_string())]
    .into_iter()
    .collect::<BTreeMap<_, _>>();
let one_of = OneOfBuilder::new()
    .item(Ref::from_schema_name("OtherSchema"))
    .discriminator(Some(discriminator))
    .build()

This is used by an implementation of a Modify trait which I am developing. You can see it here: https://github.com/1lutz/geoengine/blob/5e17832833fefc3a0915df4de5ee68bb3c8cee5d/services/src/util/apidoc.rs#L168

@juhaku
Copy link
Owner

juhaku commented Sep 15, 2023

Does utoipa support external schemas? i.e. is this part already not supported?

Not yet fully, only in certain case, there was an external ref support for responses if I remember right.

Or does it just mean that petType can be dog instead of Dog?

Yup.

If it is only that the latter, this could be described in Rust by using serde aliases....

True, that would work for the general cases.

@juhaku
Copy link
Owner

juhaku commented Sep 15, 2023

I am trying to create OneOfs with a discriminator and a mapping using code. I need that something like this is possible:

let mut discriminator = Discriminator::new("type");
discriminator.mapping = [("other".to_string(), "#/components/schemas/OtherSchema".to_string())]
    .into_iter()
    .collect::<BTreeMap<_, _>>();
let one_of = OneOfBuilder::new()
    .item(Ref::from_schema_name("OtherSchema"))
    .discriminator(Some(discriminator))
    .build()

This is used by an implementation of a Modify trait which I am developing. You can see it here: https://github.com/1lutz/geoengine/blob/5e17832833fefc3a0915df4de5ee68bb3c8cee5d/services/src/util/apidoc.rs#L168

Yeah, that is unfortunate at the moment. For the next breaking release the serde tagged enums will be prohibited with inlined schemas. In other words only enum which contains variants having a single unnamed field value will be allowed to be compliant with the OpenAPI spec. There are several open issues regarding this lost between rest of the issues.

But sure we can add the support for the discriminator mapping field as it anyway should be there.

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

Successfully merging a pull request may close this issue.

3 participants