Skip to content
This repository was archived by the owner on Oct 20, 2022. It is now read-only.

Commit ffda194

Browse files
committed
fix polymorphism support and complex models generation
1 parent f37d8ac commit ffda194

File tree

2 files changed

+98
-31
lines changed

2 files changed

+98
-31
lines changed

src/scripts/services/swagger-model.js

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,13 @@ angular
5454
*/
5555
function resolveAllOf(openApiSpec, schema) {
5656
if (schema.allOf) {
57+
schema = angular.copy(schema);
5758
angular.forEach(schema.allOf, function(def) {
58-
angular.merge(schema, resolveReference(openApiSpec, def));
59+
var ref = resolveReference(openApiSpec, def);
60+
if (!ref.discriminator) {
61+
// do not handle inhertited properties here
62+
angular.merge(schema, ref);
63+
}
5964
});
6065
delete schema.allOf;
6166
}
@@ -69,6 +74,12 @@ angular
6974
var sample, def, name, prop;
7075
currentGenerated = currentGenerated || {}; // used to handle circular references
7176
schema = resolveAllOf(openApiSpec, schema);
77+
if (schema.parentModelsRef) {
78+
angular.forEach(schema.parentModelsRef, function(ref) {
79+
var def = resolveReference(openApiSpec, ref);
80+
angular.merge(schema, def);
81+
});
82+
}
7283
if (schema.default || schema.example) {
7384
sample = schema.default || schema.example;
7485
} else if (schema.properties) {
@@ -200,30 +211,58 @@ angular
200211
return name;
201212
}
202213

214+
/**
215+
* identify models using inheritance
216+
*/
217+
this.resolveInheritance = function(openApiSpec) {
218+
angular.forEach(openApiSpec.definitions, function(schema, modelName) {
219+
if (schema.discriminator) {
220+
schema.subModelsRef = [];
221+
angular.forEach(openApiSpec.definitions, function(subSchema, subModelName) {
222+
if (schema !== subSchema && subSchema.allOf) {
223+
angular.forEach(subSchema.allOf, function(parent) {
224+
if (parent.$ref && modelName === getClassName(parent)) {
225+
subSchema.parentModelsRef = subSchema.parentModelsRef || [];
226+
subSchema.parentModelsRef.push({
227+
'$ref': '#/definitions/' + modelName
228+
});
229+
schema.subModelsRef.push({
230+
'$ref': '#/definitions/' + subModelName
231+
});
232+
}
233+
});
234+
}
235+
});
236+
}
237+
});
238+
};
239+
203240
/**
204241
* generate a model and its submodels from schema
205242
*/
206243
this.generateModel = function(openApiSpec, schema, operationId) {
207244
var model = [],
208245
subModelIds = {},
209-
subModels = {};
246+
subModels = {},
247+
def;
210248

211249
try {
212250
schema = resolveAllOf(openApiSpec, schema);
251+
// find models to generate
213252
if (schema.properties) {
214253
// if inline model
215254
subModels[getInlineModelName()] = schema;
216255
subModels = angular.merge(subModels, findAllModels(openApiSpec, schema, subModelIds));
217256
} else {
218257
subModels = findAllModels(openApiSpec, schema, subModelIds);
219258
}
220-
221-
if (!schema.$ref && !schema.properties) {
222-
// if array or map/dictionary or simple type
223-
model.push('<strong>', getModelProperty(schema, subModels, subModelIds, operationId), '</strong><br><br>');
259+
def = resolveReference(openApiSpec, schema);
260+
if (!def.properties) {
261+
// if not complex type
262+
model.push('<strong>', getModelProperty(def, subModels, subModelIds, operationId), '</strong><br><br>');
224263
}
225-
angular.forEach(subModels, function(schema, modelName) {
226-
model.push(getModel(openApiSpec, schema, modelName, subModels, subModelIds, operationId));
264+
angular.forEach(subModels, function(subSchema, subModelName) {
265+
model.push(getModel(openApiSpec, subSchema, subModelName, subModels, subModelIds, operationId));
227266
});
228267
} catch (ex) {
229268
console.error('AngularSwaggerUI: failed to generate model', schema, ex);
@@ -255,38 +294,54 @@ angular
255294
def = resolveReference(openApiSpec, subSchema),
256295
subPropertyModelName = getClassName(subSchema);
257296

258-
if (def && (def.type === 'object' || def.type === 'array')) {
297+
if (def && (!def.type || def.type === 'object' || def.type === 'array')) {
298+
def = resolveAllOf(openApiSpec, def);
259299
models[subPropertyModelName] = def;
260300
subModelIds[subPropertyModelName] = countModel++;
261301
angular.merge(models, findAllModels(openApiSpec, def, subModelIds, subPropertyModelName, onGoing));
262-
} else if (def) {
263-
schema = angular.merge(schema, def);
264-
delete schema.$ref;
265302
}
266303
} else if (schema.type === 'array') {
267304
inspectSubModel(openApiSpec, schema.items, models, subModelIds, onGoing);
268305
} else if (schema.additionalProperties) {
269306
// this is a map/dictionary
270307
inspectSubModel(openApiSpec, schema.additionalProperties, models, subModelIds, onGoing);
271308
}
272-
if (schema.discriminator) {
273-
// find subclasses
274-
angular.forEach(openApiSpec.definitions, function(subSchema, subModelName) {
275-
if (subSchema.allOf) {
276-
angular.forEach(subSchema.allOf, function(parent) {
277-
if (parent.$ref && modelName === getClassName(parent)) {
278-
subSchema.parentModel = modelName;
279-
models[subModelName] = subSchema;
280-
subModelIds[subModelName] = countModel++;
281-
angular.merge(models, findAllModels(openApiSpec, subSchema, subModelIds, subModelName, onGoing));
282-
}
283-
});
309+
if (schema.subModelsRef) {
310+
// check if subclass not already built
311+
var subClasses = schema.subModelsRef.map(getClassName),
312+
found = false,
313+
keys = Object.keys(subModelIds),
314+
i = 0;
315+
316+
for (; i < subClasses.length; i++) {
317+
if (keys.indexOf(subClasses[i]) > -1) {
318+
found = true;
319+
break;
284320
}
285-
});
321+
}
322+
if (!found) {
323+
// add sub classes
324+
addInheritanceModels(openApiSpec, schema.subModelsRef, models, subModelIds, onGoing);
325+
}
326+
}
327+
if (schema.parentModelsRef) {
328+
// find super classes
329+
addInheritanceModels(openApiSpec, schema.parentModelsRef, models, subModelIds, onGoing);
286330
}
287331
return models;
288332
}
289333

334+
function addInheritanceModels(openApiSpec, inheritanceModels, models, subModelIds, onGoing) {
335+
angular.forEach(inheritanceModels, function(ref) {
336+
var def = resolveReference(openApiSpec, ref),
337+
subModelName = getClassName(ref);
338+
339+
models[subModelName] = def;
340+
subModelIds[subModelName] = countModel++;
341+
angular.merge(models, findAllModels(openApiSpec, def, subModelIds, subModelName, onGoing));
342+
});
343+
}
344+
290345
/**
291346
* look for submodels
292347
*/
@@ -312,7 +367,7 @@ angular
312367
/**
313368
* generates an HTML link to a submodel
314369
*/
315-
function getSubModelLink(operationId, modelId, name) {
370+
function getSubModelLink(operationId, name) {
316371
var linkModelId = operationId + '-model-' + name;
317372
return ['<a class="model-link type" onclick="swaggerlink(\'', linkModelId, '\')">', name, '</a>'].join('');
318373
}
@@ -330,13 +385,24 @@ angular
330385
schema = resolveAllOf(openApiSpec, schema);
331386
if (schema.properties) {
332387
buffer.push('<div><strong>', modelName);
333-
if (schema.parentModel) {
334-
buffer.push('</strong> extends <strong>', schema.parentModel);
388+
if (schema.parentModelsRef) {
389+
buffer.push('</strong> extends <strong>');
390+
angular.forEach(schema.parentModelsRef, function(ref) {
391+
buffer.push(getSubModelLink(operationId, getClassName(ref)), ' ');
392+
});
393+
buffer.pop();
335394
}
336395
buffer.push(' {</strong></div>');
337396
var hasProperties = false;
338397
angular.forEach(schema.properties, function(property, propertyName) {
339398
hasProperties = true;
399+
if (!property.type && property.$ref) {
400+
var ref = resolveReference(openApiSpec, property);
401+
if (ref.type !== 'object' && ref.type !== 'array') {
402+
// this is a simple type
403+
property = ref;
404+
}
405+
}
340406
buffer.push('<div class="pad"><strong>', propertyName, '</strong> (<span class="type">');
341407
buffer.push(getModelProperty(property, subModels, subModelIds, operationId));
342408
buffer.push('</span>');
@@ -374,16 +440,16 @@ angular
374440
function getModelProperty(property, subModels, subModelIds, operationId) {
375441
var modelName, buffer = [];
376442
if (property.modelName) {
377-
buffer.push(getSubModelLink(operationId, subModelIds[property.modelName], property.modelName));
443+
buffer.push(getSubModelLink(operationId, property.modelName));
378444
} else if (property.schema || property.$ref) {
379445
modelName = getClassName(property.schema || property);
380-
buffer.push(getSubModelLink(operationId, subModelIds[modelName], modelName));
446+
buffer.push(getSubModelLink(operationId, modelName));
381447
} else if (property.type === 'array') {
382448
buffer.push('Array[');
383449
buffer.push(getModelProperty(property.items, subModels, subModelIds, operationId));
384450
buffer.push(']');
385451
} else if (property.properties) {
386-
buffer.push(getSubModelLink(operationId, subModelIds[property.modelName], modelName));
452+
buffer.push(getSubModelLink(operationId, modelName));
387453
} else if (property.additionalProperties) {
388454
// this is a map/dictionary
389455
buffer.push('Map&lt;string, ');

src/scripts/services/swagger-parser.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ angular
5353
parseInfos(data.openApiSpec, data.url, infos, defaultContentType);
5454
parseTags(data.openApiSpec, resources, map);
5555
parseOperations(data.openApiSpec, resources, form, map, defaultContentType, openPath);
56+
swaggerModel.resolveInheritance(data.openApiSpec);
5657
cleanUp(resources, openPath, sortResources);
5758
// prepare result
5859
data.ui = {

0 commit comments

Comments
 (0)