UNPKG

@wmfs/tymly

Version:

A framework for building and sharing workflows in Node.js

185 lines (143 loc) 4.74 kB
const findComponent = require('../find-component') const applyToTarget = require('../apply-to-target') // First pass through the references. None of the services // have yet been booted, but we can resolve static references function firstPass (loadedComponents, messages) { messages.heading('Reference resolution - first pass') const components = loadedComponents.blueprintComponents const tymlyRefs = loadedComponents.blueprintRefs const resolutions = findFirstPassResolutions(tymlyRefs, components) if (!resolutions) { messages.subHeading('Nothing to resolve') return } resolveResolutions(resolutions, components, messages) } // firstPass function findFirstPassResolutions (tymlyRefs, components) { const firstPass = [] const roots = new Set() for (const [type, referees] of Object.entries(tymlyRefs)) { for (const [targetName, references] of Object.entries(referees)) { const target = `${type}:${targetName}` const firstPassRefs = references .filter(ref => canResolve(ref.ref, components)) firstPassRefs.forEach(r => firstPass.push({ target, path: r.path, ref: r.ref }) ) firstPassRefs.forEach(r => { r.when = 'firstPass' }) roots.add(target) } // refs } // for ... checkRootsForLoops(roots, firstPass) return firstPass.length ? firstPass : null } // firstPassReferences function canResolve (reference, components) { const refParts = findParts(reference) return components[refParts[0]] && ((refParts.length === 2) || (refParts.length === 3)) } // canResolve function findParts (reference) { const parts = reference.split(':') if ( (parts.length >= 3) && (parts[2].startsWith('$.')) ) { // JSON path selector on the ref return [ parts[0], parts[1], parts.slice(2).join('') ] } return parts } function checkRootsForLoops (roots, firstPassRefs) { for (const root of roots) { checkForLoop(root, firstPassRefs, [root]) } } // checkForLoops function checkForLoop (target, firstPassRefs, step) { const deps = findDependencies(target, firstPassRefs) const root = step[0] if (deps.has(root)) { throw new Error(`Circular dependency in ${root}: ${step.join('->')}`) } for (const dep of deps) { checkForLoop(dep, firstPassRefs, [...step, dep]) } } // checkForLoop function findDependencies (target, resolutions) { const deps = resolutions.filter( resolution => resolution.target === target ).map( resolution => resolution.ref ) return new Set(deps) } // findTransitive function resolveResolutions (toResolve, components, messages) { messages.subHeading('Resolving tymlyRefs') for (const { target, path, ref } of toResolve) { const [targetName, targetObj] = findComponent(target, components) const refObj = findRefObject(ref, components, targetName) if (!refObj) { throw new Error(`Could not resolve ${ref} in ${target}`) } applyToTarget(targetObj, refObj, path) messages.info(`Resolved ${ref} in ${targetName}`) } } // resolveResolutions function findRefObject (ref, components) { const [typeAndName, jsonSelector] = typeNamePath(ref) const [, refObj] = findComponent( typeAndName, components, jsonSelector ) if (!refObj) { return findRefObjectsByWildcard(typeAndName, components) } return refObj } // findRefObject function typeNamePath (ref) { const typeDelim = ref.indexOf(':') const nameDelim = ref.indexOf(':', typeDelim + 1) if (nameDelim === -1) { return [ref, null] } return [ ref.substring(0, nameDelim), ref.substring(nameDelim + 1) ] } // typeNamePath function findRefObjectsByWildcard (ref, components) { if (isNamespaceWildcard(ref)) { return findRefObjectsByWildcardNamespace(ref, components) } if (isNameWildcard(ref)) { return findRefObjectsByWildcardName(ref, components) } return null } // findRefObjectsByWildcard function isNamespaceWildcard (ref) { return ref.endsWith(':*') } // isNamespaceWildcard function findRefObjectsByWildcardNamespace (ref, components) { const [type] = ref.split(':') const objects = components[type] return objects ? Object.values(objects) : null } // findRefObjectsByWildcardNamespace function isNameWildcard (ref) { return ref.endsWith('_*') } // isNameWildcard function findRefObjectsByWildcardName (ref, components) { const [type, qualifiedName] = ref.split(':') const [namespace] = qualifiedName.split('_') const objects = components[type] return objects ? Object.values(objects).filter(o => o.namespace === namespace) : null } // findRefObjectsByWildcardNamespace module.exports = firstPass