GithubHelp home page GithubHelp logo

acts's Introduction

Acts workflow engine

acts is a fast, tiny, extensiable workflow engine, which provides the abilities to execute workflow based on yml model.

The yml workflow model is not as same as the tranditional workflow flow. such as bpmn. The yml format is inspired by Github actions. The main point of this workflow is to create a top abstraction to run the workflow logic and interact with the client via act node.

Every user's action can be regarded as a abstract act. these acts can be generated by some rules. such as for or catches.

This workflow engine focus on the workflow logics itself and message distributions. the complex business logic will be completed by act via the act message.

Key Features

Fast

Uses rust to create the lib, there is no virtual machine, no db dependencies. The feature local_store uses the rocksdb to make sure the store performance.

Running benches\workflow.rs start_workflow time: [842.58 µs 876.99 µs 912.59 µs]

Tiny

The lib size is only 3.5mb (no local_store), you can use Adapter to create external store.

Extensiable

Supports for extending the plugin Supports for creating external store

Installation

The easiest way to get the latest version of acts is to install it via cargo

cargo add acts

Quickstart

  1. Start the workflow engine by engine.start.
  2. Load a yaml model to create a workflow.
  3. Deploy the model in step 2 by engine.manager().
  4. Config events by engine.emitter().
  5. Start the workflow by engine.executor().
use acts::{Engine, Vars, Workflow};

#[tokio::main]
async fn main() {
    let engine = Engine::new();
    engine.start();

    let text = include_str!("../examples/simple/model.yml");
    let mut workflow = Workflow::from_yml(text).unwrap();

    let executor = engine.executor();
    engine.manager().deploy(&workflow).expect("fail to deploy workflow");

    let mut vars = Vars::new();
    vars.insert("input".into(), 3.into());
    vars.insert("pid".to_string(), "w1".into());
    executor.start(&workflow.id, &vars);
    let emitter = engine.emitter();

    emitter.on_start(|e| {
        println!("start: {}", e.start_time);
    });

    emitter.on_message(|e| {
        println!("message: {:?}", e);
    });

    emitter.on_complete(|e| {
        println!("outputs: {:?} end_time: {}", e.outputs(), e.end_time);
    });

    emitter.on_error(|e| {
        println!("error on proc id: {} model id: {}", e.pid, e.mid);
    });
}

Examples

Please see examples

Model Usage

The model uses the yaml file to create, there are different type of node, which is constructed by [Workflow], [Branch], [Step] and [Act]. Every workflow can have more more steps, a step can have more branches and a branch can have if property to judge the condition.

The env property can be set the initialzed vars in workflow, in the step's run scripts, you can use env moudle to get(env.get) or set(env.set) the value

The run property is the script based on rhai script

name: model name
env:
  value: 0
steps:
  - name: step 1
    run: |
      print("step 1")

  - name: step 2
    branches:
      - name: branch 1
        if: ${ env.get("value") > 100 }
        run: |
            print("branch 1");

      - name: branch 2
        if: ${ env.get("value") <= 100 }
        steps:
            - name: step 3
              run: |
                print("branch 2")      

Outputs

In the [Workflow], you can set the outputs to output the env to use.

name: model name
outputs:
  output_key:
steps:
  - name: step1
    run: |
      env.set("output_key", "output value");

Actions

Add workflow actions to create custom event with client

name: model name
actions:
  - name: fn1
    id: fn1
    on: 
      - state: created
        nkind: workflow
      - state: completed
        nkind: workflow
  - name: fn2
    id: fn2
    on: 
      - state: completed
        nid: step2

  - name: fn3
    id: fn3
    on: 
      - state: completed
        nid: step3
    inputs:
      a: ${ env.get("value") }
steps:
  - name: step1
  - name: step2
  - name: step3

Steps

Use steps to add step to the workflow

name: model name
steps:
  - id: step1
    name: step 1
  - id: step2
    name: step 2

Branches

Use branches to add branch to the step

name: model name
steps:
  - id: step1
    name: step 1
    branches:
      - id: b1
        if: env.get("v") > 0
        steps: 
          - name: step a
          - name: step b
      - id: b2
        else: true
        steps:
          - name: step c
          - name: step d
  - id: step2
    name: step 2

Acts

Use acts to create act to interact with client

name: model name
outputs:
  output_key:
steps:
  - name: step1
    acts:
      - id: init
        name: my act init
        inputs:
          a: 6
        outputs:
          c:

1. for

There is a example to use for to generate acts, which can wait util calling the action to complete.

name: model name
steps:
  - name: step1
    acts:
      - for:
          by: any
          in: |
            let a = ["u1"];
            let b = ["u2"];
            a.union(b)

It will generate the user act and send message automationly according to the in collection. The by tells the workflow how to pass the act. There are several by rules.

  • by
  1. all to match all of the acts to complete

  2. any to match any of the acts to complete

  3. some(rule) to match some acts by giving rule name. If there is some rule, it can also generate a some act to ask the client to pass or not.

  4. ord or ord(rule) to generate the act one by one. If there is order rule, it can also generate a rule act to sort the collection.

  • in A collection to generate the acts.

The code act.role("test_role") uses the role rule to get the users through the role test_role

in: |
    let users = act.role("test_role");
    users

The following code uses the relate rule to find the user's owner of the department (d.owner).

users: |
    let users = act.relate("user(test_user).d.owner");
    users

2. catches

Use the catches to capture the act error and start a new act to run.

name: a example to catch act error
id: catches
steps:
  - name: prepare
    id: prepare
    acts:
      - id: init
  - name: step1
    id: step1
    acts:
      - id: act1
        catches:
          - id: catch1
            err: err1
          - id: catch2
            err: err2
          - id: catch_others
  - name: final
    id: final

acts's People

Contributors

yaojianpin avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.