diff --git a/packages/openapi-generator/src/openapi.ts b/packages/openapi-generator/src/openapi.ts index b1c6df07..a7fe83de 100644 --- a/packages/openapi-generator/src/openapi.ts +++ b/packages/openapi-generator/src/openapi.ts @@ -435,7 +435,17 @@ export function convertRoutesToOpenAPI( .sort((a, b) => a.localeCompare(b)) .reduce( (acc, key) => { - acc[key] = paths[key]!; + const sortedMethods = Object.keys(paths[key]!) + .sort((a, b) => a.localeCompare(b)) + .reduce( + (methodAcc, methodKey) => { + methodAcc[methodKey] = paths[key]![methodKey]!; + return methodAcc; + }, + {} as Record, + ); + + acc[key] = sortedMethods; return acc; }, {} as Record, diff --git a/packages/openapi-generator/test/openapi/base.test.ts b/packages/openapi-generator/test/openapi/base.test.ts index 16767f95..6617df93 100644 --- a/packages/openapi-generator/test/openapi/base.test.ts +++ b/packages/openapi-generator/test/openapi/base.test.ts @@ -851,3 +851,135 @@ testCase('multiple routes', MULTIPLE_ROUTES, { schemas: {}, }, }); + +const MULTIPLE_ROUTES_WITH_METHODS = ` +import * as t from 'io-ts'; +import * as h from '@api-ts/io-ts-http'; + +// Purposefully out of order to test sorting +export const route1 = h.httpRoute({ + path: '/foo', + method: 'POST', + request: h.httpRequest({ + query: { + foo: t.string, + }, + }), + response: { + 200: t.string + }, +}); + +export const route2 = h.httpRoute({ + path: '/foo', + method: 'GET', + request: h.httpRequest({ + query: { + foo: t.string, + }, + }), + response: { + 200: t.string + }, +}); + +export const route3 = h.httpRoute({ + path: '/foo', + method: 'DELETE', + request: h.httpRequest({ + query: { + foo: t.string, + }, + }), + response: { + 200: t.string + }, +}); +`; + +testCase('multiple routes with methods', MULTIPLE_ROUTES_WITH_METHODS, { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + delete: { + parameters: [ + { + in: 'query', + name: 'foo', + required: true, + schema: { + type: 'string', + }, + }, + ], + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'string', + }, + }, + }, + }, + }, + }, + get: { + parameters: [ + { + in: 'query', + name: 'foo', + required: true, + schema: { + type: 'string', + }, + }, + ], + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'string', + }, + }, + }, + }, + }, + }, + post: { + parameters: [ + { + in: 'query', + name: 'foo', + required: true, + schema: { + type: 'string', + }, + }, + ], + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'string', + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, +});