Skip to content

Framework for running asynchronous automation tasks (workties) implemented as finite state machines combined in workflows. Read more information on project's Wiki pages.

License

alxlv/workty

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

What is Workty?

The first thoughts about the platform came to me when working with Raspberry PI, when I was researching the single-board computer (sbc) market it became clear to me that now it's possible to build cheap and powerful distributed networks based on them. During the implementation of the first release of the project the sbc market has grown significantly. Now Raspberry PI has quite a number of competitors of different prices, quality and capacity. Raspberry PI already provides ARM Cortex-A53 x64 with 4 cores and Moore's law along with the principle of parallelism allow looking to the future with even more optimism. The next reason was the experience with the Workflow Foundation from Microsoft, but in the process of working with the products of this company (no matter how awesome they are), you always come across the limitations of the .NET world and it's hard to get rid of the feeling of isolation in it. Unnecessarily because utilities like nodejs/mongodb become mature, then the choice of tools for implementation of cross-platform was obvious. And the last but not least reason was the presence of a wonderful library machina-js, without it everything would be much more complicated.

Workty is the platform for running asynchronous automation tasks (workties) implemented as finite state machines combined in workflows. It's implemented on NodeJS framework and can be used on any CPU architecture which supports it. The platform supports REST API based on Restify framework. All interactions between end-users/application and application/single board computers go over HTTPS/Secure WebSockets protocols.

Read more information on project's Wiki pages.

Getting started

Before you begin, you need to complete the following steps.

  • Database

Use docker image:

docker pull alexlevshin/workty-mongodb-3.0.6-amd64

and read Database section

  • Supervisor server

Use docker image:

docker pull alexlevshin/workty-supervisor-amd64

and read Supervisor server section

  • Rest API server

Use docker image:

docker pull alexlevshin/workty-restapi-amd64

and read Rest API server section

  • Worker Use docker image:
docker pull alexlevshin/workty-worker-arm7

and read Worker section

Rest API examples

Create and run simple workflow

First, we create a regular user account. You can read more about account types here:

curl -u 'admin@account.com':'adminpassword' -H 'Content-type: application/json' --data '{ "name": "myaccount", "email": "myemail@mail.com" }' -k -v https://127.0.0.1:9999/api/v1/accounts

Create new workflow for the user:

curl -u 'myaccount':'myemail@mail.com'  -H 'Content-type: application/json' --data  '{ "name": "myworkflow", "desc": "workflow" }' -k -v https://127.0.0.1:9999/api/v1/workflows

Get all the workties installed in the system and select any:

curl -k -v -u 'myaccount':'myemail@mail.com' https://127.0.0.1:9999/api/v1/workties?pretty=true&sort=created

For example the previous request returned workty with id 5465c70e7906b5bb7960f08f. Let's add it into your workflow with id 545f95ee2f82bdb917ad6f81 at first position:

curl -u 'myaccount':'myemail@mail.com' -H 'Content-type: application/json' --data '{ "name": "newworktyinstance", "desc": "newworktydesc", "worktyId": "5465c70e7906b5bb7960f08f" }' -k -v https://127.0.0.1:9999/api/v1/workflows/545f95ee2f82bdb917ad6f81/worktiesInstances?position_type=first

Now you are ready to run your first workflow:

curl -X PUT -u 'myaccount':'myemail@mail.com' -k https://127.0.0.1:9999/api/v1/workflows/545f95ee2f82bdb917ad6f81/running

You can find the full API documentation here .

Websocket API examples

Create and run simple workflow

Let's use Restify integrated json client for websocket interaction.

'use strict';
var RootFolder = process.env.ROOT_FOLDER;

if (!global.rootRequire) {
    global.rootRequire = function (name) {
        return require(RootFolder + '/' + name);
    };
}

var _ = require('lodash');
var restify = require('restify');
var expect = require('chai').expect;
var request = require('superagent');
var ApiPrefix = '/api/v1';
var io = require('socket.io-client');
var crypto = require('crypto');
var protocolClient = rootRequire('shared/protocols/v1/client-sv-accounts.module').OPERATIONS;
var config = rootRequire('config');
var SubVersion = config.restapi.getLatestVersion().sub; // YYYY.M.D

function _generateToken(account, salt) {
    var sha256 = crypto.createHash('sha256');

    sha256.update(account.id);
    sha256.update(account.name);
    sha256.update(salt);

    return sha256.digest('hex');
}

function _getAccount(dbAccount) {
    var account = {};

    account.id = dbAccount._id;
    account.name = dbAccount.email;
    account.host = config.client.getConnectionString() + '/' + account.id;
    var salt = dbAccount.password || dbAccount.oauthID;
    account.token = _generateToken(account, salt);

    return account;
}

// Init the test client to get account
var adminClient = restify.createJsonClient({
    version: SubVersion,
    url: config.restapi.getConnectionString(),
    headers: {
        'Authorization': config.supervisor.getAuthorizationBasic() // supervisor
    },
    rejectUnauthorized: false
});

Now create a regular user account:

var user = request.agent();
var account;
var socket;

adminClient.get(ApiPrefix + '/accounts', function (err, req, res, data) {
  if (err) {
    return done(err);
  }
  
  // Login
  user
       .post(config.client.getConnectionString() + '/')
       .send({email: config.supervisor.email, password: config.supervisor.password})
       .end(function (err, res) {
         if (err) {
           return done(err);
         }

         account = _getAccount(data[0]);

         // Connect via websocket
         var host = config.client.getConnectionString() + '/' + account.id + '_' + ContextName;
         socket = io.connect(host, {
           transports: ['websocket', 'polling', 'flashsocket'],
           'log level': 2,
           'polling duration': 10
         });

         socket.on('connect', function _onClientConnected() {
           console.log('connecting...');
           socket.emit('authentication', account);
         });

         socket.on('disconnect', function () {
           console.log('disconnected...');
         });
       });
});
        
socket.emit(protocolClient.ADD.name, {
  account: {
    oauthID: '',
    name: 'myaccount',
    email: 'myemail@email.com',
    password: 'myaccount_pwd',
    aclRoleNames: ['regular']
  }
});

Create new workflow for the user:

var workflows = [];

function _onWorkflowDataReceived(data) {
  workflows.push(data.workflow);
  socket.off(protocolClient.CHANGED, _onWorkflowDataReceived);
}

socket.on(protocolClient.CHANGED, _onWorkflowDataReceived);

socket.emit(protocolClient.ADD.name, {
  workflow: {
    name: 'myworkflow',
    desc: 'workflow',
    accountId: account.id
  }
});

Get all the workties installed in the system and select any:

var workties = [];
function _onWorktiesDataReceived(data) {
   workties = data;
   socket.off(protocolClient.CHANGED, _onWorktiesDataReceived);
}

socket.on(protocolClient.CHANGED, _onWorktiesDataReceived);
socket.emit(protocolClient.REFRESH_ALL.name, {});

For example the previous request returned workty with id 5465c70e7906b5bb7960f08f. Let's add it into your workflow with id 545f95ee2f82bdb917ad6f81 at first position:

socket.emit(protocolClient.ADD_WORKTY_INSTANCE.name, {
  workflow: {
    id: workflows[0]._id, // 545f95ee2f82bdb917ad6f81
    worktyInstance: {desc: 'myworktyinstance'}
  }, 
  workty: {id: workties[0]._id} // 5465c70e7906b5bb7960f08f
});

Run your workflow:

socket.emit(protocolClient.RUN.name, {
  workflow: {
    id: workflows[0]._id // 545f95ee2f82bdb917ad6f81
  }
});

After all action you need to log out:

user.get(config.client.getConnectionString() + '/logout')
    .end(function (err, res) {
      if (err) {
        return done(err);
      }    

      // Close websocket
      if (socket && socket.connected) {
        console.log('disconnecting...');
        socket.disconnect();
      } else {
        // There will not be a connection unless you have done() in beforeEach, socket.on('connect'...)
        console.log('no connection to break...');
      }
});