Skip to content

Commit d765208

Browse files
authored
Merge pull request #800 from BitGo/DX-480-nullable-optional-union
feat: add support for unions that are nullable and optional
2 parents ddfc6e7 + ac2c974 commit d765208

File tree

2 files changed

+98
-2
lines changed

2 files changed

+98
-2
lines changed

packages/openapi-generator/src/openapi.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,17 @@ function schemaToOpenAPI(
8686
// If there are two schemas and one of the schemas is undefined, that means the union is a case of `optional` type
8787
const undefinedSchema = schema.schemas.find((s) => s.type === 'undefined');
8888
const nonUndefinedSchema = schema.schemas.find((s) => s.type !== 'undefined');
89+
// If nullSchema exists, that means that the union is also nullable
90+
const nullSchema = schema.schemas.find((s) => s.type === 'null');
8991
// and we can just return the other schema (while attaching the comment to that schema)
9092
const isOptional =
91-
schema.schemas.length == 2 && undefinedSchema && nonUndefinedSchema;
93+
schema.schemas.length >= 2 && undefinedSchema && nonUndefinedSchema;
9294
if (isOptional) {
93-
return schemaToOpenAPI({ ...nonUndefinedSchema, comment: schema.comment });
95+
return schemaToOpenAPI({
96+
...nonUndefinedSchema,
97+
comment: schema.comment,
98+
...(nullSchema ? { nullable: true } : {}),
99+
});
94100
}
95101

96102
for (const s of schema.schemas) {

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

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,3 +2736,93 @@ testCase('route with optional array query parameter and documentation', ROUTE_WI
27362736
schemas: {}
27372737
}
27382738
});
2739+
2740+
2741+
const ROUTE_WITH_ARRAY_UNION_NULL_UNDEFINED_QUERY_PARAM = `
2742+
import * as t from 'io-ts';
2743+
import * as h from '@api-ts/io-ts-http';
2744+
2745+
/**
2746+
* A simple route with type descriptions for references
2747+
*
2748+
* @operationId api.v1.test
2749+
* @tag Test Routes
2750+
*/
2751+
export const route = h.httpRoute({
2752+
path: '/foo',
2753+
method: 'GET',
2754+
request: h.httpRequest({
2755+
query: {
2756+
/**
2757+
* This is a foo description.
2758+
* @example abc
2759+
* @pattern ^[a-z]+$
2760+
*/
2761+
ipRestrict: t.union([t.array(t.string), t.null, t.undefined]),
2762+
},
2763+
}),
2764+
response: {
2765+
200: {
2766+
test: t.string
2767+
}
2768+
},
2769+
});
2770+
`;
2771+
2772+
testCase('route with array union of null and undefined', ROUTE_WITH_ARRAY_UNION_NULL_UNDEFINED_QUERY_PARAM, {
2773+
openapi: '3.0.3',
2774+
info: {
2775+
title: 'Test',
2776+
version: '1.0.0'
2777+
},
2778+
paths: {
2779+
'/foo': {
2780+
get: {
2781+
summary: 'A simple route with type descriptions for references',
2782+
operationId: 'api.v1.test',
2783+
tags: [
2784+
'Test Routes'
2785+
],
2786+
parameters: [
2787+
{
2788+
description: 'This is a foo description.',
2789+
in: 'query',
2790+
name: 'ipRestrict',
2791+
schema: {
2792+
items: {
2793+
description: 'This is a foo description.',
2794+
example: 'abc',
2795+
type: 'string',
2796+
pattern: '^[a-z]+$'
2797+
},
2798+
type: 'array'
2799+
}
2800+
}
2801+
],
2802+
responses: {
2803+
'200': {
2804+
description: 'OK',
2805+
content: {
2806+
'application/json': {
2807+
schema: {
2808+
type: 'object',
2809+
properties: {
2810+
test: {
2811+
type: 'string'
2812+
}
2813+
},
2814+
required: [
2815+
'test'
2816+
]
2817+
}
2818+
}
2819+
}
2820+
}
2821+
}
2822+
}
2823+
}
2824+
},
2825+
components: {
2826+
schemas: {}
2827+
}
2828+
});

0 commit comments

Comments
 (0)