diff --git a/docs/migrations/version-3-to-4.md b/docs/migrations/version-3-to-4.md index f220349738..e90a1dd2f9 100644 --- a/docs/migrations/version-3-to-4.md +++ b/docs/migrations/version-3-to-4.md @@ -2,6 +2,12 @@ This document contain all the breaking changes and migrations guidelines for adapting your code to the new version. +## Deprecation of `processor.interpreter` + +Since the early days we had the option to set `processorOptions.interpreter` options to change how JSON Schema is interpreted to Meta models. However, these options are more accurately part of the `processorOptions.jsonSchema` options. + +Use this instead going forward. + ## Fixed edge cases for camel case names Naming such as object properties using camel case formatting had an edge case where if they contained a number followed by an underscore and a letter it would be incorrectly formatted. This has been fixed in this version, which might mean properties, model names, etc that use camel case might be renamed. diff --git a/docs/presets.md b/docs/presets.md index 78ffbfaa18..1c75ae10b1 100644 --- a/docs/presets.md +++ b/docs/presets.md @@ -188,7 +188,7 @@ class Root { } ``` -### Ap/pre-pending to existng rendered content +### Ap/pre-pending to existing rendered content As the hello world example appended content, this time lets prepend some content to the properties. ```ts import { TypeScriptGenerator } from '@asyncapi/modelina'; @@ -199,7 +199,7 @@ const generator = new TypeScriptGenerator({ class: { property({ content }) { const description = '// Hello world!' - return `${description}\n${content}`; + return `${content}\n${description}`; } } } @@ -305,7 +305,7 @@ self({ dependencyManager, content }) { Some languages has specific helper functions, and some very basic interfaces, such as for Java. -In TypeScript because you can have different import syntaxes based on the module system such as [CJS](../examples/typescript-use-cjs/) or [ESM](../examples/typescript-use-esm/), therefore it provies a specific function `addTypeScriptDependency` that takes care of that logic, and you just have to remember `addTypeScriptDependency('ImportanWhat', 'FromWhere')`. +In TypeScript because you can have different import syntaxes based on the module system such as [CJS](../examples/typescript-use-cjs/) or [ESM](../examples/typescript-use-esm/), therefore it provides a specific function `addTypeScriptDependency` that takes care of that logic, and you just have to remember `addTypeScriptDependency('Import what', 'From where')`. ### Overriding the default preset diff --git a/examples/README.md b/examples/README.md index 332852ece6..5e84a61e02 100644 --- a/examples/README.md +++ b/examples/README.md @@ -46,9 +46,11 @@ These examples show a specific input and how they can be used: - [json-schema-draft7-from-object](./json-schema-draft7-from-object) - A basic example where a JSON Schema draft 7 JS object is used to generate models. - [json-schema-draft6-from-object](./json-schema-draft6-from-object) - A basic example where a JSON Schema draft 6 JS object is used to generate models. - [json-schema-draft4-from-object](./json-schema-draft4-from-object) - A basic example where a JSON Schema draft 4 JS object is used to generate models. +- [json-schema-single-enum-as-const](./json-schema-single-enum-as-const) - An advanced example that shows how to change how `enum` are interpreted when containing a single value. - [swagger2.0-from-object](./swagger2.0-from-object) - A basic example where a Swagger 2.0 JS object is used to generate models. - [meta-model](./meta-model) - A basic example how to provide a meta model for the generator + ## General examples These are examples that can be applied in all scenarios. - [include-custom-function](./include-custom-function) - A basic example where a custom function is included. diff --git a/examples/json-schema-single-enum-as-const/README.md b/examples/json-schema-single-enum-as-const/README.md new file mode 100644 index 0000000000..e503451261 --- /dev/null +++ b/examples/json-schema-single-enum-as-const/README.md @@ -0,0 +1,19 @@ +# JSON Schema interpret single enum as constant + +This example shows how to use the `interpretSingleEnumAsConst` option on the JSON Schema input processor to instead of interpreting `{enum: ['single value']}` as an enum, it will instead only be generated as a constant value. + +This ONLY applies when it's a single value. + +## How to run this example + +Run this example using: + +```sh +npm i && npm run start +``` + +If you are on Windows, use the `start:windows` script instead: + +```sh +npm i && npm run start:windows +``` diff --git a/examples/json-schema-single-enum-as-const/__snapshots__/index.spec.ts.snap b/examples/json-schema-single-enum-as-const/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..8957750ed2 --- /dev/null +++ b/examples/json-schema-single-enum-as-const/__snapshots__/index.spec.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render TEMPLATE and should log expected output to console 1`] = ` +Array [ + "class Root { + private _email?: 'test' = 'test'; + + constructor(input: { + + }) { + + } + + get email(): 'test' | undefined { return this._email; } +}", +] +`; diff --git a/examples/json-schema-single-enum-as-const/index.spec.ts b/examples/json-schema-single-enum-as-const/index.spec.ts new file mode 100644 index 0000000000..4787d8879e --- /dev/null +++ b/examples/json-schema-single-enum-as-const/index.spec.ts @@ -0,0 +1,15 @@ +const spy = jest.spyOn(global.console, 'log').mockImplementation(() => { + return; +}); +import { generate } from './index'; + +describe('Should be able to render TEMPLATE', () => { + afterAll(() => { + jest.restoreAllMocks(); + }); + test('and should log expected output to console', async () => { + await generate(); + expect(spy.mock.calls.length).toEqual(1); + expect(spy.mock.calls[0]).toMatchSnapshot(); + }); +}); diff --git a/examples/json-schema-single-enum-as-const/index.ts b/examples/json-schema-single-enum-as-const/index.ts new file mode 100644 index 0000000000..7b71cb324f --- /dev/null +++ b/examples/json-schema-single-enum-as-const/index.ts @@ -0,0 +1,30 @@ +import { TypeScriptGenerator } from '../../src'; + +const generator = new TypeScriptGenerator({ + processorOptions: { + jsonSchema: { + interpretSingleEnumAsConst: true + } + } +}); +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + additionalProperties: false, + properties: { + email: { + type: 'string', + enum: ['test'] + } + } +}; + +export async function generate(): Promise { + const models = await generator.generate(jsonSchemaDraft7); + for (const model of models) { + console.log(model.result); + } +} +if (require.main === module) { + generate(); +} diff --git a/examples/json-schema-single-enum-as-const/package-lock.json b/examples/json-schema-single-enum-as-const/package-lock.json new file mode 100644 index 0000000000..56ab4e873b --- /dev/null +++ b/examples/json-schema-single-enum-as-const/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "typescript-interface", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/json-schema-single-enum-as-const/package.json b/examples/json-schema-single-enum-as-const/package.json new file mode 100644 index 0000000000..e89a0f989c --- /dev/null +++ b/examples/json-schema-single-enum-as-const/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "json-schema-single-enum-as-const" }, + "scripts": { + "install": "cd ../.. && npm i", + "start": "../../node_modules/.bin/ts-node --cwd ../../ ./examples/$npm_package_config_example_name/index.ts", + "start:windows": "..\\..\\node_modules\\.bin\\ts-node --cwd ..\\..\\ .\\examples\\%npm_package_config_example_name%\\index.ts", + "test": "../../node_modules/.bin/jest --config=../../jest.config.js ./examples/$npm_package_config_example_name/index.spec.ts", + "test:windows": "..\\..\\node_modules\\.bin\\jest --config=..\\..\\jest.config.js examples/%npm_package_config_example_name%/index.spec.ts" + } +} diff --git a/src/helpers/CommonModelToMetaModel.ts b/src/helpers/CommonModelToMetaModel.ts index 946681b714..3bd452242d 100644 --- a/src/helpers/CommonModelToMetaModel.ts +++ b/src/helpers/CommonModelToMetaModel.ts @@ -18,14 +18,25 @@ import { AnyModel, MetaModelOptions } from '../models'; +import { JsonSchemaProcessorOptions } from '../processors'; -function getMetaModelOptions(commonModel: CommonModel): MetaModelOptions { +function getMetaModelOptions( + commonModel: CommonModel, + processorOptions: JsonSchemaProcessorOptions +): MetaModelOptions { const options: MetaModelOptions = {}; if (commonModel.const) { options.const = { originalInput: commonModel.const }; + } else if ( + processorOptions.interpretSingleEnumAsConst && + commonModel.enum?.length === 1 + ) { + options.const = { + originalInput: commonModel.enum[0] + }; } if (Array.isArray(commonModel.type) && commonModel.type.includes('null')) { @@ -47,89 +58,128 @@ function getMetaModelOptions(commonModel: CommonModel): MetaModelOptions { return options; } -export function convertToMetaModel( - jsonSchemaModel: CommonModel, - alreadySeenModels: Map = new Map() -): MetaModel { +interface ConverterContext { + name: string; + jsonSchemaModel: CommonModel; + options: JsonSchemaProcessorOptions; + alreadySeenModels: Map; +} + +export function convertToMetaModel(context: { + jsonSchemaModel: CommonModel; + options: JsonSchemaProcessorOptions; + alreadySeenModels: Map; +}): MetaModel { + const { jsonSchemaModel, alreadySeenModels = new Map(), options } = context; const hasModel = alreadySeenModels.has(jsonSchemaModel); if (hasModel) { return alreadySeenModels.get(jsonSchemaModel) as MetaModel; } - const modelName = jsonSchemaModel.$id || 'undefined'; + const name = jsonSchemaModel.$id || 'undefined'; - const unionModel = convertToUnionModel( - jsonSchemaModel, - modelName, - alreadySeenModels - ); + const unionModel = convertToUnionModel({ + ...context, + alreadySeenModels, + name + }); if (unionModel !== undefined) { return unionModel; } - const anyModel = convertToAnyModel(jsonSchemaModel, modelName); + const anyModel = convertToAnyModel({ + ...context, + alreadySeenModels, + name + }); if (anyModel !== undefined) { return anyModel; } - const enumModel = convertToEnumModel(jsonSchemaModel, modelName); + const enumModel = convertToEnumModel({ + ...context, + alreadySeenModels, + name + }); if (enumModel !== undefined) { return enumModel; } - const objectModel = convertToObjectModel( - jsonSchemaModel, - modelName, - alreadySeenModels - ); + const objectModel = convertToObjectModel({ + ...context, + alreadySeenModels, + name + }); if (objectModel !== undefined) { return objectModel; } - const dictionaryModel = convertToDictionaryModel( - jsonSchemaModel, - modelName, - alreadySeenModels - ); + const dictionaryModel = convertToDictionaryModel({ + ...context, + alreadySeenModels, + name + }); if (dictionaryModel !== undefined) { return dictionaryModel; } - const tupleModel = convertToTupleModel( - jsonSchemaModel, - modelName, - alreadySeenModels - ); + const tupleModel = convertToTupleModel({ + ...context, + alreadySeenModels, + name + }); if (tupleModel !== undefined) { return tupleModel; } - const arrayModel = convertToArrayModel( - jsonSchemaModel, - modelName, - alreadySeenModels - ); + const arrayModel = convertToArrayModel({ + ...context, + alreadySeenModels, + name + }); if (arrayModel !== undefined) { return arrayModel; } - const stringModel = convertToStringModel(jsonSchemaModel, modelName); + const stringModel = convertToStringModel({ + ...context, + alreadySeenModels, + name + }); if (stringModel !== undefined) { return stringModel; } - const floatModel = convertToFloatModel(jsonSchemaModel, modelName); + const floatModel = convertToFloatModel({ + ...context, + alreadySeenModels, + name + }); if (floatModel !== undefined) { return floatModel; } - const integerModel = convertToIntegerModel(jsonSchemaModel, modelName); + const integerModel = convertToIntegerModel({ + ...context, + alreadySeenModels, + name + }); if (integerModel !== undefined) { return integerModel; } - const booleanModel = convertToBooleanModel(jsonSchemaModel, modelName); + const booleanModel = convertToBooleanModel({ + ...context, + alreadySeenModels, + name + }); if (booleanModel !== undefined) { return booleanModel; } Logger.warn('Failed to convert to MetaModel, defaulting to AnyModel'); return new AnyModel( - modelName, + name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel) + getMetaModelOptions(jsonSchemaModel, options) ); } -function isEnumModel(jsonSchemaModel: CommonModel): boolean { - if (!Array.isArray(jsonSchemaModel.enum)) { +function isEnumModel( + jsonSchemaModel: CommonModel, + interpretSingleEnumAsConst: boolean = false +): boolean { + if ( + !Array.isArray(jsonSchemaModel.enum) || + (jsonSchemaModel.enum.length <= 1 && interpretSingleEnumAsConst) + ) { return false; } return true; @@ -157,10 +207,9 @@ function shouldBeAnyType(jsonSchemaModel: CommonModel): boolean { */ // eslint-disable-next-line sonarjs/cognitive-complexity export function convertToUnionModel( - jsonSchemaModel: CommonModel, - name: string, - alreadySeenModels: Map + context: ConverterContext ): UnionModel | undefined { + const { jsonSchemaModel, alreadySeenModels, options, name } = context; const containsUnions = Array.isArray(jsonSchemaModel.union); // Should not create union from two types where one is null @@ -186,7 +235,7 @@ export function convertToUnionModel( const unionModel = new UnionModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), [] ); @@ -206,10 +255,11 @@ export function convertToUnionModel( if (isSingleNullType) { unionModel.options.isNullable = true; } else { - const unionMetaModel = convertToMetaModel( - unionCommonModel, - alreadySeenModels - ); + const unionMetaModel = convertToMetaModel({ + alreadySeenModels, + jsonSchemaModel: unionCommonModel, + options + }); unionModel.union.push(unionMetaModel); } } @@ -218,83 +268,93 @@ export function convertToUnionModel( // Has simple union types // Each must have a different name then the root union model, as it otherwise clashes when code is generated - const enumModel = convertToEnumModel(jsonSchemaModel, `${name}_enum`); + const enumModel = convertToEnumModel({ + ...context, + alreadySeenModels, + name: `${name}_enum` + }); if (enumModel !== undefined) { unionModel.union.push(enumModel); } - const objectModel = convertToObjectModel( - jsonSchemaModel, - `${name}_object`, - alreadySeenModels - ); + const objectModel = convertToObjectModel({ + ...context, + alreadySeenModels, + name: `${name}_object` + }); if (objectModel !== undefined) { unionModel.union.push(objectModel); } - const dictionaryModel = convertToDictionaryModel( - jsonSchemaModel, - `${name}_dictionary`, - alreadySeenModels - ); + const dictionaryModel = convertToDictionaryModel({ + ...context, + alreadySeenModels, + name: `${name}_dictionary` + }); if (dictionaryModel !== undefined) { unionModel.union.push(dictionaryModel); } - const tupleModel = convertToTupleModel( - jsonSchemaModel, - `${name}_tuple`, - alreadySeenModels - ); + const tupleModel = convertToTupleModel({ + ...context, + alreadySeenModels, + name: `${name}_tuple` + }); if (tupleModel !== undefined) { unionModel.union.push(tupleModel); } - const arrayModel = convertToArrayModel( - jsonSchemaModel, - `${name}_array`, - alreadySeenModels - ); + const arrayModel = convertToArrayModel({ + ...context, + alreadySeenModels, + name: `${name}_array` + }); if (arrayModel !== undefined) { unionModel.union.push(arrayModel); } - const stringModel = convertToStringModel(jsonSchemaModel, `${name}_string`); + const stringModel = convertToStringModel({ + ...context, + name: `${name}_string` + }); if (stringModel !== undefined) { unionModel.union.push(stringModel); } - const floatModel = convertToFloatModel(jsonSchemaModel, `${name}_float`); + const floatModel = convertToFloatModel({ + ...context, + name: `${name}_float` + }); if (floatModel !== undefined) { unionModel.union.push(floatModel); } - const integerModel = convertToIntegerModel( - jsonSchemaModel, - `${name}_integer` - ); + const integerModel = convertToIntegerModel({ + ...context, + name: `${name}_integer` + }); if (integerModel !== undefined) { unionModel.union.push(integerModel); } - const booleanModel = convertToBooleanModel( - jsonSchemaModel, - `${name}_boolean` - ); + const booleanModel = convertToBooleanModel({ + ...context, + name: `${name}_boolean` + }); if (booleanModel !== undefined) { unionModel.union.push(booleanModel); } return unionModel; } export function convertToStringModel( - jsonSchemaModel: CommonModel, - name: string + context: ConverterContext ): StringModel | undefined { + const { jsonSchemaModel, options, name } = context; if (!jsonSchemaModel.type?.includes('string')) { return undefined; } return new StringModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel) + getMetaModelOptions(jsonSchemaModel, options) ); } export function convertToAnyModel( - jsonSchemaModel: CommonModel, - name: string + context: ConverterContext ): AnyModel | undefined { + const { jsonSchemaModel, options, name } = context; const isAnyType = shouldBeAnyType(jsonSchemaModel); if (!Array.isArray(jsonSchemaModel.type) || !isAnyType) { return undefined; @@ -302,40 +362,40 @@ export function convertToAnyModel( return new AnyModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel) + getMetaModelOptions(jsonSchemaModel, options) ); } export function convertToIntegerModel( - jsonSchemaModel: CommonModel, - name: string + context: ConverterContext ): IntegerModel | undefined { + const { jsonSchemaModel, options, name } = context; if (!jsonSchemaModel.type?.includes('integer')) { return undefined; } return new IntegerModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel) + getMetaModelOptions(jsonSchemaModel, options) ); } export function convertToFloatModel( - jsonSchemaModel: CommonModel, - name: string + context: ConverterContext ): FloatModel | undefined { + const { jsonSchemaModel, options, name } = context; if (!jsonSchemaModel.type?.includes('number')) { return undefined; } return new FloatModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel) + getMetaModelOptions(jsonSchemaModel, options) ); } export function convertToEnumModel( - jsonSchemaModel: CommonModel, - name: string + context: ConverterContext ): EnumModel | undefined { - if (!isEnumModel(jsonSchemaModel)) { + const { jsonSchemaModel, options, name } = context; + if (!isEnumModel(jsonSchemaModel, options.interpretSingleEnumAsConst)) { return undefined; } @@ -349,7 +409,7 @@ export function convertToEnumModel( const metaModel = new EnumModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), [] ); @@ -362,16 +422,16 @@ export function convertToEnumModel( return metaModel; } export function convertToBooleanModel( - jsonSchemaModel: CommonModel, - name: string + context: ConverterContext ): BooleanModel | undefined { + const { jsonSchemaModel, options, name } = context; if (!jsonSchemaModel.type?.includes('boolean')) { return undefined; } return new BooleanModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel) + getMetaModelOptions(jsonSchemaModel, options) ); } @@ -413,17 +473,14 @@ function getOriginalInputFromAdditionalAndPatterns( /** * Function creating the right meta model based on additionalProperties and patternProperties. */ -function convertAdditionalAndPatterns( - jsonSchemaModel: CommonModel, - name: string, - alreadySeenModels: Map -) { +function convertAdditionalAndPatterns(context: ConverterContext) { + const { jsonSchemaModel, options, name } = context; const modelsAsValue = new Map(); if (jsonSchemaModel.additionalProperties !== undefined) { - const additionalPropertyModel = convertToMetaModel( - jsonSchemaModel.additionalProperties, - alreadySeenModels - ); + const additionalPropertyModel = convertToMetaModel({ + ...context, + jsonSchemaModel: jsonSchemaModel.additionalProperties + }); modelsAsValue.set(additionalPropertyModel.name, additionalPropertyModel); } @@ -431,7 +488,10 @@ function convertAdditionalAndPatterns( for (const patternModel of Object.values( jsonSchemaModel.patternProperties )) { - const patternPropertyModel = convertToMetaModel(patternModel); + const patternPropertyModel = convertToMetaModel({ + ...context, + jsonSchemaModel: patternModel + }); modelsAsValue.set(patternPropertyModel.name, patternPropertyModel); } } @@ -441,17 +501,16 @@ function convertAdditionalAndPatterns( return new UnionModel( name, getOriginalInputFromAdditionalAndPatterns(jsonSchemaModel), - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), Array.from(modelsAsValue.values()) ); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion export function convertToDictionaryModel( - jsonSchemaModel: CommonModel, - name: string, - alreadySeenModels: Map + context: ConverterContext ): DictionaryModel | undefined { + const { jsonSchemaModel, options, name } = context; if (!isDictionary(jsonSchemaModel)) { return undefined; } @@ -460,17 +519,13 @@ export function convertToDictionaryModel( const keyModel = new StringModel( name, originalInput, - getMetaModelOptions(jsonSchemaModel) - ); - const valueModel = convertAdditionalAndPatterns( - jsonSchemaModel, - name, - alreadySeenModels + getMetaModelOptions(jsonSchemaModel, options) ); + const valueModel = convertAdditionalAndPatterns(context); return new DictionaryModel( name, originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), keyModel, valueModel, 'normal' @@ -478,10 +533,9 @@ export function convertToDictionaryModel( } export function convertToObjectModel( - jsonSchemaModel: CommonModel, - name: string, - alreadySeenModels: Map + context: ConverterContext ): ObjectModel | undefined { + const { jsonSchemaModel, alreadySeenModels, options, name } = context; if ( !jsonSchemaModel.type?.includes('object') || isDictionary(jsonSchemaModel) @@ -492,7 +546,7 @@ export function convertToObjectModel( const metaModel = new ObjectModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), {} ); //cache model before continuing @@ -508,7 +562,7 @@ export function convertToObjectModel( const propertyModel = new ObjectPropertyModel( propertyName, isRequired, - convertToMetaModel(prop, alreadySeenModels) + convertToMetaModel({ ...context, jsonSchemaModel: prop }) ); metaModel.properties[String(propertyName)] = propertyModel; @@ -519,7 +573,7 @@ export function convertToObjectModel( for (const extend of jsonSchemaModel.extend) { metaModel.options.extend.push( - convertToMetaModel(extend, alreadySeenModels) + convertToMetaModel({ ...context, jsonSchemaModel: extend }) ); } } @@ -537,17 +591,16 @@ export function convertToObjectModel( const keyModel = new StringModel( propertyName, originalInput, - getMetaModelOptions(jsonSchemaModel) - ); - const valueModel = convertAdditionalAndPatterns( - jsonSchemaModel, - propertyName, - alreadySeenModels + getMetaModelOptions(jsonSchemaModel, options) ); + const valueModel = convertAdditionalAndPatterns({ + ...context, + name: propertyName + }); const dictionaryModel = new DictionaryModel( propertyName, originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), keyModel, valueModel, 'unwrap' @@ -563,10 +616,9 @@ export function convertToObjectModel( } export function convertToArrayModel( - jsonSchemaModel: CommonModel, - name: string, - alreadySeenModels: Map + context: ConverterContext ): ArrayModel | undefined { + const { jsonSchemaModel, alreadySeenModels, options, name } = context; if (!jsonSchemaModel.type?.includes('array')) { return undefined; } @@ -578,20 +630,20 @@ export function convertToArrayModel( const placeholderModel = new AnyModel( '', undefined, - getMetaModelOptions(jsonSchemaModel) + getMetaModelOptions(jsonSchemaModel, options) ); const metaModel = new ArrayModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), placeholderModel ); alreadySeenModels.set(jsonSchemaModel, metaModel); if (jsonSchemaModel.items !== undefined) { - const valueModel = convertToMetaModel( - jsonSchemaModel.items as CommonModel, - alreadySeenModels - ); + const valueModel = convertToMetaModel({ + ...context, + jsonSchemaModel: jsonSchemaModel.items as CommonModel + }); metaModel.valueModel = valueModel; } return metaModel; @@ -600,13 +652,13 @@ export function convertToArrayModel( const valueModel = new UnionModel( 'union', jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), [] ); const metaModel = new ArrayModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), valueModel ); alreadySeenModels.set(jsonSchemaModel, metaModel); @@ -614,24 +666,26 @@ export function convertToArrayModel( for (const itemModel of Array.isArray(jsonSchemaModel.items) ? jsonSchemaModel.items : [jsonSchemaModel.items]) { - const itemsModel = convertToMetaModel(itemModel, alreadySeenModels); + const itemsModel = convertToMetaModel({ + ...context, + jsonSchemaModel: itemModel + }); valueModel.union.push(itemsModel); } } if (jsonSchemaModel.additionalItems !== undefined) { - const itemsModel = convertToMetaModel( - jsonSchemaModel.additionalItems, - alreadySeenModels - ); + const itemsModel = convertToMetaModel({ + ...context, + jsonSchemaModel: jsonSchemaModel.additionalItems + }); valueModel.union.push(itemsModel); } return metaModel; } export function convertToTupleModel( - jsonSchemaModel: CommonModel, - name: string, - alreadySeenModels: Map + context: ConverterContext ): TupleModel | undefined { + const { jsonSchemaModel, alreadySeenModels, options, name } = context; const isTuple = jsonSchemaModel.type?.includes('array') && Array.isArray(jsonSchemaModel.items) && @@ -645,13 +699,16 @@ export function convertToTupleModel( const tupleModel = new TupleModel( name, jsonSchemaModel.originalInput, - getMetaModelOptions(jsonSchemaModel), + getMetaModelOptions(jsonSchemaModel, options), [] ); alreadySeenModels.set(jsonSchemaModel, tupleModel); for (let i = 0; i < items.length; i++) { const item = items[Number(i)]; - const valueModel = convertToMetaModel(item, alreadySeenModels); + const valueModel = convertToMetaModel({ + ...context, + jsonSchemaModel: item + }); const tupleValueModel = new TupleValueModel(i, valueModel); tupleModel.tuple[Number(i)] = tupleValueModel; } diff --git a/src/models/ProcessorOptions.ts b/src/models/ProcessorOptions.ts index 7f5d237048..abdb729699 100644 --- a/src/models/ProcessorOptions.ts +++ b/src/models/ProcessorOptions.ts @@ -1,9 +1,16 @@ import { ParseOptions } from '@asyncapi/parser'; import { InterpreterOptions } from '../interpreter/Interpreter'; -import { TypeScriptInputProcessorOptions } from '../processors/index'; +import { + JsonSchemaProcessorOptions, + TypeScriptInputProcessorOptions +} from '../processors/index'; export interface ProcessorOptions { asyncapi?: ParseOptions; typescript?: TypeScriptInputProcessorOptions; + jsonSchema?: JsonSchemaProcessorOptions; + /** + * @deprecated Use the `jsonSchema` options instead of `interpreter` + */ interpreter?: InterpreterOptions; } diff --git a/src/processors/AsyncAPIInputProcessor.ts b/src/processors/AsyncAPIInputProcessor.ts index 1529e284c8..6c9e41f0de 100644 --- a/src/processors/AsyncAPIInputProcessor.ts +++ b/src/processors/AsyncAPIInputProcessor.ts @@ -15,7 +15,6 @@ import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; import { InputMetaModel, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import { AsyncapiV2Schema } from '../models/AsyncapiV2Schema'; -import { convertToMetaModel } from '../helpers'; import fs from 'fs'; import { fileURLToPath } from 'url'; import { @@ -104,24 +103,17 @@ export class AsyncAPIInputProcessor extends AbstractInputProcessor { const addToInputModel = (payload: AsyncAPISchemaInterface) => { const schema = AsyncAPIInputProcessor.convertToInternalSchema(payload); - const newCommonModel = - JsonSchemaInputProcessor.convertSchemaToCommonModel(schema, options); - - if (newCommonModel.$id !== undefined) { - if (inputModel.models[newCommonModel.$id] !== undefined) { - Logger.warn( - `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, - newCommonModel - ); - } - const metaModel = convertToMetaModel(newCommonModel); - inputModel.models[metaModel.name] = metaModel; - } else { + const newMetaModel = JsonSchemaInputProcessor.convertSchemaToMetaModel( + schema, + options + ); + if (inputModel.models[newMetaModel.name] !== undefined) { Logger.warn( - 'Model did not have $id which is required, ignoring.', - newCommonModel + `Overwriting existing model with name ${newMetaModel.name}, are there two models with the same name present? Overwriting the old model.`, + newMetaModel.name ); } + inputModel.models[newMetaModel.name] = newMetaModel; }; // Go over all the message payloads and convert them to models diff --git a/src/processors/JsonSchemaInputProcessor.ts b/src/processors/JsonSchemaInputProcessor.ts index e5b9071464..46382be54c 100644 --- a/src/processors/JsonSchemaInputProcessor.ts +++ b/src/processors/JsonSchemaInputProcessor.ts @@ -10,12 +10,28 @@ import { SwaggerV2Schema, OpenapiV3Schema, AsyncapiV2Schema, - ProcessorOptions + ProcessorOptions, + MetaModel } from '../models'; import { Logger } from '../utils'; -import { Interpreter } from '../interpreter/Interpreter'; +import { Interpreter, InterpreterOptions } from '../interpreter/Interpreter'; import { convertToMetaModel } from '../helpers'; import { ParserOptions } from '@apidevtools/json-schema-ref-parser/dist/lib/options'; +export interface JsonSchemaProcessorOptions extends InterpreterOptions { + /** + * This option enables that a single enum value `{enum: ['test']}` is interpreted the same as if the value was `{const: 'test'}` + * Use this option to reduce the number of enums being created and use constant values instead. + */ + interpretSingleEnumAsConst?: boolean; +} + +export const defaultJsonSchemaProcessorOptions: JsonSchemaProcessorOptions = { + allowInheritance: false, + disableCache: false, + ignoreAdditionalItems: false, + ignoreAdditionalProperties: false, + interpretSingleEnumAsConst: false +}; /** * Class for processing JSON Schema @@ -88,11 +104,10 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { ); input = await this.dereferenceInputs(input); const parsedSchema = Draft7Schema.toSchema(input); - const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + const metaModel = JsonSchemaInputProcessor.convertSchemaToMetaModel( parsedSchema, options ); - const metaModel = convertToMetaModel(newCommonModel); inputModel.models[metaModel.name] = metaModel; Logger.debug('Completed processing input as JSON Schema draft 7 document'); return inputModel; @@ -118,11 +133,10 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { ); input = await this.dereferenceInputs(input); const parsedSchema = Draft4Schema.toSchema(input); - const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + const metaModel = JsonSchemaInputProcessor.convertSchemaToMetaModel( parsedSchema, options ); - const metaModel = convertToMetaModel(newCommonModel); inputModel.models[metaModel.name] = metaModel; Logger.debug('Completed processing input as JSON Schema draft 4 document'); return inputModel; @@ -148,11 +162,10 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { ); input = await this.dereferenceInputs(input); const parsedSchema = Draft6Schema.toSchema(input); - const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + const metaModel = JsonSchemaInputProcessor.convertSchemaToMetaModel( parsedSchema, options ); - const metaModel = convertToMetaModel(newCommonModel); inputModel.models[metaModel.name] = metaModel; Logger.debug('Completed processing input as JSON Schema draft 6 document'); return inputModel; @@ -490,10 +503,37 @@ export class JsonSchemaInputProcessor extends AbstractInputProcessor { options?: ProcessorOptions ): CommonModel { const interpreter = new Interpreter(); - const model = interpreter.interpret(schema, options?.interpreter); + const model = interpreter.interpret( + schema, + options?.jsonSchema ?? options?.interpreter + ); if (model === undefined) { throw new Error('Could not interpret schema to internal model'); } return model; } + + /** + * Simplifies a JSON Schema into a common models + * + * @param schema to simplify to common model + */ + static convertSchemaToMetaModel( + schema: + | Draft4Schema + | Draft6Schema + | Draft7Schema + | SwaggerV2Schema + | OpenapiV3Schema + | AsyncapiV2Schema + | boolean, + options?: ProcessorOptions + ): MetaModel { + const commonModel = this.convertSchemaToCommonModel(schema, options); + return convertToMetaModel({ + jsonSchemaModel: commonModel, + options: { ...defaultJsonSchemaProcessorOptions, ...options?.jsonSchema }, + alreadySeenModels: new Map() + }); + } } diff --git a/src/processors/OpenAPIInputProcessor.ts b/src/processors/OpenAPIInputProcessor.ts index 2d9f2f5153..8338716db0 100644 --- a/src/processors/OpenAPIInputProcessor.ts +++ b/src/processors/OpenAPIInputProcessor.ts @@ -4,7 +4,6 @@ import { InputMetaModel, OpenapiV3Schema, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import SwaggerParser from '@apidevtools/swagger-parser'; import { OpenAPIV3, OpenAPIV3_1 } from 'openapi-types'; -import { convertToMetaModel } from '../helpers'; /** * Class for processing OpenAPI V3.0 inputs @@ -246,22 +245,17 @@ export class OpenAPIInputProcessor extends AbstractInputProcessor { schema, name ); - const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + const newMetaModel = JsonSchemaInputProcessor.convertSchemaToMetaModel( internalSchema, options ); - if (newCommonModel.$id !== undefined) { - if (inputModel.models[newCommonModel.$id] !== undefined) { - Logger.warn( - `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, - newCommonModel - ); - } - const metaModel = convertToMetaModel(newCommonModel); - inputModel.models[metaModel.name] = metaModel; - } else { - Logger.warn('Model did not have $id, ignoring.', newCommonModel); + if (inputModel.models[newMetaModel.name] !== undefined) { + Logger.warn( + `Overwriting existing model with name ${newMetaModel.name}, are there two models with the same name present? Overwriting the old model.`, + newMetaModel.name + ); } + inputModel.models[newMetaModel.name] = newMetaModel; } /** diff --git a/src/processors/SwaggerInputProcessor.ts b/src/processors/SwaggerInputProcessor.ts index 2605b24334..954c039b7f 100644 --- a/src/processors/SwaggerInputProcessor.ts +++ b/src/processors/SwaggerInputProcessor.ts @@ -4,7 +4,6 @@ import { InputMetaModel, SwaggerV2Schema, ProcessorOptions } from '../models'; import { Logger } from '../utils'; import SwaggerParser from '@apidevtools/swagger-parser'; import { OpenAPIV2 } from 'openapi-types'; -import { convertToMetaModel } from '../helpers'; /** * Class for processing Swagger inputs @@ -147,22 +146,17 @@ export class SwaggerInputProcessor extends AbstractInputProcessor { schema, name ); - const newCommonModel = JsonSchemaInputProcessor.convertSchemaToCommonModel( + const newMetaModel = JsonSchemaInputProcessor.convertSchemaToMetaModel( internalSchema, options ); - if (newCommonModel.$id !== undefined) { - if (inputModel.models[newCommonModel.$id] !== undefined) { - Logger.warn( - `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, - newCommonModel - ); - } - const metaModel = convertToMetaModel(newCommonModel); - inputModel.models[metaModel.name] = metaModel; - } else { - Logger.warn('Model did not have $id, ignoring.', newCommonModel); + if (inputModel.models[newMetaModel.name] !== undefined) { + Logger.warn( + `Overwriting existing model with name ${newMetaModel.name}, are there two models with the same name present? Overwriting the old model.`, + newMetaModel.name + ); } + inputModel.models[newMetaModel.name] = newMetaModel; } /** diff --git a/src/processors/TypeScriptInputProcessor.ts b/src/processors/TypeScriptInputProcessor.ts index 71dcc71412..7f50870300 100644 --- a/src/processors/TypeScriptInputProcessor.ts +++ b/src/processors/TypeScriptInputProcessor.ts @@ -4,7 +4,6 @@ import ts from 'typescript'; import * as TJS from 'typescript-json-schema'; import { JsonSchemaInputProcessor } from './JsonSchemaInputProcessor'; import { AbstractInputProcessor } from './AbstractInputProcessor'; -import { convertToMetaModel } from '../helpers'; import { Logger } from '../utils'; /** Class for processing Typescript code inputs to Common module*/ @@ -99,23 +98,17 @@ export class TypeScriptInputProcessor extends AbstractInputProcessor { ); if (generatedSchemas) { for (const schema of generatedSchemas) { - const newCommonModel = - JsonSchemaInputProcessor.convertSchemaToCommonModel( - schema as any, - options + const newMetaModel = JsonSchemaInputProcessor.convertSchemaToMetaModel( + schema as any, + options + ); + if (inputModel.models[newMetaModel.name] !== undefined) { + Logger.warn( + `Overwriting existing model with name ${newMetaModel.name}, are there two models with the same name present? Overwriting the old model.`, + newMetaModel.name ); - if (newCommonModel.$id !== undefined) { - if (inputModel.models[newCommonModel.$id] !== undefined) { - Logger.warn( - `Overwriting existing model with $id ${newCommonModel.$id}, are there two models with the same id present?`, - newCommonModel - ); - } - const metaModel = convertToMetaModel(newCommonModel); - inputModel.models[metaModel.name] = metaModel; - } else { - Logger.warn('Model did not have $id, ignoring.', newCommonModel); } + inputModel.models[newMetaModel.name] = newMetaModel; } } return Promise.resolve(inputModel); diff --git a/test/generators/java/presets/DescriptionPreset.spec.ts b/test/generators/java/presets/DescriptionPreset.spec.ts index 3d6634e684..4c86c30ad5 100644 --- a/test/generators/java/presets/DescriptionPreset.spec.ts +++ b/test/generators/java/presets/DescriptionPreset.spec.ts @@ -45,7 +45,7 @@ describe('JAVA_DESCRIPTION_PRESET', () => { expect(models[0].dependencies).toEqual([]); }); - test('should not render anything when isExtended is true and model is discriminator or dictionary', async () => { + test('should not render anything when allowInheritance is true and model is discriminator or dictionary', async () => { const asyncapiDoc = { asyncapi: '2.6.0', info: { diff --git a/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap b/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap index 848955ac55..9d39a26e70 100644 --- a/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap +++ b/test/generators/java/presets/__snapshots__/DescriptionPreset.spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`JAVA_DESCRIPTION_PRESET should not render anything when isExtended is true and model is discriminator or dictionary 1`] = ` +exports[`JAVA_DESCRIPTION_PRESET should not render anything when allowInheritance is true and model is discriminator or dictionary 1`] = ` Array [ "public class ExtendDoc implements Extend { private final DiscriminatorTest type = DiscriminatorTest.EXTEND_DOC; diff --git a/test/helpers/CommonModelToMetaModel.spec.ts b/test/helpers/CommonModelToMetaModel.spec.ts index dea6b7be29..db80923c02 100644 --- a/test/helpers/CommonModelToMetaModel.spec.ts +++ b/test/helpers/CommonModelToMetaModel.spec.ts @@ -14,6 +14,10 @@ import { } from '../../src'; import { convertToMetaModel } from '../../src/helpers'; describe('CommonModelToMetaModel', () => { + const defaultOptions = { + options: {}, + alreadySeenModels: new Map() + }; afterEach(() => { jest.restoreAllMocks(); }); @@ -23,7 +27,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.type = ['string', 'null']; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof StringModel).toEqual(true); @@ -34,7 +41,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.type = ['string']; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof StringModel).toEqual(true); @@ -50,7 +60,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.union = [cm1, cm2]; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof UnionModel).toEqual(true); @@ -69,7 +82,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.union = [cm1, cm2]; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof UnionModel).toEqual(true); @@ -82,7 +98,10 @@ describe('CommonModelToMetaModel', () => { const cm = new CommonModel(); cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof AnyModel).toEqual(true); @@ -100,7 +119,10 @@ describe('CommonModelToMetaModel', () => { ]; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof AnyModel).toEqual(true); @@ -110,7 +132,10 @@ describe('CommonModelToMetaModel', () => { cm.type = ['string', 'number', 'integer', 'boolean', 'object', 'array']; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof AnyModel).toEqual(true); @@ -120,14 +145,18 @@ describe('CommonModelToMetaModel', () => { const cm = new CommonModel(); cm.type = 'string'; cm.$id = 'test'; - cm.enum = ['test']; + cm.enum = ['test', 'test2']; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof EnumModel).toEqual(true); - expect((model as EnumModel).values.length).toEqual(1); + expect((model as EnumModel).values.length).toEqual(2); expect((model as EnumModel).values[0].key).toEqual('test'); + expect((model as EnumModel).values[1].key).toEqual('test2'); }); test('when different types of values', () => { const cm = new CommonModel(); @@ -135,7 +164,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.enum = [{ test: 1 }, 123, 'test', true]; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof EnumModel).toEqual(true); @@ -151,7 +183,10 @@ describe('CommonModelToMetaModel', () => { cm.type = 'string'; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof StringModel).toEqual(true); @@ -161,7 +196,10 @@ describe('CommonModelToMetaModel', () => { cm.type = 'number'; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof FloatModel).toEqual(true); @@ -171,7 +209,10 @@ describe('CommonModelToMetaModel', () => { cm.type = 'integer'; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof IntegerModel).toEqual(true); @@ -181,7 +222,10 @@ describe('CommonModelToMetaModel', () => { cm.type = 'boolean'; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof BooleanModel).toEqual(true); @@ -191,7 +235,10 @@ describe('CommonModelToMetaModel', () => { cm.type = 'object'; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ObjectModel).toEqual(true); @@ -201,7 +248,10 @@ describe('CommonModelToMetaModel', () => { cm.type = 'array'; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ArrayModel).toEqual(true); @@ -217,7 +267,10 @@ describe('CommonModelToMetaModel', () => { test: spm }; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ObjectModel).toEqual(true); @@ -233,7 +286,10 @@ describe('CommonModelToMetaModel', () => { }; cm.additionalProperties = spm; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ObjectModel).toEqual(true); @@ -252,7 +308,10 @@ describe('CommonModelToMetaModel', () => { }; cm.additionalProperties = spm; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ObjectModel).toEqual(true); @@ -272,7 +331,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.extend = [extend]; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model instanceof ObjectModel).toEqual(true); expect(model.options.extend?.length).toEqual(1); @@ -297,7 +359,10 @@ describe('CommonModelToMetaModel', () => { }; cm.additionalProperties = booleanCM; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ObjectModel).toEqual(true); @@ -333,7 +398,10 @@ describe('CommonModelToMetaModel', () => { }; cm.additionalProperties = booleanCM; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof DictionaryModel).toEqual(true); @@ -356,7 +424,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.items = spm; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ArrayModel).toEqual(true); @@ -373,7 +444,10 @@ describe('CommonModelToMetaModel', () => { cm.items = spm; cm.additionalItems = spm; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ArrayModel).toEqual(true); @@ -386,7 +460,10 @@ describe('CommonModelToMetaModel', () => { cm.type = ['string', 'number']; cm.$id = 'test'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof UnionModel).toEqual(true); @@ -400,7 +477,10 @@ describe('CommonModelToMetaModel', () => { const dog = new CommonModel(); dog.$id = 'Dog'; cm.union = [cat, dog]; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof UnionModel).toEqual(true); expect((model as UnionModel).union.length).toEqual(2); @@ -413,7 +493,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.items = [scm, scm]; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof TupleModel).toEqual(true); @@ -427,7 +510,10 @@ describe('CommonModelToMetaModel', () => { test: cm }; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof ObjectModel).toEqual(true); @@ -437,10 +523,13 @@ describe('CommonModelToMetaModel', () => { const cm = new CommonModel(); cm.$id = 'test'; cm.type = 'string'; - cm.enum = ['testConst']; + cm.enum = ['testConst', 'testConst2']; cm.const = 'testConst'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof EnumModel).toEqual(true); @@ -448,17 +537,39 @@ describe('CommonModelToMetaModel', () => { { key: cm.const, value: cm.const + }, + { + key: 'testConst2', + value: 'testConst2' } ]); expect(model.options.const?.originalInput).toEqual(cm.const); }); + test('should handle single enums as const with option', () => { + const cm = new CommonModel(); + cm.$id = 'test'; + cm.enum = ['testConst']; + + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm, + options: { interpretSingleEnumAsConst: true } + }); + + expect(model).not.toBeUndefined(); + expect(model instanceof AnyModel).toEqual(true); + expect(model.options.const?.originalInput).toEqual(cm.enum[0]); + }); test('should handle const', () => { const cm = new CommonModel(); cm.$id = 'test'; cm.const = 'testConst'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof AnyModel).toEqual(true); @@ -470,7 +581,10 @@ describe('CommonModelToMetaModel', () => { cm.$id = 'test'; cm.discriminator = 'testDiscriminator'; - const model = convertToMetaModel(cm); + const model = convertToMetaModel({ + ...defaultOptions, + jsonSchemaModel: cm + }); expect(model).not.toBeUndefined(); expect(model instanceof AnyModel).toEqual(true); diff --git a/test/processors/SwaggerInputProcessor.spec.ts b/test/processors/SwaggerInputProcessor.spec.ts index a2ff98b76e..289ce67707 100644 --- a/test/processors/SwaggerInputProcessor.spec.ts +++ b/test/processors/SwaggerInputProcessor.spec.ts @@ -2,6 +2,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { AnyModel, CommonModel } from '../../src/models'; import { SwaggerInputProcessor } from '../../src/processors/SwaggerInputProcessor'; +import { JsonSchemaInputProcessor } from '../../src'; const basicDoc = JSON.parse( fs.readFileSync( path.resolve(__dirname, './SwaggerInputProcessor/basic.json'), @@ -11,14 +12,8 @@ const basicDoc = JSON.parse( jest.mock('../../src/utils/LoggingInterface'); jest.spyOn(SwaggerInputProcessor, 'convertToInternalSchema'); const mockedReturnModels = [new CommonModel()]; -const mockedMetaModel = new AnyModel('', undefined); -jest.mock('../../src/helpers/CommonModelToMetaModel', () => { - return { - convertToMetaModel: jest.fn().mockImplementation(() => { - return mockedMetaModel; - }) - }; -}); +const mockedMetaModel = new AnyModel('', undefined, {}); + jest.mock('../../src/interpreter/Interpreter', () => { return { Interpreter: jest.fn().mockImplementation(() => { @@ -68,6 +63,11 @@ describe('SwaggerInputProcessor', () => { ); }); test('should process the swagger document accurately', async () => { + JsonSchemaInputProcessor.convertSchemaToMetaModel = jest + .fn() + .mockImplementation(() => { + return mockedMetaModel; + }); const processor = new SwaggerInputProcessor(); const commonInputModel = await processor.process(basicDoc); expect(commonInputModel).toMatchSnapshot(); diff --git a/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap b/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap index 8891c89b8c..4f18b544cb 100644 --- a/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/OpenAPIInputProcessor.spec.ts.snap @@ -2,7 +2,13 @@ exports[`OpenAPIInputProcessor process() should process the OpenAPI document accurately 1`] = ` InputMetaModel { - "models": Object {}, + "models": Object { + "": AnyModel { + "name": "", + "options": undefined, + "originalInput": undefined, + }, + }, "originalInput": Object { "components": Object { "ApiResponse": Object { diff --git a/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap b/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap index c2ddf0d677..7560277804 100644 --- a/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/SwaggerInputProcessor.spec.ts.snap @@ -2,7 +2,13 @@ exports[`SwaggerInputProcessor process() should process the swagger document accurately 1`] = ` InputMetaModel { - "models": Object {}, + "models": Object { + "": AnyModel { + "name": "", + "options": Object {}, + "originalInput": undefined, + }, + }, "originalInput": Object { "definitions": Object { "ApiResponse": Object { diff --git a/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap b/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap index 6834bd22ca..6ee4c2e06f 100644 --- a/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap +++ b/test/processors/__snapshots__/TypeScriptInputProcessor.spec.ts.snap @@ -2,7 +2,15 @@ exports[`TypeScriptInputProcessor process() should be able to process input 1`] = ` InputMetaModel { - "models": Object {}, + "models": Object { + "undefined": AnyModel { + "name": "undefined", + "options": Object { + "isNullable": false, + }, + "originalInput": undefined, + }, + }, "originalInput": "export type Shape = { size: number; }; @@ -24,7 +32,15 @@ export interface ShapesData { exports[`TypeScriptInputProcessor process() should be able to process input with user provided options 1`] = ` InputMetaModel { - "models": Object {}, + "models": Object { + "undefined": AnyModel { + "name": "undefined", + "options": Object { + "isNullable": false, + }, + "originalInput": undefined, + }, + }, "originalInput": "export type Shape = { size: number; };