Skip to content

Commit 1456f87

Browse files
committed
fix(parser): add meta and version options to input.patch
1 parent 0eb8683 commit 1456f87

File tree

9 files changed

+169
-15
lines changed

9 files changed

+169
-15
lines changed

.changeset/strong-donkeys-give.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hey-api/openapi-ts': patch
3+
---
4+
5+
fix(parser): add `meta` and `version` options to input.patch

packages/openapi-ts-tests/test/openapi-ts.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export default defineConfig(() => {
4242
// pagination: {
4343
// keywords: ['aa'],
4444
// },
45+
// patch: {
46+
// version: () => '3.1.1',
47+
// },
4548
// path: {
4649
// components: {},
4750
// info: {
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
export { parseV2_0_X } from './parser';
22
export type { OpenApiV2_0_X } from './types/spec';
33

4-
import type { SchemaObject } from './types/spec';
4+
import type { InfoObject, SchemaObject } from './types/spec';
55

66
export interface OpenApiV2_0_XTypes {
7+
InfoObject: InfoObject;
78
SchemaObject: SchemaObject;
89
}

packages/openapi-ts/src/openApi/3.0.x/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export { parseV3_0_X } from './parser';
22
export type { OpenApiV3_0_X } from './types/spec';
33

44
import type {
5+
InfoObject,
56
ParameterObject,
67
ReferenceObject,
78
RequestBodyObject,
@@ -10,6 +11,7 @@ import type {
1011
} from './types/spec';
1112

1213
export interface OpenApiV3_0_XTypes {
14+
InfoObject: InfoObject;
1315
ParameterObject: ParameterObject;
1416
ReferenceObject: ReferenceObject;
1517
RequestBodyObject: RequestBodyObject;

packages/openapi-ts/src/openApi/3.1.x/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export { parseV3_1_X } from './parser';
22
export type { OpenApiV3_1_X } from './types/spec';
33

44
import type {
5+
InfoObject,
56
ParameterObject,
67
ReferenceObject,
78
RequestBodyObject,
@@ -10,6 +11,7 @@ import type {
1011
} from './types/spec';
1112

1213
export interface OpenApiV3_1_XTypes {
14+
InfoObject: InfoObject;
1315
ParameterObject: ParameterObject;
1416
ReferenceObject: ReferenceObject;
1517
RequestBodyObject: RequestBodyObject;

packages/openapi-ts/src/openApi/shared/utils/__tests__/patch.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,38 @@ describe('patchOpenApiSpec', () => {
383383
type: 'string',
384384
});
385385
});
386+
387+
it('applies meta patch function', () => {
388+
const metaFn = vi.fn((meta) => {
389+
meta.title = 'Changed Title';
390+
});
391+
const spec: OpenApi.V3_1_X = {
392+
...specMetadataV3,
393+
};
394+
patchOpenApiSpec({
395+
patchOptions: {
396+
meta: metaFn,
397+
},
398+
spec,
399+
});
400+
expect(metaFn).toHaveBeenCalledOnce();
401+
expect(spec.info.title).toBe('Changed Title');
402+
});
403+
404+
it('applies version patch function', () => {
405+
const versionFn = vi.fn((version) => `patched-${version}`);
406+
const spec: OpenApi.V3_1_X = {
407+
...specMetadataV3,
408+
};
409+
patchOpenApiSpec({
410+
patchOptions: {
411+
version: versionFn,
412+
},
413+
spec,
414+
});
415+
expect(versionFn).toHaveBeenCalledOnce();
416+
expect(spec.openapi).toBe('patched-3.1.0');
417+
});
386418
});
387419

388420
describe('OpenAPI v2', () => {
@@ -542,6 +574,38 @@ describe('patchOpenApiSpec', () => {
542574
type: 'string',
543575
});
544576
});
577+
578+
it('applies meta patch function', () => {
579+
const metaFn = vi.fn((meta) => {
580+
meta.title = 'Changed Title';
581+
});
582+
const spec: OpenApi.V2_0_X = {
583+
...specMetadataV2,
584+
};
585+
patchOpenApiSpec({
586+
patchOptions: {
587+
meta: metaFn,
588+
},
589+
spec,
590+
});
591+
expect(metaFn).toHaveBeenCalledOnce();
592+
expect(spec.info.title).toBe('Changed Title');
593+
});
594+
595+
it('applies version patch function', () => {
596+
const versionFn = vi.fn((version) => `patched-${version}`);
597+
const spec: OpenApi.V2_0_X = {
598+
...specMetadataV2,
599+
};
600+
patchOpenApiSpec({
601+
patchOptions: {
602+
version: versionFn,
603+
},
604+
spec,
605+
});
606+
expect(versionFn).toHaveBeenCalledOnce();
607+
expect(spec.swagger).toBe('patched-2.0');
608+
});
545609
});
546610

547611
describe('real-world usage', () => {

packages/openapi-ts/src/openApi/shared/utils/patch.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@ export const patchOpenApiSpec = ({
1515
const spec = _spec as OpenApi.V2_0_X | OpenApi.V3_0_X | OpenApi.V3_1_X;
1616

1717
if ('swagger' in spec) {
18-
if (spec.definitions && patchOptions?.schemas) {
18+
if (patchOptions.version && spec.swagger) {
19+
spec.swagger = patchOptions.version(spec.swagger) as typeof spec.swagger;
20+
}
21+
22+
if (patchOptions.meta && spec.info) {
23+
patchOptions.meta(spec.info);
24+
}
25+
26+
if (patchOptions.schemas && spec.definitions) {
1927
for (const key in patchOptions.schemas) {
2028
const patchFn = patchOptions.schemas[key]!;
2129
const schema = spec.definitions[key];
@@ -27,8 +35,26 @@ export const patchOpenApiSpec = ({
2735
return;
2836
}
2937

38+
if (patchOptions.version && spec.openapi) {
39+
spec.openapi = patchOptions.version(spec.openapi) as typeof spec.openapi;
40+
}
41+
42+
if (patchOptions.meta && spec.info) {
43+
patchOptions.meta(spec.info);
44+
}
45+
3046
if (spec.components) {
31-
if (spec.components.parameters && patchOptions.parameters) {
47+
if (patchOptions.schemas && spec.components.schemas) {
48+
for (const key in patchOptions.schemas) {
49+
const patchFn = patchOptions.schemas[key]!;
50+
const schema = spec.components.schemas[key];
51+
if (schema && typeof schema === 'object') {
52+
patchFn(schema);
53+
}
54+
}
55+
}
56+
57+
if (patchOptions.parameters && spec.components.parameters) {
3258
for (const key in patchOptions.parameters) {
3359
const patchFn = patchOptions.parameters[key]!;
3460
const schema = spec.components.parameters[key];
@@ -38,7 +64,7 @@ export const patchOpenApiSpec = ({
3864
}
3965
}
4066

41-
if (spec.components.requestBodies && patchOptions.requestBodies) {
67+
if (patchOptions.requestBodies && spec.components.requestBodies) {
4268
for (const key in patchOptions.requestBodies) {
4369
const patchFn = patchOptions.requestBodies[key]!;
4470
const schema = spec.components.requestBodies[key];
@@ -48,7 +74,7 @@ export const patchOpenApiSpec = ({
4874
}
4975
}
5076

51-
if (spec.components.responses && patchOptions.responses) {
77+
if (patchOptions.responses && spec.components.responses) {
5278
for (const key in patchOptions.responses) {
5379
const patchFn = patchOptions.responses[key]!;
5480
const schema = spec.components.responses[key];
@@ -57,15 +83,5 @@ export const patchOpenApiSpec = ({
5783
}
5884
}
5985
}
60-
61-
if (spec.components.schemas && patchOptions?.schemas) {
62-
for (const key in patchOptions.schemas) {
63-
const patchFn = patchOptions.schemas[key]!;
64-
const schema = spec.components.schemas[key];
65-
if (schema && typeof schema === 'object') {
66-
patchFn(schema);
67-
}
68-
}
69-
}
7086
}
7187
};

packages/openapi-ts/src/openApi/types.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ export namespace OpenApi {
1010
export type V3_1_X = OpenApiV3_1_X;
1111
}
1212

13+
export namespace OpenApiMetaObject {
14+
export type V2_0_X = OpenApiV2_0_XTypes['InfoObject'];
15+
16+
export type V3_0_X = OpenApiV3_0_XTypes['InfoObject'];
17+
18+
export type V3_1_X = OpenApiV3_1_XTypes['InfoObject'];
19+
}
20+
1321
export namespace OpenApiParameterObject {
1422
export type V3_0_X =
1523
| OpenApiV3_0_XTypes['ParameterObject']

packages/openapi-ts/src/types/input.d.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type {
2+
OpenApiMetaObject,
23
OpenApiParameterObject,
34
OpenApiRequestBodyObject,
45
OpenApiResponseObject,
@@ -246,12 +247,43 @@ export interface Filters {
246247
}
247248

248249
export interface Patch {
250+
/**
251+
* Patch the OpenAPI meta object in place. Useful for modifying general metadata such as title, description, version, or custom fields before further processing.
252+
*
253+
* @param meta The OpenAPI meta object for the current version.
254+
*/
255+
meta?: (
256+
meta:
257+
| OpenApiMetaObject.V2_0_X
258+
| OpenApiMetaObject.V3_0_X
259+
| OpenApiMetaObject.V3_1_X,
260+
) => void;
261+
/**
262+
* Patch OpenAPI parameters in place. The key is the parameter name, and the function receives the parameter object to modify directly.
263+
*
264+
* @example
265+
* parameters: {
266+
* limit: (parameter) => {
267+
* parameter.schema.type = 'integer';
268+
* }
269+
* }
270+
*/
249271
parameters?: Record<
250272
string,
251273
(
252274
parameter: OpenApiParameterObject.V3_0_X | OpenApiParameterObject.V3_1_X,
253275
) => void
254276
>;
277+
/**
278+
* Patch OpenAPI request bodies in place. The key is the request body name, and the function receives the request body object to modify directly.
279+
*
280+
* @example
281+
* requestBodies: {
282+
* CreateUserRequest: (requestBody) => {
283+
* requestBody.required = true;
284+
* }
285+
* }
286+
*/
255287
requestBodies?: Record<
256288
string,
257289
(
@@ -260,6 +292,16 @@ export interface Patch {
260292
| OpenApiRequestBodyObject.V3_1_X,
261293
) => void
262294
>;
295+
/**
296+
* Patch OpenAPI responses in place. The key is the response name, and the function receives the response object to modify directly.
297+
*
298+
* @example
299+
* responses: {
300+
* NotFound: (response) => {
301+
* response.description = 'Resource not found.';
302+
* }
303+
* }
304+
*/
263305
responses?: Record<
264306
string,
265307
(
@@ -303,6 +345,17 @@ export interface Patch {
303345
| OpenApiSchemaObject.V3_1_X,
304346
) => void
305347
>;
348+
/**
349+
* Patch the OpenAPI version string. The function receives the current version and should return the new version string.
350+
* Useful for normalizing or overriding the version value before further processing.
351+
*
352+
* @param version The current OpenAPI version string.
353+
* @returns The new version string to use.
354+
*
355+
* @example
356+
* version: (version) => version.replace(/^v/, '')
357+
*/
358+
version?: (version: string) => string;
306359
}
307360

308361
export interface Watch {

0 commit comments

Comments
 (0)