Skip to content

Commit f302ac0

Browse files
feat: Parse annotated enums (#146)
Co-authored-by: Seam Bot <seambot@getseam.com>
1 parent 962602b commit f302ac0

File tree

8 files changed

+5018
-23
lines changed

8 files changed

+5018
-23
lines changed

src/lib/blueprint.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -239,9 +239,7 @@ interface EnumProperty extends BaseProperty {
239239
values: EnumValue[]
240240
}
241241

242-
interface EnumValue {
243-
name: string
244-
}
242+
type EnumValue = BaseProperty
245243

246244
interface RecordProperty extends BaseProperty {
247245
format: 'record'
@@ -732,9 +730,24 @@ const createParameter = (
732730
...baseParam,
733731
format: 'enum',
734732
jsonType: 'string',
735-
values: parsedProp.enum.map((value: any) => ({
736-
name: value,
737-
})),
733+
values: parsedProp.enum.map((value: string | boolean) => {
734+
const enumValue = parsedProp['x-enums']?.[String(value)]
735+
if (parsedProp['x-enums'] != null && enumValue == null) {
736+
throw new Error(
737+
`Missing enum value definition in x-enums for "${String(value)}"`,
738+
)
739+
}
740+
return {
741+
name: String(value),
742+
description: enumValue?.description ?? '',
743+
isDeprecated: Boolean(enumValue?.deprecated?.length ?? 0),
744+
deprecationMessage: enumValue?.deprecated ?? '',
745+
isUndocumented: Boolean(enumValue?.undocumented?.length ?? 0),
746+
undocumentedMessage: enumValue?.undocumented ?? '',
747+
isDraft: Boolean(enumValue?.draft?.length ?? 0),
748+
draftMessage: enumValue?.draft ?? '',
749+
}
750+
}),
738751
}
739752
}
740753
if (parsedProp.format === 'date-time') {
@@ -973,7 +986,24 @@ const createProperty = (
973986
...baseProperty,
974987
format: 'enum',
975988
jsonType: 'string',
976-
values: parsedProp.enum.map((value: any) => ({ name: value })),
989+
values: parsedProp.enum.map((value: string | boolean) => {
990+
const enumValue = parsedProp['x-enums']?.[String(value)]
991+
if (parsedProp['x-enums'] != null && enumValue == null) {
992+
throw new Error(
993+
`Missing enum value definition in x-enums for "${String(value)}"`,
994+
)
995+
}
996+
return {
997+
name: String(value),
998+
description: enumValue?.description ?? '',
999+
isDeprecated: Boolean(enumValue?.deprecated?.length ?? 0),
1000+
deprecationMessage: enumValue?.deprecated ?? '',
1001+
isUndocumented: Boolean(enumValue?.undocumented?.length ?? 0),
1002+
undocumentedMessage: enumValue?.undocumented ?? '',
1003+
isDraft: Boolean(enumValue?.draft?.length ?? 0),
1004+
draftMessage: enumValue?.draft ?? '',
1005+
}
1006+
}),
9771007
}
9781008
}
9791009
if (parsedProp.format === 'date-time') {

src/lib/openapi/schemas.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,13 @@ export const OpenapiOperationSchema = z.object({
8383
'x-draft': z.string().default(''),
8484
})
8585

86+
export const EnumValueSchema = z.object({
87+
description: z.string().default(''),
88+
undocumented: z.string().default(''),
89+
deprecated: z.string().default(''),
90+
draft: z.string().default(''),
91+
})
92+
8693
export const PropertySchema: z.ZodSchema<any> = z.object({
8794
type: z.enum(['string', 'number', 'integer', 'boolean', 'array', 'object']),
8895
description: z.string().default(''),
@@ -91,6 +98,7 @@ export const PropertySchema: z.ZodSchema<any> = z.object({
9198
'x-deprecated': z.string().default(''),
9299
'x-draft': z.string().default(''),
93100
enum: z.array(z.string().or(z.boolean())).optional(),
101+
'x-enums': z.record(z.string(), EnumValueSchema).optional(),
94102
$ref: z.string().optional(),
95103
format: z.string().optional(),
96104
})

src/lib/openapi/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ export interface OpenapiSchema {
7979
'x-draft'?: string
8080
'x-undocumented'?: string
8181
'x-route-path'?: string
82+
'x-enums'?: Record<
83+
string,
84+
{
85+
title?: string
86+
description?: string
87+
undocumented?: string
88+
deprecated?: string
89+
draft?: string
90+
}
91+
>
8292
}
8393

8494
export interface OpenapiComponents {

test/fixtures/types/openapi.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ export default {
1414
format: 'uuid',
1515
type: 'string',
1616
},
17+
foo_type: {
18+
description: 'Foo type',
19+
type: 'string',
20+
enum: ['foo_basic', 'foo_advanced'],
21+
'x-enums': {
22+
foo_basic: {
23+
description: 'Use a basic foo',
24+
},
25+
foo_advanced: {
26+
description: 'Use an advanced foo',
27+
deprecated: 'Advanced foo is deprecated',
28+
},
29+
},
30+
},
1731
name: {
1832
description: 'Foo name',
1933
type: 'string',

test/snapshots/blueprint.test.ts.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,34 @@ Generated by [AVA](https://avajs.dev).
4444
undocumentedMessage: '',
4545
values: [
4646
{
47+
deprecationMessage: '',
48+
description: '',
49+
draftMessage: '',
50+
isDeprecated: false,
51+
isDraft: false,
52+
isUndocumented: false,
4753
name: 'success',
54+
undocumentedMessage: '',
4855
},
4956
{
57+
deprecationMessage: '',
58+
description: '',
59+
draftMessage: '',
60+
isDeprecated: false,
61+
isDraft: false,
62+
isUndocumented: false,
5063
name: 'pending',
64+
undocumentedMessage: '',
5165
},
5266
{
67+
deprecationMessage: '',
68+
description: '',
69+
draftMessage: '',
70+
isDeprecated: false,
71+
isDraft: false,
72+
isUndocumented: false,
5373
name: 'error',
74+
undocumentedMessage: '',
5475
},
5576
],
5677
},
@@ -67,7 +88,14 @@ Generated by [AVA](https://avajs.dev).
6788
undocumentedMessage: '',
6889
values: [
6990
{
91+
deprecationMessage: '',
92+
description: '',
93+
draftMessage: '',
94+
isDeprecated: false,
95+
isDraft: false,
96+
isUndocumented: false,
7097
name: 'CREATE_FOO',
98+
undocumentedMessage: '',
7199
},
72100
],
73101
},
@@ -112,7 +140,14 @@ Generated by [AVA](https://avajs.dev).
112140
undocumentedMessage: '',
113141
values: [
114142
{
143+
deprecationMessage: '',
144+
description: '',
145+
draftMessage: '',
146+
isDeprecated: false,
147+
isDraft: false,
148+
isUndocumented: false,
115149
name: 'foo.created',
150+
undocumentedMessage: '',
116151
},
117152
],
118153
},
@@ -218,6 +253,40 @@ Generated by [AVA](https://avajs.dev).
218253
name: 'foo_id',
219254
undocumentedMessage: '',
220255
},
256+
{
257+
deprecationMessage: '',
258+
description: 'Foo type',
259+
draftMessage: '',
260+
format: 'enum',
261+
isDeprecated: false,
262+
isDraft: false,
263+
isUndocumented: false,
264+
jsonType: 'string',
265+
name: 'foo_type',
266+
undocumentedMessage: '',
267+
values: [
268+
{
269+
deprecationMessage: '',
270+
description: 'Use a basic foo',
271+
draftMessage: '',
272+
isDeprecated: false,
273+
isDraft: false,
274+
isUndocumented: false,
275+
name: 'foo_basic',
276+
undocumentedMessage: '',
277+
},
278+
{
279+
deprecationMessage: 'Advanced foo is deprecated',
280+
description: 'Use an advanced foo',
281+
draftMessage: '',
282+
isDeprecated: true,
283+
isDraft: false,
284+
isUndocumented: false,
285+
name: 'foo_advanced',
286+
undocumentedMessage: '',
287+
},
288+
],
289+
},
221290
{
222291
deprecationMessage: '',
223292
description: 'Foo name',
@@ -1085,13 +1154,34 @@ Generated by [AVA](https://avajs.dev).
10851154
undocumentedMessage: '',
10861155
values: [
10871156
{
1157+
deprecationMessage: '',
1158+
description: '',
1159+
draftMessage: '',
1160+
isDeprecated: false,
1161+
isDraft: false,
1162+
isUndocumented: false,
10881163
name: 'success',
1164+
undocumentedMessage: '',
10891165
},
10901166
{
1167+
deprecationMessage: '',
1168+
description: '',
1169+
draftMessage: '',
1170+
isDeprecated: false,
1171+
isDraft: false,
1172+
isUndocumented: false,
10911173
name: 'pending',
1174+
undocumentedMessage: '',
10921175
},
10931176
{
1177+
deprecationMessage: '',
1178+
description: '',
1179+
draftMessage: '',
1180+
isDeprecated: false,
1181+
isDraft: false,
1182+
isUndocumented: false,
10941183
name: 'error',
1184+
undocumentedMessage: '',
10951185
},
10961186
],
10971187
},
@@ -1108,7 +1198,14 @@ Generated by [AVA](https://avajs.dev).
11081198
undocumentedMessage: '',
11091199
values: [
11101200
{
1201+
deprecationMessage: '',
1202+
description: '',
1203+
draftMessage: '',
1204+
isDeprecated: false,
1205+
isDraft: false,
1206+
isUndocumented: false,
11111207
name: 'CREATE_FOO',
1208+
undocumentedMessage: '',
11121209
},
11131210
],
11141211
},
@@ -1153,7 +1250,14 @@ Generated by [AVA](https://avajs.dev).
11531250
undocumentedMessage: '',
11541251
values: [
11551252
{
1253+
deprecationMessage: '',
1254+
description: '',
1255+
draftMessage: '',
1256+
isDeprecated: false,
1257+
isDraft: false,
1258+
isUndocumented: false,
11561259
name: 'foo.created',
1260+
undocumentedMessage: '',
11571261
},
11581262
],
11591263
},
@@ -1259,6 +1363,40 @@ Generated by [AVA](https://avajs.dev).
12591363
name: 'foo_id',
12601364
undocumentedMessage: '',
12611365
},
1366+
{
1367+
deprecationMessage: '',
1368+
description: 'Foo type',
1369+
draftMessage: '',
1370+
format: 'enum',
1371+
isDeprecated: false,
1372+
isDraft: false,
1373+
isUndocumented: false,
1374+
jsonType: 'string',
1375+
name: 'foo_type',
1376+
undocumentedMessage: '',
1377+
values: [
1378+
{
1379+
deprecationMessage: '',
1380+
description: 'Use a basic foo',
1381+
draftMessage: '',
1382+
isDeprecated: false,
1383+
isDraft: false,
1384+
isUndocumented: false,
1385+
name: 'foo_basic',
1386+
undocumentedMessage: '',
1387+
},
1388+
{
1389+
deprecationMessage: 'Advanced foo is deprecated',
1390+
description: 'Use an advanced foo',
1391+
draftMessage: '',
1392+
isDeprecated: true,
1393+
isDraft: false,
1394+
isUndocumented: false,
1395+
name: 'foo_advanced',
1396+
undocumentedMessage: '',
1397+
},
1398+
],
1399+
},
12621400
{
12631401
deprecationMessage: '',
12641402
description: 'Foo name',
777 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)