Skip to main content

Run from code

You can start a workflow from code and, if the top-level configuration defines an onReturn hook, receive a return value when the user exits the workflow with the Confirm data button (the check icon in the toolbar). On a regular close, the workflow resolves to undefined.

The result flows like this:

  1. The user clicks Confirm data.
  2. The onReturn handler runs and calls controller.returnResult(value).
  3. Your awaited startWorkflow(...) resolves to that value.

Basic usage​

import {startWorkflow} from '@datagrok-libraries/compute-api';

const result = await startWorkflow<MyReturnShape>('SomePackage:SomeWorkflow', '1.0', {});

nqName (first argument) is the workflow provider's package:function identifier. version is passed to the provider in its params, and selects the configuration version the provider returns. It is usually the configuration's version, or one of the versions the provider's meta.versions annotation lists. The third argument is the initial PipelineInstanceConfig; pass {} to start with the provider's defaults.

With initial configuration​

For dynamic workflows you can pre-populate the initial step list and per-step values by passing a PipelineInstanceConfig as the third argument. LSP fills in the recursive field shape.

import {startWorkflow} from '@datagrok-libraries/compute-api';
import type {PipelineInstanceConfig} from '@datagrok-libraries/compute-api';

const instanceConfig: PipelineInstanceConfig = {
id: 'some_workflow',
steps: [{
id: 'load',
initialValues: {deviceClass: 'Bioreactor Class'},
inputRestrictions: {deviceClass: 'disabled'},
}],
};

const result = await startWorkflow<MyReturnShape>(
'SomePackage:SomeWorkflow',
'1.0',
instanceConfig,
);

initialValues and inputRestrictions follow the same semantics as the script node fields of the same name. See Consistency for the meaning of restriction values. initialValues of a script step may also hold output values: a step with any output value starts as run, so links that write into it mark its inputs inconsistent instead of changing them.

A workflow entry without steps gets the defaults from its configuration: the steps of a static workflow or the initialSteps of a dynamic one, expanded all the way down. Pass steps: [] for an empty workflow. A listed steps array is built as given, for static workflows too.

On a workflow entry, initialValues fills its states, and skipOnInit: true skips its init hook:

const instanceConfig: PipelineInstanceConfig = {
id: 'some_workflow',
initialValues: {startedBy: 'batch import'},
skipOnInit: true,
steps: [{id: 'load'}],
};

Run without UI​

runWorkflow runs a workflow without opening it, for example to read its outputs in another app or to check a workflow in a test. It takes the same arguments as startWorkflow:

import {runWorkflow, saveWorkflowRun} from '@datagrok-libraries/compute-api';

const result = await runWorkflow('SomePackage:SomeWorkflow', '1.0', {
id: 'some_workflow',
steps: [{id: 'load', initialValues: {deviceClass: 'Bioreactor Class'}}],
});

The workflow is created from the initial configuration, as in the editor. If a step that is ready to run has validation errors, nothing runs. Otherwise every step that can run runs in order, as Run ready steps does. A step that waits for earlier steps is checked once they have run, and is skipped if it still has errors. Each step runs only after its validators have checked its inputs, and validator debounce doesn't delay the run.

The result holds:

  • isRun: false when nothing ran because of validation errors.
  • state: the workflow tree in the shape of the initial configuration. Each step has its initialValues, which hold the inputs and, once the step has run, the outputs. It also has its status as the tree shows it, isOutputOutdated, runError, and its validations and consistency by input. Each workflow has the validations of its workflow validators.
  • summary: one row per workflow and step, as in the summary of a tree export: errors, warnings and inconsistent inputs, and for a workflow the counts of failed and outdated steps below it.
const steps = 'steps' in result.state ? result.state.steps : [result.state];
const load = steps.find((step) => step.id === 'load');
const table = load?.initialValues.table;

Tables in the initial configuration are copied, so a step that edits a table does not change yours.

state is itself an initial configuration: passed to runWorkflow or startWorkflow, it starts the workflow with the steps that ran already run. A step with outputs in initialValues is not run again, even when you change its inputs; remove its outputs, and those of the steps after it that use them, to run them again.

The run is not saved. saveWorkflowRun saves state to history as a new run, the way Duplicate copies a workflow, and the saved run opens in the editor. A step that failed is saved as not run, without its error.

const run = await saveWorkflowRun('SomePackage:SomeWorkflow', '1.0', result.state, {title: 'Batch 7'});

Packages that don't use compute-api can call the Compute2:RunWorkflow and Compute2:SaveWorkflowRun functions with the same parameters.

note

A step input that a link writes takes the linked value: the link replaces the value given in initialValues without a warning, since the step hasn't run. Pass values only for inputs that no link writes.