danielgerlag / workflow-es

Workflow / durable task library for Node.js (or modern browsers)
MIT License
202 stars 49 forks source link

Workflow introspection #19

Open oguimbal opened 6 years ago

oguimbal commented 6 years ago

I was wondering if this lib exposes any clean way to introspect workflow steps and workflow states.

1) Introspect workflow steps: Because I would like to create a graph of my workflow that will be displayed on our admin.

2) Introspect workflow states: Im seeking a way to list workflow instances and their states, because I would like to show which step a given instance of a workflow is on.

Thanks

danielgerlag commented 6 years ago

Hi,

The Persistence Provider implementations do allow you to retrieve and inspect a single workflow. It would not be hard to add some additional methods to this class to enabled this across multiple workflows. This has been done on Workflow Core which is the sister project (.NET version) of this project.

You can use WorkflowConfig.getContainer to access to IoC container to access the implementation of this.

Duwab commented 5 years ago

Hi!

To help those asking for the same question, here is an example of a simple logProgress function.

import { configureWorkflow, IWorkflowHost, WorkflowConfig, IWorkflowRegistry, TYPES, IPersistenceProvider, WorkflowStatus } from 'workflow-es';
// ...

export class HostModule {
    private workflowConfig: WorkflowConfig;

    constructor() {
        this.workflowConfig = configureWorkflow();
        this.workflowConfig.registerWorkflow(MyWorkflow1);
        this.workflowConfig.registerWorkflow(MyWorkflow2);
    }

    async logProgress(workflowId: string) {
        const container = await this.workflowConfig.getContainer();
        const persistence = container.get<IPersistenceProvider>(TYPES.IPersistenceProvider);
        const registry = container.get<IWorkflowRegistry>(TYPES.IWorkflowRegistry);

        const wf = await persistence.getWorkflowInstance(workflowId);
        const wfDefinition = await registry.getDefinition(wf.workflowDefinitionId, wf.version);
        console.log("\n\n\n------- wf introspection", `status=${this.getWorkflowStatusKey(wf.status)}`);
        console.log(wfDefinition.steps.map(step => `step(${step.id}): ${step.body.name}`));
        console.log(`execution pointers steps (${wf.executionPointers.length}) :`);
        console.log(wf.executionPointers.map((p, i) => `pointer n°${i} => step(${p.stepId})${p.active ? ' **active**' : ''}`));
    }

    private getWorkflowStatusKey(value: number): string {
        const statuses: { [key: string]: number } = {...WorkflowStatus};
        for (let key in statuses) {
            if (statuses[key] === value) {
                return key
            }
        }

        return "unknown";
    }
}

This will log something like this :

// HostModule.logProgress
------- wf introspection status=Runnable
[
  'step(0): ConfigurationTask',
  'step(1): Foreach',
  'step(2): AllocateResourcesTask',
  'step(3): Foreach',
  'step(4): SetupVaultsTask',
  'step(5): Foreach',
  'step(6): ServiceCheckTask',
  'step(7): ServiceDeployTask',
  'step(8): ServiceRestartTask',
  'step(9): NginxReloadTask',
  'step(10): Foreach',
  'step(11): ServiceCleanupTask'
]
execution pointers steps (16) :
[
  'pointer n°0 => step(0)',
  'pointer n°1 => step(1)',
  'pointer n°2 => step(2)',
  'pointer n°3 => step(2)',
  'pointer n°4 => step(2)',
  'pointer n°5 => step(3)',
  'pointer n°6 => step(4)',
  'pointer n°7 => step(4)',
  'pointer n°8 => step(4)',
  'pointer n°9 => step(5) **active**',
  'pointer n°10 => step(6)',
  'pointer n°11 => step(6)',
  'pointer n°12 => step(6)',
  'pointer n°13 => step(7) **active**',
  'pointer n°14 => step(7) **active**',
  'pointer n°15 => step(7) **active**'
]

The purpose of this is to give an idea of what to expect before diving into code. I hope someone would find it useful :D