Skip to content

Commit 01c237b

Browse files
committed
✨ feat: add enum conversion
1 parent 01b4d6a commit 01c237b

File tree

1 file changed

+102
-29
lines changed

1 file changed

+102
-29
lines changed

src/openapi.ts

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,48 @@ export const unwrapSchema = (
203203
return schema.toJSONSchema?.() ?? schema?.toJsonSchema?.()
204204
}
205205

206+
const convertEnumToOpenAPI = (schema: any): any => {
207+
if (!schema || typeof schema !== 'object') return schema
208+
209+
if (
210+
schema[Kind] === 'Union' &&
211+
schema.anyOf &&
212+
Array.isArray(schema.anyOf) &&
213+
schema.anyOf.length > 0 &&
214+
schema.anyOf.every(
215+
(item: any) =>
216+
item && typeof item === 'object' && item.const !== undefined
217+
)
218+
) {
219+
const enumValues = schema.anyOf.map((item: any) => item.const)
220+
221+
return {
222+
type: 'string',
223+
enum: enumValues
224+
}
225+
}
226+
227+
if (schema.type === 'object' && schema.properties) {
228+
const convertedProperties: any = {}
229+
for (const [key, value] of Object.entries(schema.properties)) {
230+
convertedProperties[key] = convertEnumToOpenAPI(value)
231+
}
232+
return {
233+
...schema,
234+
properties: convertedProperties
235+
}
236+
}
237+
238+
if (schema.type === 'array' && schema.items) {
239+
return {
240+
...schema,
241+
items: convertEnumToOpenAPI(schema.items)
242+
}
243+
}
244+
245+
return schema
246+
}
247+
206248
/**
207249
* Converts Elysia routes to OpenAPI 3.0.3 paths schema
208250
* @param routes Array of Elysia route objects
@@ -342,16 +384,25 @@ export function toOpenAPISchema(
342384
definitions
343385
)
344386

345-
if (params && params.type === 'object' && params.properties)
387+
if (params && params.type === 'object' && params.properties) {
388+
const convertedProperties: any = {}
346389
for (const [paramName, paramSchema] of Object.entries(
347390
params.properties
391+
)) {
392+
convertedProperties[paramName] =
393+
convertEnumToOpenAPI(paramSchema)
394+
}
395+
396+
for (const [paramName, paramSchema] of Object.entries(
397+
convertedProperties
348398
))
349399
parameters.push({
350400
name: paramName,
351401
in: 'path',
352402
required: true, // Path parameters are always required
353403
schema: paramSchema
354404
})
405+
}
355406
}
356407

357408
// Handle query parameters
@@ -362,9 +413,17 @@ export function toOpenAPISchema(
362413
)
363414

364415
if (query && query.type === 'object' && query.properties) {
365-
const required = query.required || []
416+
const convertedProperties: any = {}
366417
for (const [queryName, querySchema] of Object.entries(
367418
query.properties
419+
)) {
420+
convertedProperties[queryName] =
421+
convertEnumToOpenAPI(querySchema)
422+
}
423+
424+
const required = query.required || []
425+
for (const [queryName, querySchema] of Object.entries(
426+
convertedProperties
368427
))
369428
parameters.push({
370429
name: queryName,
@@ -383,9 +442,17 @@ export function toOpenAPISchema(
383442
)
384443

385444
if (headers && headers.type === 'object' && headers.properties) {
386-
const required = headers.required || []
445+
const convertedProperties: any = {}
387446
for (const [headerName, headerSchema] of Object.entries(
388447
headers.properties
448+
)) {
449+
convertedProperties[headerName] =
450+
convertEnumToOpenAPI(headerSchema)
451+
}
452+
453+
const required = headers.required || []
454+
for (const [headerName, headerSchema] of Object.entries(
455+
convertedProperties
389456
))
390457
parameters.push({
391458
name: headerName,
@@ -404,9 +471,17 @@ export function toOpenAPISchema(
404471
)
405472

406473
if (cookie && cookie.type === 'object' && cookie.properties) {
407-
const required = cookie.required || []
474+
const convertedProperties: any = {}
408475
for (const [cookieName, cookieSchema] of Object.entries(
409476
cookie.properties
477+
)) {
478+
convertedProperties[cookieName] =
479+
convertEnumToOpenAPI(cookieSchema)
480+
}
481+
482+
const required = cookie.required || []
483+
for (const [cookieName, cookieSchema] of Object.entries(
484+
convertedProperties
410485
))
411486
parameters.push({
412487
name: cookieName,
@@ -425,11 +500,10 @@ export function toOpenAPISchema(
425500
const body = unwrapSchema(hooks.body, vendors)
426501

427502
if (body) {
503+
const convertedBody = convertEnumToOpenAPI(body)
428504
// @ts-ignore
429-
const { type, description, $ref, ...options } = unwrapReference(
430-
body,
431-
definitions
432-
)
505+
const { type: _type, description, $ref, ...options } = convertedBody
506+
const type = _type as string | undefined
433507

434508
// @ts-ignore
435509
if (hooks.parse) {
@@ -447,26 +521,24 @@ export function toOpenAPISchema(
447521
switch (parser.fn) {
448522
case 'text':
449523
case 'text/plain':
450-
content['text/plain'] = { schema: body }
524+
content['text/plain'] = { schema: convertedBody }
451525
continue
452526

453527
case 'urlencoded':
454528
case 'application/x-www-form-urlencoded':
455529
content['application/x-www-form-urlencoded'] = {
456-
schema: body
530+
schema: convertedBody
457531
}
458532
continue
459533

460534
case 'json':
461535
case 'application/json':
462-
content['application/json'] = { schema: body }
536+
content['application/json'] = { schema: convertedBody }
463537
continue
464538

465539
case 'formdata':
466540
case 'multipart/form-data':
467-
content['multipart/form-data'] = {
468-
schema: body
469-
}
541+
content['multipart/form-data'] = { schema: convertedBody }
470542
continue
471543
}
472544
}
@@ -485,19 +557,17 @@ export function toOpenAPISchema(
485557
type === 'integer' ||
486558
type === 'boolean'
487559
? {
488-
'text/plain': {
489-
schema: body
490-
}
560+
'text/plain': convertedBody
491561
}
492562
: {
493563
'application/json': {
494-
schema: body
564+
schema: convertedBody
495565
},
496566
'application/x-www-form-urlencoded': {
497-
schema: body
567+
schema: convertedBody
498568
},
499569
'multipart/form-data': {
500-
schema: body
570+
schema: convertedBody
501571
}
502572
},
503573
required: true
@@ -522,9 +592,10 @@ export function toOpenAPISchema(
522592

523593
if (!response) continue
524594

595+
const convertedResponse = convertEnumToOpenAPI(response)
525596
// @ts-ignore Must exclude $ref from root options
526-
const { type, description, $ref, ...options } =
527-
unwrapReference(response, definitions)
597+
const { type: _type, description, $ref, ...options } = convertedResponse
598+
const type = _type as string | undefined
528599

529600
operation.responses[status] = {
530601
description:
@@ -533,19 +604,19 @@ export function toOpenAPISchema(
533604
type === 'void' ||
534605
type === 'null' ||
535606
type === 'undefined'
536-
? ({ type, description } as any)
607+
? (convertedResponse as any)
537608
: type === 'string' ||
538609
type === 'number' ||
539610
type === 'integer' ||
540611
type === 'boolean'
541612
? {
542613
'text/plain': {
543-
schema: response
614+
schema: convertedResponse
544615
}
545616
}
546617
: {
547618
'application/json': {
548-
schema: response
619+
schema: convertedResponse
549620
}
550621
}
551622
}
@@ -554,12 +625,14 @@ export function toOpenAPISchema(
554625
const response = unwrapSchema(hooks.response as any, vendors)
555626

556627
if (response) {
628+
const convertedResponse = convertEnumToOpenAPI(response)
629+
557630
// @ts-ignore
558631
const {
559632
type: _type,
560633
description,
561634
...options
562-
} = unwrapReference(response, definitions)
635+
} = convertedResponse
563636
const type = _type as string | undefined
564637

565638
// It's a single schema, default to 200
@@ -569,19 +642,19 @@ export function toOpenAPISchema(
569642
type === 'void' ||
570643
type === 'null' ||
571644
type === 'undefined'
572-
? ({ type, description } as any)
645+
? (convertedResponse as any)
573646
: type === 'string' ||
574647
type === 'number' ||
575648
type === 'integer' ||
576649
type === 'boolean'
577650
? {
578651
'text/plain': {
579-
schema: response
652+
schema: convertedResponse
580653
}
581654
}
582655
: {
583656
'application/json': {
584-
schema: response
657+
schema: convertedResponse
585658
}
586659
}
587660
}

0 commit comments

Comments
 (0)