@onlineapps/service-wrapper 2.0.17 → 2.0.19
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/README.md +1 -1
- package/docs/MIGRATION_FROM_OPENAPI.md +9 -11
- package/docs/archived-2025-09-29/SERVICE_TESTING_STANDARD.md +2 -2
- package/jest.config.js +8 -8
- package/package.json +5 -4
- package/src/ServiceWrapper.js +92 -12
- package/{test → tests}/component/ServiceWrapper.component.test.js +1 -1
- package/{test → tests}/component/connector-integration.test.js +1 -1
- package/{test → tests}/e2e/full-flow.test.js +1 -1
- package/{test → tests}/monitoring-integration.test.js +1 -1
- package/{test → tests}/unit/ServiceWrapper.test.js +1 -1
- /package/{test → tests}/integration/orchestrator-integration.test.js +0 -0
- /package/{test → tests}/mocks/connectors.js +0 -0
- /package/{test → tests}/run-tests.js +0 -0
- /package/{test → tests}/setup.js +0 -0
package/README.md
CHANGED
|
@@ -239,7 +239,7 @@ describe('My Service', () => {
|
|
|
239
239
|
If your service currently has workflow code:
|
|
240
240
|
|
|
241
241
|
1. Install service-wrapper: `npm install @onlineapps/service-wrapper`
|
|
242
|
-
2. Remove all
|
|
242
|
+
2. Remove all direct connector imports from service code (use wrapper instead)
|
|
243
243
|
3. Delete workflow processing files
|
|
244
244
|
4. Wrap your Express app with ServiceWrapper
|
|
245
245
|
5. Test that everything still works
|
|
@@ -110,7 +110,7 @@ Transform your OpenAPI spec into Operations format:
|
|
|
110
110
|
```bash
|
|
111
111
|
# Old structure
|
|
112
112
|
services/my-service/
|
|
113
|
-
├──
|
|
113
|
+
├── conn-config/
|
|
114
114
|
│ ├── manifest.json
|
|
115
115
|
│ └── openapi.json
|
|
116
116
|
|
|
@@ -121,17 +121,14 @@ services/my-service/
|
|
|
121
121
|
│ └── operations.json
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
Convert configuration:
|
|
125
125
|
```bash
|
|
126
126
|
cd services/my-service
|
|
127
|
-
mkdir -p conn-config
|
|
128
127
|
|
|
129
|
-
# If you have existing
|
|
130
|
-
|
|
128
|
+
# If you have existing manifest.json, merge it into config.json
|
|
129
|
+
# Move from manifest.json to config.json structure
|
|
131
130
|
|
|
132
131
|
# Create operations.json (see conversion below)
|
|
133
|
-
# Delete old directory
|
|
134
|
-
rm -rf connector-config/
|
|
135
132
|
```
|
|
136
133
|
|
|
137
134
|
### Step 3: Convert OpenAPI to Operations
|
|
@@ -153,11 +150,12 @@ Use this mapping:
|
|
|
153
150
|
#### Before:
|
|
154
151
|
```javascript
|
|
155
152
|
const { ServiceWrapper } = require('@onlineapps/service-wrapper');
|
|
156
|
-
const openApiSpec = require('./
|
|
153
|
+
const openApiSpec = require('./conn-config/openapi.json');
|
|
157
154
|
|
|
158
155
|
const wrapper = new ServiceWrapper({
|
|
159
156
|
app,
|
|
160
157
|
server,
|
|
158
|
+
serviceRoot: __dirname,
|
|
161
159
|
config,
|
|
162
160
|
openApiSpec // OLD
|
|
163
161
|
});
|
|
@@ -171,6 +169,7 @@ const operations = require('./conn-config/operations.json');
|
|
|
171
169
|
const wrapper = new ServiceWrapper({
|
|
172
170
|
app,
|
|
173
171
|
server,
|
|
172
|
+
serviceRoot: __dirname,
|
|
174
173
|
config,
|
|
175
174
|
operations // NEW
|
|
176
175
|
});
|
|
@@ -181,7 +180,7 @@ const wrapper = new ServiceWrapper({
|
|
|
181
180
|
#### Before:
|
|
182
181
|
```javascript
|
|
183
182
|
app.get('/specification', (req, res) => {
|
|
184
|
-
const openapi = require('./
|
|
183
|
+
const openapi = require('./conn-config/openapi.json');
|
|
185
184
|
res.json(openapi);
|
|
186
185
|
});
|
|
187
186
|
```
|
|
@@ -197,7 +196,6 @@ app.get('/specification', (req, res) => {
|
|
|
197
196
|
### Step 6: Update Documentation References
|
|
198
197
|
|
|
199
198
|
Update all references:
|
|
200
|
-
- `connector-config/` → `conn-config/`
|
|
201
199
|
- `openapi.json` → `operations.json`
|
|
202
200
|
- "OpenAPI specification" → "Operations specification"
|
|
203
201
|
|
|
@@ -271,7 +269,7 @@ function convertSchema(schema) {
|
|
|
271
269
|
}
|
|
272
270
|
|
|
273
271
|
// Usage
|
|
274
|
-
const openapi = require('./
|
|
272
|
+
const openapi = require('./conn-config/openapi.json');
|
|
275
273
|
const operations = convertOpenAPIToOperations(openapi);
|
|
276
274
|
fs.writeFileSync('./conn-config/operations.json', JSON.stringify(operations, null, 2));
|
|
277
275
|
console.log('Converted to operations.json');
|
|
@@ -365,7 +365,7 @@ servers:
|
|
|
365
365
|
description: Configured base URL
|
|
366
366
|
```
|
|
367
367
|
|
|
368
|
-
## 9. Testing with conn-
|
|
368
|
+
## 9. Testing with conn-orch-validator
|
|
369
369
|
|
|
370
370
|
The connector testing framework will:
|
|
371
371
|
1. Load your OpenAPI schema
|
|
@@ -386,4 +386,4 @@ Ensure your service is ready by:
|
|
|
386
386
|
- [JSON Schema Validation](https://json-schema.org/draft/2019-09/json-schema-validation.html)
|
|
387
387
|
- [swagger-cli Documentation](https://apitools.dev/swagger-cli/)
|
|
388
388
|
- Service Wrapper documentation: `/shared/connector/service-wrapper/README.md`
|
|
389
|
-
- Testing examples: `/shared/connector/conn-
|
|
389
|
+
- Testing examples: `/shared/connector/conn-orch-validator/examples/`
|
package/jest.config.js
CHANGED
|
@@ -7,7 +7,7 @@ module.exports = {
|
|
|
7
7
|
'!src/**/index.js'
|
|
8
8
|
],
|
|
9
9
|
testMatch: [
|
|
10
|
-
'**/
|
|
10
|
+
'**/tests/**/*.test.js'
|
|
11
11
|
],
|
|
12
12
|
testPathIgnorePatterns: [
|
|
13
13
|
'/node_modules/'
|
|
@@ -22,13 +22,13 @@ module.exports = {
|
|
|
22
22
|
},
|
|
23
23
|
moduleNameMapper: {
|
|
24
24
|
// Map connector imports to mocks in unit tests
|
|
25
|
-
'@onlineapps/conn-infra-mq': '<rootDir>/
|
|
26
|
-
'@onlineapps/conn-orch-registry': '<rootDir>/
|
|
27
|
-
'@onlineapps/conn-base-logger': '<rootDir>/
|
|
28
|
-
'@onlineapps/conn-orch-orchestrator': '<rootDir>/
|
|
29
|
-
'@onlineapps/conn-orch-api-mapper': '<rootDir>/
|
|
30
|
-
'@onlineapps/conn-orch-cookbook': '<rootDir>/
|
|
25
|
+
'@onlineapps/conn-infra-mq': '<rootDir>/tests/mocks/connectors.js',
|
|
26
|
+
'@onlineapps/conn-orch-registry': '<rootDir>/tests/mocks/connectors.js',
|
|
27
|
+
'@onlineapps/conn-base-logger': '<rootDir>/tests/mocks/connectors.js',
|
|
28
|
+
'@onlineapps/conn-orch-orchestrator': '<rootDir>/tests/mocks/connectors.js',
|
|
29
|
+
'@onlineapps/conn-orch-api-mapper': '<rootDir>/tests/mocks/connectors.js',
|
|
30
|
+
'@onlineapps/conn-orch-cookbook': '<rootDir>/tests/mocks/connectors.js'
|
|
31
31
|
},
|
|
32
|
-
setupFilesAfterEnv: ['<rootDir>/
|
|
32
|
+
setupFilesAfterEnv: ['<rootDir>/tests/setup.js'],
|
|
33
33
|
verbose: true
|
|
34
34
|
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.19",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "jest",
|
|
8
|
-
"test:unit": "jest
|
|
9
|
-
"test:component": "jest
|
|
10
|
-
"test:integration": "jest
|
|
8
|
+
"test:unit": "jest tests/unit",
|
|
9
|
+
"test:component": "jest tests/component",
|
|
10
|
+
"test:integration": "jest tests/integration",
|
|
11
11
|
"test:coverage": "jest --coverage",
|
|
12
12
|
"test:mocked": "node test/run-tests.js",
|
|
13
13
|
"docs": "jsdoc2md --files src/**/*.js > API.md",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"@onlineapps/conn-orch-cookbook": "^2.0.0",
|
|
33
33
|
"@onlineapps/conn-orch-orchestrator": "^1.0.1",
|
|
34
34
|
"@onlineapps/conn-orch-registry": "^1.1.13",
|
|
35
|
+
"@onlineapps/conn-orch-validator": "^2.0.0",
|
|
35
36
|
"@onlineapps/monitoring-core": "^1.0.0"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
package/src/ServiceWrapper.js
CHANGED
|
@@ -21,6 +21,7 @@ const ApiMapperConnector = require('@onlineapps/conn-orch-api-mapper');
|
|
|
21
21
|
const CookbookConnector = require('@onlineapps/conn-orch-cookbook');
|
|
22
22
|
const CacheConnector = require('@onlineapps/conn-base-cache');
|
|
23
23
|
const ErrorHandlerConnector = require('@onlineapps/conn-infra-error-handler');
|
|
24
|
+
const { ValidationOrchestrator } = require('@onlineapps/conn-orch-validator');
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* ServiceWrapper class
|
|
@@ -34,6 +35,7 @@ class ServiceWrapper {
|
|
|
34
35
|
* @param {Object} options.server - HTTP server instance
|
|
35
36
|
* @param {Object} options.config - Service and wrapper configuration
|
|
36
37
|
* @param {Object} options.operations - Operations schema
|
|
38
|
+
* @param {string} [options.serviceRoot] - Service root directory (for validation)
|
|
37
39
|
* @param {Object} [options.validationProof=null] - Optional validation proof {hash, data}
|
|
38
40
|
*/
|
|
39
41
|
constructor(options = {}) {
|
|
@@ -44,6 +46,7 @@ class ServiceWrapper {
|
|
|
44
46
|
this.server = options.server;
|
|
45
47
|
this.config = this._processConfig(options.config);
|
|
46
48
|
this.operations = options.operations;
|
|
49
|
+
this.serviceRoot = options.serviceRoot;
|
|
47
50
|
this.validationProof = options.validationProof || null;
|
|
48
51
|
|
|
49
52
|
// Initialize connector placeholders
|
|
@@ -146,27 +149,34 @@ class ServiceWrapper {
|
|
|
146
149
|
await this._initializeMonitoring();
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
// 2.
|
|
152
|
+
// 2. Run pre-validation (Tier 1)
|
|
153
|
+
// Validates service structure, config, operations, cookbook tests
|
|
154
|
+
// Skips if valid proof exists in conn-runtime/validation-proof.json
|
|
155
|
+
if (this.serviceRoot && this.config.wrapper?.validation?.enabled !== false) {
|
|
156
|
+
await this._ensureValidationProof();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 3. Initialize MQ connection
|
|
150
160
|
if (this.config.wrapper?.mq?.enabled !== false) {
|
|
151
161
|
await this._initializeMQ();
|
|
152
162
|
}
|
|
153
163
|
|
|
154
|
-
//
|
|
164
|
+
// 4. Initialize service registry
|
|
155
165
|
if (this.config.wrapper?.registry?.enabled !== false) {
|
|
156
166
|
await this._initializeRegistry();
|
|
157
167
|
}
|
|
158
168
|
|
|
159
|
-
//
|
|
169
|
+
// 5. Initialize cache if configured
|
|
160
170
|
if (this.config.wrapper?.cache?.enabled === true) {
|
|
161
171
|
await this._initializeCache();
|
|
162
172
|
}
|
|
163
173
|
|
|
164
|
-
//
|
|
174
|
+
// 6. Setup health checks
|
|
165
175
|
if (this.config.wrapper?.health?.enabled !== false) {
|
|
166
176
|
this._setupHealthChecks();
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
//
|
|
179
|
+
// 7. Initialize orchestrator for workflow processing
|
|
170
180
|
// NOTE: Orchestrator is prepared but workflow listeners will be started
|
|
171
181
|
// ONLY after receiving certificate from Registry (see _initializeRegistry)
|
|
172
182
|
if (this.mqClient) {
|
|
@@ -460,9 +470,16 @@ class ServiceWrapper {
|
|
|
460
470
|
message = rawMessage;
|
|
461
471
|
}
|
|
462
472
|
|
|
473
|
+
// Extract and normalize flags
|
|
474
|
+
const flags = Array.isArray(message.flags) ? message.flags : [];
|
|
475
|
+
const isTest = flags.includes('test');
|
|
476
|
+
const isTier2Validation = flags.includes('tier2-validation');
|
|
477
|
+
|
|
463
478
|
console.log(`Processing message from ${queueName}:`, {
|
|
464
479
|
workflow_id: message.workflow_id,
|
|
465
|
-
step: message.step?.operation || message.operation
|
|
480
|
+
step: message.step?.operation || message.operation,
|
|
481
|
+
flags,
|
|
482
|
+
isTest
|
|
466
483
|
});
|
|
467
484
|
|
|
468
485
|
// Check if this message is for our service
|
|
@@ -473,22 +490,42 @@ class ServiceWrapper {
|
|
|
473
490
|
}
|
|
474
491
|
|
|
475
492
|
// Process based on message type
|
|
493
|
+
let result;
|
|
476
494
|
if (message.operation && this.operations?.operations?.[message.operation]) {
|
|
477
495
|
// Direct operation call
|
|
478
|
-
|
|
479
|
-
return result;
|
|
496
|
+
result = await this._executeOperation(message.operation, message.input || {});
|
|
480
497
|
} else if (message.step?.operation && this.operations?.operations?.[message.step.operation]) {
|
|
481
498
|
// Workflow step
|
|
482
|
-
|
|
483
|
-
return result;
|
|
499
|
+
result = await this._executeOperation(message.step.operation, message.input || {});
|
|
484
500
|
} else if (this.orchestrator) {
|
|
485
501
|
// Delegate to orchestrator for complex workflow processing
|
|
486
|
-
|
|
487
|
-
return result;
|
|
502
|
+
result = await this.orchestrator.processWorkflowMessage(message, serviceName);
|
|
488
503
|
} else {
|
|
489
504
|
throw new Error(`Unknown message format or operation: ${JSON.stringify(message)}`);
|
|
490
505
|
}
|
|
491
506
|
|
|
507
|
+
// Send response to workflow.completed if workflow_id is present
|
|
508
|
+
if (message.workflow_id && this.mqClient) {
|
|
509
|
+
const workflowResponse = {
|
|
510
|
+
workflow_id: message.workflow_id,
|
|
511
|
+
service: serviceName,
|
|
512
|
+
operation: message.step?.operation || message.operation,
|
|
513
|
+
status: 'completed',
|
|
514
|
+
output: result,
|
|
515
|
+
flags,
|
|
516
|
+
timestamp: new Date().toISOString()
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
try {
|
|
520
|
+
await this.mqClient.publish('workflow.completed', workflowResponse);
|
|
521
|
+
console.log(`✓ Workflow response sent to workflow.completed: ${message.workflow_id}`);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.error(`Failed to send workflow response:`, error);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return result;
|
|
528
|
+
|
|
492
529
|
} catch (error) {
|
|
493
530
|
console.error(`Error processing message from ${queueName}:`, error);
|
|
494
531
|
throw error;
|
|
@@ -631,6 +668,49 @@ class ServiceWrapper {
|
|
|
631
668
|
}
|
|
632
669
|
}
|
|
633
670
|
|
|
671
|
+
/**
|
|
672
|
+
* Ensure validation proof exists and is valid
|
|
673
|
+
* Runs ValidationOrchestrator if proof missing or invalid
|
|
674
|
+
* @private
|
|
675
|
+
*/
|
|
676
|
+
async _ensureValidationProof() {
|
|
677
|
+
if (!this.config.service?.name) {
|
|
678
|
+
throw new Error('Service name is required for validation');
|
|
679
|
+
}
|
|
680
|
+
if (!this.config.service?.version) {
|
|
681
|
+
throw new Error('Service version is required for validation');
|
|
682
|
+
}
|
|
683
|
+
if (!this.logger) {
|
|
684
|
+
throw new Error('Monitoring must be initialized before validation');
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const { name: serviceName, version: serviceVersion } = this.config.service;
|
|
688
|
+
|
|
689
|
+
this.logger.info('[ServiceWrapper] Checking validation proof...');
|
|
690
|
+
|
|
691
|
+
const orchestrator = new ValidationOrchestrator({
|
|
692
|
+
serviceRoot: this.serviceRoot,
|
|
693
|
+
serviceName,
|
|
694
|
+
serviceVersion,
|
|
695
|
+
logger: this.logger
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
const result = await orchestrator.validate();
|
|
699
|
+
|
|
700
|
+
if (!result.success) {
|
|
701
|
+
throw new Error(`Validation failed: ${result.errors.join(', ')}`);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (result.skipped) {
|
|
705
|
+
this.logger.info('[ServiceWrapper] ✓ Using existing validation proof');
|
|
706
|
+
} else {
|
|
707
|
+
this.logger.info('[ServiceWrapper] ✓ Validation completed successfully');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Store proof for registration
|
|
711
|
+
this.validationProof = result.proof;
|
|
712
|
+
}
|
|
713
|
+
|
|
634
714
|
/**
|
|
635
715
|
* Get wrapper status
|
|
636
716
|
*/
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
const ServiceWrapper = require('../../src/ServiceWrapper');
|
|
10
10
|
const express = require('express');
|
|
11
11
|
|
|
12
|
-
describe('ServiceWrapper Component Tests', () => {
|
|
12
|
+
describe('ServiceWrapper Component Tests @component', () => {
|
|
13
13
|
let wrapper;
|
|
14
14
|
let app;
|
|
15
15
|
let server;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Run these after implementing each connector
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
describe('Connector Integration Tests', () => {
|
|
9
|
+
describe('Connector Integration Tests @component', () => {
|
|
10
10
|
describe('Orchestrator Connector', () => {
|
|
11
11
|
test.skip('should integrate with real Orchestrator connector', async () => {
|
|
12
12
|
// Enable this test after conn-orch-orchestrator is fully implemented
|
|
@@ -10,7 +10,7 @@ const MQConnector = require('@onlineapps/conn-infra-mq');
|
|
|
10
10
|
const express = require('express');
|
|
11
11
|
const http = require('http');
|
|
12
12
|
|
|
13
|
-
describe('ServiceWrapper E2E Tests', () => {
|
|
13
|
+
describe('ServiceWrapper E2E Tests @e2e', () => {
|
|
14
14
|
let testService;
|
|
15
15
|
let testServer;
|
|
16
16
|
let serviceWrapper;
|
|
@@ -20,7 +20,7 @@ jest.mock('@onlineapps/conn-orch-orchestrator', () => MockOrchestratorConnector)
|
|
|
20
20
|
jest.mock('@onlineapps/conn-orch-api-mapper', () => MockApiMapperConnector);
|
|
21
21
|
jest.mock('@onlineapps/conn-orch-cookbook', () => MockCookbookConnector);
|
|
22
22
|
|
|
23
|
-
describe('ServiceWrapper Unit Tests', () => {
|
|
23
|
+
describe('ServiceWrapper Unit Tests @unit', () => {
|
|
24
24
|
let wrapper;
|
|
25
25
|
let mockExpressApp;
|
|
26
26
|
let mockOpenApiSpec;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/{test → tests}/setup.js
RENAMED
|
File without changes
|