diff --git a/src/generators/typescript/presets/CommonPreset.ts b/src/generators/typescript/presets/CommonPreset.ts index 1b29fed19a..353dd0f9b1 100644 --- a/src/generators/typescript/presets/CommonPreset.ts +++ b/src/generators/typescript/presets/CommonPreset.ts @@ -1,7 +1,7 @@ import { TypeScriptRenderer } from '../TypeScriptRenderer'; import { TypeScriptPreset } from '../TypeScriptPreset'; -import { getUniquePropertyName, DefaultPropertyNames } from '../../../helpers'; -import { CommonModel } from '../../../models'; +import { getUniquePropertyName, DefaultPropertyNames, TypeHelpers, ModelKind } from '../../../helpers'; +import { CommonInputModel, CommonModel } from '../../../models'; import renderExampleFunction from './utils/ExampleFunction'; export interface TypeScriptCommonPresetOptions { @@ -12,36 +12,46 @@ export interface TypeScriptCommonPresetOptions { function realizePropertyFactory(prop: string) { return `$\{typeof ${prop} === 'number' || typeof ${prop} === 'boolean' ? ${prop} : JSON.stringify(${prop})}`; } - -function renderMarshalProperties(model: CommonModel, renderer: TypeScriptRenderer) { +function renderMarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel) { + if (model.$ref) { + const resolvedModel = inputModel.models[model.$ref]; + const propertyModelKind = TypeHelpers.extractKind(resolvedModel); + //Referenced enums only need standard marshalling, so lets filter those away + if (propertyModelKind !== ModelKind.ENUM) { + return `$\{${modelInstanceVariable}.marshal()}`; + } + } + return realizePropertyFactory(modelInstanceVariable); +} +function renderMarshalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { const properties = model.properties || {}; const propertyKeys = [...Object.entries(properties)]; const marshalProperties = propertyKeys.map(([prop, propModel]) => { const formattedPropertyName = renderer.nameProperty(prop, propModel); const modelInstanceVariable = `this.${formattedPropertyName}`; - const propMarshalReference = `json += \`"${prop}": $\{this.${formattedPropertyName}.marshal()},\`;`; - const propMarshal = `json += \`"${prop}": ${realizePropertyFactory(modelInstanceVariable)},\`;`; - const propMarshalCode = propModel.$ref !== undefined ? propMarshalReference : propMarshal; - return `if(this.${formattedPropertyName} !== undefined) { - ${propMarshalCode} + const propMarshalCode = renderMarshalProperty(modelInstanceVariable, propModel, inputModel); + const marshalCode = `json += \`"${prop}": ${propMarshalCode},\`;`; + return `if(${modelInstanceVariable} !== undefined) { + ${marshalCode} }`; }); return marshalProperties.join('\n'); } -function renderMarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer) { +function renderMarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { let marshalPatternProperties = ''; if (model.patternProperties !== undefined) { for (const [pattern, patternModel] of Object.entries(model.patternProperties)) { let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel); - const patternPropertyMarshalReference = 'json += `"${key}": ${value.marshal()},`;'; - const patternPropertyMarshal = `json += \`"$\{key}": ${realizePropertyFactory('value')},\`;`; + const modelInstanceVariable = 'value'; + const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, patternModel, inputModel); + const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`; marshalPatternProperties += `if(this.${patternPropertyName} !== undefined) { for (const [key, value] of this.${patternPropertyName}.entries()) { //Only render pattern properties which are not already a property if(Object.keys(this).includes(String(key))) continue; - ${patternModel.$ref !== undefined ? patternPropertyMarshalReference : patternPropertyMarshal} + ${marshalCode} } }`; } @@ -49,18 +59,19 @@ function renderMarshalPatternProperties(model: CommonModel, renderer: TypeScript return marshalPatternProperties; } -function renderMarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer) { +function renderMarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { let marshalAdditionalProperties = ''; if (model.additionalProperties !== undefined) { let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties); - const additionalPropertyMarshalReference = 'json += `"${key}": ${value.marshal()},`;'; - const additionalPropertyMarshal = `json += \`"$\{key}": ${realizePropertyFactory('value')},\`;`; + const modelInstanceVariable = 'value'; + const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel); + const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`; marshalAdditionalProperties = `if(this.${additionalPropertyName} !== undefined) { for (const [key, value] of this.${additionalPropertyName}.entries()) { //Only render additionalProperties which are not already a property if(Object.keys(this).includes(String(key))) continue; - ${model.additionalProperties.$ref !== undefined ? additionalPropertyMarshalReference : additionalPropertyMarshal} + ${marshalCode} } }`; } @@ -70,45 +81,60 @@ function renderMarshalAdditionalProperties(model: CommonModel, renderer: TypeScr /** * Render `marshal` function based on model */ -function renderMarshal({ renderer, model }: { +function renderMarshal({ renderer, model, inputModel }: { renderer: TypeScriptRenderer, model: CommonModel, + inputModel: CommonInputModel }): string { return `public marshal() : string { let json = '{' -${renderer.indent(renderMarshalProperties(model, renderer))} -${renderer.indent(renderMarshalPatternProperties(model, renderer))} -${renderer.indent(renderMarshalAdditionalProperties(model, renderer))} +${renderer.indent(renderMarshalProperties(model, renderer, inputModel))} +${renderer.indent(renderMarshalPatternProperties(model, renderer, inputModel))} +${renderer.indent(renderMarshalAdditionalProperties(model, renderer, inputModel))} //Remove potential last comma return \`$\{json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`; }`; } -function renderUnmarshalProperties(model: CommonModel, renderer: TypeScriptRenderer) { +function renderUnmarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel, renderer: TypeScriptRenderer) { + if (model.$ref) { + const resolvedModel = inputModel.models[model.$ref]; + const propertyModelKind = TypeHelpers.extractKind(resolvedModel); + //Referenced enums only need standard marshalling, so lets filter those away + if (propertyModelKind !== ModelKind.ENUM) { + return `${renderer.nameType(model.$ref)}.unmarshal(${modelInstanceVariable})`; + } + } + return `${modelInstanceVariable}`; +} +function renderUnmarshalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { const properties = model.properties || {}; const propertyKeys = [...Object.entries(properties)]; const unmarshalProperties = propertyKeys.map(([prop, propModel]) => { const formattedPropertyName = renderer.nameProperty(prop, propModel); - const propUnmarshal = propModel.$ref !== undefined ? `${renderer.nameType(propModel.$ref)}.unmarshal(obj["${prop}"])` : `obj["${prop}"]`; - return `if (obj["${prop}"] !== undefined) { - instance.${formattedPropertyName} = ${propUnmarshal}; + const modelInstanceVariable = `obj["${prop}"]`; + const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, propModel, inputModel, renderer); + return `if (${modelInstanceVariable} !== undefined) { + instance.${formattedPropertyName} = ${unmarshalCode}; }`; }); return unmarshalProperties.join('\n'); } -function renderUnmarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer) { +function renderUnmarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { let unmarshalPatternProperties = ''; let setPatternPropertiesMap = ''; if (model.patternProperties !== undefined) { for (const [pattern, patternModel] of Object.entries(model.patternProperties)) { let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`); patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel); - setPatternPropertiesMap = `if (instance.${patternPropertyName} === undefined) {instance.${patternPropertyName} = new Map();}`; + const modelInstanceVariable = 'value as any'; + const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, patternModel, inputModel, renderer); + setPatternPropertiesMap += `if (instance.${patternPropertyName} === undefined) {instance.${patternPropertyName} = new Map();}\n`; unmarshalPatternProperties += `//Check all pattern properties if (key.match(new RegExp('${pattern}'))) { - instance.${patternPropertyName}.set(key, value as any); + instance.${patternPropertyName}.set(key, ${unmarshalCode}); continue; }`; } @@ -116,15 +142,16 @@ if (key.match(new RegExp('${pattern}'))) { return {unmarshalPatternProperties, setPatternPropertiesMap}; } -function renderUnmarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer) { +function renderUnmarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) { let unmarshalAdditionalProperties = ''; let setAdditionalPropertiesMap = ''; if (model.additionalProperties !== undefined) { let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties); additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties); - const additionalPropertiesCast = model.additionalProperties.$ref !== undefined ? `${renderer.nameType(model.$id)}.unmarshal(value)` : 'value as any'; + const modelInstanceVariable = 'value as any'; + const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel, renderer); setAdditionalPropertiesMap = `if (instance.${additionalPropertyName} === undefined) {instance.${additionalPropertyName} = new Map();}`; - unmarshalAdditionalProperties = `instance.${additionalPropertyName}.set(key, ${additionalPropertiesCast});`; + unmarshalAdditionalProperties = `instance.${additionalPropertyName}.set(key, ${unmarshalCode});`; } return {unmarshalAdditionalProperties, setAdditionalPropertiesMap}; } @@ -132,20 +159,22 @@ function renderUnmarshalAdditionalProperties(model: CommonModel, renderer: TypeS /** * Render `unmarshal` function based on model */ -function renderUnmarshal({ renderer, model }: { +function renderUnmarshal({ renderer, model, inputModel }: { renderer: TypeScriptRenderer, model: CommonModel, + inputModel: CommonInputModel }): string { const properties = model.properties || {}; - const {unmarshalPatternProperties, setPatternPropertiesMap} = renderUnmarshalPatternProperties(model, renderer); - const {unmarshalAdditionalProperties, setAdditionalPropertiesMap} = renderUnmarshalAdditionalProperties(model, renderer); + const {unmarshalPatternProperties, setPatternPropertiesMap} = renderUnmarshalPatternProperties(model, renderer, inputModel); + const {unmarshalAdditionalProperties, setAdditionalPropertiesMap} = renderUnmarshalAdditionalProperties(model, renderer, inputModel); + const unmarshalProperties = renderUnmarshalProperties(model, renderer, inputModel); const formattedModelName = renderer.nameType(model.$id); const propertyNames = Object.keys(properties).map((prop => `"${prop}"`)); return `public static unmarshal(json: string | object): ${formattedModelName} { const obj = typeof json === "object" ? json : JSON.parse(json); const instance = new ${formattedModelName}({} as any); -${renderer.indent(renderUnmarshalProperties(model, renderer))} +${renderer.indent(unmarshalProperties)} //Not part of core properties ${setPatternPropertiesMap} @@ -165,13 +194,13 @@ ${renderer.indent(unmarshalAdditionalProperties, 4)} */ export const TS_COMMON_PRESET: TypeScriptPreset = { class: { - additionalContent({ renderer, model, content, options }) { + additionalContent({ renderer, model, content, options, inputModel }) { options = options || {}; const blocks: string[] = []; if (options.marshalling === true) { - blocks.push(renderMarshal({ renderer, model })); - blocks.push(renderUnmarshal({ renderer, model })); + blocks.push(renderMarshal({ renderer, model, inputModel })); + blocks.push(renderUnmarshal({ renderer, model, inputModel })); } if (options.example === true) { diff --git a/test/blackbox/Dummy.spec.ts b/test/blackbox/Dummy.spec.ts index 96dd5fe8cf..b0167fe1ac 100644 --- a/test/blackbox/Dummy.spec.ts +++ b/test/blackbox/Dummy.spec.ts @@ -36,7 +36,7 @@ describe('Dummy JSON Schema file', () => { const renderOutputPath = path.resolve(__dirname, './output/ts/class/output.ts'); await renderModels(generatedModels, renderOutputPath); const transpiledOutputPath = path.resolve(__dirname, './output/ts/class/output.js'); - const transpileAndRunCommand = `tsc -t es5 ${renderOutputPath} && node ${transpiledOutputPath}`; + const transpileAndRunCommand = `tsc --downlevelIteration -t es5 ${renderOutputPath} && node ${transpiledOutputPath}`; await execCommand(transpileAndRunCommand); }); diff --git a/test/generators/typescript/preset/MarshallingPreset.spec.ts b/test/generators/typescript/preset/MarshallingPreset.spec.ts index 157df79463..d4d99d6bc6 100644 --- a/test/generators/typescript/preset/MarshallingPreset.spec.ts +++ b/test/generators/typescript/preset/MarshallingPreset.spec.ts @@ -3,19 +3,24 @@ import { TypeScriptGenerator, TS_COMMON_PRESET } from '../../../../src/generators'; import Ajv from 'ajv'; const doc = { + definitions: { + 'NestedTest': { + type: 'object', $id: 'NestedTest', properties: {stringProp: { type: 'string' }} + } + }, $id: 'Test', type: 'object', - additionalProperties: true, + additionalProperties: {$ref: '#/definitions/NestedTest'}, required: ['string prop'], properties: { 'string prop': { type: 'string' }, + enumProp: { $id: 'EnumTest', enum: ['Some enum String', "some other enum string"]}, numberProp: { type: 'number' }, - objectProp: { type: 'object', $id: 'NestedTest', properties: {stringProp: { type: 'string' }}} + objectProp: { $ref: '#/definitions/NestedTest' } }, patternProperties: { - '^S(.?)test': { - type: 'string' - } + '^S(.?)test': { type: 'string' }, + '^S(.?)AnotherTest': { $ref: '#/definitions/NestedTest' }, }, }; describe('Marshalling preset', () => { @@ -31,6 +36,7 @@ describe('Marshalling preset', () => { ] }); const inputModel = await generator.process(doc); + const testModel = inputModel.models['Test']; const nestedTestModel = inputModel.models['NestedTest']; @@ -42,50 +48,52 @@ describe('Marshalling preset', () => { }); test('should provide a two way conversion', async () => { + enum EnumTest { + SOME_ENUM_STRING = "Some enum String", + SOME_OTHER_ENUM_STRING = "some other enum string" + } class NestedTest { private _stringProp?: string; private _additionalProperties?: Map | boolean | null | number>; - + constructor(input: { stringProp?: string, }) { this._stringProp = input.stringProp; } - + get stringProp(): string | undefined { return this._stringProp; } set stringProp(stringProp: string | undefined) { this._stringProp = stringProp; } - + get additionalProperties(): Map | boolean | null | number> | undefined { return this._additionalProperties; } set additionalProperties(additionalProperties: Map | boolean | null | number> | undefined) { this._additionalProperties = additionalProperties; } - + public marshal() : string { let json = '{' if(this.stringProp !== undefined) { - json += `\"stringProp\": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; + json += `"stringProp": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; } - - if(this.additionalProperties !== undefined) { for (const [key, value] of this.additionalProperties.entries()) { //Only render additionalProperties which are not already a property if(Object.keys(this).includes(String(key))) continue; - json += `\"${key}\": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`; + json += `"${key}": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`; } } - + //Remove potential last comma return `${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}`; } - + public static unmarshal(json: string | object): NestedTest { const obj = typeof json === "object" ? json : JSON.parse(json); const instance = new NestedTest({} as any); - - if (obj.stringProp !== undefined) { - instance.stringProp = obj["stringProp"]; + + if (obj["stringProp"] !== undefined) { + instance.stringProp = obj["stringProp"] as any; } - + //Not part of core properties if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} @@ -98,92 +106,118 @@ describe('Marshalling preset', () => { } class Test { private _stringProp: string; + private _enumProp?: EnumTest; private _numberProp?: number; private _objectProp?: NestedTest; - private _additionalProperties?: Map | boolean | null | number>; + private _additionalProperties?: Map; private _sTestPatternProperties?: Map; - + private _sAnotherTestPatternProperties?: Map; + constructor(input: { stringProp: string, + enumProp?: EnumTest, numberProp?: number, objectProp?: NestedTest, }) { this._stringProp = input.stringProp; + this._enumProp = input.enumProp; this._numberProp = input.numberProp; this._objectProp = input.objectProp; } - + get stringProp(): string { return this._stringProp; } set stringProp(stringProp: string) { this._stringProp = stringProp; } - + + get enumProp(): EnumTest | undefined { return this._enumProp; } + set enumProp(enumProp: EnumTest | undefined) { this._enumProp = enumProp; } + get numberProp(): number | undefined { return this._numberProp; } set numberProp(numberProp: number | undefined) { this._numberProp = numberProp; } - + get objectProp(): NestedTest | undefined { return this._objectProp; } set objectProp(objectProp: NestedTest | undefined) { this._objectProp = objectProp; } - - get additionalProperties(): Map | boolean | null | number> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null | number> | undefined) { this._additionalProperties = additionalProperties; } - + + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } + get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } - + + get sAnotherTestPatternProperties(): Map | undefined { return this._sAnotherTestPatternProperties; } + set sAnotherTestPatternProperties(sTest2PatternProperties: Map | undefined) { this._sAnotherTestPatternProperties = sTest2PatternProperties; } + public marshal() : string { let json = '{' if(this.stringProp !== undefined) { - json += `\"string prop\": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; + json += `"string prop": ${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},`; + } + if(this.enumProp !== undefined) { + json += `"enumProp": ${typeof this.enumProp === 'number' || typeof this.enumProp === 'boolean' ? this.enumProp : JSON.stringify(this.enumProp)},`; } if(this.numberProp !== undefined) { - json += `\"numberProp\": ${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},`; + json += `"numberProp": ${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},`; } if(this.objectProp !== undefined) { - json += `\"objectProp\": ${this.objectProp.marshal()},`; + json += `"objectProp": ${this.objectProp.marshal()},`; } - if(this.sTestPatternProperties !== undefined) { for (const [key, value] of this.sTestPatternProperties.entries()) { //Only render pattern properties which are not already a property if(Object.keys(this).includes(String(key))) continue; - json += `\"${key}\": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`; + json += `"${key}": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`; + } + }if(this.sAnotherTestPatternProperties !== undefined) { + for (const [key, value] of this.sAnotherTestPatternProperties.entries()) { + //Only render pattern properties which are not already a property + if(Object.keys(this).includes(String(key))) continue; + json += `"${key}": ${value.marshal()},`; } } - if(this.additionalProperties !== undefined) { for (const [key, value] of this.additionalProperties.entries()) { //Only render additionalProperties which are not already a property if(Object.keys(this).includes(String(key))) continue; - json += `\"${key}\": ${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},`; + json += `"${key}": ${value.marshal()},`; } } - + //Remove potential last comma return `${json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}`; } - + public static unmarshal(json: string | object): Test { const obj = typeof json === "object" ? json : JSON.parse(json); const instance = new Test({} as any); - + if (obj["string prop"] !== undefined) { instance.stringProp = obj["string prop"]; } - if (obj.numberProp !== undefined) { + if (obj["enumProp"] !== undefined) { + instance.enumProp = obj["enumProp"]; + } + if (obj["numberProp"] !== undefined) { instance.numberProp = obj["numberProp"]; } - if (obj.objectProp !== undefined) { + if (obj["objectProp"] !== undefined) { instance.objectProp = NestedTest.unmarshal(obj["objectProp"]); } - + //Not part of core properties if (instance.sTestPatternProperties === undefined) {instance.sTestPatternProperties = new Map();} + if (instance.sAnotherTestPatternProperties === undefined) {instance.sAnotherTestPatternProperties = new Map();} + if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return !["string prop","numberProp","objectProp"].includes(key);}))) { + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return !["string prop","enumProp","numberProp","objectProp"].includes(key);}))) { //Check all pattern properties if (key.match(new RegExp('^S(.?)test'))) { instance.sTestPatternProperties.set(key, value as any); continue; + }//Check all pattern properties + if (key.match(new RegExp('^S(.?)AnotherTest'))) { + instance.sAnotherTestPatternProperties.set(key, NestedTest.unmarshal(value as any)); + continue; } - instance.additionalProperties.set(key, value as any); + instance.additionalProperties.set(key, NestedTest.unmarshal(value as any)); } return instance; } @@ -191,18 +225,21 @@ describe('Marshalling preset', () => { const ajv = new Ajv(); const nestedTestInstance = new NestedTest({stringProp: "SomeTestString"}); nestedTestInstance.additionalProperties = new Map(); - const testInstance = new Test({numberProp: 0, stringProp: "SomeTestString", objectProp: nestedTestInstance}); + nestedTestInstance.additionalProperties.set("someProp", "someValue"); + const testInstance = new Test({numberProp: 0, stringProp: "SomeTestString", objectProp: nestedTestInstance, enumProp: EnumTest.SOME_ENUM_STRING}); testInstance.additionalProperties = new Map(); - testInstance.additionalProperties.set('additionalProp', ['Some test value']); + testInstance.additionalProperties.set('additionalProp', nestedTestInstance); testInstance.sTestPatternProperties = new Map(); testInstance.sTestPatternProperties.set('Stest', 'Some pattern value'); + testInstance.sAnotherTestPatternProperties = new Map(); + testInstance.sAnotherTestPatternProperties.set('SAnotherTest', nestedTestInstance); const marshalContent = testInstance.marshal(); const unmarshalInstance = Test.unmarshal(marshalContent); const validationResult = ajv.validate(doc, JSON.parse(marshalContent)); - expect(marshalContent).toEqual("{\"string prop\": \"SomeTestString\",\"numberProp\": 0,\"objectProp\": {\"stringProp\": \"SomeTestString\"},\"Stest\": \"Some pattern value\",\"additionalProp\": [\"Some test value\"]}"); + expect(marshalContent).toEqual("{\"string prop\": \"SomeTestString\",\"enumProp\": \"Some enum String\",\"numberProp\": 0,\"objectProp\": {\"stringProp\": \"SomeTestString\",\"someProp\": \"someValue\"},\"Stest\": \"Some pattern value\",\"SAnotherTest\": {\"stringProp\": \"SomeTestString\",\"someProp\": \"someValue\"},\"additionalProp\": {\"stringProp\": \"SomeTestString\",\"someProp\": \"someValue\"}}"); expect(validationResult).toEqual(true); expect(unmarshalInstance).toEqual(testInstance); }); diff --git a/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap b/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap index 852c6c11c1..bcba6ece45 100644 --- a/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap +++ b/test/generators/typescript/preset/__snapshots__/MarshallingPreset.spec.ts.snap @@ -3,17 +3,21 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` "export class Test { private _stringProp: string; + private _enumProp?: EnumTest; private _numberProp?: number; private _objectProp?: NestedTest; - private _additionalProperties?: Map | boolean | null | number>; + private _additionalProperties?: Map; private _sTestPatternProperties?: Map; + private _sAnotherTestPatternProperties?: Map; constructor(input: { stringProp: string, + enumProp?: EnumTest, numberProp?: number, objectProp?: NestedTest, }) { this._stringProp = input.stringProp; + this._enumProp = input.enumProp; this._numberProp = input.numberProp; this._objectProp = input.objectProp; } @@ -21,23 +25,32 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` get stringProp(): string { return this._stringProp; } set stringProp(stringProp: string) { this._stringProp = stringProp; } + get enumProp(): EnumTest | undefined { return this._enumProp; } + set enumProp(enumProp: EnumTest | undefined) { this._enumProp = enumProp; } + get numberProp(): number | undefined { return this._numberProp; } set numberProp(numberProp: number | undefined) { this._numberProp = numberProp; } get objectProp(): NestedTest | undefined { return this._objectProp; } set objectProp(objectProp: NestedTest | undefined) { this._objectProp = objectProp; } - get additionalProperties(): Map | boolean | null | number> | undefined { return this._additionalProperties; } - set additionalProperties(additionalProperties: Map | boolean | null | number> | undefined) { this._additionalProperties = additionalProperties; } + get additionalProperties(): Map | undefined { return this._additionalProperties; } + set additionalProperties(additionalProperties: Map | undefined) { this._additionalProperties = additionalProperties; } get sTestPatternProperties(): Map | undefined { return this._sTestPatternProperties; } set sTestPatternProperties(sTestPatternProperties: Map | undefined) { this._sTestPatternProperties = sTestPatternProperties; } + get sAnotherTestPatternProperties(): Map | undefined { return this._sAnotherTestPatternProperties; } + set sAnotherTestPatternProperties(sAnotherTestPatternProperties: Map | undefined) { this._sAnotherTestPatternProperties = sAnotherTestPatternProperties; } + public marshal() : string { let json = '{' if(this.stringProp !== undefined) { json += \`\\"string prop\\": \${typeof this.stringProp === 'number' || typeof this.stringProp === 'boolean' ? this.stringProp : JSON.stringify(this.stringProp)},\`; } + if(this.enumProp !== undefined) { + json += \`\\"enumProp\\": \${typeof this.enumProp === 'number' || typeof this.enumProp === 'boolean' ? this.enumProp : JSON.stringify(this.enumProp)},\`; + } if(this.numberProp !== undefined) { json += \`\\"numberProp\\": \${typeof this.numberProp === 'number' || typeof this.numberProp === 'boolean' ? this.numberProp : JSON.stringify(this.numberProp)},\`; } @@ -50,12 +63,18 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` if(Object.keys(this).includes(String(key))) continue; json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; } + }if(this.sAnotherTestPatternProperties !== undefined) { + for (const [key, value] of this.sAnotherTestPatternProperties.entries()) { + //Only render pattern properties which are not already a property + if(Object.keys(this).includes(String(key))) continue; + json += \`\\"\${key}\\": \${value.marshal()},\`; + } } if(this.additionalProperties !== undefined) { for (const [key, value] of this.additionalProperties.entries()) { //Only render additionalProperties which are not already a property if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; + json += \`\\"\${key}\\": \${value.marshal()},\`; } } @@ -70,6 +89,9 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` if (obj[\\"string prop\\"] !== undefined) { instance.stringProp = obj[\\"string prop\\"]; } + if (obj[\\"enumProp\\"] !== undefined) { + instance.enumProp = obj[\\"enumProp\\"]; + } if (obj[\\"numberProp\\"] !== undefined) { instance.numberProp = obj[\\"numberProp\\"]; } @@ -79,14 +101,20 @@ exports[`Marshalling preset should render un/marshal code 1`] = ` //Not part of core properties if (instance.sTestPatternProperties === undefined) {instance.sTestPatternProperties = new Map();} + if (instance.sAnotherTestPatternProperties === undefined) {instance.sAnotherTestPatternProperties = new Map();} + if (instance.additionalProperties === undefined) {instance.additionalProperties = new Map();} - for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"string prop\\",\\"numberProp\\",\\"objectProp\\"].includes(key);}))) { + for (const [key, value] of Object.entries(obj).filter((([key,]) => {return ![\\"string prop\\",\\"enumProp\\",\\"numberProp\\",\\"objectProp\\"].includes(key);}))) { //Check all pattern properties if (key.match(new RegExp('^S(.?)test'))) { instance.sTestPatternProperties.set(key, value as any); continue; + }//Check all pattern properties + if (key.match(new RegExp('^S(.?)AnotherTest'))) { + instance.sAnotherTestPatternProperties.set(key, NestedTest.unmarshal(value as any)); + continue; } - instance.additionalProperties.set(key, value as any); + instance.additionalProperties.set(key, NestedTest.unmarshal(value as any)); } return instance; } @@ -120,7 +148,7 @@ exports[`Marshalling preset should render un/marshal code 2`] = ` for (const [key, value] of this.additionalProperties.entries()) { //Only render additionalProperties which are not already a property if(Object.keys(this).includes(String(key))) continue; - json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; + json += \`\\"\${key}\\": \${typeof value === 'number' || typeof value === 'boolean' ? value : JSON.stringify(value)},\`; } }