@onlineapps/service-wrapper 2.0.59 → 2.1.3
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 -3
- package/src/ConfigLoader.js +216 -0
- package/src/ServiceWrapper.js +18 -5
- 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.3",
|
|
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,8 +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"
|
|
14
|
+
"docs:html": "jsdoc -c jsdoc.json -d docs/html"
|
|
16
15
|
},
|
|
17
16
|
"keywords": [
|
|
18
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
|
@@ -530,17 +530,33 @@ class ServiceWrapper {
|
|
|
530
530
|
*/
|
|
531
531
|
async _initializeMonitoring() {
|
|
532
532
|
const serviceName = this.config.service?.name || 'unnamed-service';
|
|
533
|
+
const fs = require('fs');
|
|
534
|
+
const path = require('path');
|
|
535
|
+
|
|
536
|
+
// Ensure logs directory exists (standardized location: {serviceRoot}/logs/)
|
|
537
|
+
const logsDir = path.resolve(process.cwd(), 'logs');
|
|
538
|
+
if (!fs.existsSync(logsDir)) {
|
|
539
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
540
|
+
}
|
|
533
541
|
|
|
534
542
|
// Init returns API object with logger, info, error, warn, debug methods
|
|
535
543
|
this.monitoring = await MonitoringConnector.init({
|
|
536
544
|
serviceName,
|
|
545
|
+
// File logging configuration (enabled by default)
|
|
546
|
+
file: {
|
|
547
|
+
enabled: true,
|
|
548
|
+
directory: 'logs',
|
|
549
|
+
maxDays: 7,
|
|
550
|
+
filename: 'app.log',
|
|
551
|
+
...(this.config.wrapper?.monitoring?.file || {})
|
|
552
|
+
},
|
|
537
553
|
...this.config.wrapper?.monitoring
|
|
538
554
|
});
|
|
539
555
|
|
|
540
556
|
// Logger is the API object returned from init (has logger, info, error, warn, debug)
|
|
541
557
|
this.logger = this.monitoring;
|
|
542
558
|
// Logger is now available, use it
|
|
543
|
-
this.logger.info('Monitoring connector initialized');
|
|
559
|
+
this.logger.info('Monitoring connector initialized', { logsDir });
|
|
544
560
|
|
|
545
561
|
// Add monitoring middleware to Express app
|
|
546
562
|
if (this.app) {
|
|
@@ -1254,13 +1270,10 @@ class ServiceWrapper {
|
|
|
1254
1270
|
}
|
|
1255
1271
|
|
|
1256
1272
|
// 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
1273
|
const normalizedMessage = {
|
|
1261
1274
|
...message,
|
|
1262
1275
|
workflow_id: message.workflow_id || message.workflowId,
|
|
1263
|
-
current_step: message.current_step ||
|
|
1276
|
+
current_step: message.current_step || (message.cookbook?.steps?.[0]?.id)
|
|
1264
1277
|
};
|
|
1265
1278
|
|
|
1266
1279
|
// 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.2';
|