From 0dde015a0bca3f227b5b06c66ab504032665faee Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 20 Aug 2024 16:07:28 +0900 Subject: [PATCH] Allow `null` value on `enum` properties. It is nonsensible to inserting `null` value to `enum` property array, but Github OpenAPI document is actually doing the crazy thing. This PR supports it by allowing the `null` value to the `enum` proeprty array. --- package.json | 2 +- src/OpenApiV3.ts | 8 ++--- src/OpenApiV3_1.ts | 8 ++--- src/SwaggerV2.ts | 8 ++--- src/internal/OpenApiV3Converter.ts | 19 ++++++++++-- src/internal/OpenApiV3_1Converter.ts | 46 ++++++++++++++++++++-------- src/internal/SwaggerV2Converter.ts | 14 +++++++-- 7 files changed, 76 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 331bb1c..d588362 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@samchon/openapi", - "version": "0.4.5", + "version": "0.4.6", "description": "OpenAPI definitions and converters for 'typia' and 'nestia'.", "main": "./lib/index.js", "module": "./lib/index.mjs", diff --git a/src/OpenApiV3.ts b/src/OpenApiV3.ts index 2082e4d..2f91e3d 100644 --- a/src/OpenApiV3.ts +++ b/src/OpenApiV3.ts @@ -182,11 +182,11 @@ export namespace OpenApiV3 { export namespace IJsonSchema { export interface IBoolean extends __ISignificant<"boolean"> { default?: boolean | null; - enum?: boolean[]; + enum?: Array; } export interface IInteger extends __ISignificant<"integer"> { /** @type int64 */ default?: number | null; - /** @type int64 */ enum?: number[]; + /** @type int64 */ enum?: Array; /** @type int64 */ minimum?: number; /** @type int64 */ maximum?: number; exclusiveMinimum?: boolean; @@ -199,7 +199,7 @@ export namespace OpenApiV3 { } export interface INumber extends __ISignificant<"number"> { default?: number | null; - enum?: number[]; + enum?: Array; minimum?: number; maximum?: number; exclusiveMinimum?: boolean; @@ -208,7 +208,7 @@ export namespace OpenApiV3 { } export interface IString extends __ISignificant<"string"> { default?: string | null; - enum?: string[]; + enum?: Array; format?: | "binary" | "byte" diff --git a/src/OpenApiV3_1.ts b/src/OpenApiV3_1.ts index 2113352..a374a71 100644 --- a/src/OpenApiV3_1.ts +++ b/src/OpenApiV3_1.ts @@ -217,11 +217,11 @@ export namespace OpenApiV3_1 { } export interface IBoolean extends __ISignificant<"boolean"> { default?: boolean | null; - enum?: boolean[]; + enum?: Array; } export interface IInteger extends __ISignificant<"integer"> { /** @type int64 */ default?: number | null; - /** @type int64 */ enum?: number[]; + /** @type int64 */ enum?: Array; /** @type int64 */ minimum?: number; /** @type int64 */ maximum?: number; /** @type int64 */ exclusiveMinimum?: number | boolean; @@ -234,7 +234,7 @@ export namespace OpenApiV3_1 { } export interface INumber extends __ISignificant<"number"> { default?: number | null; - enum?: number[]; + enum?: Array; minimum?: number; maximum?: number; exclusiveMinimum?: number | boolean; @@ -244,7 +244,7 @@ export namespace OpenApiV3_1 { export interface IString extends __ISignificant<"string"> { contentMediaType?: string; default?: string | null; - enum?: string[]; + enum?: Array; format?: | "binary" | "byte" diff --git a/src/SwaggerV2.ts b/src/SwaggerV2.ts index ea6b8c4..9b84e80 100644 --- a/src/SwaggerV2.ts +++ b/src/SwaggerV2.ts @@ -132,11 +132,11 @@ export namespace SwaggerV2 { export namespace IJsonSchema { export interface IBoolean extends __ISignificant<"boolean"> { default?: boolean | null; - enum?: boolean[]; + enum?: Array; } export interface IInteger extends __ISignificant<"integer"> { /** @type int64 */ default?: number | null; - /** @type int64 */ enum?: number[]; + /** @type int64 */ enum?: Array; /** @type int64 */ minimum?: number; /** @type int64 */ maximum?: number; exclusiveMinimum?: boolean; @@ -149,7 +149,7 @@ export namespace SwaggerV2 { } export interface INumber extends __ISignificant<"number"> { default?: number | null; - enum?: number[]; + enum?: Array; minimum?: number; maximum?: number; exclusiveMinimum?: boolean; @@ -158,7 +158,7 @@ export namespace SwaggerV2 { } export interface IString extends __ISignificant<"string"> { default?: string | null; - enum?: string[]; + enum?: Array; format?: | "binary" | "byte" diff --git a/src/internal/OpenApiV3Converter.ts b/src/internal/OpenApiV3Converter.ts index 119a912..ac791ce 100644 --- a/src/internal/OpenApiV3Converter.ts +++ b/src/internal/OpenApiV3Converter.ts @@ -268,6 +268,14 @@ export namespace OpenApiV3Converter { if ((schema as OpenApiV3.IJsonSchema.INumber).default === null) nullable.default = null; } + if ( + Array.isArray((schema as OpenApiV3.IJsonSchema.INumber).enum) && + (schema as OpenApiV3.IJsonSchema.INumber).enum?.length && + (schema as OpenApiV3.IJsonSchema.INumber).enum?.some( + (e) => e === null, + ) + ) + nullable.value ||= true; // UNION TYPE CASE if (TypeChecker.isAnyOf(schema)) schema.anyOf.forEach(visit); else if (TypeChecker.isOneOf(schema)) schema.oneOf.forEach(visit); @@ -280,8 +288,15 @@ export namespace OpenApiV3Converter { TypeChecker.isNumber(schema) || TypeChecker.isString(schema) ) - if (schema.enum?.length) - union.push(...schema.enum.map((value) => ({ const: value }))); + if ( + schema.enum?.length && + schema.enum.filter((e) => e !== null).length + ) + union.push( + ...schema.enum + .filter((v) => v !== null) + .map((value) => ({ const: value })), + ); else union.push({ ...schema, diff --git a/src/internal/OpenApiV3_1Converter.ts b/src/internal/OpenApiV3_1Converter.ts index ebc905c..c1cd971 100644 --- a/src/internal/OpenApiV3_1Converter.ts +++ b/src/internal/OpenApiV3_1Converter.ts @@ -297,6 +297,15 @@ export namespace OpenApiV3_1Converter { if ((schema as OpenApiV3_1.IJsonSchema.INumber).default === null) nullable.default = null; } + if ( + Array.isArray((schema as OpenApiV3_1.IJsonSchema.INumber).enum) && + (schema as OpenApiV3_1.IJsonSchema.INumber).enum?.length && + (schema as OpenApiV3_1.IJsonSchema.INumber).enum?.some( + (e) => e === null, + ) + ) + nullable.value ||= true; + // MIXED TYPE CASE if (TypeChecker.isMixed(schema)) { if (schema.const !== undefined) @@ -345,9 +354,10 @@ export namespace OpenApiV3_1Converter { visit({ ...schema, ...{ - enum: schema.enum?.length - ? schema.enum.filter((x) => typeof x === type) - : undefined, + enum: + schema.enum?.length && schema.enum.filter((e) => e !== null) + ? schema.enum.filter((x) => typeof x === type) + : undefined, }, type: type as any, }); @@ -355,9 +365,15 @@ export namespace OpenApiV3_1Converter { visit({ ...schema, ...{ - enum: schema.enum?.length - ? schema.enum.filter((x) => Number.isInteger(x)) - : undefined, + enum: + schema.enum?.length && schema.enum.filter((e) => e !== null) + ? schema.enum.filter( + (x) => + x !== null && + typeof x === "number" && + Number.isInteger(x), + ) + : undefined, }, type: type as any, }); @@ -370,8 +386,11 @@ export namespace OpenApiV3_1Converter { union.push(convertAllOfSchema(components)(schema)); // ATOMIC TYPE CASE (CONSIDER ENUM VALUES) else if (TypeChecker.isBoolean(schema)) - if (schema.enum?.length) - for (const value of schema.enum) + if ( + schema.enum?.length && + schema.enum.filter((e) => e !== null).length + ) + for (const value of schema.enum.filter((e) => e !== null)) union.push({ const: value, ...({ @@ -390,8 +409,8 @@ export namespace OpenApiV3_1Converter { }, }); else if (TypeChecker.isInteger(schema) || TypeChecker.isNumber(schema)) - if (schema.enum?.length) - for (const value of schema.enum) + if (schema.enum?.length && schema.enum.filter((e) => e !== null)) + for (const value of schema.enum.filter((e) => e !== null)) union.push({ const: value, ...({ @@ -431,8 +450,11 @@ export namespace OpenApiV3_1Converter { }), }); else if (TypeChecker.isString(schema)) - if (schema.enum?.length) - for (const value of schema.enum) + if ( + schema.enum?.length && + schema.enum.filter((e) => e !== null).length + ) + for (const value of schema.enum.filter((e) => e !== null)) union.push({ const: value, ...({ diff --git a/src/internal/SwaggerV2Converter.ts b/src/internal/SwaggerV2Converter.ts index f1cd896..2e29887 100644 --- a/src/internal/SwaggerV2Converter.ts +++ b/src/internal/SwaggerV2Converter.ts @@ -276,6 +276,12 @@ export namespace SwaggerV2Converter { if ((schema as SwaggerV2.IJsonSchema.INumber).default === null) nullable.default = null; } + if ( + Array.isArray((schema as SwaggerV2.IJsonSchema.INumber).enum) && + (schema as SwaggerV2.IJsonSchema.INumber).enum?.length && + (schema as SwaggerV2.IJsonSchema.INumber).enum?.some((e) => e === null) + ) + nullable.value ||= true; // UNION TYPE CASE if (TypeChecker.isAnyOf(schema)) schema["x-anyOf"].forEach(visit); else if (TypeChecker.isOneOf(schema)) schema["x-oneOf"].forEach(visit); @@ -286,8 +292,12 @@ export namespace SwaggerV2Converter { TypeChecker.isNumber(schema) || TypeChecker.isString(schema) ) - if (schema.enum?.length) - union.push(...schema.enum.map((value) => ({ const: value }))); + if (schema.enum?.length && schema.enum.filter((e) => e !== null).length) + union.push( + ...schema.enum + .filter((v) => v !== null) + .map((value) => ({ const: value })), + ); else union.push({ ...schema,