Handle circular references

This commit is contained in:
Roman Hotsiy 2016-01-16 00:44:10 +02:00
parent e5a5e692eb
commit afb9f6b1ed
4 changed files with 70 additions and 19 deletions

View File

@ -43,7 +43,9 @@ export default class JsonSchema extends BaseComponent {
}
this.joinAllOf(schema, {omitParent: true});
if (!schema.type) {
schema.type = 'object';
}
if (schema.type !== 'object') {
this.isTrivial = true;
this._displayType = schema.type;
@ -101,7 +103,7 @@ export default class JsonSchema extends BaseComponent {
if (propData.type === 'array') {
let itemType = propData.items.type;
let itemFormat = propData.items.format;
if (itemType === 'object') {
if (itemType === 'object' || !itemType) {
itemType = propData.items.title || 'object';
propData._pointer = propData.items._pointer || JsonPointer.join(this.pointer, ['properties', prop, 'items']);
}

View File

@ -99,27 +99,39 @@ export class BaseComponent {
/**
* simple in-place schema dereferencing. Schema is already bundled so no need in global dereferencing.
* TODO: doesn't support circular references
*/
dereference(schema = Object.assign({}, this.componentSchema)) {
//schema = Object.assign({}, schema);
if (schema && schema.$ref) {
let resolved = this.schemaMgr.byPointer(schema.$ref);
let baseName = JsonPointer.baseName(schema.$ref);
// if resolved schema doesn't have title use name from ref
resolved = Object.assign({}, resolved);
resolved.title = resolved.title || baseName;
resolved._pointer = schema.$ref;
Object.assign(schema, resolved);
delete schema.$ref;
}
let dereferencedCache = {};
Object.keys(schema).forEach((key) => {
let value = schema[key];
if (value && typeof value === 'object') {
this.dereference(value);
let resolve = (schema) => {
if (schema && schema.$ref) {
let resolved = this.schemaMgr.byPointer(schema.$ref);
let baseName = JsonPointer.baseName(schema.$ref);
if (!dereferencedCache[schema.$ref]) {
// if resolved schema doesn't have title use name from ref
resolved = Object.assign({}, resolved);
resolved._pointer = schema.$ref;
} else {
// for circular referenced save only title and type
resolved = {
title: resolved.title
};
}
resolved.title = resolved.title || baseName;
Object.assign(schema, resolved);
dereferencedCache[schema.$ref] = true;
delete schema.$ref;
}
});
Object.keys(schema).forEach((key) => {
let value = schema[key];
if (value && typeof value === 'object') {
resolve(value);
}
});
};
resolve(schema);
this.componentSchema = schema;
}

View File

@ -111,6 +111,25 @@ describe('Redoc components', () => {
paramWithRef.items.schema.type.should.be.equal('object');
});
});
describe('circular dereference', () => {
let paramWithRef;
beforeAll(() => {
component.pointer = '/paths/test4/get';
component.ngOnInit();
component.dereference();
paramWithRef = component.componentSchema.parameters[0];
});
it('should resolve circular schema', () => {
expect(paramWithRef.$ref).toBeUndefined();
expect(paramWithRef.items.schema.$ref).toBeUndefined();
paramWithRef.type.should.be.equal('array');
paramWithRef._pointer.should.be.equal('#/definitions/Circular');
expect(paramWithRef.items.schema._pointer).toBeUndefined();
paramWithRef.items.schema.title.should.be.equal('Circular');
});
});
});
describe('mergeAllOf', () => {

View File

@ -31,6 +31,14 @@
"$ref": "#/definitions/Simple"
}
}
},
"Circular": {
"type": "array",
"items": {
"schema": {
"$ref": "#/definitions/Circular"
}
}
}
},
"paths": {
@ -63,6 +71,16 @@
}
]
}
},
"test4": {
"get": {
"summary": "test get",
"parameters": [
{
"$ref": "#/definitions/Circular"
}
]
}
}
}
}