redoc/lib/services/search.service.ts

185 lines
5.3 KiB
TypeScript
Raw Normal View History

import { Injectable } from '@angular/core';
import { AppStateService } from './app-state.service';
2017-01-26 17:46:28 +03:00
import { SchemaNormalizer } from './schema-normalizer.service';
import { JsonPointer, groupBy, SpecManager, StringMap, snapshot } from '../utils/';
2017-01-28 19:47:12 +03:00
import * as slugify from 'slugify';
import {
Spec as SwaggerSpec,
Operation as SwaggerOperation,
Schema as SwaggerSchema,
BodyParameter
} from '@types/swagger-schema-official';
2017-01-24 00:29:52 +03:00
import * as lunr from 'lunr';
interface IndexElement {
2017-01-26 17:46:28 +03:00
menuId: string;
2017-01-24 00:29:52 +03:00
title: string;
body: string;
pointer: string;
}
2017-01-28 19:47:12 +03:00
interface SwaggerSchemaExt extends SwaggerSchema {
_pointer?: string;
}
2017-01-24 00:29:52 +03:00
const index = lunr(function () {
//this.field('menuId', {boost: 0});
2017-01-24 00:29:52 +03:00
this.field('title', {boost: 1.5});
this.field('body');
this.ref('pointer');
2017-01-28 16:57:22 +03:00
});
2017-01-24 00:29:52 +03:00
const store:StringMap<IndexElement> = {};
@Injectable()
export class SearchService {
2017-01-26 17:46:28 +03:00
normalizer: SchemaNormalizer;
2017-01-24 00:29:52 +03:00
constructor(private app: AppStateService, private spec: SpecManager) {
2017-01-26 17:46:28 +03:00
this.normalizer = new SchemaNormalizer(spec);
}
2017-01-24 00:29:52 +03:00
2017-01-28 19:47:12 +03:00
ensureSearchVisible(containingPointers: string|null[]) {
this.app.searchContainingPointers.next(containingPointers);
}
2017-01-24 00:29:52 +03:00
indexAll() {
this.indexPaths(this.spec.schema);
2017-01-28 19:47:12 +03:00
this.indexTags(this.spec.schema);
2017-01-24 00:29:52 +03:00
}
search(q):StringMap<IndexElement[]> {
var items = {};
2017-01-24 00:29:52 +03:00
const res:IndexElement[] = index.search(q).map(res => {
items[res.menuId] = res;
2017-01-24 00:29:52 +03:00
return store[res.ref];
});
2017-01-26 17:46:28 +03:00
const grouped = groupBy(res, 'menuId');
2017-01-24 00:29:52 +03:00
return grouped;
}
index(element: IndexElement) {
2017-01-26 19:21:24 +03:00
// don't reindex same pointers (for discriminator)
if (store[element.pointer]) return;
2017-01-24 00:29:52 +03:00
index.add(element);
store[element.pointer] = element;
}
2017-01-28 19:47:12 +03:00
indexTags(swagger:SwaggerSpec) {
let tags = swagger.tags;
for (let tag of tags) {
if (tag['x-traitTag']) continue;
let id = `tag/${slugify(tag.name)}`;
this.index({
menuId: id,
title: tag.name,
body: tag.description,
pointer: id
});
}
}
indexPaths(swagger:SwaggerSpec) {
2017-01-28 16:57:22 +03:00
const paths = swagger.paths;
2017-01-24 00:29:52 +03:00
const basePtr = '#/paths';
Object.keys(paths).forEach(path => {
let opearations = paths[path];
Object.keys(opearations).forEach(verb => {
const opearation = opearations[verb];
const ptr = JsonPointer.join(basePtr, [path, verb]);
this.indexOperation(opearation, ptr);
});
});
}
2017-01-28 19:47:12 +03:00
indexOperation(operation:SwaggerOperation, operationPointer:string) {
2017-01-24 00:29:52 +03:00
this.index({
2017-01-26 17:46:28 +03:00
pointer: operationPointer,
menuId: operationPointer,
2017-01-24 00:29:52 +03:00
title: operation.summary,
body: operation.description
});
2017-01-26 17:46:28 +03:00
this.indexOperationResponses(operation, operationPointer);
2017-01-28 16:57:22 +03:00
this.indexOperationParameters(operation, operationPointer);
2017-01-26 17:46:28 +03:00
}
2017-01-28 19:47:12 +03:00
indexOperationParameters(operation: SwaggerOperation, operationPointer: string) {
2017-01-26 17:46:28 +03:00
const parameters = operation.parameters;
if (!parameters) return;
2017-01-26 17:46:28 +03:00
for (let i=0; i<parameters.length; ++i) {
const param = parameters[i];
const paramPointer = JsonPointer.join(operationPointer, ['parameters', i]);
this.index({
pointer: paramPointer,
menuId: operationPointer,
title: param.in === 'body' ? '' : param.name,
body: param.description
});
if (param.in === 'body') {
this.normalizer.reset();
2017-01-28 19:47:12 +03:00
this.indexSchema((<BodyParameter>param).schema,
'', JsonPointer.join(paramPointer, ['schema']), operationPointer);
2017-01-26 17:46:28 +03:00
}
}
2017-01-24 00:29:52 +03:00
}
2017-01-28 19:47:12 +03:00
indexOperationResponses(operation:SwaggerOperation, operationPtr:string) {
2017-01-24 00:29:52 +03:00
const responses = operation.responses;
if (!responses) return;
Object.keys(responses).forEach(code => {
const resp = responses[code];
const respPtr = JsonPointer.join(operationPtr, ['responses', code]);
this.index({
pointer: respPtr,
2017-01-26 17:46:28 +03:00
menuId: operationPtr,
2017-01-24 00:29:52 +03:00
title: code,
body: resp.description
});
if (resp.schema) {
this.normalizer.reset();
2017-01-24 00:29:52 +03:00
this.indexSchema(resp.schema, '', JsonPointer.join(respPtr, 'schema'), operationPtr);
}
});
}
2017-01-28 19:47:12 +03:00
indexSchema(_schema:SwaggerSchemaExt, name: string, absolutePointer: string,
menuPointer: string, parent?: string) {
2017-01-24 00:29:52 +03:00
if (!_schema) return;
let schema = _schema;
let title = name;
schema = this.normalizer.normalize(schema, schema._pointer || absolutePointer, { childFor: parent });
2017-01-24 00:29:52 +03:00
let body = schema.description; // TODO: defaults, examples, etc...
if (schema.type === 'array') {
this.indexSchema(schema.items, title, JsonPointer.join(absolutePointer, ['items']), menuPointer);
return;
}
if (schema.discriminator) {
2017-01-26 19:21:24 +03:00
let derived = this.spec.findDerivedDefinitions(schema._pointer, schema);
for (let defInfo of derived ) {
let subSpec = this.spec.getDescendant(defInfo, schema);
this.indexSchema(snapshot(subSpec), '', absolutePointer, menuPointer, schema._pointer);
2017-01-26 19:21:24 +03:00
}
}
2017-01-24 00:29:52 +03:00
this.index({
pointer: absolutePointer,
2017-01-26 17:46:28 +03:00
menuId: menuPointer,
2017-01-24 00:29:52 +03:00
title,
body
2017-01-28 16:57:22 +03:00
});
2017-01-24 00:29:52 +03:00
if (schema.properties) {
Object.keys(schema.properties).forEach(propName => {
let propPtr = JsonPointer.join(absolutePointer, ['properties', propName]);
this.indexSchema(schema.properties[propName], propName, propPtr, menuPointer);
2017-01-28 16:57:22 +03:00
});
2017-01-24 00:29:52 +03:00
}
}
}