Skip to content

Commit 374a19f

Browse files
committed
feat: add support for array examples with the @arrayExample tag
1 parent 316add0 commit 374a19f

File tree

2 files changed

+125
-2
lines changed

2 files changed

+125
-2
lines changed

packages/openapi-generator/src/openapi.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import type { Route } from './route';
77
import type { Schema } from './ir';
88
import { Block } from 'comment-parser';
99

10+
type ExtendedOpenApiSchema = OpenAPIV3.SchemaObject & {
11+
arrayExample?: string;
12+
};
13+
1014
function schemaToOpenAPI(
1115
schema: Schema,
1216
): OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined {
@@ -50,7 +54,14 @@ function schemaToOpenAPI(
5054
if (innerSchema === undefined) {
5155
return undefined;
5256
}
53-
return { type: 'array', items: { ...innerSchema, ...defaultOpenAPIObject } };
57+
58+
const { arrayExample, ...rest } = defaultOpenAPIObject;
59+
60+
return {
61+
type: 'array',
62+
...(arrayExample ? { example: JSON.parse(arrayExample) } : {}), // Add example to array if it exists
63+
items: { ...innerSchema, ...rest },
64+
};
5465
case 'object':
5566
return {
5667
type: 'object',
@@ -173,7 +184,7 @@ function schemaToOpenAPI(
173184
}
174185
};
175186

176-
function buildDefaultOpenAPIObject(schema: Schema): OpenAPIV3.SchemaObject {
187+
function buildDefaultOpenAPIObject(schema: Schema): ExtendedOpenApiSchema {
177188
const emptyBlock: Block = { description: '', tags: [], source: [], problems: [] };
178189
const jsdoc = parseCommentBlock(schema.comment ?? emptyBlock);
179190

@@ -196,6 +207,7 @@ function schemaToOpenAPI(
196207
const writeOnly = jsdoc?.tags?.writeOnly ?? schema.writeOnly;
197208
const format = jsdoc?.tags?.format ?? schema.format ?? schema.format;
198209
const title = jsdoc?.tags?.title ?? schema.title;
210+
const arrayExample = jsdoc?.tags?.arrayExample ?? '';
199211

200212
const deprecated =
201213
Object.keys(jsdoc?.tags || {}).includes('deprecated') || !!schema.deprecated;
@@ -223,7 +235,9 @@ function schemaToOpenAPI(
223235
...(writeOnly ? { writeOnly: true } : {}),
224236
...(format ? { format } : {}),
225237
...(title ? { title } : {}),
238+
...(arrayExample ? { arrayExample } : {}),
226239
};
240+
227241
return defaultOpenAPIObject;
228242
}
229243

packages/openapi-generator/test/openapi.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3566,3 +3566,112 @@ testCase("route with titles in request bodies", SCHEMA_WITH_TITLES_IN_REQUEST_BO
35663566
}
35673567
}
35683568
});
3569+
3570+
3571+
const ROUTE_WITH_ARRAY_EXAMPLE = `
3572+
import * as t from 'io-ts';
3573+
import * as h from '@api-ts/io-ts-http';
3574+
3575+
export const route = h.httpRoute({
3576+
path: '/foo',
3577+
method: 'POST',
3578+
request: h.httpRequest({
3579+
params: {},
3580+
body: t.type({
3581+
/**
3582+
* @example "btc"
3583+
*/
3584+
array1: t.array(t.string),
3585+
/**
3586+
* @example "btc"
3587+
* @arrayExample ["btc", "eth"]
3588+
*/
3589+
array2: t.array(t.string),
3590+
objectWithArray: t.type({
3591+
/**
3592+
* @arrayExample ["btc", "eth"]
3593+
*/
3594+
nestedArray: t.array(t.string)
3595+
})
3596+
})
3597+
}),
3598+
response: {
3599+
200: t.literal('OK'),
3600+
},
3601+
});`
3602+
3603+
testCase("route with array examples", ROUTE_WITH_ARRAY_EXAMPLE, {
3604+
openapi: '3.0.3',
3605+
info: {
3606+
title: 'Test',
3607+
version: '1.0.0'
3608+
},
3609+
paths: {
3610+
'/foo': {
3611+
post: {
3612+
parameters: [],
3613+
requestBody: {
3614+
content: {
3615+
'application/json': {
3616+
schema: {
3617+
type: 'object',
3618+
properties: {
3619+
array1: {
3620+
type: 'array',
3621+
items: {
3622+
type: 'string',
3623+
example: '"btc"'
3624+
},
3625+
},
3626+
array2: {
3627+
type: 'array',
3628+
example: ['btc', 'eth'],
3629+
items: {
3630+
type: 'string',
3631+
example: '"btc"'
3632+
},
3633+
},
3634+
objectWithArray: {
3635+
properties: {
3636+
nestedArray: {
3637+
example: [
3638+
'btc',
3639+
'eth'
3640+
],
3641+
items: {
3642+
type: 'string'
3643+
},
3644+
type: 'array'
3645+
}
3646+
},
3647+
required: [
3648+
'nestedArray'
3649+
],
3650+
type: 'object'
3651+
},
3652+
},
3653+
required: [ 'array1', 'array2', 'objectWithArray' ],
3654+
},
3655+
}
3656+
}
3657+
},
3658+
responses: {
3659+
'200': {
3660+
description: 'OK',
3661+
content: {
3662+
'application/json': {
3663+
schema: {
3664+
type: 'string',
3665+
enum: [ 'OK' ]
3666+
}
3667+
}
3668+
}
3669+
}
3670+
}
3671+
}
3672+
}
3673+
},
3674+
components: {
3675+
schemas: {}
3676+
}
3677+
});

0 commit comments

Comments
 (0)