From 2cbb516059066dd671b43371630518022bdf6be6 Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Wed, 23 Mar 2016 23:16:30 +0200 Subject: [PATCH 1/5] url parameter for demo page (fixes #22) --- demo/main.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/demo/main.js b/demo/main.js index 836eaa45..2762004b 100644 --- a/demo/main.js +++ b/demo/main.js @@ -5,9 +5,31 @@ var schemaUrlInput = document.getElementById('schema-url-input'); schemaUrlForm.addEventListener('submit', function(event) { event.preventDefault(); - Redoc.init(schemaUrlInput.value); + event.stopPropagation(); + location.search = updateQueryStringParameter(location.search, 'url', schemaUrlInput.value) return false; }) + var url = window.location.search.match(/url=([^&]+)/); + if (url && url.length > 1) { + url = decodeURIComponent(url[1]); + document.getElementsByTagName('redoc')[0].setAttribute('spec-url', url); + schemaUrlInput.value = url; + } + + function updateQueryStringParameter(uri, key, value) { + var re = new RegExp("([?|&])" + key + "=.*?(&|#|$)", "i"); + if (uri.match(re)) { + return uri.replace(re, '$1' + key + "=" + value + '$2'); + } else { + var hash = ''; + if( uri.indexOf('#') !== -1 ){ + hash = uri.replace(/.*#/, '#'); + uri = uri.replace(/#.*/, ''); + } + var separator = uri.indexOf('?') !== -1 ? "&" : "?"; + return uri + separator + key + "=" + value + hash; + } + } //window.redocDebugMode = true; })(); From 8b30653fb03dfdf7478051ebc10229cc6a8110b3 Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Sun, 27 Mar 2016 00:37:56 +0200 Subject: [PATCH 2/5] Fix caching schemas with discriminator --- lib/components/JsonSchema/json-schema-lazy.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/components/JsonSchema/json-schema-lazy.js b/lib/components/JsonSchema/json-schema-lazy.js index a8c7b589..bbf20974 100644 --- a/lib/components/JsonSchema/json-schema-lazy.js +++ b/lib/components/JsonSchema/json-schema-lazy.js @@ -55,7 +55,8 @@ export default class JsonSchemaLazy { let $element = compRef.location.nativeElement; // skip caching view with tabs inside (discriminator) as it needs attached controller - if ($element.querySelector('tabs')) { + // FIXME: get rid of dependency on selector + if ($element.querySelector('.discriminator-wrap')) { this.dcl.loadNextToLocation(JsonSchema, this.elementRef).then((compRef) => { compRef.instance.pointer = this.pointer; compRef.hostView.changeDetectorRef.markForCheck(); From 603235566457fb19676ed458b97b585840ff8491 Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Sun, 27 Mar 2016 00:44:11 +0200 Subject: [PATCH 3/5] Refactor JsonSchema component + improve merging allOf --- lib/components/JsonSchema/json-schema.js | 89 ++++++++++--------- lib/components/JsonSchema/json-schema.spec.js | 4 +- lib/components/base.js | 13 +-- lib/components/base.spec.js | 14 +-- 4 files changed, 61 insertions(+), 59 deletions(-) diff --git a/lib/components/JsonSchema/json-schema.js b/lib/components/JsonSchema/json-schema.js index 56b13d5b..331eb9d7 100644 --- a/lib/components/JsonSchema/json-schema.js +++ b/lib/components/JsonSchema/json-schema.js @@ -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 ? diff --git a/lib/components/JsonSchema/json-schema.spec.js b/lib/components/JsonSchema/json-schema.spec.js index 02e54491..dd7e9ef2 100644 --- a/lib/components/JsonSchema/json-schema.spec.js +++ b/lib/components/JsonSchema/json-schema.spec.js @@ -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('< * >'); }); }); }); diff --git a/lib/components/base.js b/lib/components/base.js index 16367906..b039b7ce 100644 --- a/lib/components/base.js +++ b/lib/components/base.js @@ -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); } diff --git a/lib/components/base.spec.js b/lib/components/base.spec.js index 6091ebd6..8042b3e1 100644 --- a/lib/components/base.spec.js +++ b/lib/components/base.spec.js @@ -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']); From 422ef513a2001a23aef3489f2b6b594ce1675621 Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Sun, 27 Mar 2016 00:45:28 +0200 Subject: [PATCH 4/5] UI fix: extra line at the last object property --- .../JsonSchema/json-schema-common.scss | 6 ++++- lib/components/JsonSchema/json-schema.html | 27 ++++++++++++------- lib/components/JsonSchema/json-schema.scss | 14 +++++++++- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/lib/components/JsonSchema/json-schema-common.scss b/lib/components/JsonSchema/json-schema-common.scss index 51942c3f..c03e356b 100644 --- a/lib/components/JsonSchema/json-schema-common.scss +++ b/lib/components/JsonSchema/json-schema-common.scss @@ -133,7 +133,7 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin; height: ($param-name-height/2) + $cell-padding + 1; } -.param:last-of-type > .param-name { +.param:last-of-type > .param-name, .param.last > .param-name { position: relative; &:after { @@ -156,6 +156,10 @@ $sub-schema-offset: ($bullet-size/2) + $bullet-margin; display: none !important; } +.param-schema.last > td { + border-left: 0; +} + .param-enum { color: $text-color; font-size: 13px; diff --git a/lib/components/JsonSchema/json-schema.html b/lib/components/JsonSchema/json-schema.html index 6d2d4e8a..bd3155ee 100644 --- a/lib/components/JsonSchema/json-schema.html +++ b/lib/components/JsonSchema/json-schema.html @@ -1,10 +1,17 @@ - - {{_displayType}} + + {{schema._displayType}} {{schema._displayFormat}} + {{schema._range}} + +
+ {{enumItem.val | json}} +
- -