- Working on enum extensions

This commit is contained in:
Ferdi Koomen 2020-02-16 01:16:25 +01:00
parent 6fa5887732
commit ad49392102
26 changed files with 296 additions and 27 deletions

View File

@ -2,4 +2,5 @@ export interface Enum {
name: string;
value: string;
type: string;
description: string | null;
}

View File

@ -0,0 +1,7 @@
/**
* Supported extension for enums
*/
export interface WithEnumExtension {
'x-enum-varnames'?: string[];
'x-enum-descriptions'?: string[];
}

View File

@ -21,6 +21,6 @@ export interface OpenApiHeader {
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
enum?: string[];
enum?: (string | number)[];
multipleOf?: number;
}

View File

@ -1,7 +1,9 @@
import { WithEnumExtension } from './Extensions/WithEnumExtension';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#itemsObject
*/
export interface OpenApiItems {
export interface OpenApiItems extends WithEnumExtension {
type?: string;
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
items?: OpenApiItems;
@ -17,6 +19,6 @@ export interface OpenApiItems {
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
enum?: string[];
enum?: (string | number)[];
multipleOf?: number;
}

View File

@ -1,11 +1,12 @@
import { OpenApiItems } from './OpenApiItems';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiSchema } from './OpenApiSchema';
import { WithEnumExtension } from './Extensions/WithEnumExtension';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject
*/
export interface OpenApiParameter extends OpenApiReference {
export interface OpenApiParameter extends OpenApiReference, WithEnumExtension {
name: string;
in: 'path' | 'query' | 'header' | 'formData' | 'body';
description?: string;

View File

@ -2,11 +2,12 @@ import { Dictionary } from '../../../utils/types';
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiXml } from './OpenApiXml';
import { WithEnumExtension } from './Extensions/WithEnumExtension';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject
*/
export interface OpenApiSchema extends OpenApiReference {
export interface OpenApiSchema extends OpenApiReference, WithEnumExtension {
title?: string;
description?: string;
default?: any;

View File

@ -0,0 +1,17 @@
import { Enum } from '../../../client/interfaces/Enum';
import { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtension';
const KEY_ENUM_NAMES = 'x-enum-varnames';
const KEY_ENUM_DESCRIPTIONS = 'x-enum-descriptions';
export function extendEnum(enumerators: Enum[], definition: WithEnumExtension): Enum[] {
const names = definition[KEY_ENUM_NAMES];
const descriptions = definition[KEY_ENUM_DESCRIPTIONS];
return enumerators.map((enumerator, index) => ({
name: (names && names[index]) || enumerator.name,
description: (descriptions && descriptions[index]) || enumerator.description,
value: enumerator.value,
type: enumerator.type,
}));
}

View File

@ -0,0 +1,13 @@
import { EOL } from 'os';
import { getComment } from './getComment';
describe('getComment', () => {
it('should parse comments', () => {
const multiline = 'Testing multiline comments.' + EOL + ' * This must go to the next line.' + EOL + ' * ' + EOL + ' * This will contain a break.';
expect(getComment('')).toEqual(null);
expect(getComment('Hello')).toEqual('Hello');
expect(getComment('Hello World!')).toEqual('Hello World!');
expect(getComment('Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.')).toEqual(multiline);
expect(getComment('Testing multiline comments.\r\nThis must go to the next line.\r\n\r\nThis will contain a break.')).toEqual(multiline);
});
});

View File

@ -2,12 +2,7 @@ import { EOL } from 'os';
export function getComment(comment?: string): string | null {
if (comment) {
return comment
.split(/(\r\n|\n|\r)+/g)
.filter(line => line)
.map(line => line.trim())
.join(EOL)
.replace(/(\r\n|\n|\r)+/g, '$1 * ');
return comment.replace(/\r?\n(.*)/g, (_, w) => `${EOL} * ${w.trim()}`);
}
return null;
}

View File

@ -13,12 +13,14 @@ export function getEnum(values?: (string | number)[]): Enum[] {
name: `NUM_${value}`,
value: String(value),
type: PrimaryType.NUMBER,
description: null,
};
}
return {
name: value.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
value: `'${value}'`,
type: PrimaryType.STRING,
description: null,
};
});
}

View File

@ -17,6 +17,7 @@ export function getEnumFromDescription(description: string): Enum[] {
name: name.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
value: String(value),
type: PrimaryType.NUMBER,
description: null,
});
}
});

View File

@ -2,6 +2,7 @@ import { Model } from '../../../client/interfaces/Model';
import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { PrimaryType } from './constants';
import { extendEnum } from './extendEnum';
import { getComment } from './getComment';
import { getEnum } from './getEnum';
import { getEnumFromDescription } from './getEnumFromDescription';
@ -54,11 +55,12 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, isProperty
if (definition.enum) {
const enumerators = getEnum(definition.enum);
if (enumerators.length) {
const extendedEnumerators = extendEnum(enumerators, definition);
if (extendedEnumerators.length) {
model.export = 'enum';
model.type = PrimaryType.STRING;
model.base = PrimaryType.STRING;
model.enum.push(...enumerators);
model.enum.push(...extendedEnumerators);
return model;
}
}

View File

@ -2,6 +2,7 @@ import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiParameter } from '../interfaces/OpenApiParameter';
import { OperationParameter } from '../../../client/interfaces/OperationParameter';
import { PrimaryType } from './constants';
import { extendEnum } from './extendEnum';
import { getComment } from './getComment';
import { getEnum } from './getEnum';
import { getEnumFromDescription } from './getEnumFromDescription';
@ -58,11 +59,12 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame
if (parameter.enum) {
const enumerators = getEnum(parameter.enum);
if (enumerators.length) {
const extendedEnumerators = extendEnum(enumerators, parameter);
if (extendedEnumerators.length) {
operationParameter.export = 'enum';
operationParameter.type = PrimaryType.STRING;
operationParameter.base = PrimaryType.STRING;
operationParameter.enum.push(...enumerators);
operationParameter.enum.push(...extendedEnumerators);
operationParameter.default = getOperationParameterDefault(parameter, operationParameter);
operationParameter.isRequired = operationParameter.isRequired || operationParameter.default;
return operationParameter;

View File

@ -0,0 +1,7 @@
/**
* Supported extension for enums
*/
export interface WithEnumExtension {
'x-enum-varnames'?: string[];
'x-enum-descriptions'?: string[];
}

View File

@ -3,11 +3,12 @@ import { OpenApiDiscriminator } from './OpenApiDiscriminator';
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiXml } from './OpenApiXml';
import { WithEnumExtension } from './Extensions/WithEnumExtension';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject
*/
export interface OpenApiSchema extends OpenApiReference {
export interface OpenApiSchema extends OpenApiReference, WithEnumExtension {
title?: string;
multipleOf?: number;
maximum?: number;
@ -23,7 +24,7 @@ export interface OpenApiSchema extends OpenApiReference {
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: string[];
enum?: (string | number)[];
type?: string;
allOf?: OpenApiSchema[];
oneOf?: OpenApiSchema[];

View File

@ -1,8 +1,10 @@
import { WithEnumExtension } from './Extensions/WithEnumExtension';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverVariableObject
*/
export interface OpenApiServerVariable {
enum?: string[];
export interface OpenApiServerVariable extends WithEnumExtension {
enum?: (string | number)[];
default: string;
description?: string;
}

View File

@ -0,0 +1,17 @@
import { Enum } from '../../../client/interfaces/Enum';
import { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtension';
const KEY_ENUM_NAMES = 'x-enum-varnames';
const KEY_ENUM_DESCRIPTIONS = 'x-enum-descriptions';
export function extendEnum(enumerators: Enum[], definition: WithEnumExtension): Enum[] {
const names = definition[KEY_ENUM_NAMES];
const descriptions = definition[KEY_ENUM_DESCRIPTIONS];
return enumerators.map((enumerator, index) => ({
name: (names && names[index]) || enumerator.name,
description: (descriptions && descriptions[index]) || enumerator.description,
value: enumerator.value,
type: enumerator.type,
}));
}

View File

@ -0,0 +1,13 @@
import { EOL } from 'os';
import { getComment } from './getComment';
describe('getComment', () => {
it('should parse comments', () => {
const multiline = 'Testing multiline comments.' + EOL + ' * This must go to the next line.' + EOL + ' * ' + EOL + ' * This will contain a break.';
expect(getComment('')).toEqual(null);
expect(getComment('Hello')).toEqual('Hello');
expect(getComment('Hello World!')).toEqual('Hello World!');
expect(getComment('Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.')).toEqual(multiline);
expect(getComment('Testing multiline comments.\r\nThis must go to the next line.\r\n\r\nThis will contain a break.')).toEqual(multiline);
});
});

View File

@ -2,12 +2,7 @@ import { EOL } from 'os';
export function getComment(comment?: string): string | null {
if (comment) {
return comment
.split(/(\r\n|\n|\r)+/g)
.filter(line => line)
.map(line => line.trim())
.join(EOL)
.replace(/(\r\n|\n|\r)+/g, '$1 * ');
return comment.replace(/\r?\n(.*)/g, (_, w) => `${EOL} * ${w.trim()}`);
}
return null;
}

View File

@ -13,12 +13,14 @@ export function getEnum(values?: (string | number)[]): Enum[] {
name: `NUM_${value}`,
value: String(value),
type: PrimaryType.NUMBER,
description: null,
};
}
return {
name: value.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
value: `'${value}'`,
type: PrimaryType.STRING,
description: null,
};
});
}

View File

@ -17,6 +17,7 @@ export function getEnumFromDescription(description: string): Enum[] {
name: name.replace(/([a-z])([A-Z]+)/g, '$1_$2').toUpperCase(),
value: String(value),
type: PrimaryType.NUMBER,
description: null,
});
}
});

View File

@ -2,6 +2,7 @@ import { Model } from '../../../client/interfaces/Model';
import { OpenApi } from '../interfaces/OpenApi';
import { OpenApiSchema } from '../interfaces/OpenApiSchema';
import { PrimaryType } from './constants';
import { extendEnum } from './extendEnum';
import { getComment } from './getComment';
import { getEnum } from './getEnum';
import { getEnumFromDescription } from './getEnumFromDescription';
@ -42,11 +43,12 @@ export function getModel(openApi: OpenApi, definition: OpenApiSchema, isProperty
if (definition.enum) {
const enumerators = getEnum(definition.enum);
if (enumerators.length) {
const extendedEnumerators = extendEnum(enumerators, definition);
if (extendedEnumerators.length) {
model.export = 'enum';
model.type = PrimaryType.STRING;
model.base = PrimaryType.STRING;
model.enum.push(...enumerators);
model.enum.push(...extendedEnumerators);
model.default = getModelDefault(definition, model);
return model;
}

View File

@ -5,6 +5,11 @@
{{/if}}
export enum {{{name}}} {
{{#each enum}}
{{#if description}}
/**
* {{{description}}}
*/
{{/if}}
{{{name}}} = {{{value}}},
{{/each}}
}

View File

@ -435,6 +435,7 @@ export { DictionaryWithProperties } from './models/DictionaryWithProperties';
export { DictionaryWithReference } from './models/DictionaryWithReference';
export { DictionaryWithString } from './models/DictionaryWithString';
export { EnumFromDescription } from './models/EnumFromDescription';
export { EnumWithExtensions } from './models/EnumWithExtensions';
export { EnumWithNumbers } from './models/EnumWithNumbers';
export { EnumWithStrings } from './models/EnumWithStrings';
export { ModelLink } from './models/ModelLink';
@ -456,6 +457,7 @@ export { ModelWithOrderedProperties } from './models/ModelWithOrderedProperties'
export { ModelWithProperties } from './models/ModelWithProperties';
export { ModelWithReference } from './models/ModelWithReference';
export { ModelWithString } from './models/ModelWithString';
export { MultilineComment } from './models/MultilineComment';
export { SimpleBoolean } from './models/SimpleBoolean';
export { SimpleFile } from './models/SimpleFile';
export { SimpleInteger } from './models/SimpleInteger';
@ -474,6 +476,7 @@ export { $DictionaryWithProperties } from './schemas/$DictionaryWithProperties';
export { $DictionaryWithReference } from './schemas/$DictionaryWithReference';
export { $DictionaryWithString } from './schemas/$DictionaryWithString';
export { $EnumFromDescription } from './schemas/$EnumFromDescription';
export { $EnumWithExtensions } from './schemas/$EnumWithExtensions';
export { $EnumWithNumbers } from './schemas/$EnumWithNumbers';
export { $EnumWithStrings } from './schemas/$EnumWithStrings';
export { $ModelLink } from './schemas/$ModelLink';
@ -495,6 +498,7 @@ export { $ModelWithOrderedProperties } from './schemas/$ModelWithOrderedProperti
export { $ModelWithProperties } from './schemas/$ModelWithProperties';
export { $ModelWithReference } from './schemas/$ModelWithReference';
export { $ModelWithString } from './schemas/$ModelWithString';
export { $MultilineComment } from './schemas/$MultilineComment';
export { $SimpleBoolean } from './schemas/$SimpleBoolean';
export { $SimpleFile } from './schemas/$SimpleFile';
export { $SimpleInteger } from './schemas/$SimpleInteger';
@ -697,6 +701,32 @@ export enum EnumFromDescription {
}"
`;
exports[`generation v2 file(./test/result/v2/models/EnumWithExtensions.ts): ./test/result/v2/models/EnumWithExtensions.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/**
* This is a simple enum with numbers
*/
export enum EnumWithExtensions {
/**
* Used when the status of something is successful
*/
CUSTOM_SUCCESS = 200,
/**
* Used when the status of something has a warning
*/
CUSTOM_WARNING = 400,
/**
* Used when the status of something has an error
*/
CUSTOM_ERROR = 500,
}"
`;
exports[`generation v2 file(./test/result/v2/models/EnumWithNumbers.ts): ./test/result/v2/models/EnumWithNumbers.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
@ -1110,6 +1140,22 @@ export interface ModelWithString {
"
`;
exports[`generation v2 file(./test/result/v2/models/MultilineComment.ts): ./test/result/v2/models/MultilineComment.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/**
* Testing multiline comments.
* This must go to the next line.
*
* This will contain a break.
*/
export type MultilineComment = number;"
`;
exports[`generation v2 file(./test/result/v2/models/SimpleBoolean.ts): ./test/result/v2/models/SimpleBoolean.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
@ -1308,6 +1354,17 @@ export const $EnumFromDescription = {
};"
`;
exports[`generation v2 file(./test/result/v2/schemas/$EnumWithExtensions.ts): ./test/result/v2/schemas/$EnumWithExtensions.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
export const $EnumWithExtensions = {
type: 'Enum',
};"
`;
exports[`generation v2 file(./test/result/v2/schemas/$EnumWithNumbers.ts): ./test/result/v2/schemas/$EnumWithNumbers.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
@ -1688,6 +1745,17 @@ export const $ModelWithString = {
};"
`;
exports[`generation v2 file(./test/result/v2/schemas/$MultilineComment.ts): ./test/result/v2/schemas/$MultilineComment.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
export const $MultilineComment = {
type: 'number',
};"
`;
exports[`generation v2 file(./test/result/v2/schemas/$SimpleBoolean.ts): ./test/result/v2/schemas/$SimpleBoolean.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
@ -2637,6 +2705,7 @@ export { DictionaryWithProperties } from './models/DictionaryWithProperties';
export { DictionaryWithReference } from './models/DictionaryWithReference';
export { DictionaryWithString } from './models/DictionaryWithString';
export { EnumFromDescription } from './models/EnumFromDescription';
export { EnumWithExtensions } from './models/EnumWithExtensions';
export { EnumWithNumbers } from './models/EnumWithNumbers';
export { EnumWithStrings } from './models/EnumWithStrings';
export { ModelLink } from './models/ModelLink';
@ -2660,6 +2729,7 @@ export { ModelWithOrderedProperties } from './models/ModelWithOrderedProperties'
export { ModelWithProperties } from './models/ModelWithProperties';
export { ModelWithReference } from './models/ModelWithReference';
export { ModelWithString } from './models/ModelWithString';
export { MultilineComment } from './models/MultilineComment';
export { SimpleBoolean } from './models/SimpleBoolean';
export { SimpleFile } from './models/SimpleFile';
export { SimpleInteger } from './models/SimpleInteger';
@ -2678,6 +2748,7 @@ export { $DictionaryWithProperties } from './schemas/$DictionaryWithProperties';
export { $DictionaryWithReference } from './schemas/$DictionaryWithReference';
export { $DictionaryWithString } from './schemas/$DictionaryWithString';
export { $EnumFromDescription } from './schemas/$EnumFromDescription';
export { $EnumWithExtensions } from './schemas/$EnumWithExtensions';
export { $EnumWithNumbers } from './schemas/$EnumWithNumbers';
export { $EnumWithStrings } from './schemas/$EnumWithStrings';
export { $ModelLink } from './schemas/$ModelLink';
@ -2701,6 +2772,7 @@ export { $ModelWithOrderedProperties } from './schemas/$ModelWithOrderedProperti
export { $ModelWithProperties } from './schemas/$ModelWithProperties';
export { $ModelWithReference } from './schemas/$ModelWithReference';
export { $ModelWithString } from './schemas/$ModelWithString';
export { $MultilineComment } from './schemas/$MultilineComment';
export { $SimpleBoolean } from './schemas/$SimpleBoolean';
export { $SimpleFile } from './schemas/$SimpleFile';
export { $SimpleInteger } from './schemas/$SimpleInteger';
@ -2904,6 +2976,32 @@ export enum EnumFromDescription {
}"
`;
exports[`generation v3 file(./test/result/v3/models/EnumWithExtensions.ts): ./test/result/v3/models/EnumWithExtensions.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/**
* This is a simple enum with numbers
*/
export enum EnumWithExtensions {
/**
* Used when the status of something is successful
*/
CUSTOM_SUCCESS = 200,
/**
* Used when the status of something has a warning
*/
CUSTOM_WARNING = 400,
/**
* Used when the status of something has an error
*/
CUSTOM_ERROR = 500,
}"
`;
exports[`generation v3 file(./test/result/v3/models/EnumWithNumbers.ts): ./test/result/v3/models/EnumWithNumbers.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
@ -3358,6 +3456,22 @@ export interface ModelWithString {
"
`;
exports[`generation v3 file(./test/result/v3/models/MultilineComment.ts): ./test/result/v3/models/MultilineComment.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/**
* Testing multiline comments.
* This must go to the next line.
*
* This will contain a break.
*/
export type MultilineComment = number;"
`;
exports[`generation v3 file(./test/result/v3/models/SimpleBoolean.ts): ./test/result/v3/models/SimpleBoolean.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
@ -3556,6 +3670,17 @@ export const $EnumFromDescription = {
};"
`;
exports[`generation v3 file(./test/result/v3/schemas/$EnumWithExtensions.ts): ./test/result/v3/schemas/$EnumWithExtensions.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
export const $EnumWithExtensions = {
type: 'Enum',
};"
`;
exports[`generation v3 file(./test/result/v3/schemas/$EnumWithNumbers.ts): ./test/result/v3/schemas/$EnumWithNumbers.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
@ -3974,6 +4099,17 @@ export const $ModelWithString = {
};"
`;
exports[`generation v3 file(./test/result/v3/schemas/$MultilineComment.ts): ./test/result/v3/schemas/$MultilineComment.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
export const $MultilineComment = {
type: 'number',
};"
`;
exports[`generation v3 file(./test/result/v3/schemas/$SimpleBoolean.ts): ./test/result/v3/schemas/$SimpleBoolean.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */

View File

@ -495,6 +495,10 @@
}
},
"definitions": {
"MultilineComment": {
"description": "Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.",
"type": "integer"
},
"SimpleInteger": {
"description": "This is a simple number",
"type": "integer"
@ -535,6 +539,24 @@
"description": "Success=1,Warning=2,Error=3",
"type": "int"
},
"EnumWithExtensions": {
"description": "This is a simple enum with numbers",
"enum": [
200,
400,
500
],
"x-enum-varnames": [
"CUSTOM_SUCCESS",
"CUSTOM_WARNING",
"CUSTOM_ERROR"
],
"x-enum-descriptions": [
"Used when the status of something is successful",
"Used when the status of something has a warning",
"Used when the status of something has an error"
]
},
"ArrayWithNumbers": {
"description": "This is a simple array with numbers",
"type": "array",

View File

@ -885,6 +885,10 @@
},
"components": {
"schemas": {
"MultilineComment": {
"description": "Testing multiline comments.\nThis must go to the next line.\n\nThis will contain a break.",
"type": "integer"
},
"SimpleInteger": {
"description": "This is a simple number",
"type": "integer"
@ -925,6 +929,24 @@
"description": "Success=1,Warning=2,Error=3",
"type": "int"
},
"EnumWithExtensions": {
"description": "This is a simple enum with numbers",
"enum": [
200,
400,
500
],
"x-enum-varnames": [
"CUSTOM_SUCCESS",
"CUSTOM_WARNING",
"CUSTOM_ERROR"
],
"x-enum-descriptions": [
"Used when the status of something is successful",
"Used when the status of something has a warning",
"Used when the status of something has an error"
]
},
"ArrayWithNumbers": {
"description": "This is a simple array with numbers",
"type": "array",