zenaton / zenaton-node Goto Github PK
View Code? Open in Web Editor NEW⚡ Node.js library to run and orchestrate background jobs with Zenaton Workflow Engine
Home Page: https://zenaton.com
License: MIT License
⚡ Node.js library to run and orchestrate background jobs with Zenaton Workflow Engine
Home Page: https://zenaton.com
License: MIT License
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
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:
zenaton-node/src/v1/Traits/WithTimestamp.js
Line 138 in f39c85e
zenaton-node/src/v1/Traits/WithTimestamp.js
Line 150 in f39c85e
See zenaton/zenaton-go#7 for the fix in the go library.
For details have a look here => zenaton/zenaton-php#15
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"
}
It should:
Currently, the output is just null, without the workflow being actually launched
To be clear, the test should be that the class 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.
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:
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.
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'
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
Wrong wait duration is set with months
method
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).
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.
eg with the workflow id:
[email protected] the kill does not work.
This works:
yield this.wait.event("UserActivatedEvent").for(30);
BUT, If I do this:
yield this.wait.event("UserActivatedEvent");
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)
Would be awesome to get TypeScript support. Thanks.
Check we can use the Zenaton NodeJS library with various NodeJS frameworks without hassle.
Possibly investigate solutions if problems arise.
NodeJS frameworks: Express, Sails, Meteor, Hapi, KOA...
To test it :
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');
}
});
This works:
yield this.wait.event("UserActivatedEvent").for(30);
BUT, If I do this:
yield this.wait.event("UserActivatedEvent");
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)
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)
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.
This:
// Notice the 'null' passed
await new SequentialWorkflow(null).dispatch();
Always leads to a workflow crash.
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 } }
Example:
module.exports = Workflow("WaitWorkflow", async function() {
await new Wait().seconds(5).execute();
throw new Error("Error in TaskE");
});
Then after the error, comment the throw and change wait to 10 seconds.
When you retry, the workflow terminates nicely - it should not and should indicate a modified decider.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.