diff --git a/docs/advanced.md b/docs/advanced.md index 209a5472f2..805727db60 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -15,6 +15,7 @@ This document contains many of the advanced use-cases that you may stumble upon - [Change the generated indentation type and size](#change-the-generated-indentation-type-and-size) - [Change the type mapping](#change-the-type-mapping) - [Changing the constrain rules](#changing-the-constrain-rules) +- [Customizing the interpreter options](#customizing-the-interpreter-options) @@ -98,3 +99,13 @@ There can be multiple reasons why you want to change the default constrain rules Check out this [example out for a live demonstration](../examples/overwrite-default-constraint/) for how to overwrite the default constraints. Check out this [example out for a live demonstration](../examples/overwrite-naming-formatting/) for how to overwrite the naming formatting for models. + +## Customizing the interpreter options + +According to JSON schema if `additionalProperties` or `additionalItems` are omitted, their default value is `true`. `Interpreter` class honors this behavior, however for people coming from other code generators like `Open API Genertor` or `JSON Schema 2 POJO` this might come as a surprise. + +To avoid adding `additionalProperties: false` or `additionalItems: false` to all your specs just to get the same behavior as other code generators, this can be achieved by configuring interpreter options: +- `ignoreAdditionalProperties` - if set, it ensures that `additionalProperties` is considered to be set to `false` +- `ignoreAdditionalItems` - if set, it ensures that `additionalItems` is considered to be set to `false` + +Check out this [example out for a live demonstration](../examples/passing-interpreter-options/) for how to customize the behavior of `additionalProperties`. diff --git a/examples/passing-interpreter-options/README.md b/examples/passing-interpreter-options/README.md new file mode 100644 index 0000000000..414fa1440b --- /dev/null +++ b/examples/passing-interpreter-options/README.md @@ -0,0 +1,17 @@ +# Passing interpreter options + +This example shows how to pass interpreter options. + +## 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/passing-interpreter-options/__snapshots__/index.spec.ts.snap b/examples/passing-interpreter-options/__snapshots__/index.spec.ts.snap new file mode 100644 index 0000000000..5ee770462e --- /dev/null +++ b/examples/passing-interpreter-options/__snapshots__/index.spec.ts.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Should be able to render models with overridden additionalProperties and should log expected output to console 1`] = ` +Array [ + "class Root { + private _email?: string; + + constructor(input: { + email?: string, + }) { + this._email = input.email; + } + + get email(): string | undefined { return this._email; } + set email(email: string | undefined) { this._email = email; } +}", +] +`; diff --git a/examples/passing-interpreter-options/index.spec.ts b/examples/passing-interpreter-options/index.spec.ts new file mode 100644 index 0000000000..c83a861f1b --- /dev/null +++ b/examples/passing-interpreter-options/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 models with overridden additionalProperties', () => { + 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/passing-interpreter-options/index.ts b/examples/passing-interpreter-options/index.ts new file mode 100644 index 0000000000..e1ced36eef --- /dev/null +++ b/examples/passing-interpreter-options/index.ts @@ -0,0 +1,37 @@ +import { TypeScriptGenerator } from '../../src'; +import { CommonModel } from '../../src'; +import { + InterpreterSchemaType, + Interpreter, + InterpreterOptions +} from '../../src/interpreter/Interpreter'; +import { isModelObject } from '../../src/interpreter/Utils'; + +const generator = new TypeScriptGenerator({ + processorOptions: { + interpreter: { + ignoreAdditionalProperties: true + } + } +}); + +const jsonSchemaDraft7 = { + $schema: 'http://json-schema.org/draft-07/schema#', + type: 'object', + properties: { + email: { + type: 'string', + format: 'email' + } + } +}; + +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/passing-interpreter-options/package-lock.json b/examples/passing-interpreter-options/package-lock.json new file mode 100644 index 0000000000..9a178c99d2 --- /dev/null +++ b/examples/passing-interpreter-options/package-lock.json @@ -0,0 +1,10 @@ +{ + "name": "passing-interpreter-options", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "hasInstallScript": true + } + } +} diff --git a/examples/passing-interpreter-options/package.json b/examples/passing-interpreter-options/package.json new file mode 100644 index 0000000000..711bb65105 --- /dev/null +++ b/examples/passing-interpreter-options/package.json @@ -0,0 +1,10 @@ +{ + "config" : { "example_name" : "passing-interpreter-options" }, + "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/interpreter/InterpretAdditionalItems.ts b/src/interpreter/InterpretAdditionalItems.ts index b08121187f..1f2d5af364 100644 --- a/src/interpreter/InterpretAdditionalItems.ts +++ b/src/interpreter/InterpretAdditionalItems.ts @@ -19,6 +19,9 @@ export default function interpretAdditionalItems( interpreter: Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions ): void { + if (interpreterOptions.ignoreAdditionalItems === true) { + return; + } if (typeof schema === 'boolean' || model.type?.includes('array') === false) { return; } diff --git a/src/interpreter/InterpretAdditionalProperties.ts b/src/interpreter/InterpretAdditionalProperties.ts index 614ff5a6d6..eb9701b9f9 100644 --- a/src/interpreter/InterpretAdditionalProperties.ts +++ b/src/interpreter/InterpretAdditionalProperties.ts @@ -20,6 +20,9 @@ export default function interpretAdditionalProperties( interpreter: Interpreter, interpreterOptions: InterpreterOptions = Interpreter.defaultInterpreterOptions ): void { + if (interpreterOptions.ignoreAdditionalProperties === true) { + return; + } if (typeof schema === 'boolean' || isModelObject(model) === false) { return; } diff --git a/src/interpreter/Interpreter.ts b/src/interpreter/Interpreter.ts index b1283df907..03401a6e8d 100644 --- a/src/interpreter/Interpreter.ts +++ b/src/interpreter/Interpreter.ts @@ -24,6 +24,8 @@ import interpretOneOfWithProperties from './InterpretOneOfWithProperties'; export type InterpreterOptions = { allowInheritance?: boolean; + ignoreAdditionalProperties?: boolean; + ignoreAdditionalItems?: boolean; }; export type InterpreterSchemas = | Draft6Schema @@ -35,7 +37,9 @@ export type InterpreterSchemaType = InterpreterSchemas | boolean; export class Interpreter { static defaultInterpreterOptions: InterpreterOptions = { - allowInheritance: false + allowInheritance: false, + ignoreAdditionalProperties: false, + ignoreAdditionalItems: false }; private anonymCounter = 1;