UNPKG

@stoplight/moleculer

Version:

Fast & powerful microservices framework for Node.JS

293 lines (260 loc) 6.36 kB
/* * moleculer * Copyright (c) 2018 MoleculerJS (https://github.com/moleculerjs/moleculer) * MIT Licensed */ "use strict"; const _ = require("lodash"); const utils = require("../utils"); const Strategies = require("../strategies"); const EndpointList = require("./endpoint-list"); const EventEndpoint = require("./endpoint-event"); /** * Catalog for events * * @class EventCatalog */ class EventCatalog { /** * Creates an instance of EventCatalog. * * @param {Registry} registry * @param {ServiceBroker} broker * @param {any} StrategyFactory * @memberof EventCatalog */ constructor(registry, broker, StrategyFactory) { this.registry = registry; this.broker = broker; this.logger = registry.logger; this.StrategyFactory = StrategyFactory; this.events = []; this.EndpointFactory = EventEndpoint; } /** * Add a new event * * @param {Node} node * @param {ServiceItem} service * @param {any} event * @returns * @memberof EventCatalog */ add(node, service, event) { const eventName = event.name; const groupName = event.group || service.name; let list = this.get(eventName, groupName); if (!list) { const strategyFactory = event.strategy ? Strategies.resolve(event.strategy) || this.StrategyFactory : this.StrategyFactory; const strategyOptions = event.strategyOptions ? event.strategyOptions : this.registry.opts.strategyOptions; // Create a new EndpointList list = new EndpointList( this.registry, this.broker, eventName, groupName, this.EndpointFactory, strategyFactory, strategyOptions ); this.events.push(list); } list.add(node, service, event); return list; } /** * Get an event by name (and group name) * * @param {String} eventName * @param {String} groupName * @returns * @memberof EventCatalog */ get(eventName, groupName) { return this.events.find(list => list.name == eventName && list.group == groupName); } /** * Get balanced endpoint for event * * @param {String} eventName * @param {String|Array?} groups * @returns * @memberof EventCatalog */ getBalancedEndpoints(eventName, groups) { const res = []; this.events.forEach(list => { if (!utils.match(eventName, list.name)) return; if (groups == null || groups.length == 0 || groups.indexOf(list.group) != -1) { // Use built-in balancer, get the next endpoint const ep = list.next(); if (ep && ep.isAvailable) res.push([ep, list.group]); } }); return res; } /** * Get all groups for event * * @param {String} eventName * @returns Array<String> * @memberof EventCatalog */ getGroups(eventName) { return _.uniq( this.events.filter(list => utils.match(eventName, list.name)).map(item => item.group) ); } /** * Get all endpoints for event * * @param {String} eventName * @param {Array<String>?} groupNames * @returns * @memberof EventCatalog */ getAllEndpoints(eventName, groupNames) { const res = []; this.events.forEach(list => { if (!utils.match(eventName, list.name)) return; if ( groupNames == null || groupNames.length == 0 || groupNames.indexOf(list.group) !== -1 ) { list.endpoints.forEach(ep => { if (ep.isAvailable) res.push(ep); }); } }); return _.uniqBy(res, "id"); } /** * Call local service handlers * * @param {String} eventName * @param {any} payload * @param {Array<String>?} groupNames * @param {String} nodeID * @param {boolean} broadcast * @returns {Promise<any>} * * @memberof EventCatalog */ emitLocalServices(ctx) { const isBroadcast = ["broadcast", "broadcastLocal"].indexOf(ctx.eventType) !== -1; const sender = ctx.nodeID; const promises = []; this.events.forEach(list => { if (!utils.match(ctx.eventName, list.name)) return; if ( ctx.eventGroups == null || ctx.eventGroups.length == 0 || ctx.eventGroups.indexOf(list.group) !== -1 ) { if (isBroadcast) { list.endpoints.forEach(ep => { if (ep.local && ep.event.handler) { const newCtx = ctx.copy(ep); newCtx.nodeID = sender; promises.push(this.callEventHandler(newCtx)); } }); } else { const ep = list.nextLocal(); if (ep && ep.event.handler) { const newCtx = ctx.copy(ep); newCtx.nodeID = sender; promises.push(this.callEventHandler(newCtx)); } } } }); return this.broker.Promise.all(promises); } /** * Call local event handler and handles unhandled promise rejections. * * @param {Context} ctx * * @memberof EventCatalog */ callEventHandler(ctx) { return ctx.endpoint.event.handler(ctx); } /** * Remove endpoints by service * * @param {ServiceItem} service * @memberof EventCatalog */ removeByService(service) { this.events.forEach(list => { list.removeByService(service); }); } /** * Remove endpoint by name & nodeId * * @param {String} eventName * @param {String} nodeID * @memberof EventCatalog */ remove(eventName, nodeID) { this.events.forEach(list => { if (list.name == eventName) list.removeByNodeID(nodeID); }); } /** * Get a filtered list of events * * @param {Object} {onlyLocal = false, onlyAvailable = false, skipInternal = false, withEndpoints = false} * @returns {Array} * * @memberof EventCatalog */ list({ onlyLocal = false, onlyAvailable = false, skipInternal = false, withEndpoints = false }) { let res = []; this.events.forEach(list => { /* istanbul ignore next */ if (skipInternal && /^\$/.test(list.name)) return; if (onlyLocal && !list.hasLocal()) return; if (onlyAvailable && !list.hasAvailable()) return; let item = { name: list.name, group: list.group, count: list.count(), //service: list.service, hasLocal: list.hasLocal(), available: list.hasAvailable() }; if (item.count > 0) { const ep = list.endpoints[0]; if (ep) item.event = _.omit(ep.event, ["handler", "remoteHandler", "service"]); } if (withEndpoints) { if (item.count > 0) { item.endpoints = list.endpoints.map(ep => { return { nodeID: ep.node.id, state: ep.state, available: ep.node.available }; }); } } res.push(item); }); return res; } } module.exports = EventCatalog;