-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathoperation-inference.ts
More file actions
110 lines (105 loc) · 3.61 KB
/
operation-inference.ts
File metadata and controls
110 lines (105 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import type { RequestMethod } from '../operation.js';
import type { Flatten, IsOptional } from '../type-utils.js';
// Takes an OpenAPI paths specification and converts it into our internal operation index format,
// removing any undefined endpoints and flattening the paths into a single type
/**
* @description Converts an OpenAPI paths specification into our internal operation index format
* - REST operations are combined with their parent path e.g. 'GET /path/a'
* - Parameters are kept, or defaulted to an empty object if not present
* - Response and Request objects are reformatted with generics
*
* Only application/json request and response bodies are included
*
* @example
* ```ts
* type A = InferOperationIndex<{
* '/path/a': {
* put: {
* parameters: { a: string };
* requestBody: { content: { 'application/json': { b: string } } };
* };
* };
* }>;
* // A is {
* // 'PUT /path/a': RequiredRequestBody<{
* // a: string;
* // },
* // unknown,
* // {
* // b: string;
* // }>
* // }
*/
export type InferOperationIndex<PathsSpec> = Flatten<{
[PathStr in keyof PathsSpec & string]: PathOperationIndex<PathStr, PathsSpec[PathStr]>;
}>;
/**
* @description Formats the OpenAPI parameters, responses, and responseBody specs at a given path
* in preparation for flattening the paths in to a single index type
*
* @see InferOperationIndex for more information
*/
type PathOperationIndex<Path extends string, PathSpec> = {
[K in keyof PathSpec as PathKey<K, Path>]: PathSpec[K] extends {
parameters?: infer Params;
responses?: infer Responses;
}
? {
parameters: Params & ParamsRequestBody<PathSpec[K]>;
response: ResponseUnion<Responses>;
}
: never;
};
/**
* @description Combines the REST operation method with the endpoint path to form a unique key
* @example
* ```ts
* type A = PathKey<'get', '/path/a'>
* // A is 'GET /path/a'
*/
type PathKey<K, Path extends string> =
K extends Lowercase<RequestMethod> ? `${Uppercase<K>} ${Path}` : never;
/**
* @description Converts OpenAPI response specifications into a union of possible responses
* Only responses with a 'application/json' content type are included
*
* @example
* ```ts
* type A = Response<{
* 200: { content: { 'application/json': { a: string } } };
* 204: { content: { 'application/json': {} } };
* }>;
* // A is { status: 200, body: { a: string } } | { status: 204, body: {} }
* ```
*/
type ResponseUnion<ResponsesSpec> = {
[Status in keyof ResponsesSpec]: {
status: Status;
body: ResponsesSpec[Status] extends { content: { 'application/json': infer ResponseBody } }
? ResponseBody
: never;
};
}[keyof ResponsesSpec];
/**
* @description Extracts the request body from an OpenAPI specification
* - Keeps the optional or required nature of the request body from the original spec
* - Only includes 'application/json' content type request bodies
* - Returns a type with no body property if the original spec has no request body property (e.g. GET requests)
*
* @example
* ```ts
* type A = ParamsRequestBody<{
* requestBody: { content: { 'application/json': { a: string } } };
* }>;
* // A is { body: { a: string } }
* ```
*/
type ParamsRequestBody<PathSpec> = PathSpec extends {
requestBody?: { content: { 'application/json': infer RequestBody } };
}
? unknown extends RequestBody
? unknown
: IsOptional<PathSpec, 'requestBody'> extends true
? Partial<{ body: RequestBody }>
: { body: RequestBody }
: never;