This commit is contained in:
Roman Hotsiy 2016-06-22 21:17:48 +03:00
parent 1e97ea655e
commit 53b4f55876
33 changed files with 361 additions and 354 deletions

View File

@ -15,7 +15,7 @@ import {
import { TestComponentBuilder } from '@angular/compiler/testing'; import { TestComponentBuilder } from '@angular/compiler/testing';
import { ApiInfo } from './api-info'; import { ApiInfo } from './api-info';
import { SchemaManager } from '../../utils/SchemaManager'; import { SpecManager } from '../../utils/SpecManager';
import { OptionsService } from '../../services/index'; import { OptionsService } from '../../services/index';
describe('Redoc components', () => { describe('Redoc components', () => {
@ -24,11 +24,11 @@ describe('Redoc components', () => {
let component; let component;
let fixture; let fixture;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}), provide(SpecManager, {useValue: new SpecManager()}),
provide(OptionsService, {useClass: OptionsService}) provide(OptionsService, {useClass: OptionsService})
]); ]);
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => { beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, schemaMgr) => {
builder = tcb; builder = tcb;
return schemaMgr.load('/tests/schemas/api-info-test.json'); return schemaMgr.load('/tests/schemas/api-info-test.json');
}))); })));

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { SchemaManager, RedocComponent, BaseComponent } from '../base'; import { SpecManager, RedocComponent, BaseComponent } from '../base';
import { OptionsService } from '../../services/index'; import { OptionsService } from '../../services/index';
@RedocComponent({ @RedocComponent({
@ -11,7 +11,7 @@ import { OptionsService } from '../../services/index';
export class ApiInfo extends BaseComponent { export class ApiInfo extends BaseComponent {
data: any; data: any;
specUrl: String; specUrl: String;
constructor(schemaMgr:SchemaManager, private optionsService:OptionsService) { constructor(schemaMgr:SpecManager, private optionsService:OptionsService) {
super(schemaMgr); super(schemaMgr);
} }

View File

@ -14,7 +14,7 @@ import {
import { TestComponentBuilder } from '@angular/compiler/testing'; import { TestComponentBuilder } from '@angular/compiler/testing';
import { ApiLogo } from './api-logo'; import { ApiLogo } from './api-logo';
import { SchemaManager } from '../../utils/SchemaManager'; import { SpecManager } from '../../utils/SpecManager';
describe('Redoc components', () => { describe('Redoc components', () => {
@ -26,9 +26,9 @@ describe('Redoc components', () => {
let schemaUrl = '/tests/schemas/api-info-test.json'; let schemaUrl = '/tests/schemas/api-info-test.json';
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}) provide(SpecManager, {useValue: new SpecManager()})
]); ]);
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, _schemaMgr) => { beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, _schemaMgr) => {
builder = tcb; builder = tcb;
schemaMgr = _schemaMgr; schemaMgr = _schemaMgr;
return schemaMgr.load(schemaUrl); return schemaMgr.load(schemaUrl);
@ -70,7 +70,7 @@ describe('Redoc components', () => {
@Component({ @Component({
selector: 'test-app', selector: 'test-app',
directives: [ApiLogo], directives: [ApiLogo],
providers: [SchemaManager], providers: [SpecManager],
template: template:
`<api-logo></api-logo>` `<api-logo></api-logo>`
}) })

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import {RedocComponent, BaseComponent, SchemaManager} from '../base'; import {RedocComponent, BaseComponent, SpecManager} from '../base';
@RedocComponent({ @RedocComponent({
selector: 'api-logo', selector: 'api-logo',
@ -10,7 +10,7 @@ import {RedocComponent, BaseComponent, SchemaManager} from '../base';
export class ApiLogo extends BaseComponent { export class ApiLogo extends BaseComponent {
data:any = {}; data:any = {};
constructor(schemaMgr:SchemaManager) { constructor(schemaMgr:SpecManager) {
super(schemaMgr); super(schemaMgr);
} }

View File

@ -15,13 +15,13 @@ import { TestComponentBuilder } from '@angular/compiler/testing';
import { JsonSchemaLazy } from './json-schema-lazy'; import { JsonSchemaLazy } from './json-schema-lazy';
import { SchemaManager } from '../../utils/SchemaManager'; import { SpecManager } from '../../utils/SpecManager';
describe('Redoc components', () => { describe('Redoc components', () => {
describe('JsonSchemaLazy Component', () => { describe('JsonSchemaLazy Component', () => {
let builder; let builder;
let component; let component;
let schemaMgr = new SchemaManager(); let schemaMgr = new SpecManager();
let fixture; let fixture;
let loader; let loader;
let appRefMock = { let appRefMock = {
@ -31,7 +31,7 @@ describe('Redoc components', () => {
hostView: { changeDetectorRef: {detectChanges : () => undefined} } hostView: { changeDetectorRef: {detectChanges : () => undefined} }
}; };
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: schemaMgr}) provide(SpecManager, {useValue: schemaMgr})
]); ]);
beforeEach(inject([TestComponentBuilder, DynamicComponentLoader], (tcb, dcl) => { beforeEach(inject([TestComponentBuilder, DynamicComponentLoader], (tcb, dcl) => {
builder = tcb; builder = tcb;

View File

@ -6,7 +6,7 @@ import { DynamicComponentLoader, Input } from '@angular/core';
import { JsonSchema } from './json-schema'; import { JsonSchema } from './json-schema';
import { OptionsService } from '../../services/options.service'; import { OptionsService } from '../../services/options.service';
import { SchemaManager } from '../../utils/SchemaManager'; import { SpecManager } from '../../utils/SpecManager';
var cache = {}; var cache = {};
@ -22,7 +22,7 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit {
@Input() auto: boolean; @Input() auto: boolean;
@Input() isRequestSchema: boolean; @Input() isRequestSchema: boolean;
loaded: boolean = false; loaded: boolean = false;
constructor(private schemaMgr:SchemaManager, private viewRef:ViewContainerRef, private elementRef:ElementRef, constructor(private schemaMgr:SpecManager, private viewRef:ViewContainerRef, private elementRef:ElementRef,
private dcl:DynamicComponentLoader, private optionsService:OptionsService) { private dcl:DynamicComponentLoader, private optionsService:OptionsService) {
} }
@ -61,7 +61,7 @@ export class JsonSchemaLazy implements OnDestroy, AfterViewInit {
// skip caching view with tabs inside (discriminator) // skip caching view with tabs inside (discriminator)
// as it needs attached controller // as it needs attached controller
if (compRef.instance.hasDiscriminator) { if (compRef.instance.hasDescendants) {
this._loadAfterSelf(); this._loadAfterSelf();
return; return;
} }

View File

@ -21,7 +21,7 @@
<!-- <caption> {{_displayType}} </caption> --> <!-- <caption> {{_displayType}} </caption> -->
<template ngFor [ngForOf]="schema._properties" let-prop="$implicit" let-last="last"> <template ngFor [ngForOf]="schema._properties" let-prop="$implicit" let-last="last">
<tr class="param" [ngClass]="{'last': last, <tr class="param" [ngClass]="{'last': last,
'discriminator': prop.isDiscriminator && !derivedEmtpy, 'discriminator': hasDescendants && !activeDescendant.empty,
'complex': prop._pointer, 'complex': prop._pointer,
'additional': prop._additional 'additional': prop._additional
}"> }">
@ -43,9 +43,9 @@
<div class="param-description" [innerHtml]="prop.description | marked"></div> <div class="param-description" [innerHtml]="prop.description | marked"></div>
<div class="discriminator-info" *ngIf="prop.isDiscriminator"> <div class="discriminator-info" *ngIf="prop.isDiscriminator">
<span>This field value determines the exact schema:</span> <span>This field value determines the exact schema:</span>
<drop-down (change)="selectDerived($event)"> <drop-down (change)="selectDescendant($event)">
<option *ngFor="let derived of schema._derived; let i=index" <option *ngFor="let descendant of schema._descendants; let i=index"
[value]="i">{{derived.name}}</option> [value]="i">{{descendant.name}}</option>
</drop-down> </drop-down>
</div> </div>
</td> </td>
@ -58,11 +58,11 @@
</td> </td>
</tr> </tr>
</template> </template>
<tr *ngIf="schema._derived.length" class="param-wrap discriminator-wrap" [ngClass]="{'empty': derivedEmtpy}"> <tr *ngIf="hasDescendants" class="param-wrap discriminator-wrap">
<td colspan="2"> <td colspan="2">
<div class="derived-schema" *ngFor="let derived of schema._derived" [ngClass]="{active: derived.active}"> <div class="derived-schema" *ngFor="let descendant of schema._descendants" [ngClass]="{active: descendant.active, empty: activeDescendant.empty}">
<json-schema class="discriminator-part" *ngIf="!derived.empty" [childFor]="pointer" <json-schema class="discriminator-part" *ngIf="!descendant.empty" [childFor]="pointer"
pointer="{{derived.$ref}}" [final]="derived.final" [isRequestSchema]="isRequestSchema"> pointer="{{descendant.$ref}}" [final]="descendant.final" [isRequestSchema]="isRequestSchema">
</json-schema> </json-schema>
</div> </div>
</td> </td>

View File

@ -13,16 +13,16 @@ import { getChildDebugElement } from '../../../tests/helpers';
import { JsonSchema } from './json-schema'; import { JsonSchema } from './json-schema';
import { SchemaManager } from '../../utils/SchemaManager';; import { SpecManager } from '../../utils/SpecManager';;
describe('Redoc components', () => { describe('Redoc components', () => {
describe('JsonSchema Component', () => { describe('JsonSchema Component', () => {
let builder; let builder;
let component; let component;
let schemaMgr = new SchemaManager(); let schemaMgr = new SpecManager();
let fixture; let fixture;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: schemaMgr}) provide(SpecManager, {useValue: schemaMgr})
]); ]);
beforeEach(inject([TestComponentBuilder], (tcb) => { beforeEach(inject([TestComponentBuilder], (tcb) => {
builder = tcb; builder = tcb;
@ -66,7 +66,7 @@ describe('Redoc components', () => {
@Component({ @Component({
selector: 'test-app', selector: 'test-app',
directives: [JsonSchema], directives: [JsonSchema],
providers: [SchemaManager], providers: [SpecManager],
template: template:
`<json-schema></json-schema>` `<json-schema></json-schema>`
}) })

View File

@ -1,11 +1,10 @@
'use strict'; 'use strict';
import { ElementRef, Input } from '@angular/core'; import { Input } from '@angular/core';
import { RedocComponent, BaseComponent, SchemaManager } from '../base'; import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { DropDown } from '../../shared/components/index'; import { DropDown } from '../../shared/components/index';
import JsonPointer from '../../utils/JsonPointer'; import { SchemaNormalizer, SchemaHelper } from '../../services/index';
import { SchemaNormalizator } from '../../services/spec-helper.service';
@RedocComponent({ @RedocComponent({
selector: 'json-schema', selector: 'json-schema',
@ -15,83 +14,40 @@ import { SchemaNormalizator } from '../../services/spec-helper.service';
detect: true detect: true
}) })
export class JsonSchema extends BaseComponent { export class JsonSchema extends BaseComponent {
$element: any;
schema: any; schema: any;
derivedEmtpy: boolean; activeDescendant:any = {};
hasDiscriminator: boolean = false; hasDescendants: boolean = false;
@Input() isArray: boolean; @Input() isArray: boolean;
@Input() final: boolean = false; @Input() final: boolean = false;
@Input() nestOdd: boolean; @Input() nestOdd: boolean;
@Input() childFor: string; @Input() childFor: string;
@Input() isRequestSchema: boolean; @Input() isRequestSchema: boolean;
normalizer: SchemaNormalizator; normalizer: SchemaNormalizer;
static injectPropertyData(propertySchema, propertyName, propPointer, hostPointer?) { constructor(schemaMgr:SpecManager) {
propertySchema = Object.assign({}, propertySchema);
propertySchema._name = propertyName;
runInjectors(propertySchema, propertySchema, propPointer, hostPointer);
return propertySchema;
}
constructor(schemaMgr:SchemaManager, elementRef:ElementRef) {
super(schemaMgr); super(schemaMgr);
this.$element = elementRef.nativeElement; this.normalizer = new SchemaNormalizer(schemaMgr);
this.normalizer = new SchemaNormalizator(schemaMgr);
} }
selectDerived(subClassIdx) { get normPointer() {
let subClass = this.schema._derived[subClassIdx]; return this.schema._pointer || this.pointer;
if (!subClass || subClass.active) return; }
this.schema._derived.forEach((subSchema) => {
selectDescendant(idx) {
let activeDescendant = this.schema._descendants[idx];
if (!activeDescendant || activeDescendant.active) return;
this.schema._descendants.forEach(subSchema => {
subSchema.active = false; subSchema.active = false;
}); });
subClass.active = true; activeDescendant.active = true;
this.derivedEmtpy = false; this.activeDescendant = activeDescendant;
if (subClass.empty) this.derivedEmtpy = true;
} }
unwrapArray(schema) { initDescendants() {
var res = schema; if (!this.schema._descendants || !this.schema._descendants.length) {
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}`);
}
if (this.componentSchema['x-redoc-js-precompiled']) {
this.schema = this.unwrapArray(this.componentSchema);
return; return;
} }
this.componentSchema = this.normalizer.normalize(this.componentSchema, this.pointer); this.hasDescendants = true;
this.componentSchema['x-redoc-js-precompiled'] = true;
let schema = this.componentSchema;
this.schema = schema = this.unwrapArray(schema);
runInjectors(schema, schema, schema._pointer || this.pointer, this.pointer);
this.schema._derived = this.schema._derived || [];
if (!schema.isTrivial) {
this.prepareObjectPropertiesData(schema);
}
this.initDerived();
}
initDerived() {
if (!this.schema._derived.length) return;
this.hasDiscriminator = true;
let enumArr = this.schema._properties[this.schema._properties.length - 1].enum; let enumArr = this.schema._properties[this.schema._properties.length - 1].enum;
if (enumArr) { if (enumArr) {
let enumOrder = {}; let enumOrder = {};
@ -103,192 +59,26 @@ export class JsonSchema extends BaseComponent {
return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1; return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1;
}); });
} }
this.selectDerived(0); this.selectDescendant(0);
} }
prepareObjectPropertiesData(schema) { prepareModel() {
let requiredMap = {}; let schema = this.schema = this.componentSchema;
if (schema.required) { if (!schema) {
schema.required.forEach(prop => requiredMap[prop] = true); throw new Error(`Can't load component schema at ${this.pointer}`);
} }
let discriminatorFieldIdx = -1; schema = this.normalizer.normalize(schema, this.normPointer);
let props = schema.properties && Object.keys(schema.properties).map((prop, idx) => { this.schema = schema = SchemaHelper.unwrapArray(schema, this.normPointer);
let propertySchema = schema.properties[prop]; SchemaHelper.preprocess(schema, schema, this.normPointer, this.pointer);
let propPointer = propertySchema._pointer ||
JsonPointer.join(schema._pointer || this.pointer, ['properties', prop]);
propertySchema = JsonSchema.injectPropertyData(propertySchema, prop, propPointer);
// stop endless discriminator recursion
if (propertySchema._pointer === this.childFor) {
propertySchema._pointer = null;
}
propertySchema.required = !!requiredMap[prop];
propertySchema.isDiscriminator = (schema.discriminator === prop);
if (propertySchema.isDiscriminator) {
discriminatorFieldIdx = idx;
}
return propertySchema;
});
props = props || []; if (!schema.isTrivial) {
SchemaHelper.preprocessProperties(schema, this.normPointer, {
if (schema.additionalProperties && schema.additionalProperties !== false) { childFor: this.childFor,
let propsSchema = this.prepareAdditionalProperties(schema); skipReadOnly: this.isRequestSchema
propsSchema._additional = true; });
props.push(propsSchema);
} }
// Move discriminator field to the end of properties list this.initDescendants();
if (discriminatorFieldIdx > -1) {
let discrProp = props.splice(discriminatorFieldIdx, 1);
props.push(discrProp[0]);
}
// filter readOnly props for request schemas
if (this.isRequestSchema) {
props = props.filter(prop => !prop.readOnly);
}
schema._properties = props;
}
prepareAdditionalProperties(schema) {
var addProps = schema.additionalProperties;
return JsonSchema.injectPropertyData(addProps, '<Additional Properties> *',
JsonPointer.join(addProps._pointer || schema._pointer || this.pointer, ['additionalProperties']));
}
}
const injectors = {
general: {
check: () => true,
inject: (injectTo, propertySchema, pointer) => {
injectTo._pointer = propertySchema._pointer || pointer;
injectTo._displayType = propertySchema.type;
if (propertySchema.format) injectTo._displayFormat = `<${propertySchema.format}>`;
if (propertySchema.enum) {
injectTo.enum = propertySchema.enum.map((value) => {
return {val: value, type: typeof value};
});
}
}
},
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';
},
inject: (injectTo, propertySchema = injectTo, propPointer) => {
injectTo._isArray = true;
injectTo._pointer = propertySchema.items._pointer
|| JsonPointer.join(propertySchema._pointer || propPointer, ['items']);
runInjectors(injectTo, propertySchema.items, propPointer);
}
},
object: {
check: (propertySchema) => {
return propertySchema.type === 'object' && propertySchema.properties;
},
inject: (injectTo, propertySchema = injectTo) => {
let baseName = propertySchema._pointer && JsonPointer.baseName(propertySchema._pointer);
injectTo._displayType = propertySchema.title || baseName || 'object';
}
},
noType: {
check: (propertySchema) => !propertySchema.type,
inject: (injectTo) => {
injectTo._displayType = '< * >';
injectTo._displayTypeHint = 'This field may contain data of any type';
injectTo.isTrivial = true;
}
},
simpleType: {
check: (propertySchema) => {
if (propertySchema.type === 'object') {
return (!propertySchema.properties || !Object.keys(propertySchema.properties).length)
&& (typeof propertySchema.additionalProperties !== 'object');
}
return (propertySchema.type !== 'array') && propertySchema.type;
},
inject: (injectTo, propertySchema = injectTo) => {
injectTo.isTrivial = true;
if (injectTo._pointer) {
injectTo._pointer = undefined;
injectTo._displayType = propertySchema.title ?
`${propertySchema.title} (${propertySchema.type})` : propertySchema.type;
}
}
},
integer: {
check: (propertySchema) => (propertySchema.type === 'integer' || propertySchema.type === 'number'),
inject: (injectTo, propertySchema = injectTo) => {
var range = '';
if (propertySchema.minimum && propertySchema.maximum) {
range += propertySchema.exclusiveMinimum ? '( ' : '[ ';
range += propertySchema.minimum;
range += ' .. ';
range += propertySchema.maximum;
range += propertySchema.exclusiveMaximum ? ' )' : ' ]';
} else if (propertySchema.maximum) {
range += propertySchema.exclusiveMaximum? '< ' : '<= ';
range += propertySchema.maximum;
} else if (propertySchema.minimum) {
range += propertySchema.exclusiveMinimum ? '> ' : '>= ';
range += propertySchema.minimum;
}
if (range) {
injectTo._range = range;
}
}
},
string: {
check: propertySchema => (propertySchema.type === 'string'),
inject: (injectTo, propertySchema = injectTo) => {
var range;
if (propertySchema.minLength && propertySchema.maxLength) {
range = `[ ${propertySchema.minLength} .. ${propertySchema.maxLength} ]`;
} else if (propertySchema.maxLength) {
range = '<= ' + propertySchema.maxLength;
} else if (propertySchema.minimum) {
range = '>= ' + propertySchema.minLength;
}
if (range) {
injectTo._range = range + ' characters';
}
}
},
file: {
check: propertySchema => (propertySchema.type === 'file'),
inject: (injectTo, propertySchema = injectTo, propPointer, hostPointer) => {
injectTo.isFile = true;
let parentPtr;
if (propertySchema.in === 'formData') {
parentPtr = JsonPointer.dirName(hostPointer, 1);
} else {
parentPtr = JsonPointer.dirName(hostPointer, 3);
}
let parentParam = SchemaManager.instance().byPointer(parentPtr);
let root = SchemaManager.instance().schema;
injectTo._produces = parentParam && parentParam.produces || root.produces;
injectTo._consumes = parentParam && parentParam.consumes || root.consumes;
}
}
};
function runInjectors(injectTo, propertySchema, propertyPointer, hostPointer?) {
for (var injName in injectors) {
let injector = injectors[injName];
if (injector.check(propertySchema)) {
injector.inject(injectTo, propertySchema, propertyPointer, hostPointer);
}
} }
} }

View File

@ -13,16 +13,16 @@ import { TestComponentBuilder } from '@angular/compiler/testing';
import { getChildDebugElement } from '../../../tests/helpers'; import { getChildDebugElement } from '../../../tests/helpers';
import { Method } from './method'; import { Method } from './method';
import { SchemaManager } from '../../utils/SchemaManager';; import { SpecManager } from '../../utils/SpecManager';;
describe('Redoc components', () => { describe('Redoc components', () => {
describe('Method Component', () => { describe('Method Component', () => {
let builder; let builder;
let component; let component;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}) provide(SpecManager, {useValue: new SpecManager()})
]); ]);
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => { beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, schemaMgr) => {
builder = tcb; builder = tcb;
return schemaMgr.load('/tests/schemas/extended-petstore.yml'); return schemaMgr.load('/tests/schemas/extended-petstore.yml');
}))); })));
@ -57,7 +57,7 @@ describe('Redoc components', () => {
@Component({ @Component({
selector: 'test-app', selector: 'test-app',
directives: [Method], directives: [Method],
providers: [SchemaManager], providers: [SpecManager],
template: template:
`<method pointer='#/paths/~1user~1{username}/put'></method>` `<method pointer='#/paths/~1user~1{username}/put'></method>`
}) })

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { Input } from '@angular/core'; import { Input } from '@angular/core';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
import { RedocComponent, BaseComponent, SchemaManager} from '../base'; import { RedocComponent, BaseComponent, SpecManager} from '../base';
import { ParamsList } from '../ParamsList/params-list'; import { ParamsList } from '../ParamsList/params-list';
import { ResponsesList } from '../ResponsesList/responses-list'; import { ResponsesList } from '../ResponsesList/responses-list';
@ -19,7 +19,7 @@ import { RequestSamples } from '../RequestSamples/request-samples';
export class Method extends BaseComponent { export class Method extends BaseComponent {
data:any; data:any;
@Input() tag:string; @Input() tag:string;
constructor(schemaMgr:SchemaManager) { constructor(schemaMgr:SpecManager) {
super(schemaMgr); super(schemaMgr);
} }

View File

@ -14,7 +14,7 @@ import { getChildDebugElement } from '../../../tests/helpers';
import { MethodsList } from './methods-list'; import { MethodsList } from './methods-list';
import { SchemaManager } from '../../utils/SchemaManager'; import { SpecManager } from '../../utils/SpecManager';
describe('Redoc components', () => { describe('Redoc components', () => {
describe('MethodsList Component', () => { describe('MethodsList Component', () => {
@ -22,9 +22,9 @@ describe('Redoc components', () => {
let component; let component;
let fixture; let fixture;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}) provide(SpecManager, {useValue: new SpecManager()})
]); ]);
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => { beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, schemaMgr) => {
builder = tcb; builder = tcb;
return schemaMgr.load('/tests/schemas/methods-list-component.json'); return schemaMgr.load('/tests/schemas/methods-list-component.json');
}))); })));

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { forwardRef } from '@angular/core'; import { forwardRef } from '@angular/core';
import { RedocComponent, BaseComponent, SchemaManager } from '../base'; import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { Method } from '../Method/method'; import { Method } from '../Method/method';
import { EncodeURIComponentPipe } from '../../utils/pipes'; import { EncodeURIComponentPipe } from '../../utils/pipes';
@ -15,7 +15,7 @@ import { EncodeURIComponentPipe } from '../../utils/pipes';
}) })
export class MethodsList extends BaseComponent { export class MethodsList extends BaseComponent {
data:any; data:any;
constructor(schemaMgr:SchemaManager) { constructor(schemaMgr:SpecManager) {
super(schemaMgr); super(schemaMgr);
} }

View File

@ -1,8 +1,9 @@
'use strict'; 'use strict';
import { RedocComponent, BaseComponent, SchemaManager } from '../base'; import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { JsonSchema } from '../JsonSchema/json-schema'; import { JsonSchema } from '../JsonSchema/json-schema';
import {JsonSchemaLazy} from '../JsonSchema/json-schema-lazy'; import { JsonSchemaLazy } from '../JsonSchema/json-schema-lazy';
import { SchemaHelper } from '../../services/schema-helper.service';
function safePush(obj, prop, item) { function safePush(obj, prop, item) {
if (!obj[prop]) obj[prop] = []; if (!obj[prop]) obj[prop] = [];
@ -19,7 +20,7 @@ export class ParamsList extends BaseComponent {
data:any; data:any;
constructor(schemaMgr:SchemaManager) { constructor(schemaMgr:SpecManager) {
super(schemaMgr); super(schemaMgr);
} }
@ -27,10 +28,10 @@ export class ParamsList extends BaseComponent {
this.data = {}; this.data = {};
let paramsList = this.schemaMgr.getMethodParams(this.pointer, true); let paramsList = this.schemaMgr.getMethodParams(this.pointer, true);
paramsList = paramsList.map((paramData) => { paramsList = paramsList.map(paramSchema => {
let propPointer = paramData._pointer; let propPointer = paramSchema._pointer;
if (paramData.in === 'body') return paramData; if (paramSchema.in === 'body') return paramSchema;
return JsonSchema.injectPropertyData(paramData, paramData.name, propPointer, this.pointer); return SchemaHelper.preprocess(paramSchema, paramSchema.name, propPointer, this.pointer);
}); });
let paramsMap = this.orderParams(paramsList); let paramsMap = this.orderParams(paramsList);

View File

@ -15,7 +15,7 @@ import {
import { TestComponentBuilder } from '@angular/compiler/testing'; import { TestComponentBuilder } from '@angular/compiler/testing';
import { Redoc } from './redoc'; import { Redoc } from './redoc';
import { SchemaManager } from '../../utils/SchemaManager'; import { SpecManager } from '../../utils/SpecManager';
import { OptionsService } from '../../services/index'; import { OptionsService } from '../../services/index';
let optsMgr:OptionsService; let optsMgr:OptionsService;
@ -25,9 +25,9 @@ describe('Redoc components', () => {
let builder; let builder;
let schemaMgr; let schemaMgr;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}), provide(SpecManager, {useValue: new SpecManager()}),
]); ]);
beforeEach(async(inject([TestComponentBuilder, SchemaManager, OptionsService], beforeEach(async(inject([TestComponentBuilder, SpecManager, OptionsService],
(tcb, _schemaMgr, _optsMgr) => { (tcb, _schemaMgr, _optsMgr) => {
optsMgr = _optsMgr; optsMgr = _optsMgr;
builder = tcb; builder = tcb;
@ -106,11 +106,11 @@ describe('Redoc components', () => {
let destroySpy; let destroySpy;
let dom = new BrowserDomAdapter(); let dom = new BrowserDomAdapter();
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}), provide(SpecManager, {useValue: new SpecManager()}),
provide(BrowserDomAdapter, {useValue: new BrowserDomAdapter()}), provide(BrowserDomAdapter, {useValue: new BrowserDomAdapter()}),
provide(OptionsService, {useValue: optsMgr}) provide(OptionsService, {useValue: optsMgr})
]); ]);
beforeEach(async(inject([TestComponentBuilder, SchemaManager], (tcb, schemaMgr) => { beforeEach(async(inject([TestComponentBuilder, SpecManager], (tcb, schemaMgr) => {
builder = tcb; builder = tcb;
return schemaMgr.load('/tests/schemas/methods-list-component.json'); return schemaMgr.load('/tests/schemas/methods-list-component.json');
}))); })));

View File

@ -17,7 +17,7 @@ import { MethodsList } from '../MethodsList/methods-list';
import { SideMenu } from '../SideMenu/side-menu'; import { SideMenu } from '../SideMenu/side-menu';
import { StickySidebar } from '../../shared/components/index'; import { StickySidebar } from '../../shared/components/index';
import {SchemaManager} from '../../utils/SchemaManager'; import {SpecManager} from '../../utils/SpecManager';
import { OptionsService, RedocEventsService } from '../../services/index'; import { OptionsService, RedocEventsService } from '../../services/index';
//import redocVersion from '../../version.js'; //import redocVersion from '../../version.js';
@ -27,7 +27,7 @@ var _modeLocked = false;
@RedocComponent({ @RedocComponent({
selector: 'redoc', selector: 'redoc',
providers: [ providers: [
SchemaManager, SpecManager,
BrowserDomAdapter, BrowserDomAdapter,
RedocEventsService RedocEventsService
], ],
@ -71,7 +71,7 @@ export class Redoc extends BaseComponent implements AfterViewInit {
Redoc.destroy(); Redoc.destroy();
} }
Redoc.showLoadingAnimation(); Redoc.showLoadingAnimation();
return SchemaManager.instance().load(specUrl) return SpecManager.instance().load(specUrl)
.then(() => { .then(() => {
if (!_modeLocked && !optionsService.options.debugMode) { if (!_modeLocked && !optionsService.options.debugMode) {
enableProdMode(); enableProdMode();
@ -139,7 +139,7 @@ export class Redoc extends BaseComponent implements AfterViewInit {
} }
} }
constructor(schemaMgr: SchemaManager, optionsMgr:OptionsService, elementRef:ElementRef, constructor(schemaMgr: SpecManager, optionsMgr:OptionsService, elementRef:ElementRef,
public events:RedocEventsService) { public events:RedocEventsService) {
super(schemaMgr); super(schemaMgr);
this.element = elementRef.nativeElement; this.element = elementRef.nativeElement;

View File

@ -2,7 +2,7 @@
import { ViewChildren, QueryList, EventEmitter, Input} from '@angular/core'; import { ViewChildren, QueryList, EventEmitter, Input} from '@angular/core';
import { RedocComponent, BaseComponent, SchemaManager } from '../base'; import { RedocComponent, BaseComponent, SpecManager } from '../base';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
import { Tabs, Tab } from '../../shared/components/index'; import { Tabs, Tab } from '../../shared/components/index';
import { SchemaSample } from '../SchemaSample/schema-sample'; import { SchemaSample } from '../SchemaSample/schema-sample';
@ -25,7 +25,7 @@ export class RequestSamples extends BaseComponent {
data: any; data: any;
@Input() schemaPointer:string; @Input() schemaPointer:string;
@ViewChildren(Tabs) childQuery:QueryList<Tabs>; @ViewChildren(Tabs) childQuery:QueryList<Tabs>;
constructor(schemaMgr:SchemaManager, public events:RedocEventsService) { constructor(schemaMgr:SpecManager, public events:RedocEventsService) {
super(schemaMgr); super(schemaMgr);
this.selectedLang = this.events.samplesLanguageChanged; this.selectedLang = this.events.samplesLanguageChanged;

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import {RedocComponent, BaseComponent, SchemaManager} from '../base'; import {RedocComponent, BaseComponent, SpecManager} from '../base';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
import { JsonSchema } from '../JsonSchema/json-schema'; import { JsonSchema } from '../JsonSchema/json-schema';
import { JsonSchemaLazy } from '../JsonSchema/json-schema-lazy'; import { JsonSchemaLazy } from '../JsonSchema/json-schema-lazy';
@ -22,7 +22,7 @@ function isNumeric(n) {
export class ResponsesList extends BaseComponent { export class ResponsesList extends BaseComponent {
data: any; data: any;
options: any; options: any;
constructor(schemaMgr:SchemaManager, optionsMgr:OptionsService) { constructor(schemaMgr:SpecManager, optionsMgr:OptionsService) {
super(schemaMgr); super(schemaMgr);
this.options = optionsMgr.options; this.options = optionsMgr.options;
} }

View File

@ -1,7 +1,7 @@
'use strict'; 'use strict';
import { forwardRef } from '@angular/core'; import { forwardRef } from '@angular/core';
import { RedocComponent, BaseComponent, SchemaManager } from '../base'; import { RedocComponent, BaseComponent, SpecManager } from '../base';
import JsonPointer from '../../utils/JsonPointer'; import JsonPointer from '../../utils/JsonPointer';
import { Tabs, Tab } from '../../shared/components/index'; import { Tabs, Tab } from '../../shared/components/index';
import { SchemaSample } from '../index'; import { SchemaSample } from '../index';
@ -25,7 +25,7 @@ function hasExample(response) {
}) })
export class ResponsesSamples extends BaseComponent { export class ResponsesSamples extends BaseComponent {
data: any; data: any;
constructor(schemaMgr:SchemaManager) { constructor(schemaMgr:SpecManager) {
super(schemaMgr); super(schemaMgr);
} }

View File

@ -4,9 +4,9 @@ import { ElementRef, Input } from '@angular/core';
import * as OpenAPISampler from 'openapi-sampler'; import * as OpenAPISampler from 'openapi-sampler';
import { RedocComponent, BaseComponent, SchemaManager } from '../base'; import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { JsonFormatter } from '../../utils/JsonFormatterPipe'; import { JsonFormatter } from '../../utils/JsonFormatterPipe';
import { SchemaNormalizator } from '../../services/spec-helper.service'; import { SchemaNormalizer } from '../../services/schema-normalizer.service';
@RedocComponent({ @RedocComponent({
selector: 'schema-sample', selector: 'schema-sample',
@ -19,12 +19,12 @@ export class SchemaSample extends BaseComponent {
data: any; data: any;
@Input() skipReadOnly:boolean; @Input() skipReadOnly:boolean;
private _normalizer:SchemaNormalizator; private _normalizer:SchemaNormalizer;
constructor(schemaMgr:SchemaManager, elementRef:ElementRef) { constructor(schemaMgr:SpecManager, elementRef:ElementRef) {
super(schemaMgr); super(schemaMgr);
this.element = elementRef.nativeElement; this.element = elementRef.nativeElement;
this._normalizer = new SchemaNormalizator(schemaMgr); this._normalizer = new SchemaNormalizer(schemaMgr);
} }
init() { init() {

View File

@ -16,7 +16,7 @@ import { TestComponentBuilder } from '@angular/compiler/testing';
import { MethodsList, SideMenu } from '../index'; import { MethodsList, SideMenu } from '../index';
import { SchemaManager } from '../../utils/SchemaManager';; import { SpecManager } from '../../utils/SpecManager';;
let testOptions; let testOptions;
@ -26,9 +26,9 @@ describe('Redoc components', () => {
let component; let component;
let fixture; let fixture;
beforeEachProviders(() => [ beforeEachProviders(() => [
provide(SchemaManager, {useValue: new SchemaManager()}) provide(SpecManager, {useValue: new SpecManager()})
]); ]);
beforeEach(async(inject([TestComponentBuilder, SchemaManager, OptionsService], beforeEach(async(inject([TestComponentBuilder, SpecManager, OptionsService],
(tcb, schemaMgr, opts) => { (tcb, schemaMgr, opts) => {
builder = tcb; builder = tcb;
testOptions = opts; testOptions = opts;

View File

@ -4,7 +4,7 @@ import { ElementRef, ChangeDetectorRef } from '@angular/core';
import { BrowserDomAdapter } from '@angular/platform-browser/src/browser/browser_adapter'; import { BrowserDomAdapter } from '@angular/platform-browser/src/browser/browser_adapter';
import { global } from '@angular/core/src/facade/lang'; import { global } from '@angular/core/src/facade/lang';
import { trigger, state, animate, transition, style } from '@angular/core'; import { trigger, state, animate, transition, style } from '@angular/core';
import { RedocComponent, BaseComponent, SchemaManager } from '../base'; import { RedocComponent, BaseComponent, SpecManager } from '../base';
import { ScrollService, Hash, MenuService, OptionsService } from '../../services/index'; import { ScrollService, Hash, MenuService, OptionsService } from '../../services/index';
@RedocComponent({ @RedocComponent({
@ -35,7 +35,7 @@ export class SideMenu extends BaseComponent {
activeItemCaption: string; activeItemCaption: string;
options: any; options: any;
data: any; data: any;
constructor(schemaMgr:SchemaManager, elementRef:ElementRef, private dom:BrowserDomAdapter, constructor(schemaMgr:SpecManager, elementRef:ElementRef, private dom:BrowserDomAdapter,
private scrollService:ScrollService, private menuService:MenuService, private hash:Hash, private scrollService:ScrollService, private menuService:MenuService, private hash:Hash,
optionsService:OptionsService, private detectorRef:ChangeDetectorRef) { optionsService:OptionsService, private detectorRef:ChangeDetectorRef) {
super(schemaMgr); super(schemaMgr);

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { SchemaManager } from '../utils/SchemaManager'; import { SpecManager } from '../utils/SpecManager';
import { BaseComponent } from '../components/base'; import { BaseComponent } from '../components/base';
describe('Redoc components', () => { describe('Redoc components', () => {
@ -9,7 +9,7 @@ describe('Redoc components', () => {
let component; let component;
beforeAll(() => { beforeAll(() => {
schemaMgr = new SchemaManager(); schemaMgr = new SpecManager();
schemaMgr._schema = {tags: []}; schemaMgr._schema = {tags: []};
}); });

View File

@ -1,10 +1,10 @@
'use strict'; 'use strict';
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core'; import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { CORE_DIRECTIVES, JsonPipe, AsyncPipe } from '@angular/common'; import { CORE_DIRECTIVES, JsonPipe, AsyncPipe } from '@angular/common';
import { SchemaManager } from '../utils/SchemaManager'; import { SpecManager } from '../utils/SpecManager';
import { MarkedPipe, JsonPointerEscapePipe } from '../utils/pipes'; import { MarkedPipe, JsonPointerEscapePipe } from '../utils/pipes';
export { SchemaManager }; export { SpecManager };
// common inputs for all components // common inputs for all components
let commonInputs = ['pointer']; // json pointer to the schema chunk let commonInputs = ['pointer']; // json pointer to the schema chunk
@ -84,7 +84,7 @@ export class BaseComponent implements OnInit, OnDestroy {
pointer: String; pointer: String;
dereferencedCache = {}; dereferencedCache = {};
constructor(public schemaMgr: SchemaManager) { constructor(public schemaMgr: SpecManager) {
} }
/** /**

View File

@ -5,3 +5,5 @@ export * from './options.service';
export * from './menu.service'; export * from './menu.service';
export * from './scroll.service'; export * from './scroll.service';
export * from './hash.service'; export * from './hash.service';
export * from './schema-normalizer.service';
export * from './schema-helper.service';

View File

@ -17,7 +17,7 @@ import { Hash } from './hash.service';
import { ScrollService } from './scroll.service'; import { ScrollService } from './scroll.service';
import { RedocEventsService } from './events.service'; import { RedocEventsService } from './events.service';
import { MethodsList } from '../components/index'; import { MethodsList } from '../components/index';
import { SchemaManager } from '../utils/SchemaManager';; import { SpecManager } from '../utils/SpecManager';;
describe('Menu service', () => { describe('Menu service', () => {
let menu, hashService, scroll; let menu, hashService, scroll;
@ -30,10 +30,10 @@ describe('Menu service', () => {
provide(Hash, {useClass: Hash}), provide(Hash, {useClass: Hash}),
provide(ScrollService, {useClass: ScrollService}), provide(ScrollService, {useClass: ScrollService}),
provide(RedocEventsService, {useClass: RedocEventsService}), provide(RedocEventsService, {useClass: RedocEventsService}),
provide(SchemaManager, {useClass: SchemaManager}) provide(SpecManager, {useClass: SpecManager})
]); ]);
beforeEach(inject([Hash, ScrollService, SchemaManager, TestComponentBuilder], beforeEach(inject([Hash, ScrollService, SpecManager, TestComponentBuilder],
(_hash, _scroll, _schemaMgr, tcb) => { (_hash, _scroll, _schemaMgr, tcb) => {
hashService = _hash; hashService = _hash;
scroll = _scroll; scroll = _scroll;

View File

@ -2,7 +2,7 @@
import { Injectable, EventEmitter } from '@angular/core'; import { Injectable, EventEmitter } from '@angular/core';
import { ScrollService, INVIEW_POSITION } from './scroll.service'; import { ScrollService, INVIEW_POSITION } from './scroll.service';
import { Hash } from './hash.service'; import { Hash } from './hash.service';
import { SchemaManager } from '../utils/SchemaManager'; import { SpecManager } from '../utils/SpecManager';
const CHANGE = { const CHANGE = {
NEXT : 1, NEXT : 1,
@ -19,7 +19,7 @@ export class MenuService {
activeMethodIdx: number = -1; activeMethodIdx: number = -1;
activeMethodPtr: string; activeMethodPtr: string;
constructor(private hash:Hash, private scrollService:ScrollService, schemaMgr:SchemaManager) { constructor(private hash:Hash, private scrollService:ScrollService, schemaMgr:SpecManager) {
this.hash = hash; this.hash = hash;
this.categories = Array.from(schemaMgr.buildMenuTree().entries()).map( this.categories = Array.from(schemaMgr.buildMenuTree().entries()).map(

View File

@ -0,0 +1,219 @@
'use strict';
import { JsonPointer } from '../utils/JsonPointer';
import { SpecManager } from '../utils/SpecManager';
interface PropertyPreprocessOptions {
childFor: string;
skipReadOnly: boolean;
}
const injectors = {
general: {
check: () => true,
inject: (injectTo, propertySchema, pointer) => {
injectTo._pointer = propertySchema._pointer || pointer;
injectTo._displayType = propertySchema.type;
if (propertySchema.format) injectTo._displayFormat = `<${propertySchema.format}>`;
if (propertySchema.enum) {
injectTo.enum = propertySchema.enum.map((value) => {
return {val: value, type: typeof value};
});
}
}
},
discriminator: {
check: (propertySchema) => propertySchema.discriminator,
inject: (injectTo, propertySchema = injectTo, pointer) => {
injectTo._descendants = SpecManager.instance().findDerivedDefinitions(pointer);
injectTo.discriminator = propertySchema.discriminator;
}
},
array: {
check: (propertySchema) => {
return propertySchema.type === 'array';
},
inject: (injectTo, propertySchema = injectTo, propPointer) => {
injectTo._isArray = true;
injectTo._pointer = propertySchema.items._pointer
|| JsonPointer.join(propertySchema._pointer || propPointer, ['items']);
SchemaHelper.runInjectors(injectTo, propertySchema.items, propPointer);
}
},
object: {
check: (propertySchema) => {
return propertySchema.type === 'object' && propertySchema.properties;
},
inject: (injectTo, propertySchema = injectTo) => {
let baseName = propertySchema._pointer && JsonPointer.baseName(propertySchema._pointer);
injectTo._displayType = propertySchema.title || baseName || 'object';
}
},
noType: {
check: (propertySchema) => !propertySchema.type,
inject: (injectTo) => {
injectTo._displayType = '< * >';
injectTo._displayTypeHint = 'This field may contain data of any type';
injectTo.isTrivial = true;
}
},
simpleType: {
check: (propertySchema) => {
if (propertySchema.type === 'object') {
return (!propertySchema.properties || !Object.keys(propertySchema.properties).length)
&& (typeof propertySchema.additionalProperties !== 'object');
}
return (propertySchema.type !== 'array') && propertySchema.type;
},
inject: (injectTo, propertySchema = injectTo) => {
injectTo.isTrivial = true;
if (injectTo._pointer) {
injectTo._pointer = undefined;
injectTo._displayType = propertySchema.title ?
`${propertySchema.title} (${propertySchema.type})` : propertySchema.type;
}
}
},
integer: {
check: (propertySchema) => (propertySchema.type === 'integer' || propertySchema.type === 'number'),
inject: (injectTo, propertySchema = injectTo) => {
var range = '';
if (propertySchema.minimum && propertySchema.maximum) {
range += propertySchema.exclusiveMinimum ? '( ' : '[ ';
range += propertySchema.minimum;
range += ' .. ';
range += propertySchema.maximum;
range += propertySchema.exclusiveMaximum ? ' )' : ' ]';
} else if (propertySchema.maximum) {
range += propertySchema.exclusiveMaximum? '< ' : '<= ';
range += propertySchema.maximum;
} else if (propertySchema.minimum) {
range += propertySchema.exclusiveMinimum ? '> ' : '>= ';
range += propertySchema.minimum;
}
if (range) {
injectTo._range = range;
}
}
},
string: {
check: propertySchema => (propertySchema.type === 'string'),
inject: (injectTo, propertySchema = injectTo) => {
var range;
if (propertySchema.minLength && propertySchema.maxLength) {
range = `[ ${propertySchema.minLength} .. ${propertySchema.maxLength} ]`;
} else if (propertySchema.maxLength) {
range = '<= ' + propertySchema.maxLength;
} else if (propertySchema.minimum) {
range = '>= ' + propertySchema.minLength;
}
if (range) {
injectTo._range = range + ' characters';
}
}
},
file: {
check: propertySchema => (propertySchema.type === 'file'),
inject: (injectTo, propertySchema = injectTo, propPointer, hostPointer) => {
injectTo.isFile = true;
let parentPtr;
if (propertySchema.in === 'formData') {
parentPtr = JsonPointer.dirName(hostPointer, 1);
} else {
parentPtr = JsonPointer.dirName(hostPointer, 3);
}
let parentParam = SpecManager.instance().byPointer(parentPtr);
let root = SpecManager.instance().schema;
injectTo._produces = parentParam && parentParam.produces || root.produces;
injectTo._consumes = parentParam && parentParam.consumes || root.consumes;
}
}
};
export class SchemaHelper {
static preprocess(schema, name, pointer, hostPointer?) {
//propertySchema = Object.assign({}, propertySchema);
if (schema['x-redoc-schema-precompiled']) {
return schema;
}
schema._name = name;
SchemaHelper.runInjectors(schema, schema, pointer, hostPointer);
schema['x-redoc-schema-precompiled'] = true;
return schema;
}
static runInjectors(injectTo, schema, pointer, hostPointer?) {
for (var injName of Object.keys(injectors)) {
let injector = injectors[injName];
if (injector.check(schema)) {
injector.inject(injectTo, schema, pointer, hostPointer);
}
}
}
static preprocessProperties(schema:any, pointer:string, opts: PropertyPreprocessOptions) {
let requiredMap = {};
if (schema.required) {
schema.required.forEach(prop => requiredMap[prop] = true);
}
let discriminatorFieldIdx = -1;
let props = schema.properties && Object.keys(schema.properties).map((propName, idx) => {
let propertySchema = schema.properties[propName];
let propPointer = propertySchema._pointer ||
JsonPointer.join(pointer, ['properties', propName]);
propertySchema = SchemaHelper.preprocess(propertySchema, propName, propPointer);
// stop endless discriminator recursion
if (propertySchema._pointer === opts.childFor) {
propertySchema._pointer = null;
}
propertySchema.required = !!requiredMap[propName];
propertySchema.isDiscriminator = (schema.discriminator === propName);
if (propertySchema.isDiscriminator) {
discriminatorFieldIdx = idx;
}
return propertySchema;
});
props = props || [];
if (schema.additionalProperties && schema.additionalProperties !== false) {
let propsSchema = SchemaHelper.preprocessAdditionalProperties(schema, pointer);
propsSchema._additional = true;
props.push(propsSchema);
}
// Move discriminator field to the end of properties list
if (discriminatorFieldIdx > -1) {
let discrProp = props.splice(discriminatorFieldIdx, 1);
props.push(discrProp[0]);
}
// filter readOnly props for request schemas
if (opts.skipReadOnly) {
props = props.filter(prop => !prop.readOnly);
}
schema._properties = props;
}
static preprocessAdditionalProperties(schema:any, pointer:string) {
var addProps = schema.additionalProperties;
let ptr = addProps._pointer || JsonPointer.join(pointer, ['additionalProperties']);
return SchemaHelper.preprocess(addProps, '<Additional Properties> *', ptr);
}
static unwrapArray(schema, pointer) {
var res = schema;
if (schema && schema.type === 'array') {
let ptr = schema.items._pointer || JsonPointer.join(pointer, ['items']);
res = schema.items;
res._isArray = true;
res._pointer = ptr;
res = SchemaHelper.unwrapArray(res, ptr);
}
return res;
}
}

View File

@ -1,15 +1,15 @@
'use strict'; 'use strict';
import { SchemaNormalizator } from './spec-helper.service'; import { SchemaNormalizer } from './schema-normalizer.service';
import { import {
describe, describe,
it it
} from '@angular/core/testing'; } from '@angular/core/testing';
import { SchemaManager } from '../utils/SchemaManager';; import { SpecManager } from '../utils/SpecManager';;
describe('Spec Helper', () => { describe('Spec Helper', () => {
let schemaMgr:SchemaManager = new SchemaManager(); let schemaMgr:SpecManager = new SpecManager();
let normalizer = new SchemaNormalizator(schemaMgr); let normalizer = new SchemaNormalizer(schemaMgr);
describe('Dereference', () => { describe('Dereference', () => {
beforeAll(done => { beforeAll(done => {

View File

@ -1,6 +1,6 @@
'use strict'; 'use strict';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { SchemaManager } from '../utils/SchemaManager'; import { SpecManager } from '../utils/SpecManager';
import { JsonPointer } from '../utils/JsonPointer'; import { JsonPointer } from '../utils/JsonPointer';
import { defaults } from '../utils/helpers'; import { defaults } from '../utils/helpers';
@ -17,7 +17,7 @@ interface Schema {
} }
@Injectable() @Injectable()
export class SchemaNormalizator { export class SchemaNormalizer {
_dereferencer:SchemaDereferencer; _dereferencer:SchemaDereferencer;
constructor(private _schema:any) { constructor(private _schema:any) {
this._dereferencer = new SchemaDereferencer(_schema, this); this._dereferencer = new SchemaDereferencer(_schema, this);
@ -161,7 +161,7 @@ class RefCounter {
class SchemaDereferencer { class SchemaDereferencer {
private _refCouner = new RefCounter(); private _refCouner = new RefCounter();
constructor(private _spec: SchemaManager, private normalizator: SchemaNormalizator) { constructor(private _spec: SpecManager, private normalizator: SchemaNormalizer) {
} }
dereference(schema: Reference, pointer:string):any { dereference(schema: Reference, pointer:string):any {

View File

@ -4,21 +4,21 @@ import JsonSchemaRefParser from 'json-schema-ref-parser';
import JsonPointer from './JsonPointer'; import JsonPointer from './JsonPointer';
import {methods as swaggerMethods} from './swagger-defs'; import {methods as swaggerMethods} from './swagger-defs';
export class SchemaManager { export class SpecManager {
public _schema:any = {}; public _schema:any = {};
public apiUrl: string; public apiUrl: string;
private _instance:any; private _instance:any;
static instance() { static instance() {
return new SchemaManager(); return new SpecManager();
} }
constructor() { constructor() {
if (SchemaManager.prototype._instance) { if (SpecManager.prototype._instance) {
return SchemaManager.prototype._instance; return SpecManager.prototype._instance;
} }
SchemaManager.prototype._instance = this; SpecManager.prototype._instance = this;
} }
load(url) { load(url) {
@ -26,14 +26,11 @@ export class SchemaManager {
this._schema = {}; this._schema = {};
JsonSchemaRefParser.bundle(url, {http: {withCredentials: false}}) JsonSchemaRefParser.bundle(url, {http: {withCredentials: false}})
.then( .then(schema => {
(schema) => { this._schema = schema;
this._schema = schema; resolve(this._schema);
resolve(this._schema); this.init();
this.init(); }, err => reject(err));
},
(err) => reject(err)
);
}); });
return promise; return promise;
@ -49,14 +46,12 @@ export class SchemaManager {
} }
get schema() { get schema() {
// TODO: consider returning promise
return this._schema; return this._schema;
} }
byPointer(pointer) { byPointer(pointer) {
let res = null; let res = null;
try { try {
// TODO: remove decodeURIComponent after this issue is fixed: https://github.com/BigstickCarpet/swagger-parser/issues/31
res = JsonPointer.get(this._schema, decodeURIComponent(pointer)); res = JsonPointer.get(this._schema, decodeURIComponent(pointer));
} catch(e) {/*skip*/ } } catch(e) {/*skip*/ }
return res; return res;

View File

@ -14,7 +14,7 @@
<script> <script>
window.redocError = null; window.redocError = null;
/* init redoc */ /* init redoc */
var url = window.location.search.substr(5) || 'http://rebilly.github.io:80/SwaggerTemplateRepo/swagger.json'; var url = window.location.search.substr(5) || 'http://rebilly.github.io/SwaggerTemplateRepo/swagger.json';
Redoc.init(decodeURIComponent(url), {disableLazySchemas: true}).then(function() {}, function(err) { Redoc.init(decodeURIComponent(url), {disableLazySchemas: true}).then(function() {}, function(err) {
window.redocError = err; window.redocError = err;
}); });

View File

@ -1,17 +1,17 @@
'use strict'; 'use strict';
import { SchemaManager } from '../../lib/utils/SchemaManager'; import { SpecManager } from '../../lib/utils/SpecManager';
describe('Utils', () => { describe('Utils', () => {
describe('Schema manager', () => { describe('Schema manager', () => {
let schemaMgr; let schemaMgr;
beforeEach(() => { beforeEach(() => {
schemaMgr = new SchemaManager(); schemaMgr = new SpecManager();
}); });
it('Should be a singleton', ()=> { it('Should be a singleton', ()=> {
(new SchemaManager()).should.be.equal(schemaMgr); (new SpecManager()).should.be.equal(schemaMgr);
SchemaManager.instance().should.be.equal(schemaMgr); SpecManager.instance().should.be.equal(schemaMgr);
}); });
it('load should return a promise', ()=> { it('load should return a promise', ()=> {