@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/conn-orch-orchestrator",
3
- "version": "1.0.10",
3
+ "version": "1.0.13",
4
4
  "description": "Workflow orchestration connector for OA Drive - handles message routing and workflow execution",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -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
- this.router = this.cookbook.createRouter(this.mqClient, this.registryClient, {
42
- logger: this.logger
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
- await this.router.routeToService(step.service, message);
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
- // Use API mapper to call the service
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
- step.input,
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
- await this.router.routeToService(nextStep.service, message);
326
- this.logger.info(`Routed to service: ${nextStep.service}`, {
327
- step: nextStep.id
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);