mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-26 02:23:43 +03:00
allOf merging fixes (fixes #31)
This commit is contained in:
parent
9e10fa4a55
commit
e957a92346
|
@ -73,6 +73,7 @@ export default class JsonSchema extends BaseComponent {
|
|||
let props = Object.keys(schema.properties).map((prop, idx) => {
|
||||
let propData = schema.properties[prop];
|
||||
let propPointer = JsonPointer.join(this.pointer, ['properties', prop]);
|
||||
this.joinAllOf(propData);
|
||||
propData = JsonSchema.injectPropData(propData, prop, propPointer, this.requiredMap, schema);
|
||||
if (propData.isDiscriminator) discriminatorFieldIdx = idx;
|
||||
return propData;
|
||||
|
|
|
@ -17,6 +17,21 @@ function safeConcat(a, b) {
|
|||
return res.concat(b);
|
||||
}
|
||||
|
||||
function defaults(target, src) {
|
||||
var props = Object.keys(src);
|
||||
|
||||
var index = -1,
|
||||
length = props.length;
|
||||
|
||||
while (++index < length) {
|
||||
var key = props[index];
|
||||
if (target[key] === undefined) {
|
||||
target[key] = src[key];
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
function snapshot(obj) {
|
||||
if(obj == null || typeof(obj) != 'object') {
|
||||
return obj;
|
||||
|
@ -158,34 +173,34 @@ export class BaseComponent {
|
|||
joinAllOf(schema = this.componentSchema, opts) {
|
||||
var self = this;
|
||||
function merge(into, schemas) {
|
||||
if (into.required || into.properties) {
|
||||
let errMessage = `Can\'t merge allOf: properties or required fields are specified on the same level as allOf
|
||||
${into}`;
|
||||
throw new Error(errMessage);
|
||||
}
|
||||
into.required = [];
|
||||
into.properties = {};
|
||||
for (let subSchema of schemas) {
|
||||
if (opts && opts.omitParent && subSchema.discriminator) continue;
|
||||
|
||||
// TODO: add support for merge array schemas
|
||||
if (typeof subSchema !== 'object' || subSchema.type !== 'object') {
|
||||
let errMessage = `Can\'t merge allOf: only subschemas with type: object can be merged
|
||||
if (typeof subSchema !== 'object') {
|
||||
let errMessage = `Items of allOf should be Object: ${typeof subSchema} found
|
||||
${subSchema}`;
|
||||
throw new Error(errMessage);
|
||||
}
|
||||
|
||||
self.joinAllOf(subSchema);
|
||||
|
||||
if (into.type && into.type !== subSchema.type) {
|
||||
let errMessage = `allOf merging error: schemas with different types can't be merged`;
|
||||
throw new Error(errMessage);
|
||||
}
|
||||
|
||||
// TODO: add check if can be merged correctly (no different properties with the same name)
|
||||
if (subSchema.properties) {
|
||||
if (subSchema.type === 'object' && subSchema.properties) {
|
||||
into.properties || (into.properties = {});
|
||||
Object.assign(into.properties, subSchema.properties);
|
||||
}
|
||||
if (subSchema.required) {
|
||||
if (subSchema.type === 'object' && subSchema.required) {
|
||||
into.required || (into.required = []);
|
||||
into.required.push(...subSchema.required);
|
||||
}
|
||||
|
||||
defaults(into, subSchema);
|
||||
}
|
||||
into.type = 'object';
|
||||
into.allOf = null;
|
||||
}
|
||||
if (schema.allOf) {
|
||||
|
|
|
@ -217,15 +217,51 @@ describe('Redoc components', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Incorrect or not supported allOf', () => {
|
||||
it('should throw when properties or required is set on the same level as allOf', () => {
|
||||
component.pointer = '/definitions/BadAllOf2';
|
||||
describe('AllOf with other properties on the allOf level', () => {
|
||||
let joined;
|
||||
beforeAll(() => {
|
||||
component.pointer = '/definitions/AllOfWithOther';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
(() => component.joinAllOf()).should.throw();
|
||||
component.joinAllOf();
|
||||
joined = component.componentSchema;
|
||||
});
|
||||
|
||||
it('should throw when merging non-object schemas', () => {
|
||||
it('should remove $allOf field', () => {
|
||||
expect(joined.allOf).toBeNull();
|
||||
});
|
||||
|
||||
it('should set type object', () => {
|
||||
joined.type.should.be.equal('object');
|
||||
});
|
||||
|
||||
it('should merge properties', () => {
|
||||
Object.keys(joined.properties).length.should.be.equal(1);
|
||||
Object.keys(joined.properties).should.be.deepEqual(['id']);
|
||||
});
|
||||
|
||||
it('should merge required', () => {
|
||||
joined.required.length.should.be.equal(1);
|
||||
joined.required.should.be.deepEqual(['id']);
|
||||
});
|
||||
|
||||
it('should preserve parent properties', () => {
|
||||
joined.description.should.be.equal('Test');
|
||||
joined.readOnly.should.be.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('allOf edgecases', () => {
|
||||
it('should merge properties and required when defined on allOf level', () => {
|
||||
component.pointer = '/definitions/PropertiesOnAllOfLevel';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
(() => component.joinAllOf()).should.not.throw();
|
||||
let joined = component.componentSchema;
|
||||
Object.keys(joined.properties).length.should.be.equal(3);
|
||||
});
|
||||
|
||||
it('should throw when merging schemas with different types', () => {
|
||||
component.pointer = '/definitions/BadAllOf1';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"basePath": "/v2/",
|
||||
"definitions": {
|
||||
"Simple": {
|
||||
"description": "simple",
|
||||
"type": "object",
|
||||
"required": ["id"],
|
||||
"properties": {
|
||||
|
@ -57,6 +58,15 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"AllOfWithOther": {
|
||||
"description": "Test",
|
||||
"readOnly": true,
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Simple"
|
||||
}
|
||||
]
|
||||
},
|
||||
"BadAllOf1": {
|
||||
"allOf": [
|
||||
{
|
||||
|
@ -67,8 +77,12 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"BadAllOf2": {
|
||||
"properties": {},
|
||||
"PropertiesOnAllOfLevel": {
|
||||
"properties": {
|
||||
"prop": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Simple"
|
||||
|
|
Loading…
Reference in New Issue
Block a user