mirror of
https://github.com/Redocly/redoc.git
synced 2025-01-27 16:14:08 +03:00
Refactor JsonSchema component + improve merging allOf
This commit is contained in:
parent
8b30653fb0
commit
6032355664
|
@ -23,67 +23,58 @@ export default class JsonSchema extends BaseComponent {
|
|||
|
||||
selectDerived(subClass) {
|
||||
if (subClass.active) return;
|
||||
this.data.derived.forEach((subSchema) => {
|
||||
this.schema.derived.forEach((subSchema) => {
|
||||
subSchema.active = false;
|
||||
});
|
||||
subClass.active = true;
|
||||
}
|
||||
|
||||
prepareModel() {
|
||||
this.data = {};
|
||||
this.data.properties = [];
|
||||
this.data.derived = [];
|
||||
unwrapArray(schema) {
|
||||
var res = schema;
|
||||
if (schema && schema.type === 'array') {
|
||||
let ptr = schema.items._pointer
|
||||
|| JsonPointer.join(schema._pointer || this.pointer, ['items']);
|
||||
res = schema.items;
|
||||
res._isArray = true;
|
||||
res._pointer = ptr;
|
||||
res = this.unwrapArray(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
prepareModel() {
|
||||
if (!this.componentSchema) {
|
||||
throw new Error(`Can't load component schema at ${this.pointer}`);
|
||||
}
|
||||
|
||||
this.dereference();
|
||||
|
||||
let schema = this.componentSchema;
|
||||
BaseComponent.joinAllOf(schema, {omitParent: true});
|
||||
schema = this.unwrapArray(schema);
|
||||
runInjectors(schema, schema, schema._pointer || this.pointer);
|
||||
|
||||
if (schema.type === 'array') {
|
||||
this.isArray = true;
|
||||
if (schema._pointer) {
|
||||
this.pointer = JsonPointer.join(schema._pointer, 'items');
|
||||
}
|
||||
schema = schema.items;
|
||||
}
|
||||
let normPtr = schema._pointer || this.pointer;
|
||||
let derived = this.schemaMgr.findDerivedDefinitions( normPtr );
|
||||
if (!this.final && derived.length) {
|
||||
derived[0].active = true;
|
||||
this.data.derived = derived;
|
||||
this.data.discriminator = schema.discriminator;
|
||||
schema.derived = schema.derived || [];
|
||||
if (schema.derived.length) schema.derived[0].active = true;
|
||||
|
||||
if (!schema.isTrivial) {
|
||||
this.prepareObjectPropertiesData(schema);
|
||||
}
|
||||
|
||||
this.joinAllOf(schema, {omitParent: true});
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
if (schema.type !== 'object') {
|
||||
this.isTrivial = true;
|
||||
this._displayType = schema.type;
|
||||
if (schema.format) this._displayType = `${this.displayType} <${schema.format}>`;
|
||||
return;
|
||||
}
|
||||
|
||||
this.pointer = schema._pointer || this.pointer;
|
||||
|
||||
this.requiredMap = {};
|
||||
if (this.componentSchema.required) {
|
||||
this.componentSchema.required.forEach(prop => this.requiredMap[prop] = true);
|
||||
}
|
||||
|
||||
if (!schema.properties) {
|
||||
this.isTrivial = true;
|
||||
this._displayType = schema.type;
|
||||
return;
|
||||
prepareObjectPropertiesData(schema) {
|
||||
let requiredMap = {};
|
||||
if (schema.required) {
|
||||
schema.required.forEach(prop => requiredMap[prop] = true);
|
||||
}
|
||||
|
||||
let discriminatorFieldIdx = -1;
|
||||
let props = Object.keys(schema.properties).map((prop, idx) => {
|
||||
let propertySchema = schema.properties[prop];
|
||||
let propPointer = JsonPointer.join(this.pointer, ['properties', prop]);
|
||||
let propPointer = JsonPointer.join(schema._pointer || this.pointer, ['properties', prop]);
|
||||
propertySchema = JsonSchema.injectPropertyData(propertySchema, prop, propPointer);
|
||||
propertySchema.required = !!this.requiredMap[prop];
|
||||
propertySchema.required = !!requiredMap[prop];
|
||||
propertySchema.isDiscriminator = (schema.discriminator === prop);
|
||||
if (propertySchema.isDiscriminator) discriminatorFieldIdx = idx;
|
||||
return propertySchema;
|
||||
|
@ -93,7 +84,7 @@ export default class JsonSchema extends BaseComponent {
|
|||
let discrProp = props.splice(discriminatorFieldIdx, 1);
|
||||
props.push(discrProp[0]);
|
||||
}
|
||||
this.data.properties = props;
|
||||
schema.properties = props;
|
||||
}
|
||||
|
||||
static injectPropertyData(propertySchema, propertyName, propPointer) {
|
||||
|
@ -118,7 +109,8 @@ function runInjectors(injectTo, propertySchema, propertyPointer) {
|
|||
const injectors = {
|
||||
general: {
|
||||
check: () => true,
|
||||
inject: (injectTo, propertySchema) => {
|
||||
inject: (injectTo, propertySchema, pointer) => {
|
||||
injectTo._pointer = propertySchema._pointer || pointer;
|
||||
injectTo._displayType = propertySchema.type;
|
||||
if (propertySchema.format) injectTo._displayFormat = `<${propertySchema.format}>`;
|
||||
if (propertySchema.enum) {
|
||||
|
@ -128,7 +120,13 @@ const injectors = {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
discriminator: {
|
||||
check: (propertySchema) => propertySchema.discriminator,
|
||||
inject: (injectTo, propertySchema = injectTo, pointer) => {
|
||||
injectTo.derived = SchemaManager.instance().findDerivedDefinitions(pointer);
|
||||
injectTo.discriminator = propertySchema.discriminator;
|
||||
}
|
||||
},
|
||||
array: {
|
||||
check: (propertySchema) => {
|
||||
return propertySchema.type === 'array';
|
||||
|
@ -147,7 +145,8 @@ const injectors = {
|
|||
return propertySchema.type === 'object' && propertySchema.properties;
|
||||
},
|
||||
inject: (injectTo, propertySchema = injectTo) => {
|
||||
injectTo._displayType = propertySchema.title || 'object';
|
||||
let baseName = propertySchema._pointer && JsonPointer.baseName(propertySchema._pointer);
|
||||
injectTo._displayType = propertySchema.title || baseName || 'object';
|
||||
}
|
||||
},
|
||||
noType: {
|
||||
|
@ -155,6 +154,7 @@ const injectors = {
|
|||
inject: (injectTo) => {
|
||||
injectTo._displayType = '< * >';
|
||||
injectTo._displayTypeHint = 'This field may contain data of any type';
|
||||
injectTo.isTrivial = true;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -166,6 +166,7 @@ const injectors = {
|
|||
return (propertySchema.type !== 'array') && propertySchema.type;
|
||||
},
|
||||
inject: (injectTo, propertySchema = injectTo) => {
|
||||
injectTo.isTrivial = true;
|
||||
if (injectTo._pointer) {
|
||||
injectTo._pointer = undefined;
|
||||
injectTo._displayType = propertySchema.title ?
|
||||
|
|
|
@ -48,7 +48,7 @@ describe('Redoc components', () => {
|
|||
component.pointer = '';
|
||||
schemaMgr._schema = {type: 'string'};
|
||||
fixture.detectChanges();
|
||||
component.isTrivial.should.be.true();
|
||||
component.schema.isTrivial.should.be.true();
|
||||
});
|
||||
|
||||
it('should use < * > notation for prop without type', () => {
|
||||
|
@ -57,7 +57,7 @@ describe('Redoc components', () => {
|
|||
test: {}
|
||||
}};
|
||||
fixture.detectChanges();
|
||||
component.data.properties[0]._displayType.should.be.equal('< * >');
|
||||
component.schema.properties[0]._displayType.should.be.equal('< * >');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -98,7 +98,6 @@ export function RedocComponent(options) {
|
|||
export class BaseComponent {
|
||||
constructor(schemaMgr) {
|
||||
this.schemaMgr = schemaMgr;
|
||||
this.schema = schemaMgr.schema;
|
||||
this.componentSchema = null;
|
||||
}
|
||||
|
||||
|
@ -167,10 +166,10 @@ export class BaseComponent {
|
|||
return schema;
|
||||
};
|
||||
|
||||
this.componentSchema = resolve(schema, 1);
|
||||
this.componentSchema = snapshot(resolve(schema, 1));
|
||||
}
|
||||
|
||||
joinAllOf(schema = this.componentSchema, opts) {
|
||||
static joinAllOf(schema, opts) {
|
||||
function merge(into, schemas) {
|
||||
for (let subSchema of schemas) {
|
||||
if (opts && opts.omitParent && subSchema.discriminator) continue;
|
||||
|
@ -181,21 +180,23 @@ export class BaseComponent {
|
|||
throw new Error(errMessage);
|
||||
}
|
||||
|
||||
if (into.type && into.type !== subSchema.type) {
|
||||
if (into.type && subSchema.type && into.type !== subSchema.type) {
|
||||
let errMessage = `allOf merging error: schemas with different types can't be merged`;
|
||||
throw new Error(errMessage);
|
||||
}
|
||||
|
||||
into.type = into.type || subSchema.type;
|
||||
|
||||
if (into.type === 'array') {
|
||||
console.warn('allOf: subschemas with type array are not supported yet');
|
||||
}
|
||||
|
||||
// TODO: add check if can be merged correctly (no different properties with the same name)
|
||||
if (subSchema.type === 'object' && subSchema.properties) {
|
||||
if (into.type === 'object' && subSchema.properties) {
|
||||
into.properties || (into.properties = {});
|
||||
Object.assign(into.properties, subSchema.properties);
|
||||
}
|
||||
if (subSchema.type === 'object' && subSchema.required) {
|
||||
if (into.type === 'object' && subSchema.required) {
|
||||
into.required || (into.required = []);
|
||||
into.required.push(...subSchema.required);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('Redoc components', () => {
|
|||
|
||||
it('should set instance properties', () => {
|
||||
component.schemaMgr.should.be.equal(schemaMgr);
|
||||
component.schema.should.be.of.type('object');
|
||||
//component.schema.should.be.of.type('object');
|
||||
expect(component.componentSchema).toBeNull();
|
||||
});
|
||||
|
||||
|
@ -165,7 +165,7 @@ describe('Redoc components', () => {
|
|||
component.pointer = '/definitions/SimpleAllOf';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
component.joinAllOf();
|
||||
BaseComponent.joinAllOf(component.componentSchema);
|
||||
joined = component.componentSchema;
|
||||
});
|
||||
|
||||
|
@ -194,7 +194,7 @@ describe('Redoc components', () => {
|
|||
component.pointer = '/definitions/AllOfWithRef';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
component.joinAllOf();
|
||||
BaseComponent.joinAllOf(component.componentSchema);
|
||||
joined = component.componentSchema;
|
||||
});
|
||||
|
||||
|
@ -223,7 +223,7 @@ describe('Redoc components', () => {
|
|||
component.pointer = '/definitions/AllOfWithOther';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
component.joinAllOf();
|
||||
BaseComponent.joinAllOf(component.componentSchema);
|
||||
joined = component.componentSchema;
|
||||
});
|
||||
|
||||
|
@ -256,7 +256,7 @@ describe('Redoc components', () => {
|
|||
component.pointer = '/definitions/PropertiesOnAllOfLevel';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
(() => component.joinAllOf()).should.not.throw();
|
||||
(() => BaseComponent.joinAllOf(component.componentSchema)).should.not.throw();
|
||||
let joined = component.componentSchema;
|
||||
Object.keys(joined.properties).length.should.be.equal(3);
|
||||
});
|
||||
|
@ -265,14 +265,14 @@ describe('Redoc components', () => {
|
|||
component.pointer = '/definitions/BadAllOf1';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
(() => component.joinAllOf()).should.throw();
|
||||
(() => BaseComponent.joinAllOf(component.componentSchema)).should.throw();
|
||||
});
|
||||
|
||||
it('should handle nested allOF', () => {
|
||||
component.pointer = '/definitions/NestedAllOf';
|
||||
component.ngOnInit();
|
||||
component.dereference();
|
||||
(() => component.joinAllOf()).should.not.throw();
|
||||
(() => BaseComponent.joinAllOf(component.componentSchema)).should.not.throw();
|
||||
let joined = component.componentSchema;
|
||||
Object.keys(joined.properties).length.should.be.equal(4);
|
||||
Object.keys(joined.properties).should.be.deepEqual(['prop1', 'prop2', 'prop3', 'prop4']);
|
||||
|
|
Loading…
Reference in New Issue
Block a user