Skip to content

Commit 32dab4d

Browse files
committed
feat: sort routes by path before writing the output JSON
BREAKING CHANGE: Changing output order of routes may affect consumers
1 parent 65ded53 commit 32dab4d

File tree

2 files changed

+147
-2
lines changed

2 files changed

+147
-2
lines changed

packages/openapi-generator/src/openapi.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,11 +431,21 @@ export function convertRoutesToOpenAPI(
431431
{} as Record<string, OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject>,
432432
);
433433

434+
const sortedPaths = Object.keys(paths)
435+
.sort((a, b) => a.localeCompare(b))
436+
.reduce(
437+
(acc, key) => {
438+
acc[key] = paths[key]!;
439+
return acc;
440+
},
441+
{} as Record<string, OpenAPIV3.PathItemObject>,
442+
);
443+
434444
return {
435445
openapi: '3.0.3',
436446
info,
437447
...(servers.length > 0 ? { servers } : {}),
438-
paths,
448+
paths: sortedPaths,
439449
components: {
440450
schemas: openapiSchemas,
441451
},

packages/openapi-generator/test/openapi/base.test.ts

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -714,4 +714,139 @@ testCase('route with array union of null and undefined', ROUTE_WITH_ARRAY_UNION_
714714
components: {
715715
schemas: {}
716716
}
717-
});
717+
});
718+
719+
const MULTIPLE_ROUTES = `
720+
import * as t from 'io-ts';
721+
import * as h from '@api-ts/io-ts-http';
722+
723+
export const route1 = h.httpRoute({
724+
path: '/bar',
725+
method: 'GET',
726+
request: h.httpRequest({
727+
query: {
728+
bar: t.string,
729+
},
730+
}),
731+
response: {
732+
200: t.string
733+
},
734+
});
735+
736+
export const route2 = h.httpRoute({
737+
path: '/baz',
738+
method: 'GET',
739+
request: h.httpRequest({
740+
query: {
741+
baz: t.string,
742+
},
743+
}),
744+
response: {
745+
200: t.string
746+
},
747+
});
748+
749+
export const route3 = h.httpRoute({
750+
path: '/foo',
751+
method: 'GET',
752+
request: h.httpRequest({
753+
query: {
754+
foo: t.string,
755+
},
756+
}),
757+
response: {
758+
200: t.string
759+
},
760+
});
761+
`;
762+
763+
testCase('multiple routes', MULTIPLE_ROUTES, {
764+
openapi: '3.0.3',
765+
info: {
766+
title: 'Test',
767+
version: '1.0.0',
768+
},
769+
paths: {
770+
'/bar': {
771+
get: {
772+
parameters: [
773+
{
774+
in: 'query',
775+
name: 'bar',
776+
required: true,
777+
schema: {
778+
type: 'string',
779+
},
780+
},
781+
],
782+
responses: {
783+
200: {
784+
description: 'OK',
785+
content: {
786+
'application/json': {
787+
schema: {
788+
type: 'string',
789+
},
790+
},
791+
},
792+
},
793+
},
794+
},
795+
},
796+
'/baz': {
797+
get: {
798+
parameters: [
799+
{
800+
in: 'query',
801+
name: 'baz',
802+
required: true,
803+
schema: {
804+
type: 'string',
805+
},
806+
},
807+
],
808+
responses: {
809+
200: {
810+
description: 'OK',
811+
content: {
812+
'application/json': {
813+
schema: {
814+
type: 'string',
815+
},
816+
},
817+
},
818+
},
819+
},
820+
},
821+
},
822+
'/foo': {
823+
get: {
824+
parameters: [
825+
{
826+
in: 'query',
827+
name: 'foo',
828+
required: true,
829+
schema: {
830+
type: 'string',
831+
},
832+
},
833+
],
834+
responses: {
835+
200: {
836+
description: 'OK',
837+
content: {
838+
'application/json': {
839+
schema: {
840+
type: 'string',
841+
},
842+
},
843+
},
844+
},
845+
},
846+
},
847+
},
848+
},
849+
components: {
850+
schemas: {},
851+
},
852+
});

0 commit comments

Comments
 (0)