MetroFlow JSON Specification

MetroFlow JSON Specification

This page define the format of the JSON file used to describe a workflow in MetroFlow. The file describes a set of nodes (steps), the edges (connections between steps), and optional subworkflows (groups of steps).

Top-level Structure

The JSON file must be a dictionnary with the following top-level keys:

KeyTypeRequiredDescription
nodesArrayRequiredList of all steps in the workflow.
edgesArrayRequiredList of connections between steps.
subworkflowsDictionnaryOptionalNamed groups of nodes for visual organisation.

Nodes

Each entry in the nodes array represents a single step in the workflow.

FieldTypeRequiredDescription
idStringRequiredUnique identifier for the node. Used to reference it in edges and subworkflows.
nameStringRequiredDisplay name of the step.
positionDictionnaryRequiredVisual position on the canvas. Must contain x and y as string numbers.
codeStringOptionalThe process code associated with this step.
conditionStringOptionalCondition under which the step is executed, e.g. params.runStep1 == true.
file_refStringOptionalPath to the source file for this step, e.g. (modules/step1.nf).

Right-clicking on a step shows its code and condition, with its file_ref.

Node ID format

Node IDs follow a hierarchical naming convention using . as a separator between the subworkflow it is conatined in and the node name:

SUBWORKFLOW.nodeName

If the node belongs to multiple subworkflows, the subworkflow names are included:

SUBWORKFLOW1.SUBWORKFLOW2.nodeName

Edges

Each entry in the edges array defines a directed connection from one node to another.

FieldTypeRequiredDescription
idStringRequiredUnique identifier for the edge, typically A -> B.
AStringRequiredID of the source node.
BStringRequiredID of the destination node.
conditionStringOptionalCondition under which the source node is executed and the path is taken, e.g. params.runStep1 == true.

Subworkflows

Subworkflows are optional and allow you to visually group nodes together. The subworkflows key is an dictionnary where each key is a subworkflow ID.

FieldTypeRequiredDescription
nodesArray of stringsRequiredList of node IDs that belong to this group.
labelStringRequiredDisplay name for the group.
codeStringOptionalThe code of the subworkflow.
conditionStringOptionalCondition under which the subworkflow is executed, e.g. params.runSubworkflow == true.
file_refStringOptionalPath to the source file of the subworkflow, e.g. (subworkflows/my_subworkflow.nf).

The nodes list contains all the nodes of the subworkflow, including those of the subworkflows nested in it. Once a subworkflow is collapsed (double-click on it), right-clicking on it shows its code, its condition and its content (the subworkflows and steps it contains), with its file_ref.

Colors

Colors are computed automatically by Metroflow, any color field present in the JSON is ignored. Edges sharing the same condition are drawn with the same color (there is no limit on the number of colors), and subworkflows get a background which gets darker the deeper they are nested. Different color palettes can be selected from the map controls.

Full Example

The following is a simple 3-step workflow: load data → process data → export results.

{
  "nodes": [
    {
      "id": "MY_SUBWORKFLOW.loadData",
      "name": "loadData",
      "position": { "x": "100", "y": "150" },
      "code": "process loadData {\n  output:\n    path \"data.csv\"\n  script:\n    \"\"\"\n    cp /source/data.csv data.csv\n    \"\"\"\n}",
      "condition": "params.loadData == true",
      "file_ref": "(modules/loadData.nf)"
    },
    {
      "id": "MY_SUBWORKFLOW.processData",
      "name": "processData",
      "position": { "x": "300", "y": "150" },
      "code": "process processData {\n  input:\n    path data\n  output:\n    path \"processed.csv\"\n  script:\n    \"\"\"\n    python process.py ${data}\n    \"\"\"\n}",
      "condition": "params.processData == true",
      "file_ref": "(modules/processData.nf)"
    },
    {
      "id": "MY_SUBWORKFLOW.exportResults",
      "name": "exportResults",
      "position": { "x": "500", "y": "150" },
      "code": "process exportResults {\n  input:\n    path processed\n  output:\n    path \"results.zip\"\n  script:\n    \"\"\"\n    zip results.zip ${processed}\n    \"\"\"\n}",
      "condition": "params.exportResults == true",
      "file_ref": "(modules/exportResults.nf)"
    }
  ],
  "edges": [
    {
      "id": "MY_SUBWORKFLOW.loadData -> MY_SUBWORKFLOW.processData",
      "A": "MY_SUBWORKFLOW.loadData",
      "B": "MY_SUBWORKFLOW.processData",
      "condition": "params.loadData == true"
    },
    {
      "id": "MY_SUBWORKFLOW.processData -> MY_SUBWORKFLOW.exportResults",
      "A": "MY_SUBWORKFLOW.processData",
      "B": "MY_SUBWORKFLOW.exportResults",
      "condition": "params.processData == true"
    }
  ],
  "subworkflows": {
    "MY_SUBWORKFLOW": {
      "nodes": [
        "MY_SUBWORKFLOW.loadData",
        "MY_SUBWORKFLOW.processData",
        "MY_SUBWORKFLOW.exportResults"
      ],
      "label": "MY_SUBWORKFLOW",
      "code": "workflow MY_SUBWORKFLOW {\n  data = loadData()\n  processed = processData(data)\n  exportResults(processed)\n}",
      "condition": "params.runPipeline == true",
      "file_ref": "(subworkflows/my_subworkflow.nf)"
    }
  }
}