Skip to content

fix: [MTS-3020] derive content types from OpenAPI 3.0 requestBody/response content - #30

Merged
granojaakko merged 1 commit into
masterfrom
fix/MTS-3020-openapi3-request-response-content-types
Jun 17, 2026
Merged

fix: [MTS-3020] derive content types from OpenAPI 3.0 requestBody/response content#30
granojaakko merged 1 commit into
masterfrom
fix/MTS-3020-openapi3-request-response-content-types

Conversation

@granojaakko

Copy link
Copy Markdown

Problem

Clients generated from OpenAPI 3.0 specs send application/json for every endpoint, even ones the spec declares as XML (or any other media type).

Concretely: the cXML PunchOut Order Request tool in ecom-admin posts to POST /v2/cxml/punchout, which the spec declares as application/xml. The generated client instead sent Content-Type: application/json with the cXML JSON.stringify'd into a string. ecom-api's global express.json() parser then tried to JSON.parse the XML and returned a 400 (Unexpected token '"', ""<?xml ver"... is not valid JSON) before any cXML handler ran.

Root cause

OpenAPI 3.0 carries request/response media types under requestBody.content and responses[code].content. The 3.0 support (added in "feat: add openapi 3.0 support") read those maps only to extract the JSON schema, and never set op.contentTypes / op.accepts. The only code paths that populate those are the Swagger 2.0 consumes / produces fields — absent in 3.0 — so every operation fell through to the application/json default in spec.ts.

Fix

  • src/spec/operations.ts: populate contentTypes from requestBody.content media types, and accepts from the success (2xx) response content media types.
  • src/gen/js/genOperations.ts: emit accepts only when it's not the application/json default, so existing JSON clients regenerate byte-identically (no churn).

Impact / blast radius

Consumers: ecom-admin, ecom-storefront, ecom-order-synchronizer (all on the same pin). On regeneration:

  • JSON endpoints (the vast majority — 64 request / 166 response bodies in the current ecom-api spec): byte-identical output.
  • XML endpoints (cXML: 2 request, 3 response bodies): corrected to application/xml — this is the fix.
  • multipart/form-data (uploadEditorTemplate, 1 endpoint): flips to multipart/form-data. It is not called by application code in any of the three consumers (dead generated stub), so no runtime impact.

Testing

  • Added an OpenAPI 3.0 fixture (test/openapi3.yml) and 4 cases covering XML request/response derivation, a JSON control, and independent request/response types. Verified they fail against the unpatched parser and pass with the fix.
  • Verified end-to-end by npm link-ing this branch into ecom-admin and running npm run apigen:file: punchOutOperation now generates contentTypes: ['application/xml'], accepts: ['application/xml']; JSON ops unchanged.

Rollout

After merge, bump the openapi-client pin in ecom-admin, ecom-storefront, and ecom-order-synchronizer, regenerate their clients, and deploy. Worth a glance at each regen diff before deploying.

OpenAPI 3.0 encodes media types under requestBody.content and
responses[].content. The 3.0 support read those only to pull out schemas,
never propagating them to the operation's contentTypes/accepts. With no
Swagger 2.0 consumes/produces present, every 3.0 operation fell back to
the spec default of application/json.

For XML (or any non-JSON) endpoints this generated a client that sent the
wrong Content-Type. A cXML PunchOut endpoint declared as application/xml
in the spec was generated with contentTypes ['application/json'], so the
client JSON-stringified the XML body and sent Content-Type:
application/json — which the server's JSON body parser rejected with a
400 before any handler ran.

- spec/operations.ts: set contentTypes from requestBody.content media
  types; derive accepts from the success-response content media types.
- gen/js/genOperations.ts: emit `accepts` only when non-JSON, so existing
  JSON clients regenerate byte-identically.
- test: add an OpenAPI 3.0 fixture + cases covering request/response
  content-type derivation (the suite previously only covered Swagger 2.0).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes OpenAPI 3.0 media type handling by deriving per-operation request contentTypes (from requestBody.content) and response accepts (from 2xx responses[code].content), preventing clients from defaulting everything to application/json when the spec declares XML (or other) media types.

Changes:

  • Populate op.contentTypes from OpenAPI 3.0 requestBody.content keys and op.accepts from 2xx response content keys.
  • Adjust JS operation codegen to omit emitting accepts when it’s the default ['application/json'] (avoids churn for existing JSON-only clients).
  • Add an OpenAPI 3.0 fixture and tests covering XML, JSON control, and mixed request/response media types.

Reviewed changes

Copilot reviewed 4 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
test/spec/operations.spec.ts Adds assertions verifying OpenAPI 3.0 request/response media type derivation.
test/openapi3.yml New OpenAPI 3.0 fixture exercising XML/JSON/mixed content negotiation.
src/spec/operations.ts Derives contentTypes/accepts from OpenAPI 3.0 content maps (requestBody + 2xx responses).
src/gen/js/genOperations.ts Avoids emitting accepts when it is only application/json to keep output stable.
dist/spec/operations.js Built artifact reflecting the updated OpenAPI 3.0 parsing behavior.
dist/gen/js/genOperations.js Built artifact reflecting the updated codegen emission rules for accepts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@granojaakko
granojaakko requested a review from a team June 17, 2026 12:52
@granojaakko
granojaakko merged commit c6942ea into master Jun 17, 2026
1 check passed
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.

3 participants