@onlineapps/conn-orch-orchestrator 1.0.78 → 1.0.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/src/WorkflowOrchestrator.js +37 -56
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-orch-orchestrator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.80",
|
|
4
4
|
"description": "Workflow orchestration connector for OA Drive - handles message routing and workflow execution",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"author": "OA Drive Team",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@onlineapps/conn-base-monitoring": "1.0.
|
|
24
|
+
"@onlineapps/conn-base-monitoring": "1.0.8",
|
|
25
25
|
"@onlineapps/conn-infra-mq": "1.1.57",
|
|
26
|
-
"@onlineapps/conn-orch-registry": "1.1.
|
|
27
|
-
"@onlineapps/conn-orch-cookbook": "2.0.
|
|
28
|
-
"@onlineapps/conn-orch-api-mapper": "1.0.
|
|
26
|
+
"@onlineapps/conn-orch-registry": "1.1.34",
|
|
27
|
+
"@onlineapps/conn-orch-cookbook": "2.0.17",
|
|
28
|
+
"@onlineapps/conn-orch-api-mapper": "1.0.26"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"jest": "^29.5.0",
|
|
@@ -45,28 +45,38 @@ class WorkflowOrchestrator {
|
|
|
45
45
|
backoffMultiplier: config.retryBackoffMultiplier || 2
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
//
|
|
48
|
+
// Router is REQUIRED (no backward-compatibility fallbacks).
|
|
49
49
|
const { createRouter } = this.cookbook;
|
|
50
|
-
if (typeof createRouter
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
if (typeof createRouter !== 'function') {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'[WorkflowOrchestrator] Missing dependency - cookbook.createRouter is required. Fix: provide conn-orch-cookbook router factory.'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
this.router = createRouter(this.mqClient, this.registryClient, { logger: this.logger });
|
|
56
|
+
if (!this.router || typeof this.router.routeToService !== 'function') {
|
|
57
|
+
throw new Error(
|
|
58
|
+
'[WorkflowOrchestrator] Invalid dependency - router.routeToService is required. Fix: cookbook.createRouter must return router with routeToService(serviceName, message).'
|
|
59
|
+
);
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
/**
|
|
63
|
-
* Get step identifier (
|
|
64
|
+
* Get step identifier (V2.1 only: step_id)
|
|
64
65
|
* @private
|
|
65
66
|
* @param {Object} step - Step object
|
|
66
67
|
* @returns {string} Step identifier
|
|
67
68
|
*/
|
|
68
69
|
_getStepId(step) {
|
|
69
|
-
|
|
70
|
+
if (!step || typeof step !== 'object') {
|
|
71
|
+
throw new Error('[WorkflowOrchestrator] Invalid cookbook - Expected step object (V2.1)');
|
|
72
|
+
}
|
|
73
|
+
if (step.id && !step.step_id) {
|
|
74
|
+
throw new Error('[WorkflowOrchestrator] Deprecated cookbook format - "id" is not allowed. Fix: use "step_id" (V2.1).');
|
|
75
|
+
}
|
|
76
|
+
if (!step.step_id) {
|
|
77
|
+
throw new Error('[WorkflowOrchestrator] Invalid cookbook - Missing "step_id" on step (V2.1).');
|
|
78
|
+
}
|
|
79
|
+
return step.step_id;
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
/**
|
|
@@ -87,12 +97,15 @@ class WorkflowOrchestrator {
|
|
|
87
97
|
if (cookbookDef.steps.length > 0 && cookbookDef.steps[0].id && !cookbookDef.steps[0].step_id) {
|
|
88
98
|
throw new Error('FAIL-FAST: V1 format (array with "id") is BANNED. Use V2.1 format with "step_id".');
|
|
89
99
|
}
|
|
100
|
+
// Validate all steps are V2.1 (fail-fast)
|
|
101
|
+
cookbookDef.steps.forEach((s) => this._getStepId(s));
|
|
90
102
|
return cookbookDef.steps;
|
|
91
103
|
}
|
|
92
104
|
|
|
93
|
-
// V2.0 format (deprecated) - steps is an object keyed by step_id
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
// V2.0 format (deprecated) - steps is an object keyed by step_id -> NOT ALLOWED (fail-fast)
|
|
106
|
+
throw new Error(
|
|
107
|
+
'[WorkflowOrchestrator] Deprecated cookbook format - steps must be an ARRAY (V2.1). Fix: migrate from V2.0 object steps to V2.1 array steps.'
|
|
108
|
+
);
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
/**
|
|
@@ -141,8 +154,7 @@ class WorkflowOrchestrator {
|
|
|
141
154
|
// Check if this step is for this service
|
|
142
155
|
if (step.type === 'task' && step.service !== serviceName) {
|
|
143
156
|
this.logger.warn(`Step ${current_step} is for ${step.service}, not ${serviceName}`);
|
|
144
|
-
|
|
145
|
-
await this.mqClient.publish(serviceQueue, message);
|
|
157
|
+
await this.router.routeToService(step.service, message);
|
|
146
158
|
return { skipped: true, reason: 'wrong_service' };
|
|
147
159
|
}
|
|
148
160
|
|
|
@@ -150,8 +162,7 @@ class WorkflowOrchestrator {
|
|
|
150
162
|
// If a control-flow step has "service", only that service is allowed to execute it.
|
|
151
163
|
if (step.type !== 'task' && step.service && step.service !== serviceName) {
|
|
152
164
|
this.logger.warn(`Control-flow step ${current_step} is pinned to ${step.service}, not ${serviceName}`);
|
|
153
|
-
|
|
154
|
-
await this.mqClient.publish(serviceQueue, message);
|
|
165
|
+
await this.router.routeToService(step.service, message);
|
|
155
166
|
return { skipped: true, reason: 'wrong_service_control_flow' };
|
|
156
167
|
}
|
|
157
168
|
|
|
@@ -427,15 +438,6 @@ class WorkflowOrchestrator {
|
|
|
427
438
|
cookbook: finalCookbook, // UNIFIED cookbook with full state
|
|
428
439
|
failed_at: new Date().toISOString()
|
|
429
440
|
});
|
|
430
|
-
|
|
431
|
-
// Compatibility: also publish to workflow.dlq for DLQ dashboards/tools that only look for "*.dlq" queues.
|
|
432
|
-
// workflow.dlq is an infrastructure queue created by Gateway.
|
|
433
|
-
await this.mqClient.publish('workflow.dlq', {
|
|
434
|
-
workflow_id,
|
|
435
|
-
current_step,
|
|
436
|
-
cookbook: finalCookbook,
|
|
437
|
-
failed_at: new Date().toISOString()
|
|
438
|
-
});
|
|
439
441
|
|
|
440
442
|
// ALSO publish to monitoring.workflow so dashboard shows the DLQ entry
|
|
441
443
|
try {
|
|
@@ -772,19 +774,10 @@ class WorkflowOrchestrator {
|
|
|
772
774
|
case 'fork_join':
|
|
773
775
|
return await this._executeForkJoin(step, context, cookbookDef, serviceName);
|
|
774
776
|
default: {
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
throw new Error(`[WorkflowOrchestrator] CookbookExecutor missing - Cannot execute control-flow step '${stepId}'`);
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
const executor = new CookbookExecutor(
|
|
782
|
-
{ steps: [step] },
|
|
783
|
-
{ logger: this.logger, defaultTimeout: this.defaultTimeout }
|
|
777
|
+
throw new Error(
|
|
778
|
+
`[WorkflowOrchestrator] Unsupported control-flow step type - "${step.type}" in step "${stepId}". ` +
|
|
779
|
+
'Expected: steps|foreach|switch|fork_join.'
|
|
784
780
|
);
|
|
785
|
-
executor.context.setInput(context.api_input || {});
|
|
786
|
-
executor.context.data.steps = context.steps || {};
|
|
787
|
-
return await executor.executeStep(step);
|
|
788
781
|
}
|
|
789
782
|
}
|
|
790
783
|
}
|
|
@@ -1046,27 +1039,15 @@ class WorkflowOrchestrator {
|
|
|
1046
1039
|
throw new Error(`[WorkflowOrchestrator] FAIL-FAST: Task step '${nextStepId}' missing required 'service'`);
|
|
1047
1040
|
}
|
|
1048
1041
|
// Route to specific service
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
this.logger.info(`Routed to service: ${nextStep.service}`, {
|
|
1052
|
-
step: nextStepId
|
|
1053
|
-
});
|
|
1054
|
-
} else {
|
|
1055
|
-
// Backward compatibility: publish to service's workflow queue directly if router is not available
|
|
1056
|
-
const serviceQueue = `${nextStep.service}.workflow`;
|
|
1057
|
-
await this.mqClient.publish(serviceQueue, message);
|
|
1058
|
-
this.logger.info(`Published to queue: ${serviceQueue}`, {
|
|
1059
|
-
step: nextStepId
|
|
1060
|
-
});
|
|
1061
|
-
}
|
|
1042
|
+
await this.router.routeToService(nextStep.service, message);
|
|
1043
|
+
this.logger.info(`Routed to service: ${nextStep.service}`, { step: nextStepId });
|
|
1062
1044
|
} else {
|
|
1063
1045
|
// Control flow steps:
|
|
1064
1046
|
// - if pinned (step.service), route to that service queue
|
|
1065
1047
|
// - otherwise route to workflow.control (any business service may execute)
|
|
1066
1048
|
if (nextStep.service) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
this.logger.info(`Routed control flow to pinned service queue: ${serviceQueue}`, { step: nextStepId });
|
|
1049
|
+
await this.router.routeToService(nextStep.service, message);
|
|
1050
|
+
this.logger.info(`Routed control flow to pinned service: ${nextStep.service}`, { step: nextStepId });
|
|
1070
1051
|
} else {
|
|
1071
1052
|
await this.mqClient.publish('workflow.control', message);
|
|
1072
1053
|
this.logger.info(`Routed control flow to workflow.control`, { step: nextStepId });
|