@onlineapps/service-wrapper 2.0.58 → 2.1.1
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 +2 -4
- package/src/ConfigLoader.js +216 -0
- package/src/ServiceWrapper.js +1 -4
- package/src/index.js +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,9 +11,7 @@
|
|
|
11
11
|
"test:coverage": "jest --coverage",
|
|
12
12
|
"test:mocked": "node test/run-tests.js",
|
|
13
13
|
"docs": "jsdoc2md --files src/**/*.js > API.md",
|
|
14
|
-
"docs:html": "jsdoc -c jsdoc.json -d docs/html"
|
|
15
|
-
"prepublishOnly": "cd $(git rev-parse --show-toplevel) && bash scripts/pre-publish-compatibility-check.sh shared/connector/service-wrapper",
|
|
16
|
-
"postpublish": "cd $(git rev-parse --show-toplevel) && bash scripts/update-manifest-from-npm.sh && bash scripts/update-all-services.sh"
|
|
14
|
+
"docs:html": "jsdoc -c jsdoc.json -d docs/html"
|
|
17
15
|
},
|
|
18
16
|
"keywords": [
|
|
19
17
|
"microservices",
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfigLoader - Unified configuration file loading for business services
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: Load all service configuration files
|
|
5
|
+
* from standardized locations with consistent error handling.
|
|
6
|
+
*
|
|
7
|
+
* CENTRAL REGISTRY of all configuration files:
|
|
8
|
+
* - conn-config/config.json (REQUIRED) - Service configuration
|
|
9
|
+
* - conn-config/operations.json (REQUIRED) - Operations specification
|
|
10
|
+
* - conn-runtime/validation-proof.json (OPTIONAL) - Pre-validation proof
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* const { ConfigLoader } = require('@onlineapps/service-wrapper');
|
|
14
|
+
* const config = ConfigLoader.loadAll();
|
|
15
|
+
*
|
|
16
|
+
* Design principle: ONE place to define, validate, and load ALL configs.
|
|
17
|
+
*
|
|
18
|
+
* @module @onlineapps/service-wrapper/ConfigLoader
|
|
19
|
+
* @since 2.1.0
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* CENTRAL REGISTRY: All configuration files used by business services
|
|
27
|
+
* If you add a new config file, add it here!
|
|
28
|
+
*/
|
|
29
|
+
const CONFIG_REGISTRY = {
|
|
30
|
+
SERVICE_CONFIG: {
|
|
31
|
+
path: 'conn-config/config.json',
|
|
32
|
+
required: true,
|
|
33
|
+
description: 'Service metadata and wrapper configuration'
|
|
34
|
+
},
|
|
35
|
+
OPERATIONS: {
|
|
36
|
+
path: 'conn-config/operations.json',
|
|
37
|
+
required: true,
|
|
38
|
+
description: 'Operations specification (API contract)'
|
|
39
|
+
},
|
|
40
|
+
VALIDATION_PROOF: {
|
|
41
|
+
path: 'conn-runtime/validation-proof.json',
|
|
42
|
+
required: false,
|
|
43
|
+
description: 'Pre-validation proof from cookbook tests'
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
class ConfigLoader {
|
|
48
|
+
/**
|
|
49
|
+
* Resolve environment variable substitution
|
|
50
|
+
* Supports format: ${VAR_NAME:default_value}
|
|
51
|
+
* @param {string} value - Value to resolve
|
|
52
|
+
* @returns {string} Resolved value
|
|
53
|
+
*/
|
|
54
|
+
static resolveEnvVar(value) {
|
|
55
|
+
if (typeof value !== 'string') {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Match ${VAR_NAME:default} or ${VAR_NAME}
|
|
60
|
+
const match = value.match(/^\$\{([^:}]+)(?::([^}]+))?\}$/);
|
|
61
|
+
if (!match) {
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const varName = match[1];
|
|
66
|
+
const defaultValue = match[2];
|
|
67
|
+
|
|
68
|
+
return process.env[varName] || defaultValue || '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get list of all configuration files
|
|
73
|
+
* @returns {Object} Configuration registry
|
|
74
|
+
*/
|
|
75
|
+
static getConfigRegistry() {
|
|
76
|
+
return CONFIG_REGISTRY;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Load service configuration from conn-config/config.json
|
|
81
|
+
* @param {string} basePath - Base path (defaults to process.cwd())
|
|
82
|
+
* @returns {Object} Service configuration
|
|
83
|
+
* @throws {Error} If config file not found or invalid JSON
|
|
84
|
+
*/
|
|
85
|
+
static loadServiceConfig(basePath = process.cwd()) {
|
|
86
|
+
const configPath = path.join(basePath, 'conn-config', 'config.json');
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const content = fs.readFileSync(configPath, 'utf8');
|
|
90
|
+
const config = JSON.parse(content);
|
|
91
|
+
|
|
92
|
+
// Validate required fields
|
|
93
|
+
if (!config.service || !config.service.name || !config.service.version) {
|
|
94
|
+
throw new Error('Invalid config.json: Missing required fields (service.name, service.version)');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return config;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (error.code === 'ENOENT') {
|
|
100
|
+
throw new Error(`Service config not found: ${configPath}`);
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Load operations specification from conn-config/operations.json
|
|
108
|
+
* @param {string} basePath - Base path (defaults to process.cwd())
|
|
109
|
+
* @returns {Object} Operations specification
|
|
110
|
+
* @throws {Error} If operations file not found or invalid
|
|
111
|
+
*/
|
|
112
|
+
static loadOperations(basePath = process.cwd()) {
|
|
113
|
+
const operationsPath = path.join(basePath, 'conn-config', 'operations.json');
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const content = fs.readFileSync(operationsPath, 'utf8');
|
|
117
|
+
const operations = JSON.parse(content);
|
|
118
|
+
|
|
119
|
+
// Validate structure
|
|
120
|
+
if (!operations.operations || typeof operations.operations !== 'object') {
|
|
121
|
+
throw new Error('Invalid operations.json: Missing or invalid "operations" field');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return operations;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (error.code === 'ENOENT') {
|
|
127
|
+
throw new Error(`Operations specification not found: ${operationsPath}`);
|
|
128
|
+
}
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Load validation proof from conn-runtime/validation-proof.json (optional)
|
|
135
|
+
* @param {string} basePath - Base path (defaults to process.cwd())
|
|
136
|
+
* @returns {Object|null} Validation proof or null if not found
|
|
137
|
+
*/
|
|
138
|
+
static loadValidationProof(basePath = process.cwd()) {
|
|
139
|
+
const proofPath = path.join(basePath, 'conn-runtime', 'validation-proof.json');
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
// Check existence first (non-critical file)
|
|
143
|
+
if (!fs.existsSync(proofPath)) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const content = fs.readFileSync(proofPath, 'utf8');
|
|
148
|
+
const proof = JSON.parse(content);
|
|
149
|
+
|
|
150
|
+
// Validate structure
|
|
151
|
+
if (!proof.validationProof && !proof.hash) {
|
|
152
|
+
console.warn('[ConfigLoader] Invalid validation-proof.json structure - ignoring');
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
hash: proof.validationProof || proof.hash,
|
|
158
|
+
data: proof.validationData || proof.data
|
|
159
|
+
};
|
|
160
|
+
} catch (error) {
|
|
161
|
+
// Non-critical - just log warning
|
|
162
|
+
console.warn(`[ConfigLoader] Failed to load validation proof: ${error.message}`);
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Load all service configurations at once
|
|
169
|
+
* @param {string} basePath - Base path (defaults to process.cwd())
|
|
170
|
+
* @returns {Object} Combined configuration object
|
|
171
|
+
*/
|
|
172
|
+
static loadAll(basePath = process.cwd()) {
|
|
173
|
+
const serviceConfig = this.loadServiceConfig(basePath);
|
|
174
|
+
const operations = this.loadOperations(basePath);
|
|
175
|
+
const validationProof = this.loadValidationProof(basePath);
|
|
176
|
+
|
|
177
|
+
// Resolve environment variables in port and url
|
|
178
|
+
const resolvedPort = this.resolveEnvVar(serviceConfig.service.port);
|
|
179
|
+
const resolvedUrl = this.resolveEnvVar(serviceConfig.service.url);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
service: {
|
|
183
|
+
name: serviceConfig.service.name,
|
|
184
|
+
version: serviceConfig.service.version,
|
|
185
|
+
port: parseInt(resolvedPort, 10),
|
|
186
|
+
url: resolvedUrl,
|
|
187
|
+
env: process.env.NODE_ENV || 'development'
|
|
188
|
+
},
|
|
189
|
+
wrapper: serviceConfig.wrapper || {},
|
|
190
|
+
operations,
|
|
191
|
+
validationProof
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Get path to config directory
|
|
197
|
+
* @param {string} basePath - Base path (defaults to process.cwd())
|
|
198
|
+
* @returns {string} Absolute path to conn-config directory
|
|
199
|
+
*/
|
|
200
|
+
static getConfigDir(basePath = process.cwd()) {
|
|
201
|
+
return path.join(basePath, 'conn-config');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get path to specific config file
|
|
206
|
+
* @param {string} filename - Config filename
|
|
207
|
+
* @param {string} basePath - Base path (defaults to process.cwd())
|
|
208
|
+
* @returns {string} Absolute path to config file
|
|
209
|
+
*/
|
|
210
|
+
static getConfigPath(filename, basePath = process.cwd()) {
|
|
211
|
+
return path.join(basePath, 'conn-config', filename);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = { ConfigLoader };
|
|
216
|
+
|
package/src/ServiceWrapper.js
CHANGED
|
@@ -1254,13 +1254,10 @@ class ServiceWrapper {
|
|
|
1254
1254
|
}
|
|
1255
1255
|
|
|
1256
1256
|
// Normalize message format: Gateway sends workflowId, orchestrator expects workflow_id
|
|
1257
|
-
// V2 format uses step_id, V1 used id - support both for backwards compatibility during migration
|
|
1258
|
-
const firstStep = message.cookbook?.steps?.[0];
|
|
1259
|
-
const firstStepId = firstStep?.step_id || firstStep?.id;
|
|
1260
1257
|
const normalizedMessage = {
|
|
1261
1258
|
...message,
|
|
1262
1259
|
workflow_id: message.workflow_id || message.workflowId,
|
|
1263
|
-
current_step: message.current_step ||
|
|
1260
|
+
current_step: message.current_step || (message.cookbook?.steps?.[0]?.id)
|
|
1264
1261
|
};
|
|
1265
1262
|
|
|
1266
1263
|
// Validate normalized message has required fields
|
package/src/index.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const ServiceWrapper = require('./ServiceWrapper');
|
|
15
|
+
const { ConfigLoader } = require('./ConfigLoader');
|
|
15
16
|
|
|
16
17
|
// Note: WorkflowProcessor and ApiCaller functionality has been moved to connectors:
|
|
17
18
|
// - WorkflowProcessor -> @onlineapps/conn-orch-orchestrator
|
|
@@ -19,5 +20,6 @@ const ServiceWrapper = require('./ServiceWrapper');
|
|
|
19
20
|
|
|
20
21
|
module.exports = ServiceWrapper;
|
|
21
22
|
module.exports.ServiceWrapper = ServiceWrapper;
|
|
23
|
+
module.exports.ConfigLoader = ConfigLoader;
|
|
22
24
|
module.exports.default = ServiceWrapper;
|
|
23
|
-
module.exports.VERSION = '2.
|
|
25
|
+
module.exports.VERSION = '2.1.0';
|