@onlineapps/conn-orch-orchestrator 1.0.10 → 1.0.13
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 +1 -1
- package/src/WorkflowOrchestrator.js +96 -12
package/package.json
CHANGED
|
@@ -37,10 +37,18 @@ class WorkflowOrchestrator {
|
|
|
37
37
|
this.logger = config.logger || console;
|
|
38
38
|
this.defaultTimeout = config.defaultTimeout || 30000;
|
|
39
39
|
|
|
40
|
-
// Create cookbook router
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
// Create cookbook router - createRouter is an exported function, not a method
|
|
41
|
+
const { createRouter } = this.cookbook;
|
|
42
|
+
if (typeof createRouter === 'function') {
|
|
43
|
+
this.router = createRouter(this.mqClient, this.registryClient, {
|
|
44
|
+
logger: this.logger
|
|
45
|
+
});
|
|
46
|
+
} else {
|
|
47
|
+
// Fallback: try calling it as method (backward compatibility)
|
|
48
|
+
this.router = this.cookbook.createRouter?.(this.mqClient, this.registryClient, {
|
|
49
|
+
logger: this.logger
|
|
50
|
+
}) || null;
|
|
51
|
+
}
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
/**
|
|
@@ -87,7 +95,13 @@ class WorkflowOrchestrator {
|
|
|
87
95
|
// Check if this step is for this service
|
|
88
96
|
if (step.type === 'task' && step.service !== serviceName) {
|
|
89
97
|
this.logger.warn(`Step ${current_step} is for ${step.service}, not ${serviceName}`);
|
|
90
|
-
|
|
98
|
+
if (this.router?.routeToService) {
|
|
99
|
+
await this.router.routeToService(step.service, message);
|
|
100
|
+
} else {
|
|
101
|
+
// Fallback: publish to service's workflow queue directly
|
|
102
|
+
const serviceQueue = `${step.service}.workflow`;
|
|
103
|
+
await this.mqClient.publish(serviceQueue, message);
|
|
104
|
+
}
|
|
91
105
|
return { skipped: true, reason: 'wrong_service' };
|
|
92
106
|
}
|
|
93
107
|
|
|
@@ -251,21 +265,82 @@ class WorkflowOrchestrator {
|
|
|
251
265
|
* @returns {Promise<Object>} API call result
|
|
252
266
|
*/
|
|
253
267
|
async _executeTaskStep(step, context) {
|
|
254
|
-
//
|
|
268
|
+
// Resolve variable references in step.input (e.g. ${steps[0].output.message})
|
|
269
|
+
console.log(`[WorkflowOrchestrator] [RESOLVE] Step ${step.id} input BEFORE:`, JSON.stringify(step.input));
|
|
270
|
+
console.log(`[WorkflowOrchestrator] [RESOLVE] Context.steps:`, JSON.stringify(context.steps?.map(s => ({ id: s.id, output: s.output }))));
|
|
271
|
+
|
|
272
|
+
const resolvedInput = this._resolveInputReferences(step.input, context);
|
|
273
|
+
console.log(`[WorkflowOrchestrator] [RESOLVE] Step ${step.id} input AFTER:`, JSON.stringify(resolvedInput));
|
|
274
|
+
|
|
275
|
+
// Use API mapper to call the service with resolved input
|
|
255
276
|
const result = await this.apiMapper.callOperation(
|
|
256
277
|
step.operation || step.id,
|
|
257
|
-
|
|
278
|
+
resolvedInput,
|
|
258
279
|
context
|
|
259
280
|
);
|
|
260
281
|
|
|
261
282
|
this.logger.info(`Task step executed`, {
|
|
262
283
|
step: step.id,
|
|
263
284
|
service: step.service,
|
|
264
|
-
operation: step.operation
|
|
285
|
+
operation: step.operation,
|
|
286
|
+
inputResolved: !!resolvedInput
|
|
265
287
|
});
|
|
266
288
|
|
|
267
289
|
return result;
|
|
268
290
|
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Resolve variable references in input object
|
|
294
|
+
* Supports ${steps[N].output.field}, ${api_input.field}, ${context.field}
|
|
295
|
+
* @private
|
|
296
|
+
* @param {Object} input - Input object with potential references
|
|
297
|
+
* @param {Object} context - Current context with steps, api_input, etc
|
|
298
|
+
* @returns {Object} Resolved input object
|
|
299
|
+
*/
|
|
300
|
+
_resolveInputReferences(input, context) {
|
|
301
|
+
if (!input) return input;
|
|
302
|
+
|
|
303
|
+
const resolveValue = (value) => {
|
|
304
|
+
if (typeof value === 'string' && value.includes('${')) {
|
|
305
|
+
// Replace ${...} references
|
|
306
|
+
return value.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
307
|
+
const resolved = this._getValueFromPath(path.trim(), context);
|
|
308
|
+
return resolved !== undefined ? resolved : match;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
if (Array.isArray(value)) {
|
|
312
|
+
return value.map(resolveValue);
|
|
313
|
+
}
|
|
314
|
+
if (value && typeof value === 'object') {
|
|
315
|
+
return Object.fromEntries(
|
|
316
|
+
Object.entries(value).map(([k, v]) => [k, resolveValue(v)])
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
return value;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
return resolveValue(input);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get value from dot/bracket notation path
|
|
327
|
+
* @private
|
|
328
|
+
* @param {string} path - Path like "steps[0].output.message" or "api_input.name"
|
|
329
|
+
* @param {Object} context - Context object
|
|
330
|
+
* @returns {*} Resolved value or undefined
|
|
331
|
+
*/
|
|
332
|
+
_getValueFromPath(path, context) {
|
|
333
|
+
// Normalize bracket notation to dot notation: steps[0] -> steps.0
|
|
334
|
+
const normalized = path.replace(/\[(\d+)\]/g, '.$1');
|
|
335
|
+
const parts = normalized.split('.');
|
|
336
|
+
|
|
337
|
+
let current = context;
|
|
338
|
+
for (const part of parts) {
|
|
339
|
+
if (current === undefined || current === null) return undefined;
|
|
340
|
+
current = current[part];
|
|
341
|
+
}
|
|
342
|
+
return current;
|
|
343
|
+
}
|
|
269
344
|
|
|
270
345
|
/**
|
|
271
346
|
* Execute control flow step (foreach, switch, fork_join, etc)
|
|
@@ -322,10 +397,19 @@ class WorkflowOrchestrator {
|
|
|
322
397
|
|
|
323
398
|
if (nextStep.type === 'task') {
|
|
324
399
|
// Route to specific service
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
400
|
+
if (this.router?.routeToService) {
|
|
401
|
+
await this.router.routeToService(nextStep.service, message);
|
|
402
|
+
this.logger.info(`Routed to service: ${nextStep.service}`, {
|
|
403
|
+
step: nextStep.id
|
|
404
|
+
});
|
|
405
|
+
} else {
|
|
406
|
+
// Fallback: publish to service's workflow queue directly
|
|
407
|
+
const serviceQueue = `${nextStep.service}.workflow`;
|
|
408
|
+
await this.mqClient.publish(serviceQueue, message);
|
|
409
|
+
this.logger.info(`Published to queue: ${serviceQueue}`, {
|
|
410
|
+
step: nextStep.id
|
|
411
|
+
});
|
|
412
|
+
}
|
|
329
413
|
} else {
|
|
330
414
|
// Control flow steps handled by workflow init queue
|
|
331
415
|
await this.mqClient.publish('workflow.init', message);
|