UNPKG

vulcain-corejs

Version:
235 lines (233 loc) 9.97 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments)).next()); }); }; const http = require("http"); const actions_1 = require("../pipeline/actions"); const query_1 = require("../pipeline/query"); const requestContext_1 = require("./requestContext"); const annotations_1 = require("../di/annotations"); const metrics_1 = require("../metrics/metrics"); const system_1 = require("./../configurations/globals/system"); const response_1 = require("../pipeline/response"); const guid = require('uuid'); class AbstractAdapter { constructor(domainName, container) { this.domainName = domainName; this.container = container; this.commandManager = new actions_1.CommandManager(container); this.queryManager = new query_1.QueryManager(container); this.testUser = container.get(annotations_1.DefaultServiceNames.TestUser, true); this.domain = container.get(annotations_1.DefaultServiceNames.Domain); this.metrics = container.get(annotations_1.DefaultServiceNames.Metrics); let descriptors = this.container.get(annotations_1.DefaultServiceNames.ServiceDescriptors); let hasAsyncTasks = descriptors.getDescriptions().hasAsyncTasks; this.commandManager.startMessageBus(hasAsyncTasks); } calcDelayInMs(begin) { // ts = [seconds, nanoseconds] const ts = process.hrtime(begin); // convert seconds to miliseconds and nanoseconds to miliseconds as well return (ts[0] * 1000) + (ts[1] / 1000000); } startRequest(command) { // util.log("Request : " + JSON.stringify(command)); // TODO remove sensible data return process.hrtime(); } createRequestContext(request) { let ctx = new requestContext_1.RequestContext(this.container, requestContext_1.Pipeline.HttpRequest); // Initialize headers & hostname this.initializeRequestContext(ctx, request); ctx.correlationId = ctx.headers["X-VULCAIN-CORRELATION-ID"] || guid.v4(); ctx.correlationPath = ctx.headers["X-VULCAIN-CORRELATION-PATH"] || "-"; let tenantPolicy = ctx.container.get(annotations_1.DefaultServiceNames.TenantPolicy); ctx.tenant = tenantPolicy.resolveTenant(ctx, request); } endRequest(begin, response, ctx, e) { let value = response.value; let hasError = false; let prefix; if (response instanceof response_1.HttpResponse) { value = response.content; hasError = response.statusCode && response.statusCode >= 400; } if (value) { hasError = hasError || value.error; if (value.schema) { prefix = value.schema.toLowerCase() + "_" + value.action.toLowerCase(); } else if (value.action) { prefix = value.action.toLowerCase(); } else { value = null; // custom value - don't log it } } else { hasError = true; } const ms = this.calcDelayInMs(begin); // Duration prefix && this.metrics.timing(prefix + metrics_1.MetricsConstant.duration, ms); this.metrics.timing(metrics_1.MetricsConstant.allRequestsDuration, ms); // Failure if (hasError) { prefix && this.metrics.increment(prefix + metrics_1.MetricsConstant.failure); this.metrics.increment(metrics_1.MetricsConstant.allRequestsFailure); } // Always remove userContext if (value) { value.userContext = undefined; } let trace = { duration: ms, info: Object.assign({}, value) }; // Remove result value for trace trace.info.value = undefined; if (e) { trace.stackTrace = e.stack; trace.message = e.message; } system_1.System.log.write(ctx, trace); // Normalize return value if (value) { value.source = undefined; value.inputSchema = undefined; value.startedAt = undefined; value.completedAt = undefined; } } populateFromQuery(request) { let params = {}; let count = 0; ; Object.keys(request.query).forEach(name => { switch (name) { case "$action": case "$schema": case "$page": case "$maxByPage": break; case "$query": params = JSON.parse(request.query[name]); break; default: count++; params[name] = request.query[name]; } }); return { params, count }; } populateWithActionSchema(action, request, defaultAction) { let a; let s; if (request.params.schemaAction) { if (request.params.schemaAction.indexOf('.') >= 0) { let parts = request.params.schemaAction.split('.'); s = parts[0]; a = parts[1]; } else { a = request.params.schemaAction; } } else { a = request.query.$action; s = request.query.$schema; } action.action = action.action || a || defaultAction; action.schema = action.schema || s; } normalizeCommand(request) { let action = request.body; // Body contains only data -> create a new action object if (!action.action && !action.params && !action.schema) { action = { params: action }; } action.domain = this.domainName; this.populateWithActionSchema(action, request); action.params = action.params || {}; return action; } executeQueryRequest(request, ctx) { return __awaiter(this, void 0, void 0, function* () { if (request.user) { ctx.user = request.user; } let query = { domain: this.domainName }; this.populateWithActionSchema(query, request, "all"); if (query.action === "get") { if (!request.params.id) { return new response_1.BadRequestResponse("Id is required"); } let requestArgs = this.populateFromQuery(request); if (requestArgs.count === 0) { query.params = request.params.id; } else { query.params = requestArgs.params; query.params.id = request.params.id; } } else { query.maxByPage = (request.query.$maxByPage && parseInt(request.query.$maxByPage)) || 100; query.page = (request.query.$page && parseInt(request.query.$page)) || 0; query.params = this.populateFromQuery(request).params; } return yield this.executeRequest(this.queryManager, query, ctx); }); } executeActionRequest(request, ctx, command) { if (request.user) { ctx.user = request.user; } command = command || this.normalizeCommand(request); return this.executeRequest(this.commandManager, command, ctx); } executeRequest(manager, command, ctx) { return __awaiter(this, void 0, void 0, function* () { const begin = this.startRequest(command); if (!command || !command.domain) { return new response_1.BadRequestResponse("domain is required."); } if (command.domain.toLowerCase() !== this.domainName.toLowerCase()) { return new response_1.BadRequestResponse("this service doesn't belong to domain " + this.domainName); } try { // Check if handler exists let info = manager.getInfoHandler(command); system_1.System.log.info(ctx, `Request context - user=${ctx.user ? ctx.user.name : "<null>"}, scopes=${ctx.user ? ctx.user.scopes : "[]"}, tenant=${ctx.tenant}`); // Verify authorization if (!ctx.hasScope(info.metadata.scope)) { system_1.System.log.info(ctx, `user=${ctx.user ? ctx.user.name : "<null>"}, scopes=${ctx.user ? ctx.user.scopes : "[]"} - Unauthorized for handler scope=${info.metadata.scope} `); return new response_1.HttpResponse({ error: { message: http.STATUS_CODES[403] } }, 403); } // Process handler let result = yield manager.runAsync(command, ctx); if (result && command.correlationId) { result.addHeader("X-VULCAIN-CORRELATION-ID", command.correlationId); } // Response this.endRequest(begin, result, ctx); return result; } catch (e) { let result = command; result.error = { message: e.message || e, errors: e.errors }; this.endRequest(begin, result, ctx, e); return new response_1.HttpResponse(result, e.statusCode); } finally { ctx && ctx.dispose(); } }); } } exports.AbstractAdapter = AbstractAdapter; //# sourceMappingURL=abstractAdapter.js.map