%PDF- %PDF-
| Direktori : /usr/lib/node_modules/ncm-ng/lib/ |
| Current File : //usr/lib/node_modules/ncm-ng/lib/flow.js |
'use strict'
exports.create = create
exports.getFlowNamed = getFlowNamed
exports.getAllFlows = getAllFlows
exports.buildSteps = buildSteps // for testing
const joi = require('joi')
const nsUtil = require('../util')
const validateSchema = require('./validate-schema')
const propertyMatcher = require('./property-matcher')
const logger = nsUtil.logger.getLogger(__filename)
// create a new Flow instance
function create (spec) {
// validations throw errors; fine as these should fail fast; eg. catastrophic
validateSchema(`flow spec ${spec.name}`, spec, specSchema)
const steps = buildSteps(spec)
validateInputOutput(spec.name, steps)
const flow = new Flow(spec, steps)
addFlowNamed(flow.name, flow)
return flow
}
// models a flow as a d3 tree and map of worker.name => d3 node
class Flow {
constructor (spec, steps) {
this.name = spec.name
this.description = spec.description
this.config = spec.config
this.inProcess = spec.inProcess
this.steps = steps
this.stepsMap = buildStepsMap(this.name, steps)
}
getStep (name) {
return this.stepsMap.get(name)
}
async run (context, input) {
logger.info('TBD!')
}
}
// models the steps of a flow, as a tree
class Step {
constructor (action) {
this._action = action
this._children = []
}
get action () { return this._action }
get children () { return this._children }
addChild (child) {
this._children.push(child)
}
}
// build the tree of steps
function buildSteps (spec) {
if (!Array.isArray(spec.steps)) throw new Error('expecting steps to be an array')
const root = new Step()
buildStepsTree(root, spec.steps)
return root
}
function buildStepsTree (parent, steps) {
if (!Array.isArray(steps)) throw new Error('invalid structure')
for (let stepAction of steps) {
if (isParallel(stepAction)) {
const pStepsList = stepAction.parallel
for (let pSteps of pStepsList) {
buildStepsTree(parent, pSteps)
}
} else {
const step = new Step(stepAction)
parent.addChild(step)
parent = step
}
}
}
// build a map of worker-name -> step
function buildStepsMap (flowName, steps, map) {
if (map == null) map = new Map()
for (let child of steps.children) {
const stepName = child.action.name
if (map.has(stepName)) {
throw new Error(`duplicate worker in flow ${flowName}: ${stepName}; use worker.cloneWithSuffix('foo') to make unique`)
}
map.set(stepName, child)
buildStepsMap(flowName, child, map)
}
return map
}
function isParallel (object) {
if (object == null) return false
if (!Array.isArray(object.parallel)) return false
return true
}
// validate input/output matches for workers in steps
function validateInputOutput (flowName, steps) {
for (let child of steps.children) {
validateWorkerParentInputOutput(flowName, child)
}
}
function validateWorkerParentInputOutput (flowName, parent) {
for (let child of parent.children) {
validateWorkerInputOutput(flowName, parent.action, child.action)
}
}
// validate data beween workers is consistent
function validateWorkerInputOutput (flowName, workerFrom, workerTo) {
const fromName = workerFrom.name
const toName = workerTo.name
const errPrefix = `flow ${flowName}, from ${fromName} to ${toName}`
if (workerFrom.outputSchema == null) {
throw new Error(`${errPrefix} not valid as worker ${fromName} has no output`)
}
const missing = propertyMatcher.match(workerTo.inputSchema, workerFrom.outputSchema)
if (missing.length > 0) {
throw new Error(`${errPrefix} is missing properties ${missing.join(', ')}`)
}
}
// maintain a map of all flows, name => flow
const AllFlows = new Map()
function addFlowNamed (name, flow) {
AllFlows.set(name, flow)
}
function getFlowNamed (name) {
return AllFlows.get(name)
}
function getAllFlows () {
return Array.from(AllFlows.values())
}
// joi schema for the flow spec
const specSchema = joi.object().keys({
name: joi.string().required(),
description: joi.string().required(),
config: joi.object().optional(),
inProcess: joi.boolean(),
steps: joi.any().required(),
nextFlows: joi.array().items().optional(),
nextFlowsInProcess: joi.array().items().optional()
})
exports.type = Flow