vulcain-corejs
Version:
Vulcain micro-service framework
238 lines (236 loc) • 11.6 kB
JavaScript
"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 messageBus_1 = require('./messageBus');
const annotations_1 = require('../di/annotations');
const common_1 = require('./common');
const os = require('os');
const requestContext_1 = require('../servers/requestContext');
const eventHandlerFactory_1 = require('./eventHandlerFactory');
const conventions_1 = require('../utils/conventions');
const system_1 = require('./../configurations/globals/system');
const commandRuntimeError_1 = require('./../errors/commandRuntimeError');
const guid = require('node-uuid');
(function (EventNotificationMode) {
EventNotificationMode[EventNotificationMode["always"] = 0] = "always";
EventNotificationMode[EventNotificationMode["successOnly"] = 1] = "successOnly";
EventNotificationMode[EventNotificationMode["never"] = 2] = "never";
})(exports.EventNotificationMode || (exports.EventNotificationMode = {}));
var EventNotificationMode = exports.EventNotificationMode;
class CommandManager {
constructor(container) {
this.container = container;
this._initialized = false;
this._hostname = os.hostname();
this._serviceId = process.env[conventions_1.Conventions.instance.ENV_SERVICE_NAME] + "-" + process.env[conventions_1.Conventions.instance.ENV_SERVICE_VERSION];
if (!this._serviceId)
throw new Error("VULCAIN_SERVICE_NAME and VULCAIN_SERVICE_VERSION must be defined.");
}
/**
* Get the current domain model
* @returns {Domain}
*/
get domain() {
if (!this._domain) {
this._domain = this.container.get(annotations_1.DefaultServiceNames.Domain);
}
return this._domain;
}
get serviceId() {
return this._serviceId;
}
startMessageBus(hasAsyncTasks) {
this.messageBus = new messageBus_1.MessageBus(this, hasAsyncTasks);
this.subscribeToEvents();
}
createResponse(ctx, command, error) {
let res = {
tenant: ctx.tenant,
source: this._hostname,
startedAt: command.startedAt,
service: command.service,
schema: command.schema,
inputSchema: command.inputSchema,
action: command.action,
userContext: command.userContext,
domain: command.domain,
status: error ? "Error" : command.status,
correlationId: command.correlationId,
taskId: command.taskId
};
if (error)
res.error = { message: error.message, errors: error.errors };
return res;
}
validateRequestData(info, command) {
return __awaiter(this, void 0, void 0, function* () {
let errors;
let inputSchema = info.metadata.inputSchema;
if (inputSchema && inputSchema !== "none") {
let schema = inputSchema && this.domain.getSchema(inputSchema);
if (schema) {
command.inputSchema = schema.name;
// Custom binding if any
command.params = schema && schema.bind(command.params);
errors = schema.validate(command.params);
if (errors && !Array.isArray(errors))
errors = [errors];
}
if (!errors || errors.length === 0) {
// Search if a method naming validate<schema>[Async] exists
let methodName = 'validate' + inputSchema;
let altMethodName = methodName + 'Async';
errors = info.handler[methodName] && info.handler[methodName](command.params, command.action);
if (!errors)
errors = info.handler[altMethodName] && (yield info.handler[altMethodName](command.params, command.action));
if (errors && !Array.isArray(errors))
errors = [errors];
}
}
return errors;
});
}
getInfoHandler(command, container) {
if (!this._serviceDescriptors) {
this._serviceDescriptors = this.container.get(annotations_1.DefaultServiceNames.ServiceDescriptors);
}
let info = this._serviceDescriptors.getHandlerInfo(container, command.schema, command.action);
return info;
}
runAsync(command, ctx) {
return __awaiter(this, void 0, Promise, function* () {
let info = this.getInfoHandler(command, ctx.container);
if (info.kind !== "action")
throw new Error("Query handler must be requested with GET.");
let eventMode = info.metadata.eventMode || EventNotificationMode.successOnly;
system_1.System.log.write(ctx, { RunAction: command });
try {
let errors = yield this.validateRequestData(info, command);
if (errors && errors.length > 0)
return this.createResponse(ctx, command, { message: "Validation errors", errors: errors });
command.schema = info.metadata.schema;
command.correlationId = ctx.correlationId;
command.correlationPath = ctx.correlationPath;
command.startedAt = system_1.System.nowAsString();
command.service = this._serviceId;
command.userContext = ctx.user || {};
// Register asynchronous task
if (!info.metadata.async) {
info.handler.requestContext = ctx;
info.handler.command = command;
let result = yield info.handler[info.method](command.params);
if (result instanceof common_1.HttpResponse) {
return result; // skip normal process
}
command.status = "Success";
let res = this.createResponse(ctx, command);
res.value = common_1.HandlerFactory.obfuscateSensibleData(this.domain, this.container, result);
res.completedAt = system_1.System.nowAsString();
if (eventMode === EventNotificationMode.always || eventMode === EventNotificationMode.successOnly) {
this.messageBus.sendEvent(res);
}
return res;
}
else {
// Pending
this.messageBus.pushTask(command);
return this.createResponse(ctx, command);
}
}
catch (e) {
let error = (e instanceof commandRuntimeError_1.CommandRuntimeError) ? e.error : e;
return this.createResponse(ctx, command, error);
}
});
}
consumeTaskAsync(command) {
return __awaiter(this, void 0, void 0, function* () {
let ctx = new requestContext_1.RequestContext(this.container, requestContext_1.Pipeline.HttpRequest);
ctx.correlationId = command.correlationId || guid.v4();
ctx.correlationPath = "event-";
system_1.System.log.write(ctx, { runEvent: command });
let info = this.getInfoHandler(command, ctx.container);
let eventMode = info.metadata.eventMode || EventNotificationMode.always;
let res;
try {
ctx.user = command.userContext;
ctx.tenant = command.userContext.tenant;
info.handler.requestContext = ctx;
info.handler.command = command;
let result = yield info.handler[info.method](command.params);
if (result instanceof common_1.HttpResponse) {
throw new Error("Custom Http Response is not valid in an async action");
}
command.status = "Success";
res = this.createResponse(ctx, command);
res.value = common_1.HandlerFactory.obfuscateSensibleData(this.domain, this.container, result);
res.commandMode = "async";
res.completedAt = system_1.System.nowAsString();
if (eventMode === EventNotificationMode.always || eventMode === EventNotificationMode.successOnly) {
this.messageBus.sendEvent(res);
}
}
catch (e) {
let error = (e instanceof commandRuntimeError_1.CommandRuntimeError) ? e.error : e;
res = this.createResponse(ctx, command, { message: error.message });
res.commandMode = "async";
res.completedAt = system_1.System.nowAsString();
if (eventMode === EventNotificationMode.always) {
this.messageBus.sendEvent(res);
}
system_1.System.log.error(ctx, e, `Error when processing async action : ${JSON.stringify(command)}`);
}
finally {
ctx.dispose();
}
});
}
subscribeToEvents() {
if (!this._initialized) {
this._initialized = true;
for (let item of CommandManager.eventHandlersFactory.allHandlers()) {
this.bindEventHandler(item.metadata);
}
}
}
bindEventHandler(metadata) {
let events = this.messageBus.getEventsQueue(metadata.subscribeToDomain || this.domain.name);
events = events.filter((e) => (metadata.subscribeToSchema === "*" || e.schema === metadata.subscribeToSchema));
if (metadata.filter)
events = metadata.filter(events);
events.subscribe((evt) => {
let handlers = CommandManager.eventHandlersFactory.getFilteredHandlers(evt.domain, evt.schema, evt.action);
for (let info of handlers) {
let handler;
let ctx = new requestContext_1.RequestContext(this.container, requestContext_1.Pipeline.EventNotification);
ctx.correlationId = evt.correlationId || guid.v4();
ctx.correlationPath = "-";
try {
ctx.user = evt.userContext || {};
handler = ctx.container.resolve(info.handler);
handler.requestContext = ctx;
handler.event = evt;
}
catch (e) {
system_1.System.log.error(ctx, e, `Unable to create handler ${info.handler.name}`);
}
try {
handler[info.methodName](evt);
}
catch (e) {
let error = (e instanceof commandRuntimeError_1.CommandRuntimeError) ? e.error.toString() : (e.message || e.toString());
system_1.System.log.error(ctx, error, `Error with event handler ${info.handler.name} event : ${evt}`);
}
}
});
}
}
CommandManager.eventHandlersFactory = new eventHandlerFactory_1.EventHandlerFactory();
exports.CommandManager = CommandManager;
//# sourceMappingURL=actions.js.map