Skip to content

fix: Correct handling of default parameter values in referenced component schema #1746

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

Merged
merged 5 commits into from
Jul 17, 2024

Conversation

phk422
Copy link
Contributor

@phk422 phk422 commented Jul 5, 2024

fix: Correct handling of default parameter values in referenced component schema

Changes

Fix #1741

How to Review

Tests added; tests should pass

Checklist

  • Unit tests updated
  • docs/ updated (if necessary)
  • pnpm run update:examples run (only applicable for openapi-typescript)

@phk422 phk422 requested a review from a team as a code owner July 5, 2024 06:48
Copy link

changeset-bot bot commented Jul 5, 2024

🦋 Changeset detected

Latest commit: dd618c6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
openapi-typescript Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@phk422
Copy link
Contributor Author

phk422 commented Jul 5, 2024

Ah, I usually use eslint + prettier for code styling, and I haven't used biome to enforce code style. There seems to be a bug with the biome plugin on my VSCode, causing multiple failures in code style checks.😭

@phk422 phk422 changed the title fix: Correct handling of default parameter values in referenced compo… fix: Correct handling of default parameter values in referenced component schema Jul 5, 2024
@duncanbeevers
Copy link
Contributor

duncanbeevers commented Jul 5, 2024

I think this change is insufficient since components/schemas may still be referenced within request bodies.

Unless a field is explicitly marked required it should not need to be present in the request body; in such an instance the default value is only serving as a hint that a value may be supplied by the server.

Consider this simple schema where a single component schema is used as both the request body and as the response.

  • a buyer must specify a shirt color
  • a buyer may specify a shirt quantity

Since only a single type definition will be created for ShirtPurchase, quantity must remain an optional property, even though it will be present in the response body.

paths:
  /purchase:
    post:
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              $ref: '#/components/schemas/ShirtPurchase'
      responses:
        '200':
          description: OK
          content:
            'application/json':
              schema:
                $ref: '#/components/schemas/ShirtPurchase'
components:
  schemas:
    ShirtPurchase:
      type: object
      properties:
        color:
          type: string
          enum:
            - blue
            - yellow
        quantity:
          type: number
          minimum: 1
          default: 1
      required:
        - color

@drwpow
Copy link
Contributor

drwpow commented Jul 5, 2024

Ah, I usually use eslint + prettier for code styling, and I haven't used biome to enforce code style. There seems to be a bug with the biome plugin on my VSCode, causing multiple failures in code style checks.😭

Ah sorry—yeah that was kind of a silent change. But I’ve been blown away with the bugfixes Biome had in lots of ESLint packages, and the speed is hard to beat. Plus the team is really collaborative and I’m finding contributing to Biome easier than ESLint! But formatting-wise I’m neutral on Prettier vs Biome for formatting; that was more an experiment.

the .vscode/settings.json should be responsible for setting formatters, and .vscode/extensions.json should prompt you to install the correct extensions, but LMK if that’s not working as expected. I also added format commands to update formatting easily.

As I type all this out, this is probably all good info that I should put into the CONTRIBUTING.md guide 🙂

@phk422
Copy link
Contributor Author

phk422 commented Jul 6, 2024

Ah, I usually use eslint + prettier for code styling, and I haven't used biome to enforce code style. There seems to be a bug with the biome plugin on my VSCode, causing multiple failures in code style checks.😭

Ah sorry—yeah that was kind of a silent change. But I’ve been blown away with the bugfixes Biome had in lots of ESLint packages, and the speed is hard to beat. Plus the team is really collaborative and I’m finding contributing to Biome easier than ESLint! But formatting-wise I’m neutral on Prettier vs Biome for formatting; that was more an experiment.

the .vscode/settings.json should be responsible for setting formatters, and .vscode/extensions.json should prompt you to install the correct extensions, but LMK if that’s not working as expected. I also added format commands to update formatting easily.

As I type all this out, this is probably all good info that I should put into the CONTRIBUTING.md guide 🙂

Alright, I think I should learn more about Biome too!😊

@phk422
Copy link
Contributor Author

phk422 commented Jul 6, 2024

I think this change is insufficient since components/schemas may still be referenced within request bodies.

Unless a field is explicitly marked required it should not need to be present in the request body; in such an instance the default value is only serving as a hint that a value may be supplied by the server.

Consider this simple schema where a single component schema is used as both the request body and as the response.

  • a buyer must specify a shirt color
  • a buyer may specify a shirt quantity

Since only a single type definition will be created for ShirtPurchase, quantity must remain an optional property, even though it will be present in the response body.

paths:
  /purchase:
    post:
      requestBody:
        required: true
        content:
          'application/json':
            schema:
              $ref: '#/components/schemas/ShirtPurchase'
      responses:
        '200':
          description: OK
          content:
            'application/json':
              schema:
                $ref: '#/components/schemas/ShirtPurchase'
components:
  schemas:
    ShirtPurchase:
      type: object
      properties:
        color:
          type: string
          enum:
            - blue
            - yellow
        quantity:
          type: number
          minimum: 1
          default: 1
      required:
        - color

Oh! It seems I did overlook this situation. I'm curious, what approach do you have for handling this kind of situation? @duncanbeevers @drwpow

@duncanbeevers
Copy link
Contributor

what approach do you have for handling this kind of situation?

My suggestion is to not use the presence of default to determine whether a property should be optional, and only rely on a property's presence in required.

@drwpow
Copy link
Contributor

drwpow commented Jul 8, 2024

I'm curious, what approach do you have for handling this kind of situation? @duncanbeevers @drwpow

+1 to @duncanbeevers’ comment. For fixing this, I usually start by creating a failing test (TDD), and the one they supplied seems sufficient!

From there, tracing/resolving nodes in 7.x is one of the major internal reasons for rewriting! In previous versions it was much harder to peer into nodes. But in the current version, ctx.resolve($ref) lets you look up any $ref to see its contents.

It does add a little bit of work (especially considering nested $refs), but unless I’m misunderstanding you should only need that top-level object so I don’t think it should be complicated (i.e. you don’t have to deeply-traverse all trees from the requestBody; just peer into that top-level $ref).

@phk422
Copy link
Contributor Author

phk422 commented Jul 9, 2024

I noticed that @duncanbeevers' proposed approach conflicts with #1681, so maybe we should consider again here.@drwpow

@duncanbeevers
Copy link
Contributor

I noticed that @duncanbeevers' proposed approach conflicts with #1681, so maybe we should consider again here.

That doesn't seem like a conflict. My proposal is to make these fields optional, which seems to align with what is happening in that PR as well. I'm happy to stand corrected if I'm misunderstanding.

@phk422
Copy link
Contributor Author

phk422 commented Jul 11, 2024

what approach do you have for handling this kind of situation?

My suggestion is to not use the presence of default to determine whether a property should be optional, and only rely on a property's presence in required.

I discovered that this default behavior can be controlled through defaultNonNullable. By setting it to false, the scenario mentioned in this PR can be accommodated. It seems designed this way to meet the needs of different users. Or do we need to handle the requestBody separately?If I am misunderstanding, please correct me.

@duncanbeevers
Copy link
Contributor

@phk422 Thanks for calling out that defaultNonNullable option; I wasn't familiar with it.
Setting defaultNonNullable: false cleared up all the problems in our codebase around request bodies with default values.

I think that makes this PR the correct approach since it detects these default definitions in request bodies, and needn't concern itself with component schemas.

@phk422
Copy link
Contributor Author

phk422 commented Jul 16, 2024

Above, I think this pr can be merged,@drwpow, what do you think?

@drwpow
Copy link
Contributor

drwpow commented Jul 16, 2024

Above, I think this pr can be merged,@drwpow, what do you think?

I think so! If you could add a patch changeset please (see Changeset comment above) I’d be happy to approve and merge! Thank you @phk422 and @duncanbeevers for discussing and digging into this further!

@drwpow drwpow merged commit e705909 into openapi-ts:main Jul 17, 2024
7 checks passed
@github-actions github-actions bot mentioned this pull request Jul 17, 2024
@phk422 phk422 deleted the fix-requestBodies branch July 18, 2024 03:18
kerwanp pushed a commit to kerwanp/openapi-typescript that referenced this pull request Jul 29, 2024
…nent schema (openapi-ts#1746)

* fix: Correct handling of default parameter values in referenced component schema

* chore: lint code

* chore: lint code

* Create dull-birds-pump.md
@Nikoms
Copy link

Nikoms commented Aug 20, 2024

Hello, I still have the same issue with this OpenAPi spec: https://docs-be.here.com/bundle/tour-planning-api-api-reference/page/api.yaml (using openapi-typescript 7.3.0)

The field priority(look for the one with the description Specifies the priority of the job with 1 for high priority jobs and 2 for normal jobs.) has a default value, but, in TS, the value is required:

        priority:
          maximum: 2
          minimum: 1
          type: integer
          description: |
            Specifies the priority of the job with 1 for high priority jobs and 2 for normal jobs.
          example: 2
          default: 2

@duncanbeevers
Copy link
Contributor

duncanbeevers commented Aug 20, 2024

@Nikoms When I run the types-generator on this OpenAPI document i see the expected behavior.

  • Running with --default-non-nullable results in a non-optional priority: number
  • Running with no flags results in an optional priority?: number
$ npx openapi-typescript ./api.yaml | grep 'priority[?:]'
      priority?: number;
      priority?: number;
$ npx openapi-typescript ./api.yaml --default-non-nullable | grep 'priority[?:]'
      priority: number;
      priority: number;

@Nikoms
Copy link

Nikoms commented Aug 21, 2024

Ok then, I must have a problem with my computer or something, because I have this:

npx openapi-typescript https://docs-be.here.com/bundle/tour-planning-api-api-reference/page/api.yaml | grep 'priority[?:]'
            priority: number;
            priority: number;

Note: I also tried with npx openapi-typescript@latest https://docs-be.here.com/bundle/tour-planning-api-api-reference/page/api.yaml | grep 'priority[?:]' just to make sure I'm on the last version

I'm on macos, using node 20.16.0 (also not working on node 22), npx 10.8.1

@philios33
Copy link

I can confirm this is still a problem.

npx openapi-typescript https://docs-be.here.com/bundle/tour-planning-api-api-reference/page/api.yaml | grep 'priority[?:]'
            priority: number;
            priority: number;
npx openapi-typescript https://docs-be.here.com/bundle/tour-planning-api-api-reference/page/api.yaml --default-non-nullable | grep 'priority[?:]'
            priority: number;
            priority: number;
npx openapi-typescript https://docs-be.here.com/bundle/tour-planning-api-api-reference/page/api.yaml --default-non-nullable false | grep 'priority[?:]'
            priority?: number;
            priority?: number;

My guess is that the option (which should default to false) somehow gets overridden somewhere to be true once a $ref is followed, and then explicitly setting the flag to be false in the command seems to force it to remain false properly.

@philios33
Copy link

This option is now defaulting to true in v7, sorry I didn't realise that. I'm guessing @duncanbeevers is on v6.

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 this pull request may close these issues.

bug: request body fields with default values are treated as required
5 participants