@onlineapps/service-wrapper 2.0.21 → 2.0.23
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/API.md +0 -0
- package/README.md +0 -0
- package/docs/CONFIGURATION_GUIDE.md +0 -0
- package/docs/FINAL_ARCHITECTURE.md +0 -0
- package/docs/MIGRATION_FROM_OPENAPI.md +0 -0
- package/docs/STANDARDS_OVERVIEW.md +0 -0
- package/docs/archived-2025-09-29/API_STRUCTURE_STANDARD.md +0 -0
- package/docs/archived-2025-09-29/ARCHITECTURE_DECISION.md +0 -0
- package/docs/archived-2025-09-29/HANDLER_VS_HTTP_COMPARISON.md +0 -0
- package/docs/archived-2025-09-29/INSTALLATION_GUIDE.md +0 -0
- package/docs/archived-2025-09-29/OPERATIONS_SCHEMA.md +0 -0
- package/docs/archived-2025-09-29/PERFORMANCE.md +0 -0
- package/docs/archived-2025-09-29/PROCESS_FLOWS.md +0 -0
- package/docs/archived-2025-09-29/SERVICE_TESTING_STANDARD.md +0 -0
- package/docs/archived-2025-09-29/WRAPPER_ARCHITECTURE.md +0 -0
- package/examples/README.md +0 -0
- package/examples/cookbook-file-output.json +0 -0
- package/examples/cookbook-multi-step.json +0 -0
- package/examples/cookbook-single-operation.json +0 -0
- package/jest.config.js +0 -0
- package/jsdoc.json +0 -0
- package/package.json +1 -1
- package/src/ServiceWrapper.js +110 -24
- package/src/index.js +0 -0
- package/tests/component/ServiceWrapper.component.test.js +0 -407
- package/tests/component/connector-integration.test.js +0 -293
- package/tests/e2e/full-flow.test.js +0 -293
- package/tests/integration/orchestrator-integration.test.js +0 -170
- package/tests/mocks/connectors.js +0 -304
- package/tests/monitoring-integration.test.js +0 -150
- package/tests/run-tests.js +0 -135
- package/tests/setup.js +0 -31
- package/tests/unit/ServiceWrapper.test.js +0 -372
package/API.md
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/examples/README.md
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/jest.config.js
CHANGED
|
File without changes
|
package/jsdoc.json
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/src/ServiceWrapper.js
CHANGED
|
@@ -37,6 +37,7 @@ class ServiceWrapper {
|
|
|
37
37
|
* @param {Object} options.operations - Operations schema
|
|
38
38
|
* @param {string} [options.serviceRoot] - Service root directory (for validation)
|
|
39
39
|
* @param {Object} [options.validationProof=null] - Optional validation proof {hash, data}
|
|
40
|
+
* @param {Object} [options._validationOrchestrator=null] - Optional ValidationOrchestrator for testing
|
|
40
41
|
*/
|
|
41
42
|
constructor(options = {}) {
|
|
42
43
|
this._validateOptions(options);
|
|
@@ -48,6 +49,7 @@ class ServiceWrapper {
|
|
|
48
49
|
this.operations = options.operations;
|
|
49
50
|
this.serviceRoot = options.serviceRoot;
|
|
50
51
|
this.validationProof = options.validationProof || null;
|
|
52
|
+
this._injectedValidationOrchestrator = options._validationOrchestrator || null;
|
|
51
53
|
|
|
52
54
|
// Initialize connector placeholders
|
|
53
55
|
this.mqClient = null;
|
|
@@ -149,19 +151,26 @@ class ServiceWrapper {
|
|
|
149
151
|
await this._initializeMonitoring();
|
|
150
152
|
}
|
|
151
153
|
|
|
152
|
-
// 2.
|
|
154
|
+
// 2. Initialize MQ connection (needed for workflow.init listener)
|
|
155
|
+
if (this.config.wrapper?.mq?.enabled !== false) {
|
|
156
|
+
await this._initializeMQ();
|
|
157
|
+
|
|
158
|
+
// Start workflow.init listener immediately (independent of service registration)
|
|
159
|
+
// This allows the system to receive workflow messages even if service validation/registration fails
|
|
160
|
+
await this._startWorkflowInitListener();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 3. Run pre-validation (Tier 1)
|
|
153
164
|
// Validates service structure, config, operations, cookbook tests
|
|
154
165
|
// Skips if valid proof exists in conn-runtime/validation-proof.json
|
|
166
|
+
// NOTE: Validation is required for service-specific workflow listener and registration
|
|
155
167
|
if (this.serviceRoot && this.config.wrapper?.validation?.enabled !== false) {
|
|
156
168
|
await this._ensureValidationProof();
|
|
157
169
|
}
|
|
158
170
|
|
|
159
|
-
// 3. Initialize MQ connection
|
|
160
|
-
if (this.config.wrapper?.mq?.enabled !== false) {
|
|
161
|
-
await this._initializeMQ();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
171
|
// 4. Initialize service registry
|
|
172
|
+
// NOTE: Service-specific workflow listener ({serviceName}.workflow) will be started
|
|
173
|
+
// ONLY after successful validation and registration (see _initializeRegistry)
|
|
165
174
|
if (this.config.wrapper?.registry?.enabled !== false) {
|
|
166
175
|
await this._initializeRegistry();
|
|
167
176
|
}
|
|
@@ -306,12 +315,13 @@ class ServiceWrapper {
|
|
|
306
315
|
console.log(`✓ Certificate received: ${registrationResult.certificate.certificateId}`);
|
|
307
316
|
}
|
|
308
317
|
|
|
309
|
-
// CRITICAL: Only start workflow
|
|
318
|
+
// CRITICAL: Only start service-specific workflow listener after successful validation and registration with certificate
|
|
319
|
+
// workflow.init listener is already running (started during MQ initialization)
|
|
310
320
|
if (registrationResult.success && registrationResult.certificate) {
|
|
311
|
-
console.log('✓ Certificate validated, starting workflow
|
|
312
|
-
await this.
|
|
321
|
+
console.log('✓ Certificate validated, starting service-specific workflow listener...');
|
|
322
|
+
await this._startServiceWorkflowListener();
|
|
313
323
|
} else {
|
|
314
|
-
console.warn('⚠ Registration succeeded but no certificate received - workflow
|
|
324
|
+
console.warn('⚠ Registration succeeded but no certificate received - service-specific workflow listener NOT started');
|
|
315
325
|
}
|
|
316
326
|
|
|
317
327
|
} catch (error) {
|
|
@@ -401,12 +411,25 @@ class ServiceWrapper {
|
|
|
401
411
|
const servicePort = this.config.service?.port || process.env.PORT || 3000;
|
|
402
412
|
const serviceUrl = `http://localhost:${servicePort}`;
|
|
403
413
|
|
|
404
|
-
// Create error handler
|
|
414
|
+
// Create error handler with monitoring instance
|
|
405
415
|
const errorHandler = new ErrorHandlerConnector({
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
416
|
+
serviceName: this.config.service?.name || 'unnamed-service',
|
|
417
|
+
serviceVersion: this.config.service?.version || '0.0.0',
|
|
418
|
+
environment: process.env.NODE_ENV || 'development',
|
|
419
|
+
monitoring: this.monitoring, // Use monitoring instance from conn-base-monitoring
|
|
420
|
+
handling: {
|
|
421
|
+
maxRetries: this.config.wrapper?.errorHandling?.maxRetries || 3,
|
|
422
|
+
retryDelay: this.config.wrapper?.errorHandling?.retryDelay || 1000,
|
|
423
|
+
retryMultiplier: this.config.wrapper?.errorHandling?.retryMultiplier || 2,
|
|
424
|
+
circuitBreakerEnabled: this.config.wrapper?.errorHandling?.circuitBreakerEnabled !== false,
|
|
425
|
+
dlqEnabled: this.config.wrapper?.errorHandling?.dlqEnabled !== false,
|
|
426
|
+
mqClient: this.mqClient, // For DLQ routing
|
|
427
|
+
compensationEnabled: this.config.wrapper?.errorHandling?.compensationEnabled !== false
|
|
428
|
+
}
|
|
409
429
|
});
|
|
430
|
+
|
|
431
|
+
// Store error handler for use in message processing
|
|
432
|
+
this.errorHandler = errorHandler;
|
|
410
433
|
|
|
411
434
|
// Create API mapper for HTTP calls (never direct calls)
|
|
412
435
|
const apiMapper = ApiMapperConnector.create({
|
|
@@ -432,25 +455,43 @@ class ServiceWrapper {
|
|
|
432
455
|
}
|
|
433
456
|
|
|
434
457
|
/**
|
|
435
|
-
* Start workflow
|
|
436
|
-
*
|
|
458
|
+
* Start workflow.init listener (runs independently of service registration)
|
|
459
|
+
* This allows the system to receive workflow messages even if service validation/registration fails
|
|
437
460
|
* @private
|
|
438
461
|
*/
|
|
439
|
-
async
|
|
440
|
-
|
|
462
|
+
async _startWorkflowInitListener() {
|
|
463
|
+
if (!this.mqClient) {
|
|
464
|
+
console.warn('Cannot start workflow.init listener: MQ client not initialized');
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
441
467
|
|
|
442
468
|
// Subscribe to workflow.init queue (for all services)
|
|
443
469
|
await this.mqClient.consume('workflow.init', async (message) => {
|
|
444
470
|
await this._processWorkflowMessage(message, 'workflow.init');
|
|
445
471
|
});
|
|
446
472
|
|
|
447
|
-
|
|
473
|
+
console.log('✓ workflow.init listener started (independent of service registration)');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Start service-specific workflow listener (runs only after successful validation and registration)
|
|
478
|
+
* @private
|
|
479
|
+
*/
|
|
480
|
+
async _startServiceWorkflowListener() {
|
|
481
|
+
if (!this.mqClient) {
|
|
482
|
+
console.warn('Cannot start service workflow listener: MQ client not initialized');
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const serviceName = this.config.service?.name || 'unnamed-service';
|
|
448
487
|
const serviceQueue = `${serviceName}.workflow`;
|
|
488
|
+
|
|
489
|
+
// Subscribe to service-specific queue
|
|
449
490
|
await this.mqClient.consume(serviceQueue, async (message) => {
|
|
450
491
|
await this._processWorkflowMessage(message, serviceQueue);
|
|
451
492
|
});
|
|
452
493
|
|
|
453
|
-
console.log(
|
|
494
|
+
console.log(`✓ Service-specific workflow listener started: ${serviceQueue}`);
|
|
454
495
|
}
|
|
455
496
|
|
|
456
497
|
/**
|
|
@@ -517,17 +558,61 @@ class ServiceWrapper {
|
|
|
517
558
|
};
|
|
518
559
|
|
|
519
560
|
try {
|
|
561
|
+
// Detailed logging before publish
|
|
562
|
+
this.logger?.info(`[ServiceWrapper] Preparing to publish workflow.completed`, {
|
|
563
|
+
workflowId: message.workflow_id,
|
|
564
|
+
service: serviceName,
|
|
565
|
+
operation: workflowResponse.operation,
|
|
566
|
+
mqClientConnected: this.mqClient._connected,
|
|
567
|
+
hasTransport: !!this.mqClient._transport,
|
|
568
|
+
hasChannel: !!(this.mqClient._transport?._channel)
|
|
569
|
+
});
|
|
570
|
+
|
|
520
571
|
await this.mqClient.publish('workflow.completed', workflowResponse);
|
|
521
|
-
|
|
572
|
+
|
|
573
|
+
// Log success with details
|
|
574
|
+
this.logger?.info(`✓ Workflow response sent to workflow.completed: ${message.workflow_id}`, {
|
|
575
|
+
workflowId: message.workflow_id,
|
|
576
|
+
queue: 'workflow.completed',
|
|
577
|
+
responseSize: JSON.stringify(workflowResponse).length
|
|
578
|
+
});
|
|
522
579
|
} catch (error) {
|
|
523
|
-
|
|
580
|
+
// Use error handler to log and handle the error
|
|
581
|
+
this.logger?.error(`[ServiceWrapper] Failed to publish workflow.completed`, {
|
|
582
|
+
workflowId: message.workflow_id,
|
|
583
|
+
queue: 'workflow.completed',
|
|
584
|
+
error: error.message,
|
|
585
|
+
errorStack: error.stack,
|
|
586
|
+
mqClientConnected: this.mqClient?._connected,
|
|
587
|
+
hasTransport: !!this.mqClient?._transport,
|
|
588
|
+
hasChannel: !!(this.mqClient?._transport?._channel)
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
if (this.errorHandler) {
|
|
592
|
+
await this.errorHandler.logError({
|
|
593
|
+
moduleName: 'ServiceWrapper',
|
|
594
|
+
operation: 'sendWorkflowResponse',
|
|
595
|
+
error: error,
|
|
596
|
+
context: { workflowId: message.workflow_id, queue: 'workflow.completed' }
|
|
597
|
+
});
|
|
598
|
+
}
|
|
524
599
|
}
|
|
525
600
|
}
|
|
526
601
|
|
|
527
602
|
return result;
|
|
528
603
|
|
|
529
604
|
} catch (error) {
|
|
530
|
-
|
|
605
|
+
// Use error handler to log and handle the error
|
|
606
|
+
if (this.errorHandler) {
|
|
607
|
+
await this.errorHandler.logError({
|
|
608
|
+
moduleName: 'ServiceWrapper',
|
|
609
|
+
operation: 'processMessage',
|
|
610
|
+
error: error,
|
|
611
|
+
context: { queueName, workflowId: message?.workflow_id }
|
|
612
|
+
});
|
|
613
|
+
} else {
|
|
614
|
+
this.logger?.error(`Error processing message from ${queueName}:`, error);
|
|
615
|
+
}
|
|
531
616
|
throw error;
|
|
532
617
|
}
|
|
533
618
|
}
|
|
@@ -696,7 +781,8 @@ class ServiceWrapper {
|
|
|
696
781
|
|
|
697
782
|
console.log('[ServiceWrapper] Checking validation proof...');
|
|
698
783
|
|
|
699
|
-
|
|
784
|
+
// Use injected orchestrator (for testing) or create new one (production)
|
|
785
|
+
const orchestrator = this._injectedValidationOrchestrator || new ValidationOrchestrator({
|
|
700
786
|
serviceRoot: this.serviceRoot,
|
|
701
787
|
serviceName,
|
|
702
788
|
serviceVersion,
|
package/src/index.js
CHANGED
|
File without changes
|