@onlineapps/conn-orch-orchestrator 1.0.66 → 1.0.68

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/conn-orch-orchestrator",
3
- "version": "1.0.66",
3
+ "version": "1.0.68",
4
4
  "description": "Workflow orchestration connector for OA Drive - handles message routing and workflow execution",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -23,7 +23,7 @@
23
23
  "@onlineapps/conn-base-monitoring": "1.0.2",
24
24
  "@onlineapps/conn-infra-mq": "^1.1.0",
25
25
  "@onlineapps/conn-orch-registry": "1.1.25",
26
- "@onlineapps/conn-orch-cookbook": "2.0.12",
26
+ "@onlineapps/conn-orch-cookbook": "2.0.13",
27
27
  "@onlineapps/conn-orch-api-mapper": "1.0.15"
28
28
  },
29
29
  "devDependencies": {
@@ -133,6 +133,11 @@ class WorkflowOrchestrator {
133
133
  throw new Error(`Step not found: ${current_step}`);
134
134
  }
135
135
 
136
+ // Fail-fast: task steps MUST have service
137
+ if (step.type === 'task' && !step.service) {
138
+ throw new Error(`[WorkflowOrchestrator] FAIL-FAST: Task step '${current_step}' missing required 'service'`);
139
+ }
140
+
136
141
  // Check if this step is for this service
137
142
  if (step.type === 'task' && step.service !== serviceName) {
138
143
  this.logger.warn(`Step ${current_step} is for ${step.service}, not ${serviceName}`);
@@ -1028,6 +1033,9 @@ class WorkflowOrchestrator {
1028
1033
  };
1029
1034
 
1030
1035
  if (nextStep.type === 'task') {
1036
+ if (!nextStep.service) {
1037
+ throw new Error(`[WorkflowOrchestrator] FAIL-FAST: Task step '${nextStepId}' missing required 'service'`);
1038
+ }
1031
1039
  // Route to specific service
1032
1040
  if (this.router?.routeToService) {
1033
1041
  await this.router.routeToService(nextStep.service, message);
@@ -1035,7 +1043,7 @@ class WorkflowOrchestrator {
1035
1043
  step: nextStepId
1036
1044
  });
1037
1045
  } else {
1038
- // Fallback: publish to service's workflow queue directly
1046
+ // Backward compatibility: publish to service's workflow queue directly if router is not available
1039
1047
  const serviceQueue = `${nextStep.service}.workflow`;
1040
1048
  await this.mqClient.publish(serviceQueue, message);
1041
1049
  this.logger.info(`Published to queue: ${serviceQueue}`, {
@@ -1045,14 +1053,14 @@ class WorkflowOrchestrator {
1045
1053
  } else {
1046
1054
  // Control flow steps:
1047
1055
  // - if pinned (step.service), route to that service queue
1048
- // - otherwise fall back to workflow.init (any service may execute)
1056
+ // - otherwise route to workflow.control (any business service may execute)
1049
1057
  if (nextStep.service) {
1050
1058
  const serviceQueue = `${nextStep.service}.workflow`;
1051
1059
  await this.mqClient.publish(serviceQueue, message);
1052
1060
  this.logger.info(`Routed control flow to pinned service queue: ${serviceQueue}`, { step: nextStepId });
1053
1061
  } else {
1054
- await this.mqClient.publish('workflow.init', message);
1055
- this.logger.info(`Routed control flow to workflow.init`, { step: nextStepId });
1062
+ await this.mqClient.publish('workflow.control', message);
1063
+ this.logger.info(`Routed control flow to workflow.control`, { step: nextStepId });
1056
1064
  }
1057
1065
  }
1058
1066
  }
@@ -85,7 +85,7 @@ describe('WorkflowOrchestrator - Component Tests @component', () => {
85
85
  );
86
86
  });
87
87
 
88
- it('routes next pinned control-flow step to pinned service queue (not workflow.init)', async () => {
88
+ it('routes next pinned control-flow step to pinned service queue (not workflow.init/workflow.control)', async () => {
89
89
  const cookbook = {
90
90
  version: '2.1.0',
91
91
  api_input: { items: ['a'] },
@@ -115,6 +115,36 @@ describe('WorkflowOrchestrator - Component Tests @component', () => {
115
115
  })
116
116
  );
117
117
  });
118
+
119
+ it('routes next unpinned control-flow step to workflow.control', async () => {
120
+ const cookbook = {
121
+ version: '2.1.0',
122
+ api_input: { items: ['a'] },
123
+ steps: [
124
+ { step_id: 's1', type: 'task', service: 'svc-a', operation: 'op1', input: { x: 1 } },
125
+ {
126
+ step_id: 'loop',
127
+ type: 'foreach',
128
+ iterator: '{{api_input.items}}',
129
+ body: [
130
+ { step_id: 'inner', type: 'task', service: 'hello-service', operation: 'good-day', input: { name: '{{current}}' } }
131
+ ]
132
+ }
133
+ ],
134
+ delivery: { handler: 'none' }
135
+ };
136
+
137
+ const msg = { workflow_id: 'wf-3', cookbook, current_step: 's1' };
138
+ await orchestrator.processWorkflowMessage(msg, 'svc-a');
139
+
140
+ expect(mqClientStub.publish).toHaveBeenCalledWith(
141
+ 'workflow.control',
142
+ expect.objectContaining({
143
+ workflow_id: 'wf-3',
144
+ current_step: 'loop'
145
+ })
146
+ );
147
+ });
118
148
  });
119
149
 
120
150