Fix search inside circular discriminators

This commit is contained in:
Roman Hotsiy 2017-01-28 13:03:51 +02:00
parent 6e13acbc64
commit c52703f1ed
No known key found for this signature in database
GPG Key ID: 5CB7B3ACABA57CB0
8 changed files with 43 additions and 37 deletions

View File

@ -64,7 +64,8 @@ export class JsonSchemaLazy implements OnDestroy, OnInit, AfterViewInit {
// skip caching view with descendant schemas // skip caching view with descendant schemas
// as it needs attached controller // as it needs attached controller
if (!this.disableLazy && (compRef.instance.hasDescendants || compRef.instance._hasSubSchemas)) { let hasDescendants = compRef.instance.descendants && compRef.instance.descendants.length;
if (!this.disableLazy && (hasDescendants || compRef.instance._hasSubSchemas)) {
this._loadAfterSelf(); this._loadAfterSelf();
return; return;
} }

View File

@ -31,7 +31,7 @@ export class JsonSchema extends BaseSearchableComponent implements OnInit {
schema: any = {}; schema: any = {};
activeDescendant:any = {}; activeDescendant:any = {};
hasDescendants: boolean = false; discriminator: string = null;
_hasSubSchemas: boolean = false; _hasSubSchemas: boolean = false;
properties: any; properties: any;
_isArray: boolean; _isArray: boolean;
@ -75,8 +75,7 @@ export class JsonSchema extends BaseSearchableComponent implements OnInit {
initDescendants() { initDescendants() {
this.descendants = this.specMgr.findDerivedDefinitions(this.normPointer, this.schema); this.descendants = this.specMgr.findDerivedDefinitions(this.normPointer, this.schema);
if (!this.descendants.length) return; if (!this.descendants.length) return;
this.hasDescendants = true; let discriminator = this.discriminator = this.schema.discriminator || this.schema['x-extendedDiscriminator'];
let discriminator = this.schema.discriminator || this.schema['x-extendedDiscriminator'];
let discrProperty = this.schema.properties && let discrProperty = this.schema.properties &&
this.schema.properties[discriminator]; this.schema.properties[discriminator];
if (discrProperty && discrProperty.enum) { if (discrProperty && discrProperty.enum) {
@ -91,6 +90,7 @@ export class JsonSchema extends BaseSearchableComponent implements OnInit {
}).sort((a, b) => { }).sort((a, b) => {
return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1; return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1;
}); });
this.descendants.forEach((d, idx) => d.idx = idx);
} }
this.selectDescendantByIdx(0); this.selectDescendantByIdx(0);
} }
@ -119,11 +119,12 @@ export class JsonSchema extends BaseSearchableComponent implements OnInit {
if (!this.schema.isTrivial) { if (!this.schema.isTrivial) {
SchemaHelper.preprocessProperties(this.schema, this.normPointer, { SchemaHelper.preprocessProperties(this.schema, this.normPointer, {
childFor: this.childFor childFor: this.childFor,
discriminator: this.discriminator
}); });
} }
this.properties = this.schema._properties; this.properties = this.schema._properties || [];
if (this.isRequestSchema) { if (this.isRequestSchema) {
this.properties = this.properties && this.properties.filter(prop => !prop.readOnly); this.properties = this.properties && this.properties.filter(prop => !prop.readOnly);
} }

View File

@ -6,22 +6,6 @@ import { Subscription } from 'rxjs/Subscription';
export { SpecManager }; export { SpecManager };
function snapshot(obj) {
if(obj == undefined || typeof(obj) !== 'object') {
return obj;
}
var temp = new obj.constructor();
for(var key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = snapshot(obj[key]);
}
}
return temp;
}
/** /**
* Generic Component * Generic Component
* @class * @class

View File

@ -7,6 +7,7 @@ import * as slugify from 'slugify';
interface PropertyPreprocessOptions { interface PropertyPreprocessOptions {
childFor: string; childFor: string;
skipReadOnly?: boolean; skipReadOnly?: boolean;
discriminator?: string;
} }
// global var for this module // global var for this module
@ -220,8 +221,7 @@ export class SchemaHelper {
propertySchema._pointer = null; propertySchema._pointer = null;
} }
propertySchema._required = !!requiredMap[propName]; propertySchema._required = !!requiredMap[propName];
propertySchema.isDiscriminator = (schema.discriminator === propName propertySchema.isDiscriminator = opts.discriminator === propName;
|| schema['x-extendedDiscriminator'] === propName);
return propertySchema; return propertySchema;
}); });

View File

@ -27,6 +27,7 @@ export class SchemaNormalizer {
let hasPtr = !!schema.$ref; let hasPtr = !!schema.$ref;
if (opts.resolved && !hasPtr) this._dereferencer.visit(ptr); if (opts.resolved && !hasPtr) this._dereferencer.visit(ptr);
if (opts.childFor) this._dereferencer.visit(opts.childFor);
if (schema['x-redoc-normalized']) return schema; if (schema['x-redoc-normalized']) return schema;
let res = SchemaWalker.walk(schema, ptr, (subSchema, ptr) => { let res = SchemaWalker.walk(schema, ptr, (subSchema, ptr) => {
let resolved = this._dereferencer.dereference(subSchema, ptr); let resolved = this._dereferencer.dereference(subSchema, ptr);
@ -38,6 +39,7 @@ export class SchemaNormalizer {
return resolved; return resolved;
}); });
if (opts.resolved && !hasPtr) this._dereferencer.exit(ptr); if (opts.resolved && !hasPtr) this._dereferencer.exit(ptr);
if (opts.childFor) this._dereferencer.exit(opts.childFor);
res['x-redoc-normalized'] = true; res['x-redoc-normalized'] = true;
return res; return res;
} }
@ -113,6 +115,7 @@ export class AllOfMerger {
defaults(into, subSchema); defaults(into, subSchema);
subSchema._pointer = tmpPtr; subSchema._pointer = tmpPtr;
} }
into.discriminator = null;
into.allOf = null; into.allOf = null;
} }

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AppStateService } from './app-state.service'; import { AppStateService } from './app-state.service';
import { SchemaNormalizer } from './schema-normalizer.service'; import { SchemaNormalizer } from './schema-normalizer.service';
import { JsonPointer, groupBy, SpecManager, StringMap } from '../utils/'; import { JsonPointer, groupBy, SpecManager, StringMap, snapshot } from '../utils/';
import * as lunr from 'lunr'; import * as lunr from 'lunr';
@ -13,7 +13,7 @@ interface IndexElement {
} }
const index = lunr(function () { const index = lunr(function () {
this.field('menuId', {boost: 0}); //this.field('menuId', {boost: 0});
this.field('title', {boost: 1.5}); this.field('title', {boost: 1.5});
this.field('body'); this.field('body');
this.ref('pointer'); this.ref('pointer');
@ -33,13 +33,13 @@ export class SearchService {
} }
indexAll() { indexAll() {
const swagger = this.spec.schema; this.indexPaths(this.spec.schema);
this.indexPaths(swagger);
} }
search(q):StringMap<IndexElement[]> { search(q):StringMap<IndexElement[]> {
var items = {};
const res:IndexElement[] = index.search(q).map(res => { const res:IndexElement[] = index.search(q).map(res => {
items[res.menuId] = res;
return store[res.ref]; return store[res.ref];
}); });
const grouped = groupBy(res, 'menuId'); const grouped = groupBy(res, 'menuId');
@ -79,6 +79,7 @@ export class SearchService {
indexOperationParameters(operation: any, operationPointer: string) { indexOperationParameters(operation: any, operationPointer: string) {
const parameters = operation.parameters; const parameters = operation.parameters;
if (!parameters) return;
for (let i=0; i<parameters.length; ++i) { for (let i=0; i<parameters.length; ++i) {
const param = parameters[i]; const param = parameters[i];
const paramPointer = JsonPointer.join(operationPointer, ['parameters', i]); const paramPointer = JsonPointer.join(operationPointer, ['parameters', i]);
@ -90,6 +91,7 @@ export class SearchService {
}); });
if (param.in === 'body') { if (param.in === 'body') {
this.normalizer.reset();
this.indexSchema(param.schema, '', JsonPointer.join(paramPointer, ['schema']), operationPointer); this.indexSchema(param.schema, '', JsonPointer.join(paramPointer, ['schema']), operationPointer);
} }
} }
@ -109,18 +111,17 @@ export class SearchService {
}); });
if (resp.schema) { if (resp.schema) {
this.normalizer.reset();
this.indexSchema(resp.schema, '', JsonPointer.join(respPtr, 'schema'), operationPtr); this.indexSchema(resp.schema, '', JsonPointer.join(respPtr, 'schema'), operationPtr);
} }
}); });
} }
indexSchema(_schema:any, name: string, absolutePointer: string, menuPointer: string) { indexSchema(_schema:any, name: string, absolutePointer: string, menuPointer: string, parent?: string) {
if (!_schema) return; if (!_schema) return;
let schema = _schema; let schema = _schema;
let title = name; let title = name;
schema = this.normalizer.normalize(schema, schema._pointer || absolutePointer, { childFor: parent });
this.normalizer.reset();
schema = this.normalizer.normalize(schema, schema._pointer || absolutePointer);
let body = schema.description; // TODO: defaults, examples, etc... let body = schema.description; // TODO: defaults, examples, etc...
@ -129,11 +130,11 @@ export class SearchService {
return; return;
} }
if (schema.discriminator && !schema['x-derived-from']) { if (schema.discriminator) {
let derived = this.spec.findDerivedDefinitions(schema._pointer, schema); let derived = this.spec.findDerivedDefinitions(schema._pointer, schema);
for (let defInfo of derived ) { for (let defInfo of derived ) {
let subSpec = this.spec.getDescendant(defInfo, schema); let subSpec = this.spec.getDescendant(defInfo, schema);
this.indexSchema(subSpec, '', absolutePointer, menuPointer); this.indexSchema(snapshot(subSpec), '', absolutePointer, menuPointer, schema._pointer);
} }
} }

View File

@ -94,3 +94,19 @@ export function throttle(fn, threshhold, scope) {
export const isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 export const isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0
|| (function (p) { return p.toString() === '[object SafariRemoteNotification]'; })(!window['safari'] || (function (p) { return p.toString() === '[object SafariRemoteNotification]'; })(!window['safari']
|| safari.pushNotification); || safari.pushNotification);
export function snapshot(obj) {
if(obj == undefined || typeof(obj) !== 'object') {
return obj;
}
var temp = new obj.constructor();
for(var key in obj) {
if (obj.hasOwnProperty(key)) {
temp[key] = snapshot(obj[key]);
}
}
return temp;
}

View File

@ -176,7 +176,7 @@ export class SpecManager {
if (!definition.discriminator && !definition['x-extendedDiscriminator']) return []; if (!definition.discriminator && !definition['x-extendedDiscriminator']) return [];
let globalDefs = this._schema.definitions || {}; let globalDefs = this._schema.definitions || {};
let res = []; let res:DescendantInfo[] = [];
let extendedDiscriminatorProp = definition['x-extendedDiscriminator']; let extendedDiscriminatorProp = definition['x-extendedDiscriminator'];
for (let defName of Object.keys(globalDefs)) { for (let defName of Object.keys(globalDefs)) {
let def = globalDefs[defName]; let def = globalDefs[defName];
@ -208,7 +208,7 @@ export class SpecManager {
} }
} }
res.push({name: derivedName, $ref: `#/definitions/${defName}`, idx: res.length}); res.push({name: derivedName, $ref: `#/definitions/${defName}`, idx: null});
} }
return res; return res;
} }