@loopback/context-explorer
Version:
Visualize context hierarchy, bindings, configurations, and dependencies
107 lines (99 loc) • 3.38 kB
text/typescript
// Copyright IBM Corp. and LoopBack contributors 2020. All Rights Reserved.
// Node module: @loopback/context-explorer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import {config, Constructor, Context, inject, JSONObject} from '@loopback/core';
import {
api,
get,
HttpErrors,
param,
RequestContext,
Response,
RestBindings,
} from '@loopback/rest';
import {ContextGraph} from './context-graph';
import {ContextExplorerBindings} from './keys';
import {ContextExplorerConfig} from './types';
import {renderGraph} from './visualizer';
export function contextExplorerControllerFactory(
basePath = '/context-explorer',
): Constructor<unknown> {
({basePath, paths: {}})
class ContextExplorerController {
constructor(
private explorerConfig: ContextExplorerConfig = {
path: '/context-explorer',
},
(RestBindings.Http.CONTEXT) private ctx: RequestContext,
) {}
({fromBinding: ContextExplorerBindings.COMPONENT})
// Map to `GET /inspect`
('/inspect', {
'x-visibility': 'undocumented',
responses: {},
})
inspect(
.query.boolean('includeInjections') includeInjections = true,
.query.boolean('includeParent') includeParent = true,
.query.boolean('includeGraph') includeGraph = true,
): JSONObject {
if (this.explorerConfig.enableInspection === false) {
throw new HttpErrors.NotFound();
}
const result = this.ctx.inspect({includeInjections, includeParent});
if (includeGraph) {
const graph = new ContextGraph(result).render();
result.graph = graph;
}
return result;
}
// Map to `GET /inspect`
('/graph', {'x-visibility': 'undocumented', responses: {}})
async graph(
.query.boolean('includeInjections') includeInjections = true,
.query.boolean('includeParent') includeParent = true,
.query.string('format') format = 'svg',
): Promise<Response> {
if (this.explorerConfig.enableSVG === false) {
throw new HttpErrors.NotFound();
}
const result = this.ctx.inspect({includeInjections, includeParent});
const graph = new ContextGraph(result).render();
if (format === 'dot') {
this.ctx.response.contentType('text/plain').send(graph);
} else {
const svg = await renderGraph(graph);
this.ctx.response.contentType('image/svg+xml').send(svg);
}
return this.ctx.response;
}
/**
* Create an array of graphviz dot graphs for d3 animation
*/
('/dots', {
'x-visibility': 'undocumented',
responses: {},
})
async dots() {
if (this.explorerConfig.enableD3Animation === false) {
throw new HttpErrors.NotFound();
}
let ctx: Context | undefined = this.ctx;
const dots: string[] = [];
while (ctx != null) {
// Add one graph with injections
const ctxData = ctx.inspect({
includeParent: true,
includeInjections: true,
});
const graph = new ContextGraph(ctxData).render();
dots.push(graph);
ctx = ctx.parent;
}
// Show app, app+server, and app+server+request
return dots.reverse();
}
}
return ContextExplorerController;
}