Overview
Introduction
WES (workflow engine service) is a workflow engine that can be expose some useful api endpoints for clients and servers and reduced coding for your application. More features of WES :
- Easy to use and easy to learn
- common api endpoints for clients
- microservice architecture
- Fast and powerful
- written on node.js, typescript
- fields validation
- provide interfaces in python3
- using mongodb and docker
- cli support
Contributing
We welcome contributions of all kinds.
You can contribute to this book by opening an issue or forking and sending a pull request to the main Workflow-engine-service repository. Knowing what people use this book for the most helps direct our attention to making those sections the best that they can be. We also want the reference to be as normative as possible, so if you see anything that is wrong, please also file an issue.
Installation
developer mode
you must install node.js 12 or later. also you need to run an instance of mongo. for run mongo db, you can use docker:
sudo docker run -p 27017:27017 --name mongo -d mongo
you can access monogo cli with
docker exec -it mongo mongosh --quiet
then you need to install npm dependencies of project by npm i
on project root directory.
finally you can run dev server with npm run dev
and you can see on http://localhost:8082
production mode :
first go to cli/
directory and then run command dat p i
and Done!
for install
dat
command, see Command Line Tool
you can create a
configs.prod.json
file and set your custom configs
Concepts
In this section you will learn how to:
- deploy workflow schema
- create processes
- execute actions
Workflow
a workflow just a json schema that once deployed on workflow engine and NOT CHANGED forever until reset mongo db.
so you can just deploy your workflow once. if you can upgrade your workflow schema, you can deploy workflow with different version
sample workflow schema:
{
"workflow_name": "register_user",
"version": 1,
"create_access_roles": [
"_all_"
],
"auto_delete_after_end": true,
"start_state": "enter_info",
"end_state": "finish",
"fields": [
{
"name": "firstname"
},
{
"name": "lastname",
"type": "string",
"meta": {
"title": "نام خانوادگی",
"help": "نام خود را وارد کنید",
"class": "col-sm col"
}
},
{
"name": "phone",
"type": "number"
},
{
"name": "email",
"validation": [
{
"builtin_check": "email",
"error": "email not valid"
}
]
},
{
"name": "avatar",
"type": "file",
"validation": [
{
"builtin_check": "file_type",
"builtin_params": {
"types": [
"image/png",
"image/jpg"
]
},
"error": "avatar image not valid"
},
{
"builtin_check": "file_size",
"builtin_params": {
"max": 10000
},
"error": "avatar image size not valid"
}
]
}
],
"states": [
{
"name": "enter_info",
"meta": {
"title": "ورود اطلاعات"
},
"access_roles": [
"_all_"
],
"actions": [
{
"access_roles": [
"manager"
],
"required_fields": [
"firstname",
"lastname",
"phone"
],
"optional_fields": [
"email"
],
"name": "approve",
"type": "hook_url",
"url": "http://127.0.0.1/django/api/v1/register",
"method": "post",
"message_required": "false",
"meta": {
"color": "green",
"title": "تایید اولیه"
}
},
{
"name": "reject",
"type": "redis",
"channel": "register",
"message_required": "true",
"response_channel": "resp_register",
"set_fields": {
"is_done": false
},
"meta": {
"color": "red",
"isCircleButton": true
}
},
{
"name": "edit",
"type": "local",
"next_state": "finish"
}
]
},
{
"name": "finish"
}
]
}
Schema details (20220727.2 edition)
main schema
name | type | required | Description |
---|---|---|---|
workflow_name | string | YES | name of workflow |
version | number | NO | default is 1 |
create_access_roles | string[] | NO | user roles that access to create process from this workflow (default: ['_all_'] ) |
read_access_roles | string[] | NO | user roles that access to read process from this workflow (default: ['_all_'] ) |
process_init_check | WorkflowProcessOnInit | NO | check when a process wants to start |
auto_delete_after_end | boolean | NO | delete process after enter to end state |
start_state | string | YES | start state of process |
end_state | string | YES | end state of process |
fields | WorkflowField | NO | workflow fields |
states | WorkflowState | YES | workflow states |
WorkflowProcessOnInit schema
name | type | required | Description |
---|---|---|---|
type | string | YES | can be local or api |
local_check | string | NO | check in local mode by workflow engine. options: just_one_user_running_process: every user can only create one running process |
api_url | string | NO | api url can only response boolean or a error string like 'you can not create new process' |
WorkflowField schema
name | type | required | Description |
---|---|---|---|
name | string | YES | field name |
type | string | NO | 'string' or 'number' or 'boolean' or 'file' (default: string) |
meta | object | NO | any data useful for client |
validation | WorkflowFieldValidation[] | NO | you can set more validations on field |
WorkflowState schema
name | type | required | Description |
---|---|---|---|
name | string | YES | state name |
access_role | string[] | NO | roles to view this state (default: ['_all_'] ) |
meta | object | NO | any data useful for client |
actions | WorkflowStateAction[] | NO | actions of state |
events | WorkflowStateEvent[] | NO | you can define events on state |
WorkflowStateAction schema
for more information about state actions, continue from here
name | type | required | Description |
---|---|---|---|
name | string | YES | action name |
access_role | string[] | NO | roles to execute this action (default: ['_all_'] ) |
required_fields | string[] | NO | fields must get from client |
optional_fields | string[] | NO | fields that client can send |
send_fields | string[] | NO | fields that must be send to app server 1 |
type | string | NO | 'hook_url' or 'redis' or 'local' |
alias_name | string | NO | name of an alias 2 |
meta | object | NO | any data useful for client |
message_required | boolean | NO | client must send a message or not |
set_fields | object | NO | fields that can set hardcoded |
url | string | NO | can be a url like http://sample.com/hook . used for 'hook_url' type |
method | string | NO | can be a request method like 'get' or 'post'. used for 'hook_url' type |
headers | string[] | NO | headers that can be set on hook request. used for 'hook_url' type |
channel | string | NO | publish channel name. used for 'redis' type |
response_channel | string | NO | subscribe channel name. used for 'redis' type |
redis_instance | string | NO | a redis instance name. used for 'redis' type. (default is first instance defined on configs) |
next_state | string | NO | next state to be go. used for 'local' type |
2 added in 20220727.1
1 added in 20220727.2
Process
A process is an instance of a deployed workflow. you can create new process like this:
curl -X 'POST' \
'http://localhost:8082/api/v1/workflow/create' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"name": "sample_workflow",
"version": 1
}'
if you do not specify workflow version, in default create process from with latest version of that workflow
Schema details (20220727.2 edition)
main schema
name | type | required | Description |
---|---|---|---|
_id | string | YES | id of process |
workflow_name | string | NO | workflow name |
workflow_version | number | NO | default is 1 |
current_state | string | YES | current state of process |
field_values | WorkflowProcessField[] | NO | process fields that filled |
history | WorkflowProcessHistoryModel[] | YES | all history of process change states |
workflow | DeployedWorkflowModel | YES | workflow reference |
created_at | number | YES | create process at date (timestamp) |
created_by | number | YES | who create process |
updated_at | number | YES | update process at date (timestamp) |
WorkflowProcessField schema
name | type | required | Description |
---|---|---|---|
name | string | YES | name of field |
value | any | YES | value of field |
meta | object | NO | meta data of field |
Access Roles
every user that call one of endpoint api, must be authorized. and every user can have some roles. (just a label)
for example, a user can be manager
and normal_user
and supporter
and these are just labels that save for every user.
but if you want all users access to read a process or execute an action, how?
we have a reserved access role for this. this is _all_
. in default of all access roles on every where is _all_
.
State Action
every state of workflow, has some actions or not! you can see details of state action schema on here
every action can be one of these types for execute:
you can use,
alias
forhook_url
,redis
types byalias_name
property.
hook_url
when your server must be determine that next state is which or you can manage action execute by your self app server.
for this you need to set a endpoint url as url
, and your accept request method like get
or post
(default is post
method)
and you can set custom headers on hook request that raised by workflow engine like a authenticate token:
"headers": {
"auth_token": "workflow_engine_token"
}
redis
when you communicate with workflow engine by redis, you can use this way.
you must to set a channel name that subscribe on it in your app server. and set a response channel name that publish on it your response (like next state) in your app server.
in default, workflow service use first redis instance that defined in configs, but you can use specify redis instance by redis_instance
parameter.
channel
andresponse_channel
not be same!
local
if your action, can be execute locally, you can do this. you can just set your next_state
name.
for example for reject actions, you can do this way.
State Action Response
when you use hook_url
or redis
types for state action, your server receive an object that contains:
name | type | Description |
---|---|---|
state_name | string | state name |
state_action_name | string | state action name |
workflow_name | string | workflow name |
workflow_version | number | workflow version |
process_id | number | current process id |
user_id | number | user that call this action |
message | string | message that user send on call this action |
required_fields | WorkflowProcessField[] | all required fields that user send them |
optional_fields | WorkflowProcessField[] | all optional fields that user send them |
send_fields | WorkflowProcessField[] | all fields that before set |
in
hook_url
type, if no response or error response receive, then failed action.
when you set state action as hook_url
or redis
, you must response to complete action:
string
response
you can send just next state as string
object
response
name | type | required | Description |
---|---|---|---|
state_name | string | YES | the state that go on, if null, then failed state |
response_message | string | NO | message of responsible server |
fields | object | NO | update some fields of process |
after a timeout (defined in server configs), if no receive any response, failed action.
Command Line Tool
we use dat-tool tool for integrating a cli for project.
Install & Use
- install node 12 or higher version
- install DAT by
npm install -g dat-tool
- go to
cli
folder - type
npm install
to install node types - then, type
dat m
, if you want to develop script - And for use cli script, type
dat play
or justdat p
the new command
just support
python3
language for now
for generate new workflow interfaces.
the new
command is used like this
dat p n -n sample_workflow
most used parameters:
name (n)
workflow name that need to create
language (l)
language to generate interface files
programming languages to support:
- python3 (default)
version (v)
version of workflow (default : 1)
output (o)
directory path for generate interface files
overwrite (ow)
force to overwrite workflow interfaces, if exist
the install command
install workflow engine as dockerize.
the install
command is used like this
dat p i
most used parameters:
skip-remove-docker-cache (s1)
skip to remove docker unused images, containers
skip-build-image (s2)
skip to build workflow image
Configure
for customize default configure, if you in development mode, must edit configs.dev.json
and if in production mode, you need to edit configs.prod.json
file in project root
configs.prod.json
file not in git tracking and you should create it for self.
configs json file includes some namespaces that some is required to set (for development mode) and some is optional.
after update
configs.prod.json
file (in production mode), you should to reset workflow engine by commanddat p i
(install command)
config namespaces
- redis (optional)
- mongo (required)
- server (required)
- admin_users (required)
- auth_user (required)
- docker (optional) (just for production)
- alias (optional)
redis
config namespace
you can define some redis server connections used for redis type of state action
if you not use any redis server, redis server in docker compose not be loaded!
sample code:
"redis": {
"redis1": {
"host": "127.0.0.1",
"port": 6379
}
},
for define redis connection, you declare a name for it (like redis1) and define redis properties on it.
properties
name | type | required | Description |
---|---|---|---|
host | string | Yes | host name of redis server |
port | number | YES | - |
mongo
config namespace
you must define main database connection for workflow engine
sample code:
"mongo": {
"host": "127.0.0.1",
"port": 27017,
"name": "workflow_db",
"timezone": "Asia/Tehran"
},
properties
name | type | required | Description |
---|---|---|---|
host | string | Yes | host name of mongo server |
port | number | YES | - |
name | string | YES | database name |
username | string | NO | - |
password | string | NO | - |
server
config namespace
you must define server configs like port or disable document services
sample code:
"server": {
"port": 8082,
"host": "0.0.0.0",
"logs_path": "./logs",
"uploads_path": "./uploads",
"debug_mode": false,
"swagger_disabled": true,
"wiki_disabled": true
},
properties
name | type | required | Description |
---|---|---|---|
port | number | YES | - |
host | string | NO | host name of main server (default: localhost ) |
logs_path | string | NO | local logs folder path for store app logs (default: ./logs ) |
uploads_path | string | NO | uploads folder path for store users upload files (default: ./uploads ) |
debug_mode | boolean | NO | activate debug logs |
wiki_base_url | string | NO | wiki service base url (default: /wiki ) |
swagger_base_url | string | NO | swagger service base url (default: /api-docs ) |
swagger_disabled | boolean | NO | is swagger service disabled? |
worker_timeout | number | NO | time for wait a worker to get response (default: 30s) |
max_worker_running | number | NO | max workers to async running (default: 10) |
admin_users
config namespace
you must at least one admin user to connect workflow service. set admin users on workflow engine as hard coded
you can set custom role for your admin users
sample code:
"admin_users": [
{
"username": "admin",
"secretkey": "admin",
"roles": [
"admin"
]
}
],
properties
name | type | required | Description |
---|---|---|---|
username | string | YES | - |
secretkey | string | YES | recommend that admin secret key be very hard |
roles | string[] | NO | you can set custom roles for your admin user (default: [_admin_] ) |
auth_user
config namespace
you must define set authenticate configs
sample code:
"auth_user": {
"type": "directly",
"lifetime": 86400,
"param_name": "access_token"
},
properties
name | type | required | Description |
---|---|---|---|
type | string | YES | can be 'api_based' or 'directly' or 'dual' |
lifetime | number | YES | in seconds |
url | string | NO | for api_based type |
method | string | NO | for api_based type (can be get or post or put) |
param_name | string | NO | for api_based type (default: access_token) |
header_name | string | NO | for authenticate user request (default: authentication) |
docker
config namespace
you can define docker configs
this namespace used just for production mode
sample code:
"docker": {
"mongo_image": "mongo",
"redis_image": "redis"
}
properties
name | type | required | Description |
---|---|---|---|
mongo_image | string | NO | docker image used for mongo service (you can use it with tag) |
redis_image | string | NO | docker image used for redis service |
alias
config namespace
you can define some alias in different types like hook_url
. and then use it on every where that accept this type with alias_name
property.
sample code:
"alias": {
"app_hook": {
"type": "hook_url",
"url": "http://app.com/hook",
"headers": {
"auth_token": "436gbg45b4by54c45c4"
}
},
"app_redis": {
"type": "redis",
"channel": "action",
"response_channel": "res_action"
}
},
properties
name | type | required | Description |
---|---|---|---|
type | string | Yes | type of alias |
types
every type of alias has different properties
hook_url
name | type | required | Description |
---|---|---|---|
url | string | YES | can be a url like http://sample.com/hook . used for 'hook_url' type |
method | string | NO | can be a request method like 'get' or 'post'. used for 'hook_url' type |
headers | string[] | NO | headers that can be set on hook request. used for 'hook_url' type |
redis
name | type | required | Description |
---|---|---|---|
channel | string | YES | publish channel name. used for 'redis' type |
response_channel | string | YES | subscribe channel name. used for 'redis' type |
redis_instance | string | NO | a redis instance name. used for 'redis' type. (default is first instance defined on configs) |