@devup-api/generator 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -3
- package/src/__tests__/convert-case.test.ts +0 -125
- package/src/__tests__/create-url-map.test.ts +0 -318
- package/src/__tests__/index.test.ts +0 -9
- package/src/__tests__/wrap-interface-key-guard.test.ts +0 -42
- package/src/convert-case.ts +0 -22
- package/src/create-url-map.ts +0 -43
- package/src/generate-interface.ts +0 -594
- package/src/generate-schema.ts +0 -482
- package/src/index.ts +0 -2
- package/src/wrap-interface-key-guard.ts +0 -6
- package/tsconfig.json +0 -34
|
@@ -1,594 +0,0 @@
|
|
|
1
|
-
import type { DevupApiTypeGeneratorOptions } from '@devup-api/core'
|
|
2
|
-
import { toPascal } from '@devup-api/utils'
|
|
3
|
-
import type { OpenAPIV3_1 } from 'openapi-types'
|
|
4
|
-
import { convertCase } from './convert-case'
|
|
5
|
-
import {
|
|
6
|
-
extractParameters,
|
|
7
|
-
extractRequestBody,
|
|
8
|
-
formatTypeValue,
|
|
9
|
-
getTypeFromSchema,
|
|
10
|
-
} from './generate-schema'
|
|
11
|
-
import { wrapInterfaceKeyGuard } from './wrap-interface-key-guard'
|
|
12
|
-
|
|
13
|
-
export interface ParameterDefinition
|
|
14
|
-
extends Omit<OpenAPIV3_1.ParameterObject, 'schema'> {
|
|
15
|
-
type: unknown
|
|
16
|
-
default?: unknown
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface EndpointDefinition {
|
|
20
|
-
params?: Record<string, ParameterDefinition>
|
|
21
|
-
body?: unknown
|
|
22
|
-
query?: Record<string, ParameterDefinition>
|
|
23
|
-
response?: unknown
|
|
24
|
-
error?: unknown
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Helper function to extract schema names from $ref
|
|
28
|
-
function extractSchemaNameFromRef(ref: string): string | null {
|
|
29
|
-
if (ref.startsWith('#/components/schemas/')) {
|
|
30
|
-
return ref.replace('#/components/schemas/', '')
|
|
31
|
-
}
|
|
32
|
-
return null
|
|
33
|
-
}
|
|
34
|
-
export function generateInterface(
|
|
35
|
-
schema: OpenAPIV3_1.Document,
|
|
36
|
-
options?: DevupApiTypeGeneratorOptions,
|
|
37
|
-
): string {
|
|
38
|
-
const endpoints: Record<
|
|
39
|
-
'get' | 'post' | 'put' | 'delete' | 'patch',
|
|
40
|
-
Record<string, EndpointDefinition>
|
|
41
|
-
> = {
|
|
42
|
-
get: {},
|
|
43
|
-
post: {},
|
|
44
|
-
put: {},
|
|
45
|
-
delete: {},
|
|
46
|
-
patch: {},
|
|
47
|
-
} as const
|
|
48
|
-
const convertCaseType = options?.convertCase ?? 'camel'
|
|
49
|
-
|
|
50
|
-
// Helper function to collect schema names from a schema object
|
|
51
|
-
const collectSchemaNames = (
|
|
52
|
-
schemaObj: OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject,
|
|
53
|
-
targetSet: Set<string>,
|
|
54
|
-
): void => {
|
|
55
|
-
if ('$ref' in schemaObj) {
|
|
56
|
-
const schemaName = extractSchemaNameFromRef(schemaObj.$ref)
|
|
57
|
-
if (schemaName) {
|
|
58
|
-
targetSet.add(schemaName)
|
|
59
|
-
}
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const schema = schemaObj as OpenAPIV3_1.SchemaObject
|
|
64
|
-
|
|
65
|
-
// Check allOf, anyOf, oneOf
|
|
66
|
-
if (schema.allOf) {
|
|
67
|
-
schema.allOf.forEach((s) => {
|
|
68
|
-
collectSchemaNames(s, targetSet)
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
if (schema.anyOf) {
|
|
72
|
-
schema.anyOf.forEach((s) => {
|
|
73
|
-
collectSchemaNames(s, targetSet)
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
if (schema.oneOf) {
|
|
77
|
-
schema.oneOf.forEach((s) => {
|
|
78
|
-
collectSchemaNames(s, targetSet)
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Check properties
|
|
83
|
-
if (schema.properties) {
|
|
84
|
-
Object.values(schema.properties).forEach((prop) => {
|
|
85
|
-
collectSchemaNames(prop, targetSet)
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Check items (for arrays)
|
|
90
|
-
if (schema.type === 'array' && 'items' in schema && schema.items) {
|
|
91
|
-
collectSchemaNames(schema.items, targetSet)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Track which schemas are used in request body and responses
|
|
96
|
-
const requestSchemaNames = new Set<string>()
|
|
97
|
-
const responseSchemaNames = new Set<string>()
|
|
98
|
-
const errorSchemaNames = new Set<string>()
|
|
99
|
-
|
|
100
|
-
// Helper function to check if a status code is an error response
|
|
101
|
-
const isErrorStatusCode = (statusCode: string): boolean => {
|
|
102
|
-
if (statusCode === 'default') return true
|
|
103
|
-
const code = parseInt(statusCode, 10)
|
|
104
|
-
return code >= 400 && code < 600
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// First, collect schema names used in request body and responses
|
|
108
|
-
if (schema.paths) {
|
|
109
|
-
for (const pathItem of Object.values(schema.paths)) {
|
|
110
|
-
if (!pathItem) continue
|
|
111
|
-
|
|
112
|
-
const methods = ['get', 'post', 'put', 'delete', 'patch'] as const
|
|
113
|
-
for (const method of methods) {
|
|
114
|
-
const operation = pathItem[method]
|
|
115
|
-
if (!operation) continue
|
|
116
|
-
|
|
117
|
-
// Collect request body schemas
|
|
118
|
-
if (operation.requestBody) {
|
|
119
|
-
if ('$ref' in operation.requestBody) {
|
|
120
|
-
// Extract schema name from $ref if it's a schema reference
|
|
121
|
-
const schemaName = extractSchemaNameFromRef(
|
|
122
|
-
operation.requestBody.$ref,
|
|
123
|
-
)
|
|
124
|
-
if (schemaName) {
|
|
125
|
-
requestSchemaNames.add(schemaName)
|
|
126
|
-
}
|
|
127
|
-
} else {
|
|
128
|
-
const content = operation.requestBody.content
|
|
129
|
-
const jsonContent = content?.['application/json']
|
|
130
|
-
if (jsonContent && 'schema' in jsonContent && jsonContent.schema) {
|
|
131
|
-
collectSchemaNames(jsonContent.schema, requestSchemaNames)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Collect response and error schemas
|
|
137
|
-
if (operation.responses) {
|
|
138
|
-
for (const [statusCode, response] of Object.entries(
|
|
139
|
-
operation.responses,
|
|
140
|
-
)) {
|
|
141
|
-
const isError = isErrorStatusCode(statusCode)
|
|
142
|
-
if ('$ref' in response) {
|
|
143
|
-
// Extract schema name from $ref if it's a schema reference
|
|
144
|
-
const schemaName = extractSchemaNameFromRef(response.$ref)
|
|
145
|
-
if (schemaName) {
|
|
146
|
-
if (isError) {
|
|
147
|
-
errorSchemaNames.add(schemaName)
|
|
148
|
-
} else {
|
|
149
|
-
responseSchemaNames.add(schemaName)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} else if ('content' in response) {
|
|
153
|
-
const content = response.content
|
|
154
|
-
const jsonContent = content?.['application/json']
|
|
155
|
-
if (
|
|
156
|
-
jsonContent &&
|
|
157
|
-
'schema' in jsonContent &&
|
|
158
|
-
jsonContent.schema
|
|
159
|
-
) {
|
|
160
|
-
if (isError) {
|
|
161
|
-
collectSchemaNames(jsonContent.schema, errorSchemaNames)
|
|
162
|
-
} else {
|
|
163
|
-
collectSchemaNames(jsonContent.schema, responseSchemaNames)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Iterate through OpenAPI paths and extract each endpoint
|
|
174
|
-
if (schema.paths) {
|
|
175
|
-
for (const [path, pathItem] of Object.entries(schema.paths)) {
|
|
176
|
-
if (!pathItem) continue
|
|
177
|
-
|
|
178
|
-
// Process each HTTP method
|
|
179
|
-
const methods = ['get', 'post', 'put', 'delete', 'patch'] as const
|
|
180
|
-
|
|
181
|
-
for (const method of methods) {
|
|
182
|
-
const operation = pathItem[method]
|
|
183
|
-
if (!operation) continue
|
|
184
|
-
|
|
185
|
-
const endpoint: EndpointDefinition = {}
|
|
186
|
-
|
|
187
|
-
// Extract parameters (path, query, header)
|
|
188
|
-
const { pathParams, queryParams } = extractParameters(
|
|
189
|
-
pathItem,
|
|
190
|
-
operation,
|
|
191
|
-
schema,
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
// Apply case conversion to parameter names
|
|
195
|
-
const convertedPathParams: Record<string, ParameterDefinition> = {}
|
|
196
|
-
for (const [key, value] of Object.entries(pathParams)) {
|
|
197
|
-
const convertedKey = convertCase(key, convertCaseType)
|
|
198
|
-
convertedPathParams[convertedKey] = value
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const convertedQueryParams: Record<string, ParameterDefinition> = {}
|
|
202
|
-
for (const [key, value] of Object.entries(queryParams)) {
|
|
203
|
-
const convertedKey = convertCase(key, convertCaseType)
|
|
204
|
-
convertedQueryParams[convertedKey] = value
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (Object.keys(convertedPathParams).length > 0) {
|
|
208
|
-
endpoint.params = convertedPathParams
|
|
209
|
-
}
|
|
210
|
-
if (Object.keys(convertedQueryParams).length > 0) {
|
|
211
|
-
endpoint.query = convertedQueryParams
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Extract request body
|
|
215
|
-
// Check if request body uses a component schema
|
|
216
|
-
let requestBodyType: unknown
|
|
217
|
-
if (operation.requestBody) {
|
|
218
|
-
if ('$ref' in operation.requestBody) {
|
|
219
|
-
// RequestBodyObject reference - skip for now
|
|
220
|
-
const requestBody = extractRequestBody(
|
|
221
|
-
operation.requestBody,
|
|
222
|
-
schema,
|
|
223
|
-
)
|
|
224
|
-
if (requestBody !== undefined) {
|
|
225
|
-
requestBodyType = requestBody
|
|
226
|
-
}
|
|
227
|
-
} else {
|
|
228
|
-
const content = operation.requestBody.content
|
|
229
|
-
const jsonContent = content?.['application/json']
|
|
230
|
-
if (jsonContent && 'schema' in jsonContent && jsonContent.schema) {
|
|
231
|
-
// Check if schema is a direct reference to components.schemas
|
|
232
|
-
if ('$ref' in jsonContent.schema) {
|
|
233
|
-
const schemaName = extractSchemaNameFromRef(
|
|
234
|
-
jsonContent.schema.$ref,
|
|
235
|
-
)
|
|
236
|
-
// Check if schema exists in components.schemas and is used in request body
|
|
237
|
-
if (
|
|
238
|
-
schemaName &&
|
|
239
|
-
schema.components?.schemas?.[schemaName] &&
|
|
240
|
-
requestSchemaNames.has(schemaName)
|
|
241
|
-
) {
|
|
242
|
-
// Use component reference
|
|
243
|
-
requestBodyType = `DevupRequestComponentStruct['${schemaName}']`
|
|
244
|
-
} else {
|
|
245
|
-
const requestBody = extractRequestBody(
|
|
246
|
-
operation.requestBody,
|
|
247
|
-
schema,
|
|
248
|
-
)
|
|
249
|
-
if (requestBody !== undefined) {
|
|
250
|
-
requestBodyType = requestBody
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
} else {
|
|
254
|
-
const requestBody = extractRequestBody(
|
|
255
|
-
operation.requestBody,
|
|
256
|
-
schema,
|
|
257
|
-
)
|
|
258
|
-
if (requestBody !== undefined) {
|
|
259
|
-
requestBodyType = requestBody
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (requestBodyType !== undefined) {
|
|
266
|
-
endpoint.body = requestBodyType
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Extract response
|
|
270
|
-
// Check if response uses a component schema
|
|
271
|
-
let responseType: unknown
|
|
272
|
-
if (operation.responses) {
|
|
273
|
-
// Prefer 200 response, fallback to first available response
|
|
274
|
-
const successResponse =
|
|
275
|
-
operation.responses['200'] ||
|
|
276
|
-
operation.responses['201'] ||
|
|
277
|
-
Object.values(operation.responses)[0]
|
|
278
|
-
|
|
279
|
-
if (successResponse) {
|
|
280
|
-
if ('$ref' in successResponse) {
|
|
281
|
-
// ResponseObject reference - skip for now
|
|
282
|
-
// Could resolve if needed
|
|
283
|
-
} else if ('content' in successResponse) {
|
|
284
|
-
const content = successResponse.content
|
|
285
|
-
const jsonContent = content?.['application/json']
|
|
286
|
-
if (
|
|
287
|
-
jsonContent &&
|
|
288
|
-
'schema' in jsonContent &&
|
|
289
|
-
jsonContent.schema
|
|
290
|
-
) {
|
|
291
|
-
// Check if schema is a direct reference to components.schemas
|
|
292
|
-
if ('$ref' in jsonContent.schema) {
|
|
293
|
-
const schemaName = extractSchemaNameFromRef(
|
|
294
|
-
jsonContent.schema.$ref,
|
|
295
|
-
)
|
|
296
|
-
// Check if schema exists in components.schemas and is used in response
|
|
297
|
-
if (
|
|
298
|
-
schemaName &&
|
|
299
|
-
schema.components?.schemas?.[schemaName] &&
|
|
300
|
-
responseSchemaNames.has(schemaName)
|
|
301
|
-
) {
|
|
302
|
-
// Use component reference
|
|
303
|
-
responseType = `DevupResponseComponentStruct['${schemaName}']`
|
|
304
|
-
} else {
|
|
305
|
-
// Extract schema type with response options
|
|
306
|
-
const responseDefaultNonNullable =
|
|
307
|
-
options?.responseDefaultNonNullable ?? true
|
|
308
|
-
const { type: schemaType } = getTypeFromSchema(
|
|
309
|
-
jsonContent.schema,
|
|
310
|
-
schema,
|
|
311
|
-
{ defaultNonNullable: responseDefaultNonNullable },
|
|
312
|
-
)
|
|
313
|
-
responseType = schemaType
|
|
314
|
-
}
|
|
315
|
-
} else {
|
|
316
|
-
// Check if it's an array with items referencing a component schema
|
|
317
|
-
const schemaObj =
|
|
318
|
-
jsonContent.schema as OpenAPIV3_1.SchemaObject
|
|
319
|
-
if (
|
|
320
|
-
schemaObj.type === 'array' &&
|
|
321
|
-
schemaObj.items &&
|
|
322
|
-
'$ref' in schemaObj.items
|
|
323
|
-
) {
|
|
324
|
-
const schemaName = extractSchemaNameFromRef(
|
|
325
|
-
schemaObj.items.$ref,
|
|
326
|
-
)
|
|
327
|
-
// Check if schema exists in components.schemas and is used in response
|
|
328
|
-
if (
|
|
329
|
-
schemaName &&
|
|
330
|
-
schema.components?.schemas?.[schemaName] &&
|
|
331
|
-
responseSchemaNames.has(schemaName)
|
|
332
|
-
) {
|
|
333
|
-
// Use component reference for array items
|
|
334
|
-
responseType = `Array<DevupResponseComponentStruct['${schemaName}']>`
|
|
335
|
-
} else {
|
|
336
|
-
// Extract schema type with response options
|
|
337
|
-
const responseDefaultNonNullable =
|
|
338
|
-
options?.responseDefaultNonNullable ?? true
|
|
339
|
-
const { type: schemaType } = getTypeFromSchema(
|
|
340
|
-
jsonContent.schema,
|
|
341
|
-
schema,
|
|
342
|
-
{ defaultNonNullable: responseDefaultNonNullable },
|
|
343
|
-
)
|
|
344
|
-
responseType = schemaType
|
|
345
|
-
}
|
|
346
|
-
} else {
|
|
347
|
-
// Extract schema type with response options
|
|
348
|
-
const responseDefaultNonNullable =
|
|
349
|
-
options?.responseDefaultNonNullable ?? true
|
|
350
|
-
const { type: schemaType } = getTypeFromSchema(
|
|
351
|
-
jsonContent.schema,
|
|
352
|
-
schema,
|
|
353
|
-
{ defaultNonNullable: responseDefaultNonNullable },
|
|
354
|
-
)
|
|
355
|
-
responseType = schemaType
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
if (responseType !== undefined) {
|
|
363
|
-
endpoint.response = responseType
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Extract error
|
|
367
|
-
// Check if error uses a component schema
|
|
368
|
-
let errorType: unknown
|
|
369
|
-
if (operation.responses) {
|
|
370
|
-
// Find error responses (4xx, 5xx, or default)
|
|
371
|
-
const errorResponse =
|
|
372
|
-
operation.responses['400'] ||
|
|
373
|
-
operation.responses['401'] ||
|
|
374
|
-
operation.responses['403'] ||
|
|
375
|
-
operation.responses['404'] ||
|
|
376
|
-
operation.responses['422'] ||
|
|
377
|
-
operation.responses['500'] ||
|
|
378
|
-
operation.responses.default ||
|
|
379
|
-
Object.entries(operation.responses).find(([statusCode]) =>
|
|
380
|
-
isErrorStatusCode(statusCode),
|
|
381
|
-
)?.[1]
|
|
382
|
-
|
|
383
|
-
if (errorResponse) {
|
|
384
|
-
if ('$ref' in errorResponse) {
|
|
385
|
-
// ResponseObject reference - skip for now
|
|
386
|
-
// Could resolve if needed
|
|
387
|
-
} else if ('content' in errorResponse) {
|
|
388
|
-
const content = errorResponse.content
|
|
389
|
-
const jsonContent = content?.['application/json']
|
|
390
|
-
if (
|
|
391
|
-
jsonContent &&
|
|
392
|
-
'schema' in jsonContent &&
|
|
393
|
-
jsonContent.schema
|
|
394
|
-
) {
|
|
395
|
-
// Check if schema is a direct reference to components.schemas
|
|
396
|
-
if ('$ref' in jsonContent.schema) {
|
|
397
|
-
const schemaName = extractSchemaNameFromRef(
|
|
398
|
-
jsonContent.schema.$ref,
|
|
399
|
-
)
|
|
400
|
-
// Check if schema exists in components.schemas and is used in error
|
|
401
|
-
if (
|
|
402
|
-
schemaName &&
|
|
403
|
-
schema.components?.schemas?.[schemaName] &&
|
|
404
|
-
errorSchemaNames.has(schemaName)
|
|
405
|
-
) {
|
|
406
|
-
// Use component reference
|
|
407
|
-
errorType = `DevupErrorComponentStruct['${schemaName}']`
|
|
408
|
-
} else {
|
|
409
|
-
// Extract schema type with response options
|
|
410
|
-
const responseDefaultNonNullable =
|
|
411
|
-
options?.responseDefaultNonNullable ?? true
|
|
412
|
-
const { type: schemaType } = getTypeFromSchema(
|
|
413
|
-
jsonContent.schema,
|
|
414
|
-
schema,
|
|
415
|
-
{ defaultNonNullable: responseDefaultNonNullable },
|
|
416
|
-
)
|
|
417
|
-
errorType = schemaType
|
|
418
|
-
}
|
|
419
|
-
} else {
|
|
420
|
-
// Check if it's an array with items referencing a component schema
|
|
421
|
-
const schemaObj =
|
|
422
|
-
jsonContent.schema as OpenAPIV3_1.SchemaObject
|
|
423
|
-
if (
|
|
424
|
-
schemaObj.type === 'array' &&
|
|
425
|
-
schemaObj.items &&
|
|
426
|
-
'$ref' in schemaObj.items
|
|
427
|
-
) {
|
|
428
|
-
const schemaName = extractSchemaNameFromRef(
|
|
429
|
-
schemaObj.items.$ref,
|
|
430
|
-
)
|
|
431
|
-
// Check if schema exists in components.schemas and is used in error
|
|
432
|
-
if (
|
|
433
|
-
schemaName &&
|
|
434
|
-
schema.components?.schemas?.[schemaName] &&
|
|
435
|
-
errorSchemaNames.has(schemaName)
|
|
436
|
-
) {
|
|
437
|
-
// Use component reference for array items
|
|
438
|
-
errorType = `Array<DevupErrorComponentStruct['${schemaName}']>`
|
|
439
|
-
} else {
|
|
440
|
-
// Extract schema type with response options
|
|
441
|
-
const responseDefaultNonNullable =
|
|
442
|
-
options?.responseDefaultNonNullable ?? true
|
|
443
|
-
const { type: schemaType } = getTypeFromSchema(
|
|
444
|
-
jsonContent.schema,
|
|
445
|
-
schema,
|
|
446
|
-
{ defaultNonNullable: responseDefaultNonNullable },
|
|
447
|
-
)
|
|
448
|
-
errorType = schemaType
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
// Extract schema type with response options
|
|
452
|
-
const responseDefaultNonNullable =
|
|
453
|
-
options?.responseDefaultNonNullable ?? true
|
|
454
|
-
const { type: schemaType } = getTypeFromSchema(
|
|
455
|
-
jsonContent.schema,
|
|
456
|
-
schema,
|
|
457
|
-
{ defaultNonNullable: responseDefaultNonNullable },
|
|
458
|
-
)
|
|
459
|
-
errorType = schemaType
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (errorType !== undefined) {
|
|
467
|
-
endpoint.error = errorType
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Generate path key (normalize path by replacing {param} with converted param and removing slashes)
|
|
471
|
-
const normalizedPath = path.replace(/\{([^}]+)\}/g, (_, param) => {
|
|
472
|
-
// Convert param name based on case type
|
|
473
|
-
return `{${convertCase(param, convertCaseType)}}`
|
|
474
|
-
})
|
|
475
|
-
|
|
476
|
-
endpoints[method][normalizedPath] = endpoint
|
|
477
|
-
if (operation.operationId) {
|
|
478
|
-
// If operationId exists, create both operationId and path keys
|
|
479
|
-
const operationIdKey = convertCase(
|
|
480
|
-
operation.operationId,
|
|
481
|
-
convertCaseType,
|
|
482
|
-
)
|
|
483
|
-
endpoints[method][operationIdKey] = endpoint
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
// Extract components schemas
|
|
490
|
-
const requestComponents: Record<string, unknown> = {}
|
|
491
|
-
const responseComponents: Record<string, unknown> = {}
|
|
492
|
-
const errorComponents: Record<string, unknown> = {}
|
|
493
|
-
if (schema.components?.schemas) {
|
|
494
|
-
for (const [schemaName, schemaObj] of Object.entries(
|
|
495
|
-
schema.components.schemas,
|
|
496
|
-
)) {
|
|
497
|
-
if (schemaObj) {
|
|
498
|
-
const requestDefaultNonNullable =
|
|
499
|
-
options?.requestDefaultNonNullable ?? false
|
|
500
|
-
const responseDefaultNonNullable =
|
|
501
|
-
options?.responseDefaultNonNullable ?? true
|
|
502
|
-
|
|
503
|
-
// Determine which defaultNonNullable to use based on where schema is used
|
|
504
|
-
let defaultNonNullable = responseDefaultNonNullable
|
|
505
|
-
if (requestSchemaNames.has(schemaName)) {
|
|
506
|
-
defaultNonNullable = requestDefaultNonNullable
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
const { type: schemaType } = getTypeFromSchema(
|
|
510
|
-
schemaObj as OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject,
|
|
511
|
-
schema,
|
|
512
|
-
{ defaultNonNullable },
|
|
513
|
-
)
|
|
514
|
-
// Keep original schema name as-is
|
|
515
|
-
if (requestSchemaNames.has(schemaName)) {
|
|
516
|
-
requestComponents[schemaName] = schemaType
|
|
517
|
-
}
|
|
518
|
-
if (responseSchemaNames.has(schemaName)) {
|
|
519
|
-
responseComponents[schemaName] = schemaType
|
|
520
|
-
}
|
|
521
|
-
if (errorSchemaNames.has(schemaName)) {
|
|
522
|
-
errorComponents[schemaName] = schemaType
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Generate TypeScript interface string
|
|
529
|
-
const interfaceContent = Object.entries(endpoints)
|
|
530
|
-
.flatMap(([method, value]) => {
|
|
531
|
-
const entries = Object.entries(value)
|
|
532
|
-
if (entries.length > 0) {
|
|
533
|
-
const interfaceEntries = entries
|
|
534
|
-
.map(([key, endpointValue]) => {
|
|
535
|
-
const formattedValue = formatTypeValue(endpointValue, 2)
|
|
536
|
-
// Top-level keys in ApiStruct should never be optional
|
|
537
|
-
// Only params, query, body etc. can be optional if all their properties are optional
|
|
538
|
-
return ` ${wrapInterfaceKeyGuard(key)}: ${formattedValue}`
|
|
539
|
-
})
|
|
540
|
-
.join(';\n')
|
|
541
|
-
|
|
542
|
-
return [
|
|
543
|
-
` interface Devup${toPascal(method)}ApiStruct {\n${interfaceEntries};\n }`,
|
|
544
|
-
]
|
|
545
|
-
}
|
|
546
|
-
return []
|
|
547
|
-
})
|
|
548
|
-
.join('\n')
|
|
549
|
-
|
|
550
|
-
// Generate RequestComponentStruct interface
|
|
551
|
-
const requestComponentEntries = Object.entries(requestComponents)
|
|
552
|
-
.map(([key, value]) => {
|
|
553
|
-
const formattedValue = formatTypeValue(value, 2)
|
|
554
|
-
return ` ${wrapInterfaceKeyGuard(key)}: ${formattedValue}`
|
|
555
|
-
})
|
|
556
|
-
.join(';\n')
|
|
557
|
-
|
|
558
|
-
const requestComponentInterface =
|
|
559
|
-
requestComponentEntries.length > 0
|
|
560
|
-
? ` interface DevupRequestComponentStruct {\n${requestComponentEntries};\n }`
|
|
561
|
-
: ' interface DevupRequestComponentStruct {}'
|
|
562
|
-
|
|
563
|
-
// Generate ResponseComponentStruct interface
|
|
564
|
-
const responseComponentEntries = Object.entries(responseComponents)
|
|
565
|
-
.map(([key, value]) => {
|
|
566
|
-
const formattedValue = formatTypeValue(value, 2)
|
|
567
|
-
return ` ${wrapInterfaceKeyGuard(key)}: ${formattedValue}`
|
|
568
|
-
})
|
|
569
|
-
.join(';\n')
|
|
570
|
-
|
|
571
|
-
const responseComponentInterface =
|
|
572
|
-
responseComponentEntries.length > 0
|
|
573
|
-
? ` interface DevupResponseComponentStruct {\n${responseComponentEntries};\n }`
|
|
574
|
-
: ' interface DevupResponseComponentStruct {}'
|
|
575
|
-
|
|
576
|
-
// Generate ErrorComponentStruct interface
|
|
577
|
-
const errorComponentEntries = Object.entries(errorComponents)
|
|
578
|
-
.map(([key, value]) => {
|
|
579
|
-
const formattedValue = formatTypeValue(value, 2)
|
|
580
|
-
return ` ${wrapInterfaceKeyGuard(key)}: ${formattedValue}`
|
|
581
|
-
})
|
|
582
|
-
.join(';\n')
|
|
583
|
-
|
|
584
|
-
const errorComponentInterface =
|
|
585
|
-
errorComponentEntries.length > 0
|
|
586
|
-
? ` interface DevupErrorComponentStruct {\n${errorComponentEntries};\n }`
|
|
587
|
-
: ' interface DevupErrorComponentStruct {}'
|
|
588
|
-
|
|
589
|
-
const allInterfaces = interfaceContent
|
|
590
|
-
? `${interfaceContent}\n\n${requestComponentInterface}\n\n${responseComponentInterface}\n\n${errorComponentInterface}`
|
|
591
|
-
: `${requestComponentInterface}\n\n${responseComponentInterface}\n\n${errorComponentInterface}`
|
|
592
|
-
|
|
593
|
-
return `import "@devup-api/fetch";\n\ndeclare module "@devup-api/fetch" {\n${allInterfaces}\n}`
|
|
594
|
-
}
|