All files / lib compute.ts

100% Statements 46/46
100% Branches 16/16
100% Functions 5/5
100% Lines 45/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118    1x 1x     1x   1x   1x         1x 9x 10x             1x       9x 7x 6x     7x     2x   2x                           1x 17x 17x 17x   17x 8x               9x 9x 9x   9x 8x 8x 8x   8x 8x   8x 7x 7x   7x     8x 1x       1x 1x   1x             1x   1x               1x 7x   7x   7x    
import {PluginParams, GroupByConfig} from '@grnsft/if-core/types';
 
import {debugLogger} from '../util/debug-logger';
import {mergeObjects} from '../util/helpers';
 
import {ComputeParams, Node, Params} from '../types/compute';
import {isExecute, isGroupBy} from '../types/interface';
 
import {STRINGS} from '../config/strings';
 
const {MERGING_DEFAULTS_WITH_INPUT_DATA, COMPUTING_PIPELINE_FOR_NODE} = STRINGS;
 
/**
 * Traverses all child nodes based on children grouping.
 */
const traverse = async (children: any, params: Params) => {
  for (const child in children) {
    await computeNode(children[child], params);
  }
};
 
/**
 * Appends `default` values to `inputs`.
 */
const mergeDefaults = (
  inputs: PluginParams[],
  defaults: PluginParams | undefined
) => {
  if (inputs) {
    const response = defaults
      ? inputs.map(input => mergeObjects(defaults, input))
      : inputs;
 
    return response;
  }
 
  console.debug(MERGING_DEFAULTS_WITH_INPUT_DATA);
 
  return defaults ? [defaults] : [];
};
 
/**
 * 1. If the node has it's own pipeline, defaults or config then use that,
 *    otherwise use whatever has been passed down from further up the tree.
 * 2. If it's a grouping node, then first of all computes all it's children.
 *    This is doing a depth first traversal.
 * 3. Otherwise merges the defaults into the inputs.
 * 4. Goes through the pipeline plugins, by checking if it's `execute` plugin. If so sets outputs.
 *    If is a `groupby` plugin, it will return child components rather than outputs.
 * 5. Since after `groupby`, there are new child components, then computes them.
 *    Note: `pipeline` now equals the remaining plu.gins to apply to each child
 */
const computeNode = async (node: Node, params: Params): Promise<any> => {
  const pipeline = (node.pipeline || params.pipeline) as string[];
  const config = node.config || params.config;
  const defaults = node.defaults || params.defaults;
 
  if (node.children) {
    return traverse(node.children, {
      ...params,
      pipeline,
      defaults,
      config,
    });
  }
 
  let inputStorage = structuredClone(node.inputs) as PluginParams[];
  inputStorage = mergeDefaults(inputStorage, defaults);
  const pipelineCopy = structuredClone(pipeline);
 
  while (pipelineCopy.length !== 0) {
    const pluginName = pipelineCopy.shift() as string;
    const plugin = params.pluginStorage.get(pluginName);
    const nodeConfig = config && config[pluginName];
 
    console.debug(COMPUTING_PIPELINE_FOR_NODE(pluginName));
    debugLogger.setExecutingPluginName(pluginName);
 
    if (isExecute(plugin)) {
      inputStorage = await plugin.execute(inputStorage, nodeConfig);
      debugLogger.setExecutingPluginName();
 
      node.outputs = inputStorage;
    }
 
    if (isGroupBy(plugin)) {
      node.children = await plugin.execute(
        inputStorage,
        nodeConfig as GroupByConfig
      );
      delete node.inputs;
      delete node.outputs;
 
      await traverse(node.children, {
        ...params,
        pipeline: pipelineCopy,
        defaults,
        config,
      });
 
      debugLogger.setExecutingPluginName();
 
      break;
    }
  }
};
 
/**
 * Creates copy of existing tree, then applies computing strategy.
 */
export const compute = async (tree: any, params: ComputeParams) => {
  const copyOfTree = structuredClone(tree);
 
  await computeNode(copyOfTree, params);
 
  return copyOfTree;
};