-
-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator?
- 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
When using the openapi-yaml generator to produce a merged (flattened) OpenAPI specification with all $ref references resolved, the HTTP operations (methods) under each path are reordered alphabetically instead of preserving the original declaration order from the source YAML file.
For example, if a path defines operations in the order GET, POST, PUT, PATCH, DELETE, the merged output reorders them to DELETE, GET, PATCH, POST, PUT. This silently breaks the intended ordering despite the source OpenAPI files being defined correctly.
We maintain internal API documentation conventions that prescribe a specific HTTP method order (e.g., CRUD: GET -> POST -> PUT -> PATCH -> DELETE). The merged spec is used as the source of truth for downstream API consumers and documentation tools (e.g., Swagger). Because the generator reorders operations, consumers see them in an unexpected sequence, leading to confusion and inconsistency between the authored spec and the rendered documentation.
openapi-generator version
Reproduced with v7.14.0. The issue likely affects all versions of the openapi-yaml generator that do not set skipSortingOperations.
OpenAPI declaration file content or url
Input spec (sample-api-v1.yaml):
openapi: 3.0.3
info:
title: Sample API
version: 1.0.0
tags:
- name: orders
description: Order management operations
paths:
/orders:
get:
tags:
- orders
summary: List all orders
operationId: listOrders
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Order'
post:
tags:
- orders
summary: Create a new order
operationId: createOrder
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
responses:
'201':
description: order created
/orders/{orderId}:
get:
tags:
- orders
summary: Get order by ID
operationId: getOrderById
parameters:
- name: orderId
in: path
required: true
schema:
type: string
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
put:
tags:
- orders
summary: Update an order
operationId: updateOrder
parameters:
- name: orderId
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
responses:
'200':
description: order updated
patch:
tags:
- orders
summary: Partially update an order
operationId: patchOrder
parameters:
- name: orderId
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Order'
responses:
'200':
description: order patched
delete:
tags:
- orders
summary: Delete an order
operationId: deleteOrder
parameters:
- name: orderId
in: path
required: true
schema:
type: string
responses:
'204':
description: order deleted
components:
schemas:
Order:
type: object
required:
- id
properties:
id:
type: string
description:
type: string
status:
type: string
enum:
- pending
- completed
- cancelledGeneration Details
java -jar openapi-generator-cli.jar generate \
-i sample-api-v1.yaml \
-g openapi-yaml \
-o ./outputOr via Gradle plugin:
tasks.register<GenerateTask>("generateMergedSpec") {
generatorName.set("openapi-yaml")
inputSpec.set("$projectDir/api/sample-api-v1.yaml")
outputDir.set("${layout.buildDirectory.get().asFile}/merged-spec")
configOptions.put("outputFile", "sample-api-v1.yaml")
}Steps to reproduce
- Save the YAML spec above as
sample-api-v1.yaml. - Generate the merged spec using the
openapi-yamlgenerator (via CLI or Gradle plugin). - Open the generated output file (e.g.
build/merged-spec/sample-api-v1.yamlfor the Gradle example, oroutput/openapi/openapi.yamlfor the CLI). - Inspect the HTTP methods under
/orders/{orderId}.
Expected: Operations preserve the source file order:
/orders/{orderId}:
get:
...
put:
...
patch:
...
delete:
...Actual: Operations are reordered alphabetically by HTTP method:
/orders/{orderId}:
delete:
...
get:
...
patch:
...
put:
...This causes documentation tools to display operations in an unintuitive order (DELETE before GET), creating confusion for API consumers who see a different ordering than what was intentionally defined in the source spec.
Related issues/PRs
- [python-fastapi] Fix: Skip sorting of path operations (#22163) #22166 This issue was fixed for
python-fastapiby enablingskipSortingOperations, but the same problem exists in theopenapi-yamlgenerator. - [REQ] Organise generated HTML doc by the yaml order, not alphabetically #12950 Open feature request for similar behavior in the HTML generator.
- [GO] Go Server: preserve order of the routes as defined in the OpenAPI file #19550 Related fix for another generator addressing path operation ordering.
Suggest a fix
The openapi-yaml generator (OpenAPIYamlGenerator) should override getSkipSortingOperations() to return true by default, similar to the fix applied in #22163 for python-fastapi.
The openapi-yaml generator is primarily used to resolve $ref references and produce a flat, self-contained YAML file. Sorting operations is counterproductive in this use case, as the purpose is to faithfully represent the original spec with resolved references, not to rearrange it.
Alternatively, a global skipSortingOperations config option could be exposed so users of any generator can opt out of operation sorting.