UNPKG

wmmihaa-microservicebus-core

Version:

node.js node for microservicebus. Please visit microservicebus.com for more information.

1,152 lines (1,036 loc) 80.3 kB
/* The MIT License (MIT) Copyright (c) 2014 microServiceBus.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* jshint node: true */ /* jshint esversion: 6 */ /* jshint strict:false */ 'use strict'; var moment = require('moment'); var async = require('async'); var reload = require('require-reload')(require); var fs = require('fs'); var path = require('path'); var guid = require('uuid'); var util = require('./utils.js'); var Applicationinsights = require("./Applicationinsights"); var MicroService = require('./services/microService'); var Com = require("./Com.js"); var PersistHelper = require("./PersistHelper"); var Orchestrator = require('./Orchestrator'); var TTLCollection = require("./TTLCollection"); function MicroServiceBusNode(settingsHelper) { var self = this; this.settingsHelper = settingsHelper; // Callbacks this.onStarted = null; this.onStopped = null; this.onSignedIn = null; this.onPingResponse = null; this.onUpdatedItineraryComplete = null; this.onLog = null; this.onAction = null; this.onCreateNode = null; this.onCreateNodeFromMacAddress = null; this.onReportLocation = null; this.onRequestHistory = null; // Handle settings var _hostPrefix = 'node'; // Used for creating new hosts var _itineraries; // all downloaded itineries for this host this._microServices = []; // all started services var _downloadedScripts = []; var _firstStart = true; var _loadingState = "none"; // node -> loading -> done -> stopped var _comSettings; var _persistHelper; var _historyCollection; var _failedHistoryCollection; var _eventHistoryCollection var _restoreTimeout; var signInResponse; var com; var exceptionsLoadingItineraries = 0; var _startWebServer = false; var port = process.env.PORT || 1337; var app;// = express(); var server; var applicationinsights = new Applicationinsights(); var _orchestrator = new Orchestrator(); var http; var express; var bodyParser; var serviceCount = 0; this.nodeVersion = null; // History persistance and ttl stuff const TTLHISTORY_TTL = 7 * 24 * 60 * 60 * 1000; // one week const TTLHISTORY_CHECKINTERVAL = 5 * 60 * 1000; // every 5 minutes const TTLHISTORY_PERSISTINTERVAL = 5 * 60 * 1000; // 5 minutes _historyCollection = new TTLCollection({ key: 'TRANSMIT_SUCCESS_HISTORY', ttl: TTLHISTORY_TTL, checkPeriod: TTLHISTORY_CHECKINTERVAL, // Interval to check for expired items persistPeriod: TTLHISTORY_PERSISTINTERVAL, // Interval (this.options.persistPeriod) for persising self._collection persistDir: path.resolve(settingsHelper.homeDirectory, "history"), persistFileName: 'TRANSMIT_SUCCESS_HISTORY.json' }); _failedHistoryCollection = new TTLCollection({ key: 'TRANSMIT_FAILED_HISTORY', ttl: TTLHISTORY_TTL, checkPeriod: TTLHISTORY_CHECKINTERVAL, // Interval to check for expired items persistPeriod: TTLHISTORY_PERSISTINTERVAL, // Interval (this.options.persistPeriod) for persising self._collection persistDir: path.resolve(settingsHelper.homeDirectory, "history"), persistFileName: 'TRANSMIT_FAILED_HISTORY.json' }); _eventHistoryCollection = new TTLCollection({ key: 'TRANSMIT_EVENTS_HISTORY', ttl: TTLHISTORY_TTL, checkPeriod: TTLHISTORY_CHECKINTERVAL, // Interval to check for expired items persistPeriod: TTLHISTORY_PERSISTINTERVAL, // Interval (this.options.persistPeriod) for persising self._collection persistDir: path.resolve(settingsHelper.homeDirectory, "history"), persistFileName: 'TRANSMIT_EVENTS_HISTORY.json' }); // Called by HUB if it wasn't able to process the request MicroServiceBusNode.prototype.ErrorMessage = function (message) { self.onLog("errorMessage => " + message); self.onStarted(0, 1); }; // Called by HUB to receive all active serices MicroServiceBusNode.prototype.GetEndpoints = function (message) { self.onLog("getEndpoints => " + message); }; // Called by HUB when itineraries has been updated MicroServiceBusNode.prototype.UpdateItinerary = function (updatedItinerary) { try { self.onLog(); self.onLog("Updating flows".green); self.onLog(); // Stop all services stopAllServices(function () { self.onLog("All services stopped".yellow); }); var itinerary = _itineraries.find(function (i) { return i.itineraryId === updatedItinerary.itineraryId; }); //var itinerary = new linq(_itineraries).First(function (i) { return i.itineraryId === updatedItinerary.itineraryId; }); for (var i = _itineraries.length; i--;) { if (_itineraries[i].itineraryId === updatedItinerary.itineraryId) { _itineraries.splice(i, 1); } } _itineraries.push(updatedItinerary); //loadItineraries(settings.organizationId, _itineraries); startAllServices(_itineraries, function () { if (self.onUpdatedItineraryComplete) self.onUpdatedItineraryComplete(); _restoreTimeout = setTimeout(function () { restorePersistedMessages(); }, 3000); }); } catch (e) { if (self.onUpdatedItineraryComplete) self.onUpdatedItineraryComplete(e); } }; // Called by HUB when itineraries has been updated MicroServiceBusNode.prototype.Stop = function (callback) { self.onLog(); self.onLog("Stopping".green); self.onLog(); // Stop all services stopAllServices(function () { self.onLog("All services stopped".yellow); callback(); }); }; // Called by HUB when itineraries has been updated MicroServiceBusNode.prototype.ChangeState = function (state) { self.onLog(); //_isWaitingForSignInResponse = false; settingsHelper.settings.state = state; if (state === "Active") { _downloadedScripts = []; self._microServices = []; self.onLog("Starting up COM and all services..."); startAllServices(_itineraries, function () { self.onLog("State:".white + state.green); self.onLog(); }); } else { self.onLog("Stopping COM and all services..."); com.Stop(function (err) { if (err) { self.onLog("Unalble to start COM"); } else { stopAllServices(function () { self.onLog("All services stopped".yellow); self.onLog("State:".white + state.yellow); self.onLog(); }); } }); } }; // New token has been received from mSB.com MicroServiceBusNode.prototype.UpdateToken = function (token) { settingsHelper.settings.sas = token; settingsHelper.save(); _eventHistoryCollection.push(false, 'Token updated'); }; // Called by the HUB when disabling/enabling flow MicroServiceBusNode.prototype.UpdateFlowState = function (itineraryId, environment, enabled) { var itinerary = _itineraries.find(function (i) { return i.itineraryId === itineraryId; }); if (!itinerary) { self.onLog("Tracking: ".white + "Itinerary not found".red); return; } if (enabled) { // Remove old itinerary let oldItinerary = _itineraries.find(function (i) { return i.itineraryId === itineraryId; }); var index = _itineraries.indexOf(oldItinerary); if (index > -1) { _itineraries.splice(index, 1); } // Add updated itinerary itinerary.enabled = true; _itineraries.push(itinerary); // Restart all services startAllServices(_itineraries, function () { }); } else { let iStatus = enabled ? "enabled".green : "disabled".yellow; self.onLog(); self.onLog("Itinerary [".grey + itinerary.integrationName + "] has been ".grey + iStatus); // Get all activities from itinerary var microServices = []; for (var i = 0; i < itinerary.activities.length; i++) { if (itinerary.activities[i].userData.config != undefined) { var host = itinerary.activities[i].userData.config.generalConfig.find(function (c) { return c.id === 'host'; }).value; if (host == settingsHelper.settings.nodeName) { microServices.push({ itinerary: itinerary, activity: itinerary.activities[i] }); } else if (settingsHelper.settings.tags !== undefined) { var tags = settingsHelper.settings.tags.find(function (tag) { return tag === host; }); if (tags !== undefined && tags.length > 0) { if (itinerary.activities[i].userData.baseType === 'onewayreceiveadapter' || itinerary.activities[i].userData.baseType === 'twowayreceiveadapter') { itinerary.activities[i].userData.config.generalConfig.find(function (c) { return c.id === 'host'; }).value = settingsHelper.settings.nodeName; } microServices.push({ itinerary: itinerary, activity: itinerary.activities[i] }); } } } } self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); self.onLog("|" + util.padRight("Inbound service", 20, ' ') + "| Status |" + util.padRight("Flow", 40, ' ') + "|"); self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); let activeActivities = itinerary.activities.find(function (a) { return a.userData.config.generalConfig.find(function (c) { return c.id === 'enabled'; }).value; }); let activeActivitiesLength = activeActivities ? activeActivities.length : 0; serviceCount = serviceCount - activeActivitiesLength; for (var i = 0; i < microServices.length; i++) { let service = self._microServices.find(function (m) { return m.Name === microServices[i].activity.userData.id; }); if (service) { let serviceStatus = "Stopped".yellow; try { if (enabled && service.Config.general.enabled) { serviceStatus = "Started".green; service.Start(); serviceCount++; } else { service.Stop(); var index = self._microServices.indexOf(service); if (index > -1) { self._microServices.splice(index, 1); } } var lineStatus = "|" + util.padRight(service.Name, 20, ' ') + "| " + serviceStatus + " |" + util.padRight(service.IntegrationName, 40, ' ') + "|"; self.onLog(lineStatus); } catch (ex) { self.onLog('Unable to stop '.red + service.Name.red); self.onLog(ex.message.red); } } } } self.onLog(); }; // Called by HUB to enable or disable tracking MicroServiceBusNode.prototype.SetTracking = function (enableTracking) { settingsHelper.settings.enableTracking = enableTracking; if (enableTracking) self.onLog("Tracking: ".white + "enabled".green); else self.onLog("Tracking: ".white + "disabled".yellow); }; // Update debug mode MicroServiceBusNode.prototype.ChangeDebug = function (debug) { self.onLog("Debug: ".white + debug); settingsHelper.settings.debug = debug; }; // Incoming message from HUB MicroServiceBusNode.prototype.SendMessage = function (message, destination) { //receiveMessage(message, destination); }; // Called by HUB when signin has been successful MicroServiceBusNode.prototype.SignInComplete = function (response) { if (response.sas != undefined) { settingsHelper.settings.sas = response.sas; settingsHelper.settings.debug = undefined; settingsHelper.settings.state = undefined; settingsHelper.settings.port = undefined; settingsHelper.settings.tags = undefined; settingsHelper.settings.offlineSettings = response; settingsHelper.settings.policies = response.policies; settingsHelper.save(); } if (settingsHelper.settings.debug != null && settingsHelper.settings.debug == true) {// jshint ignore:line self.onLog(settingsHelper.settings.nodeName.gray + ' successfully logged in'.green); } signInResponse = response; settingsHelper.settings.state = response.state; settingsHelper.settings.debug = response.debug; settingsHelper.settings.port = response.port == null ? 80 : response.port; settingsHelper.settings.tags = response.tags; settingsHelper.settings.enableTracking = response.enableTracking; settingsHelper.settings.timezone = response.timezone; settingsHelper.settings.policies = response.policies; settingsHelper.retentionPeriod = response.retentionPeriod; _comSettings = response; _persistHelper = new PersistHelper(settingsHelper); if (settingsHelper.settings.enableTracking) self.onLog("Tracking: " + "Enabled".green); else self.onLog("Tracking: " + "Disabled".grey); if (settingsHelper.settings.state == "Active") self.onLog("State: " + settingsHelper.settings.state.green); else self.onLog("State: " + settingsHelper.settings.state.yellow); _itineraries = signInResponse.itineraries; applicationinsights.init(response.instrumentationKey, settingsHelper.settings.nodeName) .then(function (resp) { if (resp) self.onLog("Application Insights:" + " Successfully initiated".green); else self.onLog("Application Insights:" + " Disabled".grey); }, function (error) { self.onLog("Application Insights:" + " Failed to initiate!".green); }); if (_firstStart) { _firstStart = false; self.onLog("IoT Provider: " + response.protocol.green); com = new Com(settingsHelper.settings.nodeName, response, settingsHelper.settings.hubUri, settingsHelper); // New state received from IoT Hob com.OnStateReceived(function (stateMessage) { receiveState(stateMessage); }); // Inbound D2C message has no destination com.OnMessageReceived(function (cloudMessage) { receiveCloudMessage(cloudMessage); }); // InboundD2D message with destination (service) defined com.OnQueueMessageReceived(function (sbMessage) { var message = sbMessage.body; var service = sbMessage.applicationProperties.value.service; receiveMessage(message, service); if (com.IsConnected()) { // com is no longer in disconnected state com.dissconnectedSince = undefined; com.receivedQueueErrorCount = 0; self.onLog("COM seems to have recovered from disconnected state".green); } }); /* * Unable to download NPM package * Unable to connect * Receiver error * Unable to get twin * Uncaught exception in start * Error changing state * Service is stopped * Unable to connect to Azure IoT Hub (send) * * Unable to aquire tracking token * Device twin not registered * Unable to send tracking message */ com.OnReceivedQueueError(function (message) { self.onLog("OnReceivedQueueError: ".red + message); }); // Unauthorized error on connecting receiver com.OnUnauthorizedError(function () { self.onLog("OnUnauthorizedError".red); self.onAction({ action: "restart" }); _eventHistoryCollection.push(false, 'Unauthorized error'); }); /* * Event has been sent to Azure IoT Hub * If com was not intentially stopped or the dissconnect event was triggered * com will continue trying to submit messages */ com.OnSubmitQueueSuccess(function (data) { // if (com.dissconnectedSince) { // com is no longer in disconnected state // com.dissconnectedSince = undefined; // com.receivedQueueErrorCount = 0; // self.onLog("COM seems to have recovered from disconnected state".green); // } _historyCollection.push(true); }); /* * Unable to send event to Azure IoT Hub */ com.OnSubmitQueueError(function (message) { self.onLog("OnSubmitQueueError: ".red + message); recoverDisconnectedComState(); _failedHistoryCollection.push(false); }); // Debug callback from services com.OnQueueDebugCallback(function (message) { if (settingsHelper.settings.debug != null && settingsHelper.settings.debug == true) {// jshint ignore:line self.onLog("COM: ".green + message); } else{ console.log("COM: ".green + message); } }); // Dissconnect event was triggered com.OnDisconnect(function (message, isFault) { if (settingsHelper.settings.debug != null && settingsHelper.settings.debug == true) {// jshint ignore:line if (isFault) { self.onLog("COM: ".yellow + "Uncontrolled disconnect.".red); } else { self.onLog("COM: ".yellow + message); } } if (isFault) { recoverDisconnectedComState(); } _eventHistoryCollection.push(false, 'Dissconnected'); }); com.OnActionCallback(function (message) { if (message.source == "core") { switch (message.action) { default: self.onLog("Unsupported action: " + message.action); break; } } else { if (self.onAction) { self.onAction(message); } } }); // Persistance stuff com.OnPersistEventCallback(function (message) { _persistHelper.persist(message, 'event', function (err) { }); }); com.OnPersistMessageCallback(function (message) { _persistHelper.persist(message, 'message', function (err) { }); }); com.OnPersistTrackingCallback(function (message) { _persistHelper.persist(message, 'tracking', function (err) { }); }); com.OnPersistHistoryCallback(function (message) { _persistHelper.persist(message, 'history', function (err) { }); }); port = process.env.PORT || 1337; } else { com.Update(response); } startAllServices(_itineraries, function () { self.onPingResponse(); _restoreTimeout = setTimeout(function () { restorePersistedMessages(); }, 3000); }); }; // Called by HUB when node has been successfully created /* istanbul ignore next */ MicroServiceBusNode.prototype.NodeCreated = function () { if (settingsHelper.settings.aws) { var awsSettings = { region: settingsHelper.settings.aws.region }; let pemPath = path.resolve(settingsHelper.certDirectory, settingsHelper.settings.nodeName + ".cert.pem"); let privateKeyPath = path.resolve(settingsHelper.certDirectory, settingsHelper.settings.nodeName + ".private.key"); let settingsPath = path.resolve(settingsHelper.certDirectory, settingsHelper.settings.nodeName + ".settings"); let caRootPath = path.resolve(settingsHelper.certDirectory, ".root-ca.crt"); fs.writeFileSync(pemPath, settingsHelper.settings.aws.certificatePem); fs.writeFileSync(privateKeyPath, settingsHelper.settings.aws.privateKey); fs.writeFileSync(settingsPath, JSON.stringify(awsSettings)); self.onLog("AWS node certificates installed"); var caUri = "https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem"; require("request")(caUri, function (err, response, certificateContent) { if (response.statusCode != 200 || err != null) { self.onLog("unable to get aws root certificate"); } else { self.onLog("AWS root certificate installed"); fs.writeFileSync(caRootPath, certificateContent); self.SignIn(); } }); } else self.SignIn(); }; // Signing in the to HUB MicroServiceBusNode.prototype.SignIn = function (newNodeName, temporaryVerificationCode, useMacAddress, recoveredSignIn) { if (useMacAddress) { require('getmac').getMac(function (err, macAddress) { if (err) { self.onLog('Unable to fetch mac address.'); } else { self.onCreateNodeFromMacAddress(macAddress); } }); } // Logging in using code else if (settingsHelper.settings.nodeName == null || settingsHelper.settings.nodeName.length == 0) { // jshint ignore:line if (temporaryVerificationCode != undefined && temporaryVerificationCode.length == 0) { // jshint ignore:line self.onLog('No hostname or temporary verification code has been provided.'); } else { this.onCreateNode( temporaryVerificationCode, _hostPrefix, newNodeName ); } } // Logging in using settings else { var hostData = { Name: settingsHelper.settings.nodeName, machineName: settingsHelper.settings.machineName, OrganizationID: settingsHelper.settings.organizationId, npmVersion: this.nodeVersion, sas: settingsHelper.settings.sas, recoveredSignIn: recoveredSignIn }; this.onSignedIn(hostData); if (settingsHelper.settings.debug != null && settingsHelper.settings.debug && !recoveredSignIn) {// jshint ignore:line self.onLog("Waiting for signin response".grey); } } }; MicroServiceBusNode.prototype.InboundServices = function () { return self._microServices; }; MicroServiceBusNode.prototype.RestorePersistedMessages = function () { restorePersistedMessages(); }; MicroServiceBusNode.prototype.ServiceCountCheck = function () { serviceCountCheck(); }; MicroServiceBusNode.prototype.SetDebug = function (debug) { self.onLog(debug ? "Debug: ".white + "enabled".green : "Debug: ".white + "disabled".yellow); settingsHelper.settings.debug = debug; }; MicroServiceBusNode.prototype.TrackException = function (msg, lastActionId, status, fault, faultDescription) { trackException(msg, lastActionId, status, fault, faultDescription); }; MicroServiceBusNode.prototype.ResendHistory = function (startdate, enddate) { restorePersistedHistoryMessages(startdate, enddate); }; MicroServiceBusNode.prototype.RequestHistory = function (startdate, enddate, connId) { _historyCollection.filterCollection(startdate, enddate, 'hour', function (err, historyCollection) { _failedHistoryCollection.filterCollection(startdate, enddate, 'hour', function (err, failedHistoryCollection) { _eventHistoryCollection.filterCollection(startdate, enddate, 'hour', function (err, eventHistoryCollection) { if (self.onRequestHistory) self.onRequestHistory({ connId: connId, history: historyCollection, failed: failedHistoryCollection, events: eventHistoryCollection }); }); }); }); }; MicroServiceBusNode.prototype.ForceStop = function (callback) { stopAllServices(function () { self.onLog("All services stopped".yellow); callback(); }); }; MicroServiceBusNode.prototype.ReportEvent = function (event) { _eventHistoryCollection.push(false, event); }; // Events MicroServiceBusNode.prototype.OnSignedIn = function (callback) { this.onSignedIn = callback; }; MicroServiceBusNode.prototype.OnStarted = function (callback) { this.onStarted = callback; }; MicroServiceBusNode.prototype.OnStopped = function (callback) { this.onStopped = callback; }; MicroServiceBusNode.prototype.OnPingResponse = function (callback) { this.onPingResponse = callback; }; MicroServiceBusNode.prototype.OnUpdatedItineraryComplete = function (callback) { this.onUpdatedItineraryComplete = callback; }; MicroServiceBusNode.prototype.OnLog = function (callback) { this.onLog = callback; }; MicroServiceBusNode.prototype.OnAction = function (callback) { this.onAction = callback; }; MicroServiceBusNode.prototype.OnCreateNode = function (callback) { this.onCreateNode = callback; }; MicroServiceBusNode.prototype.OnCreateNodeFromMacAddress = function (callback) { this.onCreateNodeFromMacAddress = callback; }; MicroServiceBusNode.prototype.OnReportLocation = function (callback) { this.onReportLocation = callback; }; MicroServiceBusNode.prototype.OnRequestHistory = function (callback) { this.onRequestHistory = callback; }; MicroServiceBusNode.prototype.PersistEvent = function (event) { _eventHistoryCollection.push(false, event); }; // Internal functions // Starting up all services function startAllServices(itineraries, callback) { stopAllServices(function () { loadItineraries(settingsHelper.settings.organizationId, itineraries, function () { callback(); }); }); } // This method is called then COM gets dissconnected or when unable to // transmit messages or events. function recoverDisconnectedComState(message) { if (!com.dissconnectedSince) { com.dissconnectedSince = new Date(); self.onLog("COM is in disconnected state!!".red); setInterval(function () { try{ if (!com.IsConnected()) { self.onLog("Trying to recover COM from disconnected state".yellow); com.Stop(function () { self.onLog("COM has been stopped".yellow); com.Start(function (err) { if (err) { self.onLog("Still trying to recover COM from disconnected state".yellow); } }); }); } else { self.onLog("COM seems to have recovered from disconnected state".green); com.dissconnectedSince = undefined; clearInterval(this); } } catch(e){ self.onLog("Error in interval for restarting COM: " + e); } }, 10 * 1000); let msg = { InterchangeId: guid.v1(), IntegrationId: "", IntegrationName: "", Environment: "", TrackingLevel: "", ItineraryId: "", CreatedBy: "", LastActivity: "", ContentType: "text/plain", Itinerary: "", MessageBuffer: null,//messageBuffer.toString('base64'), _messageBuffer: new Buffer("").toString('base64'), IsBinary: true, IsLargeMessage: false, IsCorrelation: false, IsFirstAction: true, FaultCode: "90020", FaultDescription: "COM is in disconnected state.", Variables: [], CreateTicket: true }; trackException(msg, "", "Failed", "COM is in disconnected state.", message); } } // Stopping COM and all services function stopAllServices(callback) { stopAllServicesSync(); callback(); // com.Stop(function () { // stopAllServicesSync(); // callback(); // }); } // Stopping all services function stopAllServicesSync() { if (_startWebServer) { self.onLog("Server: " + "Shutting down web server".yellow); server.close(); app = null; app = express(); } if (self._microServices.length > 0) { self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); self.onLog("|" + util.padRight("Inbound service", 20, ' ') + "| Status |" + util.padRight("Flow", 40, ' ') + "|"); self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); for (var i = 0; i < self._microServices.length; i++) { var service = self._microServices[i]; try { service.Stop(); var lineStatus = "|" + util.padRight(service.Name, 20, ' ') + "| " + "Stopped".yellow + " |" + util.padRight(service.IntegrationName, 40, ' ') + "|"; self.onLog(lineStatus); service = undefined; //delete service; } catch (ex) { self.onLog('Unable to stop '.red + service.Name.red); self.onLog(ex.message.red); } } if (server != undefined && server != null) server.close(); _startWebServer = false; _downloadedScripts = undefined; //delete _downloadedScripts; self._microServices = undefined; //delete _inboundServices; _downloadedScripts = []; self._microServices = []; serviceCount = 0; } } // Incoming cloudMessage function receiveCloudMessage(cloudMessage) { var microService = self._microServices.find(function (i) { return i.baseType === "messagereceiveadapter"; }); if (!microService) return; microService.OnCompleted(function (message, destination) { //trackMessage(message, destination, "Completed"); }); // Submit cloudMessage to service microService.Process(cloudMessage, null); } // Incomming state function receiveState(newstate) { settingsHelper.settings.deviceState = newstate; settingsHelper.save(); var serviceName = "Unknown"; try { if (newstate.desired.msbaction) { if (newstate.desired.msbaction.action) { if (!newstate.reported || !newstate.reported.msbaction || (newstate.reported.msbaction && (newstate.desired.msbaction.id !== newstate.reported.msbaction.id))) { self.onLog("MSBACTION: ".green + newstate.desired.msbaction.action.grey); com.currentState.reported = { msbaction: com.currentState.desired.msbaction }; var reportState = { reported: { msbaction: com.currentState.desired.msbaction } }; com.ChangeState(reportState, settingsHelper.settings.nodeName); // Wait a bit for the state to update... setTimeout(function () { performActions(com.currentState.desired.msbaction); }, 5000); } // return; } } var microServices = self._microServices.filter(function (i) { return i.baseType === "statereceiveadapter"; }); microServices.forEach(function (microService) { serviceName = microService.Name; var message = {}; message.IsFirstAction = true; message.ContentType = 'application/json'; message.body = newstate; let jsonMsg = JSON.stringify(newstate.desired); message.messageBuffer = new Buffer(jsonMsg); message._messageBuffer = new Buffer(jsonMsg).toString('base64'); // Track incoming message trackMessage(message, microService.Name, "Started"); // Submit state to service microService.Process(newstate, null); }); } catch (err) { self.onLog("Error at: ".red + serviceName); self.onLog("Error id: ".red + err.name); self.onLog("Error description: ".red + err.message); } } // Incoming messages from other nodes function receiveMessage(message, destination) { try { var microService = self._microServices.find(function (i) { return i.Name === destination && i.ItineraryId == message.ItineraryId; }); /* istanbul ignore if */ if (microService == null) { // isDynamicRoute means the node of the service was set to dynamic. // A dynamicly configured node setting whould mean the node was never initilized // and not part of the _inboundServices array. // Therefor it need to be initilized and started. if (message.isDynamicRoute) { // Find the activity var activity = message.Itinerary.activities.find(function (c) { return c.userData.id === destination; }); // Create a startServiceAsync request var intineratyActivity = { activity: activity, itinerary: message.Itinerary }; // Call startServiceAsync to initilized and start the service. startServiceAsync(intineratyActivity, settingsHelper.settings.organizationId, true, function () { self.onLog(""); self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); self.onLog("|" + util.padRight("Inbound service", 20, ' ') + "| Status |" + util.padRight("Flow", 40, ' ') + "|"); self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); microService = self._microServices[self._microServices.length - 1]; var lineStatus = "|" + util.padRight(microService.Name, 20, ' ') + "| " + "Started".green + " |" + util.padRight(microService.IntegrationName, 40, ' ') + "|"; self.onLog(lineStatus); self.onLog(); // Set the isDynamicRoute to false and call this method again. microService.Start(); serviceCount++; message.isDynamicRoute = false; receiveMessage(message, destination); }); return; } else { var logm = "The service receiving this message is no longer configured to run on this node. This can happen when a service has been shut down and restarted on a different machine"; trackException(message, destination, "Failed", "90101", logm); self.onLog(logm); self.onLog("Error: ".red + logm); return; } } message.IsFirstAction = false; microService.OnCompleted(function (integrationMessage, destination) { trackMessage(integrationMessage, destination, "Completed"); }); // Track incoming message trackMessage(message, destination, "Started"); var buf = new Buffer(message._messageBuffer, 'base64'); // Encrypted? if (message.Encrypted) { buf = util.decrypt(buf); } // CONSIDER CHANGE // const decoder = new StringEncoder('utf8'); // var messageString = decoder.write(buf); var messageString = buf.toString('utf8'); // Submit message to service if (message.ContentType != 'application/json') { microService.Process(messageString, message); } else { var obj = JSON.parse(messageString); microService.Process(obj, message); } } catch (err) { self.onLog("Error at: ".red + destination); self.onLog("Error id: ".red + err.name); self.onLog("Error description: ".red + err.message); trackException(message, destination, "Failed", err.name, err.message); } } // Restore persisted messages from ./persist folder function restorePersistedMessages() { try { if (!com) { return; } if (!com.IsConnected()) { return; } _persistHelper.forEach(function (key, data) { if (key.startsWith('_h_')) { // history var dt = new Date(key.replace('_h_', '')); } else if (key.startsWith('_e_')) { // events let variables = [{ Variable: "resent", Value: new Date().toISOString() }]; com.SubmitEvent(JSON.parse(data.message), data.service, variables); _persistHelper.remove(key); self.onLog("Transmitting persisted message".yellow); } else if (key.startsWith('_m_')) { // messages com.Submit(JSON.parse(data.message), data.node, data.service); _persistHelper.remove(key); } else if (key.startsWith('_t_')) { // tracking com.Track(data); _persistHelper.remove(key); } }); } catch (ex) { self.onLog("Unable to restore persisted messages".red); self.onLog(ex.red); } } // Restore persisted messages from ./persist folder function restorePersistedHistoryMessages(startdate, enddate) { try { if (!com) { return; } if (!com.IsConnected()) { return; } _persistHelper.forEach(function (key, data) { if (key.startsWith('_h_')) { // history let dt = new Date(key.replace('_h_', '')); if (dt >= startdate && dt <= enddate) { self.onLog("Re-submitting " + dt); let variables = [{ Variable: "resent", Value: new Date().toISOString() }]; com.SubmitEvent(data, null, variables); //_historyCollection.push(true); } } }); } catch (ex) { self.onLog("Unable to restore persisted messages".red); self.onLog(ex.red); } } // Make sure all services are active function serviceCountCheck() { if (serviceCount != self._microServices.length && settingsHelper.settings.state === "Active") { self.onLog("A service has dropped, restarting services (" + serviceCount + "/" + self._microServices.length + ")"); self.onAction({ action: "restart" }); } } // Handle incomming maintinance actions function performActions(msbAction) { switch (msbAction.action) { case 'stop': self.onLog("State changed to " + "Inactive".yellow); settingsHelper.settings.state = "InActive"; stopAllServicesSync(function () { self.onLog("All services stopped".yellow); }); break; case 'start': self.onLog("State changed to " + "Active".green); settingsHelper.settings.state = "Active"; _downloadedScripts = []; self._microServices = []; serviceCount = 0; startAllServices(_itineraries, function () { }); break; case 'restart': break; case 'reboot': break; case 'script': break; default: } } // Called after successfull signin. // Iterates through all itineries and download the scripts, afterwhich the services is started function loadItineraries(organizationId, itineraries, callback) { // Prevent double loading if (_loadingState == "loading") { return; } if (itineraries.length == 0) self.onStarted(0, 0); async.map(itineraries, function (itinerary, callback) { // encapsulate each activity to work in async var intineratyActivities = []; for (var i = 0; i < itinerary.activities.length; i++) { if (itinerary.activities[i].userData.config != undefined) { let hosts = itinerary.activities[i].userData.config.generalConfig.find(function (c) { return c.id === 'host'; }).value.split(','); for (let h = 0; h < hosts.length; h++) { let host = hosts[h]; if (host == settingsHelper.settings.nodeName) { intineratyActivities.push({ itinerary: itinerary, activity: itinerary.activities[i] }); break; } else if (settingsHelper.settings.tags !== undefined) { var tags = settingsHelper.settings.tags.find(function (tag) { return tag === host; }); if (tags !== undefined && tags.length > 0) { if (itinerary.activities[i].userData.baseType === 'onewayreceiveadapter' || itinerary.activities[i].userData.baseType === 'twowayreceiveadapter' || itinerary.activities[i].userData.baseType === "statereceiveadapter") { itinerary.activities[i].userData.config.generalConfig.find(function (c) { return c.id === 'host'; }).value = settingsHelper.settings.nodeName; } intineratyActivities.push({ itinerary: itinerary, activity: itinerary.activities[i] }); break; } } } } } async.map(intineratyActivities, function (intineratyActivity, callback) { if (itinerary.enabled === false) { callback(null, null); } else { startServiceAsync(intineratyActivity, organizationId, false, function () { callback(null, null); }); } }, function (err, results) { callback(null, null); }); }, function (err, results) { // Start com to receive messages self.onLog(""); self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); self.onLog("|" + util.padRight("Inbound service", 20, ' ') + "| Status |" + util.padRight("Flow", 40, ' ') + "|"); self.onLog("|" + util.padLeft("", 20, '-') + "|-----------|" + util.padLeft("", 40, '-') + "|"); if (settingsHelper.settings.state === "Active") { com.Start(function () { com.dissconnectedSince = undefined; com.receivedQueueErrorCount = 0; for (var i = 0; i < self._microServices.length; i++) { var newMicroService = self._microServices[i]; var serviceStatus = "Started".green; if (settingsHelper.settings.state == "Active" && (newMicroService.Itinerary.enabled || newMicroService.Itinerary.enabled === undefined)) { newMicroService.Start(); serviceCount++; } else serviceStatus = "Stopped".y