@wmfs/statebox
Version:
Orchestrate Node functions using Amazon States Language
103 lines (84 loc) • 2.8 kB
JavaScript
const _ = require('lodash')
const dottie = require('dottie')
const jp = require('jsonpath')
const findSelector = require('./find-selector')
// See https://states-language.net/spec.html#filters
// A _Path === null is specified as being distinct from an undefined Path
// InputPath
function inputPathHandler (inputPath, parameters) {
if (!parameters) {
return findSelector(inputPath)
}
return parameterHandler(parameters)
} // inputPathHandler
function parameterHandler (parameters) {
const params = _.cloneDeep(parameters)
const replacements = findReplacements(params)
return replacements.length
? makeDynamicHandler(params, replacements)
: makeFixedHandler(params)
} // parameterHandler
function findReplacements (params) {
const paths = findSelectors(params)
const replacements = paths.map(p => [p, dottie.get(params, p)])
return replacements
} // findReferences
function findSelectors (params, path = [], selectors = []) {
for (const [key, value] of Object.entries(params)) {
if (isSelector(key)) {
selectors.push([...path, key])
} else if (typeof value === 'object') {
findSelectors(value, [...path, key], selectors)
}
} // findSelectors
return selectors
} // findSelectors
function isSelector (path) {
return path.endsWith('.$')
} // isSelector
function makeDynamicHandler (params, references) {
const skeleton = skeletonizeParams(params, references)
const replacers = makeReplacers(references)
return ctx => {
const parameters = _.cloneDeep(skeleton)
for (const [path, expr] of replacers) {
const values = _.cloneDeep(jp.query(ctx, expr))
const extractedValue = extractValue(values)
dottie.set(parameters, path, extractedValue)
}
return parameters
}
} // makeDynamicHandler
function extractValue (values) {
switch (values.length) {
case 0:
// States.ParameterPathFailure.raise(`JSONPath ${expr} did not match an input node`)
return null
case 1:
return values[0]
default:
return values
}
}
function makeReplacers (references) {
return references.map(([path, expr]) => {
const lastIndex = path.length - 1
const lastPath = path[lastIndex]
const replacePath = [...path]
replacePath[lastIndex] = lastPath.substring(0, lastPath.length - 2)
return [replacePath, expr]
})
} // makeReplacers
function skeletonizeParams (params, references) {
for (const path of references.map(r => r[0])) {
const parentPath = path.slice(0, path.length - 1)
const key = path[path.length - 1]
const parent = dottie.get(params, parentPath)
delete parent[key]
}
return params
} // skeletonizeParams
function makeFixedHandler (params) {
return () => params
} // makeFixedHandler
module.exports = inputPathHandler