@onlineapps/conn-orch-orchestrator 1.0.19 → 1.0.21
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 +97 -8
package/package.json
CHANGED
|
@@ -75,6 +75,9 @@ class WorkflowOrchestrator {
|
|
|
75
75
|
const { workflow_id, cookbook: cookbookDef, current_step, context } = message;
|
|
76
76
|
|
|
77
77
|
try {
|
|
78
|
+
// FAIL-FAST: Validate V2 format BEFORE anything else
|
|
79
|
+
this._validateV2Format(cookbookDef);
|
|
80
|
+
|
|
78
81
|
// Validate cookbook structure
|
|
79
82
|
this.cookbook.validateCookbook(cookbookDef);
|
|
80
83
|
|
|
@@ -89,7 +92,7 @@ class WorkflowOrchestrator {
|
|
|
89
92
|
// Find current step (V2: step_id only)
|
|
90
93
|
const step = cookbookDef.steps.find(s => s.step_id === current_step);
|
|
91
94
|
if (!step) {
|
|
92
|
-
throw new Error(`Step not found: ${current_step}`);
|
|
95
|
+
throw new Error(`Step not found: ${current_step}. Available steps: ${cookbookDef.steps.map(s => s.step_id).join(', ')}`);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
// Get step_id (V2)
|
|
@@ -300,7 +303,9 @@ class WorkflowOrchestrator {
|
|
|
300
303
|
|
|
301
304
|
/**
|
|
302
305
|
* Resolve variable references in input object
|
|
303
|
-
* Supports
|
|
306
|
+
* Supports both syntaxes:
|
|
307
|
+
* - {{steps.step_id.output.field}}, {{api_input.field}} (preferred, V2)
|
|
308
|
+
* - ${steps[N].output.field}, ${api_input.field} (legacy)
|
|
304
309
|
* @private
|
|
305
310
|
* @param {Object} input - Input object with potential references
|
|
306
311
|
* @param {Object} context - Current context with steps, api_input, etc
|
|
@@ -310,12 +315,39 @@ class WorkflowOrchestrator {
|
|
|
310
315
|
if (!input) return input;
|
|
311
316
|
|
|
312
317
|
const resolveValue = (value) => {
|
|
313
|
-
if (typeof value === 'string'
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
318
|
+
if (typeof value === 'string') {
|
|
319
|
+
let result = value;
|
|
320
|
+
|
|
321
|
+
// Replace {{...}} references (V2 preferred syntax)
|
|
322
|
+
if (result.includes('{{')) {
|
|
323
|
+
result = result.replace(/\{\{([^}]+)\}\}/g, (match, path) => {
|
|
324
|
+
const resolved = this._getValueFromPath(path.trim(), context);
|
|
325
|
+
if (resolved !== undefined) {
|
|
326
|
+
// If resolved value is an object, stringify it for embedding in strings
|
|
327
|
+
if (typeof resolved === 'object' && result !== match) {
|
|
328
|
+
return JSON.stringify(resolved);
|
|
329
|
+
}
|
|
330
|
+
return resolved;
|
|
331
|
+
}
|
|
332
|
+
return match;
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Replace ${...} references (legacy syntax)
|
|
337
|
+
if (result.includes('${')) {
|
|
338
|
+
result = result.replace(/\$\{([^}]+)\}/g, (match, path) => {
|
|
339
|
+
const resolved = this._getValueFromPath(path.trim(), context);
|
|
340
|
+
if (resolved !== undefined) {
|
|
341
|
+
if (typeof resolved === 'object' && result !== match) {
|
|
342
|
+
return JSON.stringify(resolved);
|
|
343
|
+
}
|
|
344
|
+
return resolved;
|
|
345
|
+
}
|
|
346
|
+
return match;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return result;
|
|
319
351
|
}
|
|
320
352
|
if (Array.isArray(value)) {
|
|
321
353
|
return value.map(resolveValue);
|
|
@@ -568,6 +600,63 @@ class WorkflowOrchestrator {
|
|
|
568
600
|
const mapper = new ResponseMapper();
|
|
569
601
|
return mapper.mapResponse(response, mapping);
|
|
570
602
|
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* FAIL-FAST: Validate V2 format strictly
|
|
606
|
+
* Rejects V1 format immediately with clear error message
|
|
607
|
+
* @private
|
|
608
|
+
* @param {Object} cookbook - Cookbook definition
|
|
609
|
+
* @throws {Error} If cookbook is not V2 format
|
|
610
|
+
*/
|
|
611
|
+
_validateV2Format(cookbook) {
|
|
612
|
+
if (!cookbook) {
|
|
613
|
+
throw new Error('FAIL-FAST: Cookbook is required');
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Check version format
|
|
617
|
+
const version = cookbook.version;
|
|
618
|
+
if (!version) {
|
|
619
|
+
throw new Error('FAIL-FAST: Cookbook version is required');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// V1 format detection - REJECT immediately
|
|
623
|
+
if (/^1\.\d+\.\d+$/.test(version)) {
|
|
624
|
+
throw new Error(`FAIL-FAST: V1 format (${version}) is DEPRECATED and no longer supported. Use V2 format (2.x.x) with step_id instead of id.`);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// Must be V2 format
|
|
628
|
+
if (!/^2\.\d+\.\d+$/.test(version)) {
|
|
629
|
+
throw new Error(`FAIL-FAST: Invalid version format '${version}'. Must be 2.x.x (e.g., 2.0.0)`);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Check steps
|
|
633
|
+
if (!cookbook.steps || !Array.isArray(cookbook.steps) || cookbook.steps.length === 0) {
|
|
634
|
+
throw new Error('FAIL-FAST: Cookbook must have at least one step');
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Validate each step has step_id (V2) not id (V1)
|
|
638
|
+
const invalidSteps = [];
|
|
639
|
+
cookbook.steps.forEach((step, index) => {
|
|
640
|
+
if (step.id && !step.step_id) {
|
|
641
|
+
invalidSteps.push(`Step ${index}: has 'id' (V1) but missing 'step_id' (V2)`);
|
|
642
|
+
}
|
|
643
|
+
if (!step.step_id) {
|
|
644
|
+
invalidSteps.push(`Step ${index}: missing required 'step_id'`);
|
|
645
|
+
}
|
|
646
|
+
if (!step.type) {
|
|
647
|
+
invalidSteps.push(`Step ${index} (${step.step_id || 'unknown'}): missing required 'type'`);
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
if (invalidSteps.length > 0) {
|
|
652
|
+
throw new Error(`FAIL-FAST: Invalid V2 cookbook format:\n${invalidSteps.join('\n')}`);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
this.logger.info('[WorkflowOrchestrator] V2 format validation passed', {
|
|
656
|
+
version: cookbook.version,
|
|
657
|
+
steps: cookbook.steps.map(s => s.step_id)
|
|
658
|
+
});
|
|
659
|
+
}
|
|
571
660
|
}
|
|
572
661
|
|
|
573
662
|
module.exports = WorkflowOrchestrator;
|