Skip to content
This repository was archived by the owner on Nov 19, 2024. It is now read-only.

Commit 9012a43

Browse files
authored
Feat: Add response headers to OpenApiMeta (#334)
1 parent 9c4eac4 commit 9012a43

File tree

4 files changed

+126
-1
lines changed

4 files changed

+126
-1
lines changed

src/generator/paths.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export const getOpenApiPathsObject = (
9494
) || []),
9595
],
9696
}),
97-
responses: getResponsesObject(outputParser, openapi.example?.response),
97+
responses: getResponsesObject(outputParser, openapi.example?.response, openapi.responseHeaders),
9898
...(openapi.deprecated ? { deprecated: openapi.deprecated } : {}),
9999
},
100100
};

src/generator/schema.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ export const errorResponseObject: OpenAPIV3.ResponseObject = {
189189
export const getResponsesObject = (
190190
schema: unknown,
191191
example: Record<string, any> | undefined,
192+
headers: Record<string, OpenAPIV3.HeaderObject | OpenAPIV3.ReferenceObject> | undefined
192193
): OpenAPIV3.ResponsesObject => {
193194
if (!instanceofZodType(schema)) {
194195
throw new TRPCError({
@@ -199,6 +200,7 @@ export const getResponsesObject = (
199200

200201
const successResponseObject: OpenAPIV3.ResponseObject = {
201202
description: 'Successful response',
203+
headers: headers,
202204
content: {
203205
'application/json': {
204206
schema: zodSchemaToOpenApiSchemaObject(schema),

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export type OpenApiMeta<TMeta = TRPCMeta> = TMeta & {
3131
request?: Record<string, any>;
3232
response?: Record<string, any>;
3333
};
34+
responseHeaders?: Record<string, OpenAPIV3.HeaderObject | OpenAPIV3.ReferenceObject>;
3435
};
3536
};
3637

test/generator.test.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ describe('generator', () => {
559559
},
560560
},
561561
"description": "Successful response",
562+
"headers": undefined,
562563
},
563564
"default": Object {
564565
"$ref": "#/components/responses/error",
@@ -616,6 +617,7 @@ describe('generator', () => {
616617
},
617618
},
618619
"description": "Successful response",
620+
"headers": undefined,
619621
},
620622
"default": Object {
621623
"$ref": "#/components/responses/error",
@@ -652,6 +654,7 @@ describe('generator', () => {
652654
},
653655
},
654656
"description": "Successful response",
657+
"headers": undefined,
655658
},
656659
"default": Object {
657660
"$ref": "#/components/responses/error",
@@ -701,6 +704,7 @@ describe('generator', () => {
701704
},
702705
},
703706
"description": "Successful response",
707+
"headers": undefined,
704708
},
705709
"default": Object {
706710
"$ref": "#/components/responses/error",
@@ -766,6 +770,7 @@ describe('generator', () => {
766770
},
767771
},
768772
"description": "Successful response",
773+
"headers": undefined,
769774
},
770775
"default": Object {
771776
"$ref": "#/components/responses/error",
@@ -945,6 +950,7 @@ describe('generator', () => {
945950
},
946951
},
947952
"description": "Successful response",
953+
"headers": undefined,
948954
},
949955
"default": Object {
950956
"$ref": "#/components/responses/error",
@@ -1001,6 +1007,7 @@ describe('generator', () => {
10011007
},
10021008
},
10031009
"description": "Successful response",
1010+
"headers": undefined,
10041011
},
10051012
"default": Object {
10061013
"$ref": "#/components/responses/error",
@@ -1036,6 +1043,7 @@ describe('generator', () => {
10361043
},
10371044
},
10381045
"description": "Successful response",
1046+
"headers": undefined,
10391047
}
10401048
`);
10411049
}
@@ -1061,6 +1069,7 @@ describe('generator', () => {
10611069
},
10621070
},
10631071
"description": "Successful response",
1072+
"headers": undefined,
10641073
}
10651074
`);
10661075
}
@@ -1092,6 +1101,7 @@ describe('generator', () => {
10921101
},
10931102
},
10941103
"description": "Successful response",
1104+
"headers": undefined,
10951105
}
10961106
`);
10971107
});
@@ -1122,6 +1132,7 @@ describe('generator', () => {
11221132
},
11231133
},
11241134
"description": "Successful response",
1135+
"headers": undefined,
11251136
}
11261137
`);
11271138
});
@@ -1157,6 +1168,7 @@ describe('generator', () => {
11571168
},
11581169
},
11591170
"description": "Successful response",
1171+
"headers": undefined,
11601172
}
11611173
`);
11621174
});
@@ -1186,6 +1198,7 @@ describe('generator', () => {
11861198
},
11871199
},
11881200
"description": "Successful response",
1201+
"headers": undefined,
11891202
}
11901203
`);
11911204
});
@@ -1249,6 +1262,7 @@ describe('generator', () => {
12491262
},
12501263
},
12511264
"description": "Successful response",
1265+
"headers": undefined,
12521266
}
12531267
`);
12541268
expect(openApiDocument.paths['/optional-object']!.get!.parameters).toMatchInlineSnapshot(`
@@ -1293,6 +1307,7 @@ describe('generator', () => {
12931307
},
12941308
},
12951309
"description": "Successful response",
1310+
"headers": undefined,
12961311
}
12971312
`);
12981313
});
@@ -1357,6 +1372,7 @@ describe('generator', () => {
13571372
},
13581373
},
13591374
"description": "Successful response",
1375+
"headers": undefined,
13601376
}
13611377
`);
13621378
expect(openApiDocument.paths['/optional-object']!.post!.requestBody).toMatchInlineSnapshot(`
@@ -1402,6 +1418,7 @@ describe('generator', () => {
14021418
},
14031419
},
14041420
"description": "Successful response",
1421+
"headers": undefined,
14051422
}
14061423
`);
14071424
});
@@ -1445,6 +1462,7 @@ describe('generator', () => {
14451462
},
14461463
},
14471464
"description": "Successful response",
1465+
"headers": undefined,
14481466
}
14491467
`);
14501468
});
@@ -1681,6 +1699,7 @@ describe('generator', () => {
16811699
},
16821700
},
16831701
"description": "Successful response",
1702+
"headers": undefined,
16841703
}
16851704
`);
16861705
});
@@ -1722,6 +1741,7 @@ describe('generator', () => {
17221741
},
17231742
},
17241743
"description": "Successful response",
1744+
"headers": undefined,
17251745
}
17261746
`);
17271747
});
@@ -2075,6 +2095,7 @@ describe('generator', () => {
20752095
},
20762096
},
20772097
"description": "Successful response",
2098+
"headers": undefined,
20782099
}
20792100
`);
20802101
});
@@ -2316,6 +2337,7 @@ describe('generator', () => {
23162337
},
23172338
},
23182339
"description": "Successful response",
2340+
"headers": undefined,
23192341
},
23202342
"default": Object {
23212343
"$ref": "#/components/responses/error",
@@ -2363,6 +2385,7 @@ describe('generator', () => {
23632385
},
23642386
},
23652387
"description": "Successful response",
2388+
"headers": undefined,
23662389
},
23672390
"default": Object {
23682391
"$ref": "#/components/responses/error",
@@ -2410,6 +2433,7 @@ describe('generator', () => {
24102433
},
24112434
},
24122435
"description": "Successful response",
2436+
"headers": undefined,
24132437
},
24142438
"default": Object {
24152439
"$ref": "#/components/responses/error",
@@ -2704,6 +2728,7 @@ describe('generator', () => {
27042728
},
27052729
},
27062730
"description": "Successful response",
2731+
"headers": undefined,
27072732
}
27082733
`);
27092734
expect(openApiDocument.paths['/mutation-example/{name}']!.post!.parameters)
@@ -2769,6 +2794,103 @@ describe('generator', () => {
27692794
},
27702795
},
27712796
"description": "Successful response",
2797+
"headers": undefined,
2798+
}
2799+
`);
2800+
});
2801+
2802+
test('with response headers', () => {
2803+
const appRouter = t.router({
2804+
queryExample: t.procedure
2805+
.meta({
2806+
openapi: {
2807+
method: 'GET',
2808+
path: '/query-example/{name}',
2809+
responseHeaders: {
2810+
"X-RateLimit-Limit": {
2811+
description: "Request limit per hour.",
2812+
schema: {
2813+
type: "integer"
2814+
}
2815+
},
2816+
"X-RateLimit-Remaining": {
2817+
description: "The number of requests left for the time window.",
2818+
schema: {
2819+
type: "integer"
2820+
}
2821+
}
2822+
}
2823+
},
2824+
})
2825+
.input(z.object({ name: z.string(), greeting: z.string() }))
2826+
.output(z.object({ output: z.string() }))
2827+
.query(({ input }) => ({
2828+
output: `${input.greeting} ${input.name}`,
2829+
}))
2830+
});
2831+
2832+
const openApiDocument = generateOpenApiDocument(appRouter, defaultDocOpts);
2833+
2834+
expect(openApiSchemaValidator.validate(openApiDocument).errors).toEqual([]);
2835+
expect(openApiDocument.paths['/query-example/{name}']!.get!.parameters).toMatchInlineSnapshot(`
2836+
Array [
2837+
Object {
2838+
"description": undefined,
2839+
"example": undefined,
2840+
"in": "path",
2841+
"name": "name",
2842+
"required": true,
2843+
"schema": Object {
2844+
"type": "string",
2845+
},
2846+
},
2847+
Object {
2848+
"description": undefined,
2849+
"example": undefined,
2850+
"in": "query",
2851+
"name": "greeting",
2852+
"required": true,
2853+
"schema": Object {
2854+
"type": "string",
2855+
},
2856+
},
2857+
]
2858+
`);
2859+
expect(openApiDocument.paths['/query-example/{name}']!.get!.responses[200])
2860+
.toMatchInlineSnapshot(`
2861+
Object {
2862+
"content": Object {
2863+
"application/json": Object {
2864+
"example": undefined,
2865+
"schema": Object {
2866+
"additionalProperties": false,
2867+
"properties": Object {
2868+
"output": Object {
2869+
"type": "string",
2870+
},
2871+
},
2872+
"required": Array [
2873+
"output",
2874+
],
2875+
"type": "object",
2876+
},
2877+
},
2878+
},
2879+
"description": "Successful response",
2880+
"headers": Object {
2881+
"X-RateLimit-Limit": Object {
2882+
"description": "Request limit per hour.",
2883+
"schema": Object {
2884+
"type": "integer",
2885+
},
2886+
},
2887+
"X-RateLimit-Remaining": Object {
2888+
"description": "The number of requests left for the time window.",
2889+
"schema": Object {
2890+
"type": "integer",
2891+
},
2892+
},
2893+
},
27722894
}
27732895
`);
27742896
});

0 commit comments

Comments
 (0)