@onlineapps/conn-orch-orchestrator 1.0.33 → 1.0.39

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.33",
3
+ "version": "1.0.39",
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,14 +21,14 @@
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
23
  "@onlineapps/conn-base-monitoring": "^1.0.0",
24
- "@onlineapps/conn-infra-mq": "^1.1.0",
25
- "@onlineapps/conn-orch-registry": "^1.1.4",
24
+ "@onlineapps/conn-infra-mq": "1.1.53",
25
+ "@onlineapps/conn-orch-api-mapper": "^1.0.0",
26
26
  "@onlineapps/conn-orch-cookbook": "^2.0.0",
27
- "@onlineapps/conn-orch-api-mapper": "^1.0.0"
27
+ "@onlineapps/conn-orch-registry": "^1.1.4"
28
28
  },
29
29
  "devDependencies": {
30
- "jest": "^29.5.0",
31
30
  "eslint": "^8.30.0",
31
+ "jest": "^29.5.0",
32
32
  "jsdoc-to-markdown": "^8.0.0"
33
33
  },
34
34
  "jest": {
@@ -61,6 +61,25 @@ class WorkflowOrchestrator {
61
61
  return step?.step_id || step?.id;
62
62
  }
63
63
 
64
+ /**
65
+ * Get steps as array (supports both V1 array and V2 object formats)
66
+ * V2 FORMAT: steps is an object keyed by step_id
67
+ * V1 FORMAT (deprecated): steps is an array
68
+ * @private
69
+ * @param {Object} cookbookDef - Cookbook definition
70
+ * @returns {Array} Array of step objects
71
+ */
72
+ _getStepsArray(cookbookDef) {
73
+ if (!cookbookDef?.steps) return [];
74
+ if (Array.isArray(cookbookDef.steps)) {
75
+ // V1 format (deprecated) - steps is already an array
76
+ this.logger.warn('Using deprecated V1 cookbook format (steps as array). Please migrate to V2 (steps as object).');
77
+ return cookbookDef.steps;
78
+ }
79
+ // V2 format - steps is an object keyed by step_id
80
+ return Object.values(cookbookDef.steps);
81
+ }
82
+
64
83
  /**
65
84
  * Process a workflow message
66
85
  * @async
@@ -97,7 +116,9 @@ class WorkflowOrchestrator {
97
116
  };
98
117
 
99
118
  // Find current step (supports both V1 'id' and V2 'step_id')
100
- const step = cookbookDef.steps.find(s => this._getStepId(s) === current_step);
119
+ // V2 FORMAT: steps is an object keyed by step_id
120
+ const stepsArray = this._getStepsArray(cookbookDef);
121
+ const step = stepsArray.find(s => this._getStepId(s) === current_step);
101
122
  if (!step) {
102
123
  throw new Error(`Step not found: ${current_step}`);
103
124
  }
@@ -135,13 +156,14 @@ class WorkflowOrchestrator {
135
156
 
136
157
  // Update context with result - steps as ARRAY (consistent with cookbook.steps)
137
158
  // Each step preserves its definition (id/step_id, type, service, operation, input) and adds output
138
- const currentIndex = cookbookDef.steps.findIndex(s => this._getStepId(s) === current_step);
139
- const stepDefinition = cookbookDef.steps[currentIndex];
159
+ // stepsArray is already defined above via _getStepsArray()
160
+ const currentIndex = stepsArray.findIndex(s => this._getStepId(s) === current_step);
161
+ const stepDefinition = stepsArray[currentIndex];
140
162
 
141
163
  // Initialize steps array from cookbook if not present
142
164
  const existingSteps = Array.isArray(enrichedContext.steps)
143
165
  ? [...enrichedContext.steps]
144
- : cookbookDef.steps.map(s => ({ ...s })); // Deep copy of step definitions
166
+ : stepsArray.map(s => ({ ...s })); // Deep copy of step definitions
145
167
 
146
168
  // Update the current step with output (preserve id, type, service, operation, input)
147
169
  existingSteps[currentIndex] = {
@@ -182,8 +204,8 @@ class WorkflowOrchestrator {
182
204
  });
183
205
  }
184
206
 
185
- // Find next step (currentIndex already defined above)
186
- const nextStep = cookbookDef.steps[currentIndex + 1];
207
+ // Find next step (currentIndex and stepsArray already defined above)
208
+ const nextStep = stepsArray[currentIndex + 1];
187
209
 
188
210
  if (nextStep) {
189
211
  // Route to next step
@@ -304,7 +326,7 @@ class WorkflowOrchestrator {
304
326
 
305
327
  /**
306
328
  * Resolve variable references in input object
307
- * Supports ${steps[N].output.field}, ${api_input.field}, ${context.field}
329
+ * Supports {{steps.step_id.output.field}}, {{api_input.field}}, {{context.field}}
308
330
  * @private
309
331
  * @param {Object} input - Input object with potential references
310
332
  * @param {Object} context - Current context with steps, api_input, etc
@@ -314,11 +336,24 @@ class WorkflowOrchestrator {
314
336
  if (!input) return input;
315
337
 
316
338
  const resolveValue = (value) => {
317
- if (typeof value === 'string' && value.includes('${')) {
318
- // Replace ${...} references
319
- return value.replace(/\$\{([^}]+)\}/g, (match, path) => {
339
+ if (typeof value === 'string' && value.includes('{{')) {
340
+ // Check if the ENTIRE string is a single template reference
341
+ // e.g. "{{steps.greeting.output}}" should return the object directly
342
+ const singleRefMatch = value.match(/^\{\{([^}]+)\}\}$/);
343
+ if (singleRefMatch) {
344
+ // Entire value is a single reference - return the resolved value directly (preserves objects)
345
+ const resolved = this._getValueFromPath(singleRefMatch[1].trim(), context);
346
+ return resolved !== undefined ? resolved : value;
347
+ }
348
+
349
+ // Multiple references or mixed with text - use string replacement
350
+ // e.g. "Hello {{name}}, your order {{orderId}}" becomes "Hello John, your order 123"
351
+ return value.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
320
352
  const resolved = this._getValueFromPath(path.trim(), context);
321
- return resolved !== undefined ? resolved : match;
353
+ if (resolved === undefined) return match;
354
+ // For string concatenation, convert objects to JSON string
355
+ if (typeof resolved === 'object') return JSON.stringify(resolved);
356
+ return String(resolved);
322
357
  });
323
358
  }
324
359
  if (Array.isArray(value)) {
@@ -338,7 +373,7 @@ class WorkflowOrchestrator {
338
373
  /**
339
374
  * Get value from dot/bracket notation path
340
375
  * @private
341
- * @param {string} path - Path like "steps[0].output.message" or "api_input.name"
376
+ * @param {string} path - Path like "steps.greeting.output.message" or "api_input.name"
342
377
  * @param {Object} context - Context object
343
378
  * @returns {*} Resolved value or undefined
344
379
  */
@@ -356,9 +391,20 @@ class WorkflowOrchestrator {
356
391
  const parts = normalized.split('.');
357
392
 
358
393
  let current = context;
359
- for (const part of parts) {
394
+ for (let i = 0; i < parts.length; i++) {
395
+ const part = parts[i];
360
396
  if (current === undefined || current === null) return undefined;
361
- current = current[part];
397
+
398
+ // Special handling for 'steps' array - lookup by step id
399
+ // context.steps is an array of {id/step_id: "step_id", output: {...}}
400
+ // Path like "steps.greeting.output" should find step with id="greeting"
401
+ if (parts[i - 1] === 'steps' && Array.isArray(context.steps)) {
402
+ // 'part' is the step_id, find the step in the array (support both 'id' and 'step_id')
403
+ const step = context.steps.find(s => s.id === part || s.step_id === part);
404
+ current = step;
405
+ } else {
406
+ current = current[part];
407
+ }
362
408
  }
363
409
  return current;
364
410
  }