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

fix #13150 Do not add schema / class name mapping where custom mapping exists #13815

Conversation

bernie-schelberg-mywave
Copy link
Contributor

Having multiple names for a model mapping can cause issues with clients that are expecting a particular types to always have the same model name. Refer #13150.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package 
    ./bin/generate-samples.sh
    ./bin/utils/export_docs_generators.sh
    
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    For Windows users, please run the script in Git BASH.
  • File the PR against the correct branch: master (6.1.0) (minor release - breaking changes with fallbacks), 7.0.x (breaking changes without fallbacks)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

@bernie-schelberg-mywave
Copy link
Contributor Author

@cachescrubber (2022/02) @welshm (2022/02) @MelleD (2022/02) @atextor (2022/02) @manedev79 (2022/02) @javisst (2022/02) @borsch (2022/02) @banlevente (2022/02) @Zomzog (2022/09)

Optional.ofNullable(vendorExtensions)
.map(ve -> ve.get("x-discriminator-value"))
.map(discriminatorValue -> (String) discriminatorValue)
.orElse(currentSchemaName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is changing - since this value previously would have been null

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, prior to this change currentSchemaName was added as a mapping name on line 3365, and then another mapping was added on line 3371 if an x-discriminator-value was set. With this change, it's one or the other, not both.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this change is causing the issues you're seeing with Dart. I'd have to look more closely at how the schemas are being used later on, but this adds less schemas now; so perhaps some filterins logic is applied at some point?

I would try reverting this part of the change and see what the Dart output looks like.

@welshm
Copy link
Contributor

welshm commented Nov 22, 2022

Is this still required after #13958 ?

@bernie-schelberg-mywave
Copy link
Contributor Author

Is this still required after #13958 ?

They seem unrelated to me. The other is about adding additional annotations to the model class / interface. This one is about having singular mapping names for oneOf subclasses.

@wing328
Copy link
Member

wing328 commented Nov 25, 2022

cc @OpenAPITools/generator-core-team as the change impacts default codegen.

@jorgerod
Copy link
Contributor

Hello @bernie-schelberg-mywave @wing328

What is the status of this issue?
IMHO this is a bug and should be fixed as soon as possible. We have been blocked by this issue since 5.3.1 and I think this problem will happen to many teams.

@bernie-schelberg-mywave
Copy link
Contributor Author

@jorgerod as far as I know, it is planned to merge this one into the version 7 branch. I don't really have any update on a timeline though, sorry.

@jorgerod
Copy link
Contributor

Hi @bernie-schelberg-mywave

I have done a serialization test with your changes. The JsonSubTypes are no longer duplicated.
I used the spec from this PR and the test is as follows.

  @Test
  void testSerialization() throws JsonProcessingException {
    final ObjectMapper objectMapper = new ObjectMapper();

    final String foo = objectMapper.writeValueAsString(new FooDTO());
    assertThat(foo).contains("{\"type\":\"Foo\"}");

  }

The output is:

java.lang.AssertionError: 
Expecting actual:
  "{"type":"Foo"}"
to contain:
  "{"type":"foo"}" 

I'm not a Jackson expert, but it seems that in the serialization Jackson is using the value of @JsonTypeName instead of the value defined in the mapping.

What do you think?

@bernie-schelberg-mywave
Copy link
Contributor Author

Hi @jorgerod, the error message in your comment doesn't match your test. In your test you say that you expect foo to contain {"type":"Foo"}, but the assertion error states that it's expecting {"type":"foo"}. The actual value in the assertion error matches the assertion that you have in the test. Can you check and clarify what it is that you're expecting?

Also, what is FooDTO? It's not specified in the schema that is part of this PR. Have you defined it separately?

@troehling
Copy link

Hi @bernie-schelberg-mywave, I have experienced a similar issue as @jorgerod. Regarding your question of the FooDTO: In my case I have configured the suffix dto as modelNameSuffix in my generator. This leads to @JsonTypeName("Foo") being generated, which is used by Jackson for the serialization overriding the value configured in @JsonSubTypes.

@jorgerod
Copy link
Contributor

jorgerod commented Feb 16, 2023

Hi @bernie-schelberg-mywave

I have done a serialization test with your changes. The JsonSubTypes are no longer duplicated. I used the spec from this PR and the test is as follows.

  @Test
  void testSerialization() throws JsonProcessingException {
    final ObjectMapper objectMapper = new ObjectMapper();

    final String foo = objectMapper.writeValueAsString(new FooDTO());
    assertThat(foo).contains("{\"type\":\"Foo\"}");

  }

The output is:

java.lang.AssertionError: 
Expecting actual:
  "{"type":"Foo"}"
to contain:
  "{"type":"foo"}" 

I'm not a Jackson expert, but it seems that in the serialization Jackson is using the value of @JsonTypeName instead of the value defined in the mapping.

What do you think?

Hi @bernie-schelberg-mywave
In the example, in the hurry, there is a typo.

spec

openapi: '3.0.0'
info:
  version: '1.0.0'
  title: 'FooService'
paths:
  /parent:
    put:
      summary: put parent
      operationId: putParent
      parameters:
        - name: name
          in: path
          required: true
          description: Name of the account being updated.
          schema:
            type: string
      requestBody:
        description: The updated account definition to save.
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Parent'
      responses:
        '200':
          $ref: '#/components/responses/Parent'
components:
  schemas:
    Parent:
      type: object
      description: Defines an account by name.
      properties:
        name:
          type: string
          description: The account name.
        type:
          type: string
          description: The account type discriminator.
      required:
        - name
        - type
      discriminator:
        propertyName: type
        mapping:
          foo: '#/components/schemas/Foo'

    Foo:
      allOf:
        - $ref: "#/components/schemas/Parent"
        - type: object
          properties:
            fooType:
              type: string
  responses:
    Parent:
      description: The saved account definition.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Parent'

Test:

  @Test
  void testSerialization() throws JsonProcessingException {
    final ObjectMapper objectMapper = new ObjectMapper();

    final String foo = objectMapper.writeValueAsString(new FooDTO());
    assertThat(foo).contains("{\"type\":\"foo\"}");

  }

Output:

java.lang.AssertionError: 
Expecting actual:
  "{"type":"foo"}"
to contain:
  "{"type":"Foo"}" 

As @troehling says, Jackson use @JsonTypeName("Foo") for serialization.

I'm testing and I think that modifying pojo.mustache for the discriminator case with mapping will solve it.

Currently there is this:

{{#jackson}}
{{#isClassnameSanitized}}
@JsonTypeName("{{name}}")
{{/isClassnameSanitized}}
{{/jackson}}

It would be to put this:

{{#jackson}}
{{#isClassnameSanitized}}
    {{^hasDiscriminatorWithNonEmptyMapping}}
@JsonTypeName("{{name}}")
    {{/hasDiscriminatorWithNonEmptyMapping}}
{{/isClassnameSanitized}}
{{/jackson}}

If there is a discriminator with mapping, don't add @JsonTypeName

I also think this bugfix should go in a separate PR from this one as they are different problems.

How do you see it @bernie-schelberg-mywave @troehling @wing328 @welshm ?

EDIT: With the change I mention, this issue would be solved.
Thank you

@bernie-schelberg-mywave
Copy link
Contributor Author

@jorgerod I think I understand now. I think it may be related, but a different issue to this one. I think the issue you describe is present in the master branch, is it not?

@welshm
Copy link
Contributor

welshm commented Feb 16, 2023

Sorry this fell off my radar for a bit! Can you regenerate the samples to include with the PR? Or did no samples change? This looks fine to me from a SpringCodegen point of view - but the changes are in the default codegen so may need more reviewers - although @wing328 may be able to take a look as well

@welshm
Copy link
Contributor

welshm commented Feb 16, 2023

@jorgerod I agree with your suggestion that the fix should be in a separate PR - it will also be quicker to merge and simpler to review as it will just be touching a Spring codegen mustache.

@bernie-schelberg-mywave
Copy link
Contributor Author

@welshm I merged master again, and re-generated samples. There were no changes.

@welshm
Copy link
Contributor

welshm commented Feb 17, 2023

@welshm I merged master again, and re-generated samples. There were no changes.

Might want to add some samples then that show this behavior being used. Not required though.

@bernie-schelberg-mywave
Copy link
Contributor Author

@welshm I modified one of the examples to have a discriminator mapping, renaming className to type in the process. That changed more than was really necessary, but className didn't seem right when the class name was no longer being used as the discriminator.
Generated all samples and pushed the changes. I noticed that the cs generator seems not to use the discriminator mapping values, but that's a separate issue I think.

@jorgerod
Copy link
Contributor

Hi @bernie-schelberg-mywave @welshm
I have created this PR

Copy link
Contributor

@welshm welshm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for updating the samples (and modifying them to use excercise the code change!)

Minor suggestion on parameter naming, but otherwise I think this is good.

If you are not on the Slack, I would recommend joining - Link

You will need to get someone from the default codegen maintainers to review still

properties:
className:
type:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I would not use type here since it's the name of another property of the YAML - maybe animalType ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, it's confusing. I've changed it to species.

@@ -21,6 +21,8 @@
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.annotation.JsonValue;
import org.openapitools.client.model.Animal;
import org.openapitools.client.model.Cat;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems weird to import itself, but separate problem

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's weird. I've seen that some subclasses have the @JsonSubTypes annotation as well (here for example), which I don't think is necessary (not because of this change, the master branch shows this behaviour as well, but it wasn't in the samples before because the source schema didn't use discriminator values). Because of this annotation, every subclass refers to every other subclass. This may be the reason for the unnecessary import here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed it's something we can deal with later.

properties:
className:
type:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same nit - would probably call this animalType or animalSpecies

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to species

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i've cherry-pick some commits and created #14984 instead.

i didn't rename className to something else

# Conflicts:
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java
@bernie-schelberg-mywave
Copy link
Contributor Author

@OpenAPITools/generator-core-team please review.

@wing328 , @welshm , I'm not sure I'm using the right handle, how do I cc / request review from the core team?

@wing328 wing328 changed the base branch from master to 7.0.x March 16, 2023 13:59
@wing328
Copy link
Member

wing328 commented Mar 16, 2023

I've updated the target branch to 7.0.x as I think that's what this change is based on.

@wing328 wing328 added this to the 7.0.0 milestone Mar 16, 2023
@wing328 wing328 changed the base branch from 7.0.x to master March 17, 2023 12:12
@wing328 wing328 changed the base branch from master to 7.0.x March 17, 2023 12:14
@wing328
Copy link
Member

wing328 commented Mar 20, 2023

closed via #14984 instead

@wing328 wing328 closed this Mar 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants