Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html>
- <head>
- <meta name="description" content="[A simple workflow orchestration engine design.]">
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width">
- <title>JS Bin</title>
- </head>
- <body>
- <script id="jsbin-javascript">
- let definition = {
- "name":"sampleWorkflowDefinition",
- "creaitonTime":1100,
- "data":{
- "name":"string",
- "ic":"string",
- "amt":"number",
- "addr":"string",
- "identity":"object"
- },
- "processes":{
- "0":{
- "id":0,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":"root",
- "type":"EQ",
- "field":"state",
- "value":"started"
- }
- ]
- },
- "service":"service0",
- "data":[
- "ic"
- ]
- },
- "1":{
- "id":1,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":0,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- }
- ]
- },
- "service":"service1",
- "data":[
- "name","addr"
- ]
- },
- "2":{
- "id":2,
- "startCondition":{
- "type":"MULTIPLE",
- "subConditions":[
- {
- "id":0,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- },
- {
- "type":"AND",
- "subConditions":[
- {
- "id":3,
- "type":"EQ",
- "field":"state",
- "value":"failed"
- },
- {
- "id":3,
- "type":"NEQ",
- "field":"state",
- "value":"complete"
- }
- ]
- }
- ]
- },
- "service":"service2",
- "data":[
- "identity"
- ]
- },
- "3":{
- "id":3,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":2,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- }
- ]
- },
- "service":"service3",
- "data":[
- "ic"
- ]
- },
- "4":{
- "id":4,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":1,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- },
- {
- "id":2,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- },
- {
- "id":3,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- }
- ]
- },
- "service":"service4",
- "data":[
- "ic"
- ]
- }
- }
- };
- function isEmpty(obj) {
- for(var key in obj) {
- if(obj.hasOwnProperty(key))
- return false;
- }
- return true;
- }
- let eventList = {};
- function orchestrate(workflow){
- // Show meta
- let time = new Date();
- console.log(`Starting workflow (${workflow.name}) @ ${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`);
- let sampleInput = {name:"tim",
- ic:"1128696556565",
- amt:100,
- addr:"1 jln 100, msia",
- identity:{
- "name":"tim",
- "secret":"awesome"
- }
- };
- console.log(`Sample Input: ${JSON.stringify(sampleInput, null, 2)}`);
- // Generate workflow state
- let state = generateWorkflowState(workflow, sampleInput);
- console.log(`Initial workflow State: ${JSON.stringify(state,null,2)}`);
- // Build event list
- buildEventList(workflow);
- console.log(`Event list: ${JSON.stringify(eventList, null, 2)}`);
- // Emit root event
- eventList.root.forEach((event)=>{
- startConditionEvaluation(state,event);
- });
- }
- orchestrate(definition);
- function generateWorkflowState(workflow, input){
- let state = {
- "definition":workflow,
- "states":{
- "data":input,
- "processes":{}
- }
- }
- for (let processId in workflow.processes){
- let process = workflow.processes[processId];
- state.states.processes[processId] = {
- data:{
- id:processId
- }
- };
- process.data.forEach((key)=>{
- state.states.processes[processId]['data'][key] = state.states.data[key];
- });
- }
- return state;
- }
- function buildEventList(workflow){
- // Build event handling chain from workflow definition
- for(let processId in workflow.processes){
- let process = workflow.processes[processId];
- if (!isEmpty(process.startCondition)){
- let rootCondition = process.startCondition;
- rootCondition.processToStart = process.id;
- let addedList = []
- addEventEntry(rootCondition, rootCondition, addedList);
- }
- }
- }
- function addEventEntry(condition, rootCondition, addedList){
- if (condition.type === 'AND' || condition.type === 'OR'){
- condition.subConditions.forEach((subCondition)=>{
- return addEventEntry(subCondition, rootCondition, addedList);
- });
- }else if (condition.type === 'MULTIPLE'){
- condition.subConditions.forEach((subCondition)=>{
- subCondition.processToStart = rootCondition.processToStart;
- addEventEntry(subCondition, subCondition, addedList);
- });
- }else{
- if(!addedList.includes(condition.id)){
- addedList.push(condition.id);
- if(isEmpty(eventList[`${condition.id}`])){
- eventList[`${condition.id}`] = [rootCondition];
- }else{
- eventList[`${condition.id}`].push(rootCondition);
- }
- return;
- }
- }
- }
- async function startConditionEvaluation(state, event){
- console.log(`Begin evaluating starting condition for process #${event.processToStart}.`);
- let passed = conditionEvaluation(state, event);
- console.log(`Root condition evaluation for process #${event.processToStart} passed = ${passed}`);
- if (passed){
- console.log(`Proceed to start process #${event.processToStart}`);
- let result = await delegateService(state, event.processToStart);
- completeProcess(state, event.processToStart, result);
- return;
- }
- //console.log('Do Nothing');
- return;
- }
- function conditionEvaluation(state, condition){
- //console.log(`Evaluating ${JSON.stringify(condition, null, 2)}`);
- let passed = true;
- if (condition.type === 'AND'){
- condition.subConditions.forEach((subCondition)=>{
- if (!conditionEvaluation(state, subCondition)){
- passed = false;
- }
- });
- //console.log(`AND evaluation passed: ${passed}`);
- }else if (condition.type === 'OR'){
- passed = false;
- condition.subConditions.forEach((subCondition)=>{
- if (conditionEvaluation(state, subCondition)){
- passed = true;
- }
- });
- //console.log(`OR evaluation passed: ${passed}`);
- }else if (condition.type === 'EQ' || condition.type === 'NEQ'){
- if (condition.id == 'root'){
- passed = true;
- }else{
- if(!state.states.processes[condition.id]){
- console.log(`State of ${condition.id} does not exists, check evaluated as failed.`);
- passed = false;
- }else if (!state.states.processes[condition.id].result[condition.field]){
- console.log(`State of ${condition.id} doesnt contain ${condition.field}, check evaluated as failed.`);
- passed = false;
- }else {
- //console.log(`State value of ${condition.field} is ${state.states.processes[condition.id].result[condition.field]}`);
- if (condition.type === 'EQ' && condition.value !== state.states.processes[condition.id].result[condition.field]){
- passed = false;
- }else if (condition.type === 'NEQ' && condition.value === state.states.processes[condition.id].result[condition.field]){
- passed = false;
- }
- }
- }
- }
- //console.log(`Completed evaluation on ${JSON.stringify(condition, null, 2)}`);
- return passed;
- }
- function completeProcess(state, processId, result){
- console.log(`Process #${processId} completed, updating states.`);
- // Update state
- state.states.processes[processId].result = result;
- //console.log(`Updated state: ${JSON.stringify(state.states,null,2)}`);
- console.log(`State updated with results from process #${processId}, emitting process completion event`);
- eventList[processId].forEach((event)=>{
- startConditionEvaluation(state,event);
- });
- }
- function delegateService(state, processId){
- // Extract data based on definition
- let data = state.states.processes[processId]['data'];
- if (processId === 0){
- return service0(data);
- }else if (processId === 1){
- return service1(data);
- }else if (processId === 2){
- return service2(data);
- }else if (processId === 3){
- return service3(data);
- }else if (processId === 4){
- return service4(data);
- }
- }
- // ====================================Other chaincode=================================================
- // Mock Services
- function service0(data){
- return new Promise((resolve, reject)=>{
- console.log(`service0 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service0 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 1000);
- });
- }
- function service1(data){
- return new Promise((resolve, reject)=>{
- console.log(`service1 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service1 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 1500);
- });
- }
- function service2(data){
- return new Promise((resolve, reject)=>{
- console.log(`service2 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service2 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 3000);
- });
- }
- function service3(data){
- return new Promise((resolve, reject)=>{
- console.log(`service3 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service3 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 2000);
- });
- }
- function service4(data){
- return new Promise((resolve, reject)=>{
- console.log(`service4 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service4 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 2500);
- });
- }
- </script>
- <script id="jsbin-source-javascript" type="text/javascript">let definition = {
- "name":"sampleWorkflowDefinition",
- "creaitonTime":1100,
- "data":{
- "name":"string",
- "ic":"string",
- "amt":"number",
- "addr":"string",
- "identity":"object"
- },
- "processes":{
- "0":{
- "id":0,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":"root",
- "type":"EQ",
- "field":"state",
- "value":"started"
- }
- ]
- },
- "service":"service0",
- "data":[
- "ic"
- ]
- },
- "1":{
- "id":1,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":0,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- }
- ]
- },
- "service":"service1",
- "data":[
- "name","addr"
- ]
- },
- "2":{
- "id":2,
- "startCondition":{
- "type":"MULTIPLE",
- "subConditions":[
- {
- "id":0,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- },
- {
- "type":"AND",
- "subConditions":[
- {
- "id":3,
- "type":"EQ",
- "field":"state",
- "value":"failed"
- },
- {
- "id":3,
- "type":"NEQ",
- "field":"state",
- "value":"complete"
- }
- ]
- }
- ]
- },
- "service":"service2",
- "data":[
- "identity"
- ]
- },
- "3":{
- "id":3,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":2,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- }
- ]
- },
- "service":"service3",
- "data":[
- "ic"
- ]
- },
- "4":{
- "id":4,
- "startCondition":{
- "type":"AND",
- "subConditions":[
- {
- "id":1,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- },
- {
- "id":2,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- },
- {
- "id":3,
- "type":"EQ",
- "field":"state",
- "value":"complete"
- }
- ]
- },
- "service":"service4",
- "data":[
- "ic"
- ]
- }
- }
- };
- function isEmpty(obj) {
- for(var key in obj) {
- if(obj.hasOwnProperty(key))
- return false;
- }
- return true;
- }
- let eventList = {};
- function orchestrate(workflow){
- // Show meta
- let time = new Date();
- console.log(`Starting workflow (${workflow.name}) @ ${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}`);
- let sampleInput = {name:"tim",
- ic:"1128696556565",
- amt:100,
- addr:"1 jln 100, msia",
- identity:{
- "name":"tim",
- "secret":"awesome"
- }
- };
- console.log(`Sample Input: ${JSON.stringify(sampleInput, null, 2)}`);
- // Generate workflow state
- let state = generateWorkflowState(workflow, sampleInput);
- console.log(`Initial workflow State: ${JSON.stringify(state,null,2)}`);
- // Build event list
- buildEventList(workflow);
- console.log(`Event list: ${JSON.stringify(eventList, null, 2)}`);
- // Emit root event
- eventList.root.forEach((event)=>{
- startConditionEvaluation(state,event);
- });
- }
- orchestrate(definition);
- function generateWorkflowState(workflow, input){
- let state = {
- "definition":workflow,
- "states":{
- "data":input,
- "processes":{}
- }
- }
- for (let processId in workflow.processes){
- let process = workflow.processes[processId];
- state.states.processes[processId] = {
- data:{
- id:processId
- }
- };
- process.data.forEach((key)=>{
- state.states.processes[processId]['data'][key] = state.states.data[key];
- });
- }
- return state;
- }
- function buildEventList(workflow){
- // Build event handling chain from workflow definition
- for(let processId in workflow.processes){
- let process = workflow.processes[processId];
- if (!isEmpty(process.startCondition)){
- let rootCondition = process.startCondition;
- rootCondition.processToStart = process.id;
- let addedList = []
- addEventEntry(rootCondition, rootCondition, addedList);
- }
- }
- }
- function addEventEntry(condition, rootCondition, addedList){
- if (condition.type === 'AND' || condition.type === 'OR'){
- condition.subConditions.forEach((subCondition)=>{
- return addEventEntry(subCondition, rootCondition, addedList);
- });
- }else if (condition.type === 'MULTIPLE'){
- condition.subConditions.forEach((subCondition)=>{
- subCondition.processToStart = rootCondition.processToStart;
- addEventEntry(subCondition, subCondition, addedList);
- });
- }else{
- if(!addedList.includes(condition.id)){
- addedList.push(condition.id);
- if(isEmpty(eventList[`${condition.id}`])){
- eventList[`${condition.id}`] = [rootCondition];
- }else{
- eventList[`${condition.id}`].push(rootCondition);
- }
- return;
- }
- }
- }
- async function startConditionEvaluation(state, event){
- console.log(`Begin evaluating starting condition for process #${event.processToStart}.`);
- let passed = conditionEvaluation(state, event);
- console.log(`Root condition evaluation for process #${event.processToStart} passed = ${passed}`);
- if (passed){
- console.log(`Proceed to start process #${event.processToStart}`);
- let result = await delegateService(state, event.processToStart);
- completeProcess(state, event.processToStart, result);
- return;
- }
- //console.log('Do Nothing');
- return;
- }
- function conditionEvaluation(state, condition){
- //console.log(`Evaluating ${JSON.stringify(condition, null, 2)}`);
- let passed = true;
- if (condition.type === 'AND'){
- condition.subConditions.forEach((subCondition)=>{
- if (!conditionEvaluation(state, subCondition)){
- passed = false;
- }
- });
- //console.log(`AND evaluation passed: ${passed}`);
- }else if (condition.type === 'OR'){
- passed = false;
- condition.subConditions.forEach((subCondition)=>{
- if (conditionEvaluation(state, subCondition)){
- passed = true;
- }
- });
- //console.log(`OR evaluation passed: ${passed}`);
- }else if (condition.type === 'EQ' || condition.type === 'NEQ'){
- if (condition.id == 'root'){
- passed = true;
- }else{
- if(!state.states.processes[condition.id]){
- console.log(`State of ${condition.id} does not exists, check evaluated as failed.`);
- passed = false;
- }else if (!state.states.processes[condition.id].result[condition.field]){
- console.log(`State of ${condition.id} doesnt contain ${condition.field}, check evaluated as failed.`);
- passed = false;
- }else {
- //console.log(`State value of ${condition.field} is ${state.states.processes[condition.id].result[condition.field]}`);
- if (condition.type === 'EQ' && condition.value !== state.states.processes[condition.id].result[condition.field]){
- passed = false;
- }else if (condition.type === 'NEQ' && condition.value === state.states.processes[condition.id].result[condition.field]){
- passed = false;
- }
- }
- }
- }
- //console.log(`Completed evaluation on ${JSON.stringify(condition, null, 2)}`);
- return passed;
- }
- function completeProcess(state, processId, result){
- console.log(`Process #${processId} completed, updating states.`);
- // Update state
- state.states.processes[processId].result = result;
- //console.log(`Updated state: ${JSON.stringify(state.states,null,2)}`);
- console.log(`State updated with results from process #${processId}, emitting process completion event`);
- eventList[processId].forEach((event)=>{
- startConditionEvaluation(state,event);
- });
- }
- function delegateService(state, processId){
- // Extract data based on definition
- let data = state.states.processes[processId]['data'];
- if (processId === 0){
- return service0(data);
- }else if (processId === 1){
- return service1(data);
- }else if (processId === 2){
- return service2(data);
- }else if (processId === 3){
- return service3(data);
- }else if (processId === 4){
- return service4(data);
- }
- }
- // ====================================Other chaincode=================================================
- // Mock Services
- function service0(data){
- return new Promise((resolve, reject)=>{
- console.log(`service0 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service0 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 1000);
- });
- }
- function service1(data){
- return new Promise((resolve, reject)=>{
- console.log(`service1 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service1 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 1500);
- });
- }
- function service2(data){
- return new Promise((resolve, reject)=>{
- console.log(`service2 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service2 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 3000);
- });
- }
- function service3(data){
- return new Promise((resolve, reject)=>{
- console.log(`service3 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service3 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 2000);
- });
- }
- function service4(data){
- return new Promise((resolve, reject)=>{
- console.log(`service4 started with data ${JSON.stringify(data)}`);
- let timeout = setTimeout(()=>{
- clearTimeout(timeout);
- console.log("service4 completed.");
- let result = {
- id:data.id,
- state:"complete",
- status:200,
- result:"You done it!"
- }
- resolve(result);
- }, 2500);
- });
- }</script></body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement