UNPKG

nanocyte-configuration-generator

Version:
303 lines (236 loc) 9.44 kB
_ = require 'lodash' debug = require('debug')('nanocyte-configuration-generator') NodeUuid = require 'node-uuid' ChannelConfig = require './channel-config' DEFAULT_REGISTRY_URL = 'https://s3-us-west-2.amazonaws.com/nanocyte-registry/latest/registry.json' METRICS_DEVICE_ID = 'f952aacb-5156-4072-bcae-f830334376b1' VIRTUAL_NODES = 'engine-input': config: {} data: {} 'engine-output': config: {} data: {} 'engine-data': config: {} data: {} 'engine-debug': config: {} data: {} 'engine-pulse': config: {} data: {} 'router': config: {} data: {} 'engine-start': config: {} data: {} 'engine-stop': config: {} data: {} 'subscribe-devices': config: {} data: {} class ConfigurationGenerator constructor: (options, dependencies={}) -> {@registryUrl, @meshbluJSON, @metricsDeviceId} = options @registryUrl ?= DEFAULT_REGISTRY_URL @metricsDeviceId ?= METRICS_DEVICE_ID {@request, @channelConfig} = dependencies @request ?= require 'request' @channelConfig ?= new ChannelConfig accessKeyId: options.accessKeyId secretAccessKey: options.secretAccessKey configure: (options, callback=->) => {flowData, flowToken, deploymentUuid} = options debug 'configuring flow...', flowData debug 'fetching registry' @channelConfig.fetch (error) => return callback error if error? @_getNodeRegistry (error, nodeRegistry) => return callback error if error? debug 'fetched registry', nodeRegistry flowMetricNode = id: @_generateFlowMetricId() category: 'flow-metrics' flowUuid: flowData.flowId deviceId: @metricsDeviceId deploymentUuid: deploymentUuid flowData.nodes ?= [] flowData.nodes.push flowMetricNode flowNodes = _.indexBy flowData.nodes, 'id' flowConfig = _.mapValues flowNodes, (nodeConfig) => nodeConfig.nanocyte ?= {} nodeConfig.nanocyte.nonce = @_generateNonce() config: nodeConfig data: {} flowConfig = _.assign flowConfig, _.cloneDeep(VIRTUAL_NODES) instanceMap = @_generateInstances flowData.links, flowConfig, nodeRegistry _.each instanceMap, (instanceConfig, instanceId) => {config,data} = flowConfig[instanceConfig.nodeUuid] config = @_legacyConversion _.cloneDeep config # prevent accidental mutation config.templateOriginalMessage = instanceConfig.templateOriginalMessage getSetConfig = @_mutilateGetSetNodes uuid: flowData.flowId, token: flowToken, config channelApiMatch = @channelConfig.get config.type defaultConfig = {} defaultConfig.channelApiMatch = channelApiMatch if channelApiMatch? config = _.defaultsDeep defaultConfig, config, getSetConfig flowConfig[instanceId] = {config: config, data: data} links = @_buildLinks(flowData.links, instanceMap) flowConfig.router.config = links flowConfig['engine-data'].config = @_buildNodeMap instanceMap flowConfig['engine-pulse'].config = @_buildNodeMap instanceMap flowConfig['engine-debug'].config = @_buildNodeMap instanceMap flowConfig['engine-input'].config = @_buildMeshblutoNodeMap flowConfig, instanceMap flowConfig['engine-output'].config = _.extend {}, @meshbluJSON, uuid: flowData.flowId, token: flowToken flowConfig['subscribe-devices'].config = @_getSubscribeDevices flowNodes flowStopConfig = _.cloneDeep flowConfig engineStopLinks = flowConfig['router']['config']['engine-stop']?.linkedTo engineStopLinks ?= [] stopRouterConfig = _.pick flowConfig['router']['config'], 'engine-stop', 'engine-output', engineStopLinks... flowStopConfig['router']['config'] = stopRouterConfig callback null, flowConfig, flowStopConfig _buildNodeMap: (flowNodeMap) => _.mapValues flowNodeMap, (flowNode) => nodeId: flowNode.nodeUuid _buildMeshblutoNodeMap: (flowConfig, instanceMap) => inputInstances = _.where instanceMap, linkedToInput: true nodeMap = {} _.each inputInstances, (instance) => nodeConfig = flowConfig[instance.nodeUuid] nodeMap[nodeConfig.config.uuid] ?= [] alias = nodeConfig.config.alias aNodeMap = nodeId: instance.nodeUuid aNodeMap.alias = alias if alias? nodeMap[nodeConfig.config.uuid].push aNodeMap return nodeMap _generateInstances: (links, flowNodes, nodeRegistry) => instanceMap = {} _.each flowNodes, (nodeConfig, nodeUuid) => config = nodeConfig.config ? {} nanocyteConfig = config.nanocyte ? {} type = config.category type = config.type.replace('operation:', '') if type == 'operation' nodeFromRegistry = nodeRegistry[type] ? {} composedOf = nodeFromRegistry.composedOf ? {} linkedToData = _.detect composedOf, (value, key) => value.linkedToData == true transactionGroupId = @_generateTransactionGroupId() if linkedToData? _.each composedOf, (template, templateId) => instanceId = @_generateInstanceId() composedConfig = _.cloneDeep template composedConfig.nodeUuid = nodeUuid composedConfig.templateId = templateId composedConfig.debug = config.debug composedConfig.transactionGroupId = transactionGroupId if linkedToData? instanceMap[instanceId] = composedConfig return instanceMap _getNodeRegistry: (callback) => @request.get @registryUrl, json: true, (error, response, nodeRegistry) => callback error, nodeRegistry _getSubscribeDevices: (flowConfig) => devices = _.where flowConfig, category: 'device' return broadcast: _.pluck devices, 'uuid' _buildLinks: (links, instanceMap) => debug 'building links with', links result = {} _.each instanceMap, (config, instanceId) => nodeLinks = _.filter links, from: config.nodeUuid templateLinks = config.linkedTo linkedTo = [] if config.linkedToInput result[config.nodeUuid] ?= type: 'engine-input' linkedTo: [] result[config.nodeUuid].linkedTo.push instanceId if config.linkedFromStart result['engine-start'] ?= type: 'engine-start' linkedTo: [] result['engine-start'].linkedTo.push instanceId if config.linkedFromStop result['engine-stop'] ?= type: 'engine-stop' linkedTo: [] result['engine-stop'].linkedTo.push instanceId if config.linkedToNext linkUuids = _.pluck nodeLinks, 'to' _.each instanceMap, (data, key) => if _.contains linkUuids, data.nodeUuid linkedTo.push key if data.linkedToPrev _.each config.linkedTo, (templateLinkId) => _.each instanceMap, (data, key) => if data.nodeUuid == config.nodeUuid && data.templateId == templateLinkId linkedTo.push key linkedTo.push 'engine-output' if config.linkedToOutput linkedTo.push 'engine-pulse' if config.linkedToNext || config.linkedToPulse || config.linkedToOutput linkedTo.push 'engine-data' if config.linkedToData linkedTo.push 'engine-debug' if config.debug result[instanceId] = type: config.type linkedTo: linkedTo linkedToNext: config.linkedToNext result[instanceId].transactionGroupId = config.transactionGroupId if config.transactionGroupId? result['engine-output'] = type: 'engine-output' linkedTo: [] result['engine-debug'] = type: 'engine-debug' linkedTo: [] result['engine-pulse'] = type: 'engine-pulse' linkedTo: [] result['engine-data'] = type: 'engine-data' linkedTo: [] debug 'router config is', result return result _legacyConversion: (config) => if config.type == 'operation:debounce' config.timeout = config.interval delete config.interval if config.type == 'operation:throttle' config.repeat = config.interval delete config.interval if config.type == 'operation:delay' config.fireOnce = true config.noUnsubscribe = true return config _mutilateGetSetNodes: (options, template) => return {} unless template.type == 'operation:get-key' || template.type == 'operation:set-key' {uuid, token} = options bearerToken = new Buffer("#{uuid}:#{token}").toString('base64') {host,protocol,port} = @meshbluJSON host ?= 'meshblu.octoblu.com:443' if host == 'meshblu-messages.octoblu.com:443' host = 'meshblu.octoblu.com:443' port ?= 443 protocol ?= 'http' protocol = 'https' if parseInt(port) == 443 config = bodyEncoding: 'json' url: "#{protocol}://#{host}/v2/devices/#{uuid}" method: 'GET' headerKeys: [ 'Content-Type' 'Authorization' ] headerValues: [ 'application/json' "Bearer #{bearerToken}" ] if template.type == 'operation:set-key' config.method = 'PATCH' config.bodyKeys = [ 'data.{{msg.key}}' ] config.bodyValues = [ '{{msg.value}}' ] return config _generateFlowMetricId: => NodeUuid.v4() _generateInstanceId: => NodeUuid.v4() _generateNonce: => NodeUuid.v4() _generateTransactionGroupId: => NodeUuid.v4() module.exports = ConfigurationGenerator