UNPKG

vulcain-corejs

Version:
310 lines (308 loc) 14.1 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; 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 annotations_1 = require("./../../di/annotations"); const os = require("os"); require("reflect-metadata"); const system_1 = require("./../../configurations/globals/system"); const applicationRequestError_1 = require("./../../errors/applicationRequestError"); const metrics_1 = require("../../metrics/metrics"); const abstractAdapter_1 = require("../../servers/abstractAdapter"); const httpCommandError_1 = require("../../errors/httpCommandError"); const rest = require('unirest'); /** * * * @export * @abstract * @class AbstractCommand * @template T */ let AbstractServiceCommand = class AbstractServiceCommand { /** * Creates an instance of AbstractCommand. * * @param {IContainer} container * @param {any} providerFactory */ constructor(container) { this.metrics = container.get(annotations_1.DefaultServiceNames.Metrics); this.initializeMetricsInfo(); this.logger = this.container.get(annotations_1.DefaultServiceNames.Logger); } get container() { return this.requestContext.container; } /** * Set (or reset) user context to use for calling service. * * @protected * @param {string} apiKey - null for reset * @param {string} tenant - null for reset * * @memberOf AbstractServiceCommand */ setRequestContext(apiKey, tenant) { if (!apiKey) { this.overrideAuthorization = null; } else { this.overrideAuthorization = "ApiKey " + apiKey; } this.overrideTenant = tenant; } initializeMetricsInfo() { let dep = this.constructor["$dependency:service"]; if (!dep) { throw new Error("ServiceDependency annotation is required on command " + Object.getPrototypeOf(this).name); } this.setMetricsTags(dep.service, dep.version); } setMetricsTags(targetServiceName, targetServiceVersion) { let exists = system_1.System.manifest.dependencies.services.find(svc => svc.service === targetServiceName && svc.version === targetServiceVersion); if (!exists) { system_1.System.manifest.dependencies.services.push({ service: targetServiceName, version: targetServiceVersion }); } this.customTags = this.metrics.encodeTags("targetServiceName=" + targetServiceName, "targetServiceVersion=" + targetServiceVersion); this.logger.logAction(this.requestContext, "BC", "Service", targetServiceName + "-" + targetServiceVersion); } onCommandCompleted(duration, success) { this.metrics.timing(AbstractServiceCommand.METRICS_NAME + metrics_1.MetricsConstant.duration, duration, this.customTags); if (!success) this.metrics.increment(AbstractServiceCommand.METRICS_NAME + metrics_1.MetricsConstant.failure, this.customTags); this.logger.logAction(this.requestContext, 'EC'); } /** * * * @private * @param {string} serviceName * @param {number} version * @returns */ createServiceName(serviceName, version) { if (!serviceName) throw new Error("You must provide a service name"); if (!version || !version.match(/[0-9]+\.[0-9]+/)) throw new Error("Invalid version number. Must be on the form major.minor"); let alias = system_1.System.resolveAlias(serviceName, version); if (alias) return alias; return system_1.System.createContainerEndpoint(serviceName, version); } /** * get a domain element * @param serviceName - full service name * @param version - version of the service * @param id - Element id * @param schema - optional element schema * @returns A vulcain request response * * @protected * @template T * @param {string} serviceName * @param {number} version * @param {string} id * @param {string} [schema] * @returns {Promise<QueryResponse<T>>} */ getRequestAsync(serviceName, version, id, schema) { if (system_1.System.hasMocks) { let result = system_1.System.mocks.applyMockService(serviceName, version, schema ? schema + ".get" : "get", { id }); if (result !== undefined) { return result; } } let url = schema ? `http://${this.createServiceName(serviceName, version)}/api/{schema}/get/${id}` : `http://${this.createServiceName(serviceName, version)}/api/get/${id}`; let res = this.sendRequestAsync("get", url); return res; } /** * * * @protected * @template T * @param {string} serviceName * @param {number} version * @param {string} action * @param {*} [query] * @param {number} [page] * @param {number} [maxByPage] * @param {string} [schema] * @returns {Promise<QueryResponse<T>>} */ getQueryAsync(serviceName, version, verb, query, page, maxByPage, schema) { let args = {}; args.$maxByPage = maxByPage; args.$page = page; args.$query = query && JSON.stringify(query); if (system_1.System.hasMocks) { let result = system_1.System.mocks.applyMockService(serviceName, version, verb, args); if (result !== undefined) { return result; } } let url = system_1.System.createUrl(`http://${this.createServiceName(serviceName, version)}/api/${verb}`, args); let res = this.sendRequestAsync("get", url); return res; } /** * * * @protected * @param {string} serviceName * @param {number} version * @param {string} action * @param {*} data * @returns {Promise<ActionResponse<T>>} */ sendActionAsync(serviceName, version, verb, data) { let command = { params: data, correlationId: this.requestContext.correlationId }; if (system_1.System.hasMocks) { let result = system_1.System.mocks.applyMockService(serviceName, version, verb, command); if (result !== undefined) { return result; } } let url = `http://${this.createServiceName(serviceName, version)}/api/${verb}`; let res = this.sendRequestAsync("post", url, (req) => req.json(command)); return res; } calculateRequestPath() { if (this.requestContext.correlationId[this.requestContext.correlationId.length - 1] === "-") this.requestContext.correlationPath += "1"; else { let parts = this.requestContext.correlationPath.split('-'); let ix = parts.length - 1; let nb = (parseInt(parts[ix]) || 0) + 1; parts[ix] = nb.toString(); this.requestContext.correlationPath = parts.join('-'); } return this.requestContext.correlationPath; } setUserContextAsync(request) { return __awaiter(this, void 0, void 0, function* () { request.header(abstractAdapter_1.VulcainHeaderNames.X_VULCAIN_TENANT, this.overrideTenant || this.requestContext.tenant); if (this.overrideAuthorization) { request.header("Authorization", this.overrideAuthorization); return; } let token = this.requestContext.bearer; if (!token) { if (!this.requestContext.user) { return; } let tokens = this.requestContext.container.get("TokenService"); // Ensures jwtToken exists for user context propagation let result = this.requestContext.bearer = yield tokens.createTokenAsync(this.requestContext.user); token = result.token; } request.header("Authorization", "Bearer " + token); }); } /** * Send a http request * * @protected * @param {string} http verb to use * @param {string} url * @param {(req:types.IHttpRequest) => void} [prepareRequest] Callback to configure request before sending * @returns request response */ sendRequestAsync(verb, url, prepareRequest) { return __awaiter(this, void 0, void 0, function* () { let request = rest[verb](url); // Propagate context request.header(abstractAdapter_1.VulcainHeaderNames.X_VULCAIN_CORRELATION_ID, this.requestContext.correlationId); request.header(abstractAdapter_1.VulcainHeaderNames.X_VULCAIN_CORRELATION_PATH, this.calculateRequestPath() + "-"); request.header(abstractAdapter_1.VulcainHeaderNames.X_VULCAIN_SERVICE_NAME, system_1.System.serviceName); request.header(abstractAdapter_1.VulcainHeaderNames.X_VULCAIN_SERVICE_VERSION, system_1.System.serviceVersion); request.header(abstractAdapter_1.VulcainHeaderNames.X_VULCAIN_ENV, system_1.System.environment); request.header(abstractAdapter_1.VulcainHeaderNames.X_VULCAIN_CONTAINER, os.hostname()); yield this.setUserContextAsync(request); prepareRequest && prepareRequest(request); this.requestContext.logInfo("Calling vulcain service on " + url); return new Promise((resolve, reject) => { try { request.end((response) => { if (response.error || response.status !== 200) { let err = new Error(response.error ? response.error.message : response.body); system_1.System.log.error(this.requestContext, err, `Service request ${verb} ${url} failed with status code ${response.status}`); reject(err); return; } let vulcainResponse = response.body; if (vulcainResponse.error) { system_1.System.log.info(this.requestContext, `Service request ${verb} ${url} failed with status code ${response.status}`); reject(new applicationRequestError_1.ApplicationRequestError(vulcainResponse.error.message, vulcainResponse.error.errors, response.status)); } else { system_1.System.log.info(this.requestContext, `Service request ${verb} ${url} completed with status code ${response.status}`); resolve(vulcainResponse); } }); } catch (err) { let msg = `Service request ${verb} ${url} failed`; system_1.System.log.error(this.requestContext, err, msg); if (!(err instanceof Error)) { let tmp = err; err = new Error(msg); err.error = tmp; } reject(new httpCommandError_1.HttpCommandError(msg, err)); } }); }); } exec(kind, serviceName, version, verb, userContext, data, page, maxByPage) { return __awaiter(this, void 0, void 0, function* () { switch (kind) { case 'action': { userContext && this.setRequestContext(userContext.apiKey, userContext.tenant); let response = yield this.sendActionAsync(serviceName, version, verb, data); return response.value; } case 'query': { userContext && this.setRequestContext(userContext.apiKey, userContext.tenant); let response = yield this.getQueryAsync(serviceName, version, verb, data, page, maxByPage); return { values: response.value, total: response.total, page }; } case 'get': { userContext && this.setRequestContext(userContext.apiKey, userContext.tenant); let response = yield this.getRequestAsync(serviceName, version, data); return response.value; } } }); } runAsync(...args) { return this.exec(...args); } }; AbstractServiceCommand.METRICS_NAME = "service_call"; AbstractServiceCommand = __decorate([ __param(0, annotations_1.Inject(annotations_1.DefaultServiceNames.Container)), __metadata("design:paramtypes", [Object]) ], AbstractServiceCommand); exports.AbstractServiceCommand = AbstractServiceCommand; //# sourceMappingURL=abstractServiceCommand.js.map