Skip to content

Commit 0885834

Browse files
authored
fix: un/marshal TS class functions did not render references correctly (#337)
1 parent 8a17451 commit 0885834

File tree

4 files changed

+187
-93
lines changed

4 files changed

+187
-93
lines changed

src/generators/typescript/presets/CommonPreset.ts

Lines changed: 67 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { TypeScriptRenderer } from '../TypeScriptRenderer';
22
import { TypeScriptPreset } from '../TypeScriptPreset';
3-
import { getUniquePropertyName, DefaultPropertyNames } from '../../../helpers';
4-
import { CommonModel } from '../../../models';
3+
import { getUniquePropertyName, DefaultPropertyNames, TypeHelpers, ModelKind } from '../../../helpers';
4+
import { CommonInputModel, CommonModel } from '../../../models';
55
import renderExampleFunction from './utils/ExampleFunction';
66

77
export interface TypeScriptCommonPresetOptions {
@@ -12,55 +12,66 @@ export interface TypeScriptCommonPresetOptions {
1212
function realizePropertyFactory(prop: string) {
1313
return `$\{typeof ${prop} === 'number' || typeof ${prop} === 'boolean' ? ${prop} : JSON.stringify(${prop})}`;
1414
}
15-
16-
function renderMarshalProperties(model: CommonModel, renderer: TypeScriptRenderer) {
15+
function renderMarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel) {
16+
if (model.$ref) {
17+
const resolvedModel = inputModel.models[model.$ref];
18+
const propertyModelKind = TypeHelpers.extractKind(resolvedModel);
19+
//Referenced enums only need standard marshalling, so lets filter those away
20+
if (propertyModelKind !== ModelKind.ENUM) {
21+
return `$\{${modelInstanceVariable}.marshal()}`;
22+
}
23+
}
24+
return realizePropertyFactory(modelInstanceVariable);
25+
}
26+
function renderMarshalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) {
1727
const properties = model.properties || {};
1828
const propertyKeys = [...Object.entries(properties)];
1929
const marshalProperties = propertyKeys.map(([prop, propModel]) => {
2030
const formattedPropertyName = renderer.nameProperty(prop, propModel);
2131
const modelInstanceVariable = `this.${formattedPropertyName}`;
22-
const propMarshalReference = `json += \`"${prop}": $\{this.${formattedPropertyName}.marshal()},\`;`;
23-
const propMarshal = `json += \`"${prop}": ${realizePropertyFactory(modelInstanceVariable)},\`;`;
24-
const propMarshalCode = propModel.$ref !== undefined ? propMarshalReference : propMarshal;
25-
return `if(this.${formattedPropertyName} !== undefined) {
26-
${propMarshalCode}
32+
const propMarshalCode = renderMarshalProperty(modelInstanceVariable, propModel, inputModel);
33+
const marshalCode = `json += \`"${prop}": ${propMarshalCode},\`;`;
34+
return `if(${modelInstanceVariable} !== undefined) {
35+
${marshalCode}
2736
}`;
2837
});
2938
return marshalProperties.join('\n');
3039
}
3140

32-
function renderMarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer) {
41+
function renderMarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) {
3342
let marshalPatternProperties = '';
3443
if (model.patternProperties !== undefined) {
3544
for (const [pattern, patternModel] of Object.entries(model.patternProperties)) {
3645
let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`);
3746
patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel);
38-
const patternPropertyMarshalReference = 'json += `"${key}": ${value.marshal()},`;';
39-
const patternPropertyMarshal = `json += \`"$\{key}": ${realizePropertyFactory('value')},\`;`;
47+
const modelInstanceVariable = 'value';
48+
const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, patternModel, inputModel);
49+
const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`;
4050
marshalPatternProperties += `if(this.${patternPropertyName} !== undefined) {
4151
for (const [key, value] of this.${patternPropertyName}.entries()) {
4252
//Only render pattern properties which are not already a property
4353
if(Object.keys(this).includes(String(key))) continue;
44-
${patternModel.$ref !== undefined ? patternPropertyMarshalReference : patternPropertyMarshal}
54+
${marshalCode}
4555
}
4656
}`;
4757
}
4858
}
4959
return marshalPatternProperties;
5060
}
5161

52-
function renderMarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer) {
62+
function renderMarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) {
5363
let marshalAdditionalProperties = '';
5464
if (model.additionalProperties !== undefined) {
5565
let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties);
5666
additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties);
57-
const additionalPropertyMarshalReference = 'json += `"${key}": ${value.marshal()},`;';
58-
const additionalPropertyMarshal = `json += \`"$\{key}": ${realizePropertyFactory('value')},\`;`;
67+
const modelInstanceVariable = 'value';
68+
const patternPropertyMarshalCode = renderMarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel);
69+
const marshalCode = `json += \`"$\{key}": ${patternPropertyMarshalCode},\`;`;
5970
marshalAdditionalProperties = `if(this.${additionalPropertyName} !== undefined) {
6071
for (const [key, value] of this.${additionalPropertyName}.entries()) {
6172
//Only render additionalProperties which are not already a property
6273
if(Object.keys(this).includes(String(key))) continue;
63-
${model.additionalProperties.$ref !== undefined ? additionalPropertyMarshalReference : additionalPropertyMarshal}
74+
${marshalCode}
6475
}
6576
}`;
6677
}
@@ -70,82 +81,100 @@ function renderMarshalAdditionalProperties(model: CommonModel, renderer: TypeScr
7081
/**
7182
* Render `marshal` function based on model
7283
*/
73-
function renderMarshal({ renderer, model }: {
84+
function renderMarshal({ renderer, model, inputModel }: {
7485
renderer: TypeScriptRenderer,
7586
model: CommonModel,
87+
inputModel: CommonInputModel
7688
}): string {
7789
return `public marshal() : string {
7890
let json = '{'
79-
${renderer.indent(renderMarshalProperties(model, renderer))}
80-
${renderer.indent(renderMarshalPatternProperties(model, renderer))}
81-
${renderer.indent(renderMarshalAdditionalProperties(model, renderer))}
91+
${renderer.indent(renderMarshalProperties(model, renderer, inputModel))}
92+
${renderer.indent(renderMarshalPatternProperties(model, renderer, inputModel))}
93+
${renderer.indent(renderMarshalAdditionalProperties(model, renderer, inputModel))}
8294
8395
//Remove potential last comma
8496
return \`$\{json.charAt(json.length-1) === ',' ? json.slice(0, json.length-1) : json}}\`;
8597
}`;
8698
}
8799

88-
function renderUnmarshalProperties(model: CommonModel, renderer: TypeScriptRenderer) {
100+
function renderUnmarshalProperty(modelInstanceVariable: string, model: CommonModel, inputModel: CommonInputModel, renderer: TypeScriptRenderer) {
101+
if (model.$ref) {
102+
const resolvedModel = inputModel.models[model.$ref];
103+
const propertyModelKind = TypeHelpers.extractKind(resolvedModel);
104+
//Referenced enums only need standard marshalling, so lets filter those away
105+
if (propertyModelKind !== ModelKind.ENUM) {
106+
return `${renderer.nameType(model.$ref)}.unmarshal(${modelInstanceVariable})`;
107+
}
108+
}
109+
return `${modelInstanceVariable}`;
110+
}
111+
function renderUnmarshalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) {
89112
const properties = model.properties || {};
90113
const propertyKeys = [...Object.entries(properties)];
91114
const unmarshalProperties = propertyKeys.map(([prop, propModel]) => {
92115
const formattedPropertyName = renderer.nameProperty(prop, propModel);
93-
const propUnmarshal = propModel.$ref !== undefined ? `${renderer.nameType(propModel.$ref)}.unmarshal(obj["${prop}"])` : `obj["${prop}"]`;
94-
return `if (obj["${prop}"] !== undefined) {
95-
instance.${formattedPropertyName} = ${propUnmarshal};
116+
const modelInstanceVariable = `obj["${prop}"]`;
117+
const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, propModel, inputModel, renderer);
118+
return `if (${modelInstanceVariable} !== undefined) {
119+
instance.${formattedPropertyName} = ${unmarshalCode};
96120
}`;
97121
});
98122
return unmarshalProperties.join('\n');
99123
}
100124

101-
function renderUnmarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer) {
125+
function renderUnmarshalPatternProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) {
102126
let unmarshalPatternProperties = '';
103127
let setPatternPropertiesMap = '';
104128
if (model.patternProperties !== undefined) {
105129
for (const [pattern, patternModel] of Object.entries(model.patternProperties)) {
106130
let patternPropertyName = getUniquePropertyName(model, `${pattern}${DefaultPropertyNames.patternProperties}`);
107131
patternPropertyName = renderer.nameProperty(patternPropertyName, patternModel);
108-
setPatternPropertiesMap = `if (instance.${patternPropertyName} === undefined) {instance.${patternPropertyName} = new Map();}`;
132+
const modelInstanceVariable = 'value as any';
133+
const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, patternModel, inputModel, renderer);
134+
setPatternPropertiesMap += `if (instance.${patternPropertyName} === undefined) {instance.${patternPropertyName} = new Map();}\n`;
109135
unmarshalPatternProperties += `//Check all pattern properties
110136
if (key.match(new RegExp('${pattern}'))) {
111-
instance.${patternPropertyName}.set(key, value as any);
137+
instance.${patternPropertyName}.set(key, ${unmarshalCode});
112138
continue;
113139
}`;
114140
}
115141
}
116142
return {unmarshalPatternProperties, setPatternPropertiesMap};
117143
}
118144

119-
function renderUnmarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer) {
145+
function renderUnmarshalAdditionalProperties(model: CommonModel, renderer: TypeScriptRenderer, inputModel: CommonInputModel) {
120146
let unmarshalAdditionalProperties = '';
121147
let setAdditionalPropertiesMap = '';
122148
if (model.additionalProperties !== undefined) {
123149
let additionalPropertyName = getUniquePropertyName(model, DefaultPropertyNames.additionalProperties);
124150
additionalPropertyName = renderer.nameProperty(additionalPropertyName, model.additionalProperties);
125-
const additionalPropertiesCast = model.additionalProperties.$ref !== undefined ? `${renderer.nameType(model.$id)}.unmarshal(value)` : 'value as any';
151+
const modelInstanceVariable = 'value as any';
152+
const unmarshalCode = renderUnmarshalProperty(modelInstanceVariable, model.additionalProperties, inputModel, renderer);
126153
setAdditionalPropertiesMap = `if (instance.${additionalPropertyName} === undefined) {instance.${additionalPropertyName} = new Map();}`;
127-
unmarshalAdditionalProperties = `instance.${additionalPropertyName}.set(key, ${additionalPropertiesCast});`;
154+
unmarshalAdditionalProperties = `instance.${additionalPropertyName}.set(key, ${unmarshalCode});`;
128155
}
129156
return {unmarshalAdditionalProperties, setAdditionalPropertiesMap};
130157
}
131158

132159
/**
133160
* Render `unmarshal` function based on model
134161
*/
135-
function renderUnmarshal({ renderer, model }: {
162+
function renderUnmarshal({ renderer, model, inputModel }: {
136163
renderer: TypeScriptRenderer,
137164
model: CommonModel,
165+
inputModel: CommonInputModel
138166
}): string {
139167
const properties = model.properties || {};
140-
const {unmarshalPatternProperties, setPatternPropertiesMap} = renderUnmarshalPatternProperties(model, renderer);
141-
const {unmarshalAdditionalProperties, setAdditionalPropertiesMap} = renderUnmarshalAdditionalProperties(model, renderer);
168+
const {unmarshalPatternProperties, setPatternPropertiesMap} = renderUnmarshalPatternProperties(model, renderer, inputModel);
169+
const {unmarshalAdditionalProperties, setAdditionalPropertiesMap} = renderUnmarshalAdditionalProperties(model, renderer, inputModel);
170+
const unmarshalProperties = renderUnmarshalProperties(model, renderer, inputModel);
142171
const formattedModelName = renderer.nameType(model.$id);
143172
const propertyNames = Object.keys(properties).map((prop => `"${prop}"`));
144173
return `public static unmarshal(json: string | object): ${formattedModelName} {
145174
const obj = typeof json === "object" ? json : JSON.parse(json);
146175
const instance = new ${formattedModelName}({} as any);
147176
148-
${renderer.indent(renderUnmarshalProperties(model, renderer))}
177+
${renderer.indent(unmarshalProperties)}
149178
150179
//Not part of core properties
151180
${setPatternPropertiesMap}
@@ -165,13 +194,13 @@ ${renderer.indent(unmarshalAdditionalProperties, 4)}
165194
*/
166195
export const TS_COMMON_PRESET: TypeScriptPreset = {
167196
class: {
168-
additionalContent({ renderer, model, content, options }) {
197+
additionalContent({ renderer, model, content, options, inputModel }) {
169198
options = options || {};
170199
const blocks: string[] = [];
171200

172201
if (options.marshalling === true) {
173-
blocks.push(renderMarshal({ renderer, model }));
174-
blocks.push(renderUnmarshal({ renderer, model }));
202+
blocks.push(renderMarshal({ renderer, model, inputModel }));
203+
blocks.push(renderUnmarshal({ renderer, model, inputModel }));
175204
}
176205

177206
if (options.example === true) {

test/blackbox/Dummy.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('Dummy JSON Schema file', () => {
3636
const renderOutputPath = path.resolve(__dirname, './output/ts/class/output.ts');
3737
await renderModels(generatedModels, renderOutputPath);
3838
const transpiledOutputPath = path.resolve(__dirname, './output/ts/class/output.js');
39-
const transpileAndRunCommand = `tsc -t es5 ${renderOutputPath} && node ${transpiledOutputPath}`;
39+
const transpileAndRunCommand = `tsc --downlevelIteration -t es5 ${renderOutputPath} && node ${transpiledOutputPath}`;
4040
await execCommand(transpileAndRunCommand);
4141
});
4242

0 commit comments

Comments
 (0)