tangled
alpha
login
or
join now
mokkenstorm.dev
/
openapi-ts
0
fork
atom
fork of hey-api/openapi-ts because I need some additional things
0
fork
atom
overview
issues
pulls
pipelines
chore: remove unused config option
Lubos
1 year ago
255c4280
22b113db
+113
-134
5 changed files
expand all
collapse all
unified
split
packages
openapi-ts
src
ir
schema.ts
plugins
fastify
config.ts
plugin.ts
types.d.ts
test
sample.cjs
+13
-15
packages/openapi-ts/src/ir/schema.ts
···
80
80
type: 'object',
81
81
};
82
82
83
83
-
if (parameters) {
84
84
-
const properties: Record<string, IRSchemaObject> = {};
85
85
-
const required: Array<string> = [];
83
83
+
const properties: Record<string, IRSchemaObject> = {};
84
84
+
const required: Array<string> = [];
86
85
87
87
-
for (const name in parameters) {
88
88
-
const parameter = parameters[name];
86
86
+
for (const name in parameters) {
87
87
+
const parameter = parameters[name];
89
88
90
90
-
properties[name] = deduplicateSchema({
91
91
-
schema: parameter.schema,
92
92
-
});
89
89
+
properties[name] = deduplicateSchema({
90
90
+
schema: parameter.schema,
91
91
+
});
93
92
94
94
-
if (parameter.required) {
95
95
-
required.push(name);
96
96
-
}
93
93
+
if (parameter.required) {
94
94
+
required.push(name);
97
95
}
96
96
+
}
98
97
99
99
-
irSchema.properties = properties;
98
98
+
irSchema.properties = properties;
100
99
101
101
-
if (required.length) {
102
102
-
irSchema.required = required;
103
103
-
}
100
100
+
if (required.length) {
101
101
+
irSchema.required = required;
104
102
}
105
103
106
104
return irSchema;
-1
packages/openapi-ts/src/plugins/fastify/config.ts
···
7
7
_handler: handler,
8
8
_handlerLegacy: () => {},
9
9
name: 'fastify',
10
10
-
operationId: true,
11
10
output: 'fastify',
12
11
};
13
12
+97
-110
packages/openapi-ts/src/plugins/fastify/plugin.ts
···
1
1
-
import ts from 'typescript';
2
2
-
3
1
import { compiler, type Property } from '../../compiler';
4
2
import type { IRContext } from '../../ir/context';
5
3
import type {
6
4
IROperationObject,
7
7
-
IRParameterObject,
8
5
IRPathItemObject,
9
6
IRPathsObject,
10
7
} from '../../ir/ir';
11
8
import { irParametersToIrSchema } from '../../ir/schema';
12
9
import type { PluginHandler } from '../types';
13
13
-
import {
14
14
-
componentsToType,
15
15
-
schemaToType,
16
16
-
type SchemaToTypeOptions,
17
17
-
} from '../utils/types';
10
10
+
import { componentsToType, schemaToType } from '../utils/types';
18
11
import type { Config } from './types';
19
12
20
13
const fastifyId = 'fastify';
21
21
-
const ROUTE_HANDLER_NAME = 'RouteHandler';
22
22
-
const OPERATIONS_IDENTIFIER = 'RouteHandlers';
23
23
-
const ROUTE_PROPERTY_NAME = {
24
24
-
BODY: 'Body',
25
25
-
HEADER: 'Headers',
26
26
-
PATH: 'Params',
27
27
-
QUERY: 'Querystring',
28
28
-
RESPONSE: 'Reply',
29
29
-
};
30
30
-
const NUMERIC_CODE_REGEX = /\b[0-9]{3}\b/;
31
14
32
32
-
const parameterToProperty = ({
33
33
-
options,
34
34
-
parameter,
35
35
-
name,
36
36
-
}: {
37
37
-
name: string;
38
38
-
options: SchemaToTypeOptions;
39
39
-
parameter: Record<string, IRParameterObject>;
40
40
-
}): Property => {
41
41
-
const schema = irParametersToIrSchema({
42
42
-
parameters: parameter,
43
43
-
});
44
44
-
return {
45
45
-
isRequired: !!schema.required,
46
46
-
name,
47
47
-
type: schemaToType({ options, schema }),
48
48
-
};
49
49
-
};
50
50
-
51
51
-
const operationToProperty = ({
15
15
+
const operationToRouteHandler = ({
16
16
+
context,
52
17
operation,
53
53
-
options,
54
18
}: {
19
19
+
context: IRContext;
55
20
operation: IROperationObject;
56
56
-
options: SchemaToTypeOptions;
57
21
}): Property => {
58
58
-
const operationProperties: Array<Property> = [];
22
22
+
const file = context.file({ id: fastifyId })!;
23
23
+
24
24
+
const properties: Array<Property> = [];
59
25
60
26
if (operation.body) {
61
61
-
operationProperties.push({
27
27
+
properties.push({
62
28
isRequired: operation.body.required,
63
63
-
name: ROUTE_PROPERTY_NAME.BODY,
29
29
+
name: 'Body',
64
30
type: schemaToType({
65
65
-
options,
31
31
+
options: { file },
66
32
schema: operation.body.schema,
67
33
}),
68
34
});
···
70
36
71
37
if (operation.parameters) {
72
38
if (operation.parameters.header) {
73
73
-
operationProperties.push(
74
74
-
parameterToProperty({
75
75
-
name: ROUTE_PROPERTY_NAME.HEADER,
76
76
-
options,
77
77
-
parameter: operation.parameters.header,
39
39
+
const schema = irParametersToIrSchema({
40
40
+
parameters: operation.parameters.header,
41
41
+
});
42
42
+
properties.push({
43
43
+
isRequired: Boolean(schema.required),
44
44
+
name: 'Headers',
45
45
+
type: schemaToType({
46
46
+
options: { file },
47
47
+
schema,
78
48
}),
79
79
-
);
49
49
+
});
80
50
}
81
51
82
82
-
if (operation.parameters.query) {
83
83
-
operationProperties.push(
84
84
-
parameterToProperty({
85
85
-
name: ROUTE_PROPERTY_NAME.QUERY,
86
86
-
options,
87
87
-
parameter: operation.parameters.query,
52
52
+
if (operation.parameters.path) {
53
53
+
const schema = irParametersToIrSchema({
54
54
+
parameters: operation.parameters.path,
55
55
+
});
56
56
+
properties.push({
57
57
+
isRequired: Boolean(schema.required),
58
58
+
name: 'Params',
59
59
+
type: schemaToType({
60
60
+
options: { file },
61
61
+
schema,
88
62
}),
89
89
-
);
63
63
+
});
90
64
}
91
65
92
92
-
if (operation.parameters.path) {
93
93
-
operationProperties.push(
94
94
-
parameterToProperty({
95
95
-
name: ROUTE_PROPERTY_NAME.PATH,
96
96
-
options,
97
97
-
parameter: operation.parameters.path,
66
66
+
if (operation.parameters.query) {
67
67
+
const schema = irParametersToIrSchema({
68
68
+
parameters: operation.parameters.query,
69
69
+
});
70
70
+
properties.push({
71
71
+
isRequired: Boolean(schema.required),
72
72
+
name: 'Querystring',
73
73
+
type: schemaToType({
74
74
+
options: { file },
75
75
+
schema,
98
76
}),
99
99
-
);
77
77
+
});
100
78
}
101
79
}
102
80
103
81
if (operation.responses) {
104
82
const responseProperties: Array<Property> = [];
83
83
+
105
84
for (const code in operation.responses) {
106
106
-
if (code === 'default') continue;
107
107
-
const response = operation.responses[code];
85
85
+
if (code === 'default') {
86
86
+
continue;
87
87
+
}
88
88
+
89
89
+
const response = operation.responses[code]!;
108
90
responseProperties.push({
109
109
-
name: NUMERIC_CODE_REGEX.test(code)
110
110
-
? ts.factory.createNumericLiteral(code)
111
111
-
: code,
91
91
+
name: code,
112
92
type: schemaToType({
113
113
-
options,
114
114
-
schema: response?.schema ?? {},
93
93
+
options: { file },
94
94
+
schema: response.schema,
115
95
}),
116
96
});
117
97
}
118
118
-
operationProperties.push({
119
119
-
name: ROUTE_PROPERTY_NAME.RESPONSE,
98
98
+
99
99
+
properties.push({
100
100
+
name: 'Reply',
120
101
type: compiler.typeInterfaceNode({
121
102
properties: responseProperties,
122
103
useLegacyResolution: false,
···
124
105
});
125
106
}
126
107
127
127
-
const operationType = compiler.typeInterfaceNode({
128
128
-
properties: operationProperties,
129
129
-
useLegacyResolution: false,
130
130
-
});
131
131
-
const property: Property = {
108
108
+
const routeHandler: Property = {
132
109
name: operation.id,
133
133
-
type: compiler.typeNode(ROUTE_HANDLER_NAME, [operationType]),
110
110
+
type: compiler.typeNode('RouteHandler', [
111
111
+
compiler.typeInterfaceNode({
112
112
+
properties,
113
113
+
useLegacyResolution: false,
114
114
+
}),
115
115
+
]),
134
116
};
135
135
-
return property;
117
117
+
return routeHandler;
136
118
};
137
119
138
138
-
const pathsToType = ({
139
139
-
context,
140
140
-
options,
141
141
-
}: {
142
142
-
context: IRContext;
143
143
-
options: SchemaToTypeOptions;
144
144
-
}): ts.Node => {
145
145
-
const operationsProperties = [];
120
120
+
const processRouteHandlers = ({ context }: { context: IRContext }) => {
121
121
+
const file = context.file({ id: fastifyId })!;
122
122
+
123
123
+
const routeHandlers: Array<Property> = [];
124
124
+
146
125
for (const path in context.ir.paths) {
147
126
const pathItem = context.ir.paths[path as keyof IRPathsObject];
148
148
-
for (const method in pathItem) {
149
149
-
const operation = pathItem[method as keyof IRPathItemObject];
150
150
-
if (operation) {
151
151
-
const operationProperty = operationToProperty({ operation, options });
152
152
-
operationsProperties.push(operationProperty);
153
153
-
}
127
127
+
128
128
+
for (const _method in pathItem) {
129
129
+
const method = _method as keyof IRPathItemObject;
130
130
+
const operation = pathItem[method]!;
131
131
+
132
132
+
const routeHandler = operationToRouteHandler({ context, operation });
133
133
+
routeHandlers.push(routeHandler);
154
134
}
155
135
}
156
136
157
157
-
const identifier = context.file({ id: fastifyId })!.identifier({
158
158
-
$ref: OPERATIONS_IDENTIFIER,
137
137
+
const identifier = file.identifier({
138
138
+
$ref: 'RouteHandlers',
159
139
create: true,
160
140
namespace: 'type',
161
141
});
162
162
-
const paths = compiler.typeAliasDeclaration({
163
163
-
exportType: true,
164
164
-
name: identifier.name || '',
165
165
-
type: compiler.typeInterfaceNode({
166
166
-
properties: operationsProperties,
167
167
-
useLegacyResolution: false,
168
168
-
}),
169
169
-
});
170
170
-
return paths;
142
142
+
if (identifier.name) {
143
143
+
file.add(
144
144
+
compiler.typeAliasDeclaration({
145
145
+
exportType: true,
146
146
+
name: identifier.name,
147
147
+
type: compiler.typeInterfaceNode({
148
148
+
properties: routeHandlers,
149
149
+
useLegacyResolution: false,
150
150
+
}),
151
151
+
}),
152
152
+
);
153
153
+
}
171
154
};
172
155
173
156
export const handler: PluginHandler<Config> = ({ context, plugin }) => {
···
175
158
id: fastifyId,
176
159
path: plugin.output,
177
160
});
178
178
-
179
179
-
const options: SchemaToTypeOptions = { file };
180
180
-
file.import({ asType: true, module: 'fastify', name: ROUTE_HANDLER_NAME });
161
161
+
file.import({
162
162
+
asType: true,
163
163
+
module: 'fastify',
164
164
+
name: 'RouteHandler',
165
165
+
});
181
166
componentsToType({
182
167
context,
183
183
-
options,
168
168
+
options: {
169
169
+
file,
170
170
+
},
184
171
});
185
185
-
file.add(pathsToType({ context, options }));
172
172
+
processRouteHandlers({ context });
186
173
};
-6
packages/openapi-ts/src/plugins/fastify/types.d.ts
···
1
1
import type { PluginName } from '../types';
2
2
3
3
export interface Config extends PluginName<'fastify'> {
4
4
-
// TODO: parser - rename operationId option to something like inferId?: boolean
5
5
-
/**
6
6
-
* Use operation ID to generate operation names?
7
7
-
* @default true
8
8
-
*/
9
9
-
operationId?: boolean;
10
4
/**
11
5
* Name of the generated file.
12
6
* @default 'fastify'
+3
-2
packages/openapi-ts/test/sample.cjs
···
13
13
input: {
14
14
// include:
15
15
// '^(#/components/schemas/import|#/paths/api/v{api-version}/simple/options)$',
16
16
-
path: './test/spec/3.1.x/discriminator-one-of.yaml',
17
17
-
// path: './test/spec/3.0.x/full.json',
16
16
+
// path: './test/spec/3.1.x/discriminator-one-of.yaml',
17
17
+
path: './test/spec/3.0.x/full.json',
18
18
// path: 'https://mongodb-mms-prod-build-server.s3.amazonaws.com/openapi/2caffd88277a4e27c95dcefc7e3b6a63a3b03297-v2-2023-11-15.json',
19
19
},
20
20
// name: 'foo',
···
51
51
},
52
52
{
53
53
// name: 'zod',
54
54
+
name: 'fastify',
54
55
},
55
56
],
56
57
// useOptions: false,