GithubHelp home page GithubHelp logo

zenaton / zenaton-node Goto Github PK

View Code? Open in Web Editor NEW
52.0 3.0 15.0 1009 KB

⚡ Node.js library to run and orchestrate background jobs with Zenaton Workflow Engine

Home Page: https://zenaton.com

License: MIT License

JavaScript 100.00%
nodejs jobs workflow-engine queues orchestration job-orchestration workflow engine automation process

zenaton-node's People

Contributors

antoinereyt avatar ciboulette avatar dependabot[bot] avatar geomagilles avatar jalric avatar louisgraffeuil avatar mryawe avatar pylebecq avatar strato avatar yjouffrault avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

zenaton-node's Issues

Error when defining custom methods in Tasks and Workflows

the following code

const { Task } = require("zenaton");

module.exports = Task("TaskA", {
  handle: async function() {
    console.log("Task A starts");

    await this.test(0);

    console.log("Task A ends");
    return 0;
  },
  test: async function(a) {
    if (0 <= a) {
      console.log("1");
    } else {
      console.log("2");
    }
  }
});

returns an error Error: "test" is defined more than once in "TaskA" task. Same with workflows.

It should work.

Generate ID when scheduling things

When tasks and workflows are scheduled, we want to add a generated ID from the library.
This ID will be a canonical ID representing the user intent.

Missing binding of first argument of task defined as functions

The documentation states the following:

Note: when executing the provided function, this is binded to the first argument provided. Eg. in new SimpleTask({id: 4}), this is binded to {id: 4} when executing the task.

Considering the following example:

require("./client");

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const { Task, Workflow } = require("zenaton");

const saveInDb = Task('saveInDb', async (done) => {
    console.log(this);
    done(null, 0);
});

const workflow1 = Workflow("workflow1", {
    init(initData) {
        this.id = initData.id
        this.name = initData.name
        this.sandbox = initData.sandbox
        this.temporary = initData.temporary
    },
    handle() {
        new saveInDb(this).dispatch();
    }
});

if (process.env.LAUNCH) {
    new workflow1({
        id: 1,
        name: "Test",
        sandbox: true,
        temporary: true
    }).dispatch();
}

We would expect it to print

{id: 1, name: "Test", sandbox: true, temporary: true}

Actually, it prints

{}

To run this example easily, save it to a file issue-bind.js in the examples-node repository.
Then, you can run zenaton listen --boot issue-bind.js and to start the workflow, run LAUNCH=1 node issue-bind.js

Either the documentation is wrong, or the binding is not done correctly.

Note: using the init() method to define the task work as expected:

require("./client");

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

const { Task, Workflow } = require("zenaton");

const saveInDb = Task('saveInDb', {
    init(data) {
        this.data = data
    },
    handle(done) {
        console.log(this);
        done(null, 0);
    }
});

const workflow1 = Workflow("workflow1", {
    init(initData) {
        this.id = initData.id
        this.name = initData.name
        this.sandbox = initData.sandbox
        this.temporary = initData.temporary
    },
    handle() {
        new saveInDb(this).dispatch();
    }
});

if (process.env.LAUNCH) {
    new workflow1({
        id: 1,
        name: "Test",
        sandbox: true,
        temporary: true
    }).dispatch();
}

This prints the following:

{ data: { id: 1, name: 'Test', sandbox: true, temporary: true } }

Ability to load ES6 imports

The library can't comprehend ES6 imports, which is a problem for users who have already switched to this kind of imports on their stack (with Meteor for example).

Use of `execute` method outside a workflow should thrown an error

Expected behavior of execute method does make sense only within a workflow.

To be clear, the test should be that the method is used within a workflow - NOT that it is processed by an agent. So locally executing a workflow is still perfectly fine.

A specific Zenaton error should be thrown and documented.

ModifiedDeciderException in Wait following upgrade to 0.5.7

Since version 0.5.7, probably because of #44 , workflows containing wait are now throwing a ModifiedDeciderError, even when the workflow was not modified.

To reproduce:

  1. Start a workflow using zenaton node lib 0.5.6, with a Wait of 5 minutes
  2. When the wait start unlisten
  3. Upgrade your zenaton node lib to 0.5.7
  4. Listen again
  5. When the Wait completes, the next decision executed will result in a ModifiedDeciderError.

Useless dependency

The dotenv dependency seems not used by the library.
Environment variable must comes from client side, it's probably just code legacy to delete.

"dependencies": {
  "@babel/runtime": "7.3.1",
  "axios": "0.18.0",
  "dotenv": "6.2.0",
  "moment-timezone": "0.5.23"
}

No Init check library side: provided variable or connection

No errors are triggered when I init Zenaton client like this

Zenaton.init(emptyVariable, null, '')

Checks can be implemented code client side like in zenaton/exemples-node but is more the concern of the library. If I omit to implement checks and provide empty environment variables, no errors are displayed until I launch a workflow.

Improvement proposal:
Some library like elasticsearch client provide method to check connection.
Can be useful when deploy in production.

var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
  host: 'localhost:9200',
  log: 'trace'
});

client.ping({
  requestTimeout: 30000,
}, function (error) {
  if (error) {
    console.error('elasticsearch cluster is down!');
  } else {
    console.log('All is well');
  }
});

Event triggered 2 times

Play with this example : https://zenaton.com/tutorial/node/event
I've modified EventWorkflow to display the internal propery of my event

const { Workflow } = require("zenaton");
const TaskA = require("../Tasks/TaskA");
const TaskB = require("../Tasks/TaskB");
const TaskC = require("../Tasks/TaskC");
const TaskD = require("../Tasks/TaskD");

module.exports = Workflow("EventWorkflow", {
  init(id) {
    this.id = id;
    this.state = true;
  },
  async handle() {
    await new TaskA().execute();

    if (this.state) {
      await new TaskB().execute();
    } else {
      await new TaskC().execute();
    }
  },
  async onEvent(eventName, eventData) {
    if (eventName === "MyEvent") {
      this.state = false;
      console.log(new Date(), "What is foo : ", eventData.foo ? eventData.foo : "?")
      await new TaskD().execute();
    }
  },
  id() {
    return this.id;
  }
});

with the following launch_event.js

require("./client");

const uniqid = require("uniqid");
const EventWorkflow = require("./Workflows/EventWorkflow");

const id = uniqid();
new EventWorkflow(id).dispatch().catch(err => {
  console.error(err);
});
console.log("Event will be sent in 2s");
setTimeout(function() {
  EventWorkflow.whereId(id)
    .send("MyEvent", { foo: "bar"})
    .catch(err => {
      console.error(err);
    });
  console.log("Event sent");
}, 2000);

The output in the agent is :

2019-03-24T08:38:20.142Z 'Task A starts'
2019-03-24T08:38:21.706Z 'What is foo : ' 'bar'
2019-03-24T08:38:22.096Z 'Task D starts'
2019-03-24T08:38:24.101Z 'Task D ends'                      // 2000 correct
2019-03-24T08:38:24.148Z 'Task A ends'                     // 4000 correct
2019-03-24T08:38:24.600Z 'What is foo : ' 'bar'
2019-03-24T08:38:25.412Z 'Task C starts'
2019-03-24T08:38:27.418Z 'Task C ends'                     // 2000 correct

As you can see the console log within the event is called 2 times ....

node launch_event.js 
2019-03-24T08:38:19.297Z 'Event will be sent in 2s'
2019-03-24T08:38:21.307Z 'Event sent'

You cannot wait and event without a timeout

This works:

yield this.wait.event("UserActivatedEvent").for(30);

BUT, If I do this:

yield this.wait.event("UserActivatedEvent");

I run into this:
Screen Shot 2019-10-25 at 16 42 38

The full stacktrace:

Error: "Wait {
	eventName: 'UserActivatedEvent',
	timestamp: null,
	duration: null
}" behind "yield" is not valid instruction - check Zenaton syntax
    at Branch._checkYieldValue (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/lib/Code/yield/Decider/Branch.js:114:23)
    at Branch.run (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/lib/Code/yield/Decider/Branch.js:79:12)
    at _callee$ (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/lib/Code/yield/Decider/Decider.js:40:33)
    at tryCatch (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/regenerator-runtime/runtime.js:45:40)
    at Generator.invoke [as _invoke] (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/regenerator-runtime/runtime.js:271:22)
    at Generator.prototype.(anonymous function) [as next] (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/regenerator-runtime/runtime.js:97:21)
    at asyncGeneratorStep (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
    at _next (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/@babel/runtime/helpers/asyncToGenerator.js:25:9)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:229:7)

Correct the behaviour of Event Subclasses in the Node Library

As known for some times, the when subclassing and Event class, the child Event is not handled properly by Zenaton changes has to be done, in coordination between the engine, agent and client libraries.

For more details please check:

zenaton/rfcs#19

Workflow and Task with the same name does not work

When I define a workflow and a task having the same name, the execution crashes at some point. It seems like we're trying to run the task as if we are running a workflow so we don't provide the done() callback function to the task.

For example, I defined a Workflow named HelloZenaton:

var { Workflow } = require("zenaton");

var Task = require("../Tasks/LaunchHelloZenaton");

module.exports = Workflow("HelloZenaton", function() {
  console.log('HelloZenaton workflow starts');
  new Task().execute();
  console.log('HelloZenaton workflow ends');
});

I define the task LaunchHelloZenaton, that will in turn execute a task having the same name as the workflow:

var {Task} = require("zenaton");
var HelloZenaton = require("./HelloZenaton");

module.exports = Task("LaunchHelloZenaton", function(done) {
  console.log("LaunchHelloZenaton task starts");
  new HelloZenaton().execute();
  console.log("LaunchHelloZenaton task ends");
  done(null, "LaunchHelloZenaton");
});

And the last task definition, having the same name as the workflow:

var {Task} = require("zenaton");

module.exports = Task("HelloZenaton", function(done) {
  console.log("HelloZenaton task starts");
  console.log("HelloZenaton task ends")
  done(null, "HelloZenaton");
});

Now if you try running this workflow, for example using the following code:

require("./client");

var AsynchronousWorkflow = require("./Workflows/HelloZenaton");
new AsynchronousWorkflow().dispatch();

Then it crashes at some point. This is the content found in zenaton.err file:

/Users/pierre-yves/dev/core/worker/libraries/javascript/lib/Worker/v1/Worker.js:29
throw e;
^

TypeError: done is not a function
at /Users/pierre-yves/dev/examples-node/Tasks/HelloZenaton.js:6:3
at /Users/pierre-yves/dev/core/worker/libraries/javascript/lib/Worker/v1/Processor.js:45:28
at Array.forEach (<anonymous>)
at module.exports.processFromTask (/Users/pierre-yves/dev/core/worker/libraries/javascript/lib/Worker/v1/Processor.js:42:11)
at module.exports.process (/Users/pierre-yves/dev/core/worker/libraries/javascript/lib/Worker/v1/Processor.js:28:19)
at Engine.execute (/Users/pierre-yves/dev/examples-node/node_modules/zenaton/lib/v1/Engine/Engine.js:50:27)
at TaskClass.execute (/Users/pierre-yves/dev/examples-node/node_modules/zenaton/lib/v1/Tasks/AbstractTask.js:16:25)
at /Users/pierre-yves/dev/examples-node/Tasks/LaunchHelloZenaton.js:7:22
at Worker.process (/Users/pierre-yves/dev/core/worker/libraries/javascript/lib/Worker/v1/Worker.js:19:17)
at Slave.processJob (/Users/pierre-yves/dev/core/worker/libraries/javascript/lib/Loader/Slave.js:59:51)

Weekday and DayOfMonth methods should wait when the day provided is today

Expected Behavior:

per the discussion with @geomagilles:

if we are on Monday, 11am on the 23rd of the month. Then:

.Monday(1) should wait until next Monday at 11am
.Monday(1).At("9") should wait until next Monday at 9am
.Monday(1).At("13") should wait 2 hours only
.DayOfMonth(23) should wait for one month
.DayOfMonth(23).At("13") should wait for only 2 hours

Problem:

Currently, if today is a Monday, when calling new Wait().monday() you don't actually wait until the next Monday, and the wait terminates immediately.

We have the same issue with the dayOfMonth method. If it's the 23rd and you call new Wait().dayOfMonth(23) you won't wait until the next month, but instead the wait will terminate immediately.

Specifically, I believe the two problematic lines are these:

if (now.isAfter(then)) {

then = (d > day) ? then.add(n, 'weeks') : then.add(n - 1, 'weeks')

Fix:

See zenaton/zenaton-go#7 for the fix in the go library.

You cannot wait and event without a timeout

This works:

yield this.wait.event("UserActivatedEvent").for(30);

BUT, If I do this:

yield this.wait.event("UserActivatedEvent");

I run into this:
Screen Shot 2019-10-25 at 16 42 38

The full stacktrace:

Error: "Wait {
	eventName: 'UserActivatedEvent',
	timestamp: null,
	duration: null
}" behind "yield" is not valid instruction - check Zenaton syntax
    at Branch._checkYieldValue (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/lib/Code/yield/Decider/Branch.js:114:23)
    at Branch.run (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/lib/Code/yield/Decider/Branch.js:79:12)
    at _callee$ (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/lib/Code/yield/Decider/Decider.js:40:33)
    at tryCatch (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/regenerator-runtime/runtime.js:45:40)
    at Generator.invoke [as _invoke] (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/regenerator-runtime/runtime.js:271:22)
    at Generator.prototype.(anonymous function) [as next] (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/regenerator-runtime/runtime.js:97:21)
    at asyncGeneratorStep (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
    at _next (/Users/antoine/.zenaton/lib/worker-0.8.3/priv/javascript/node_modules/@babel/runtime/helpers/asyncToGenerator.js:25:9)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:229:7)

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.