@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/service-wrapper",
3
- "version": "2.0.59",
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
+
@@ -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 || firstStepId
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.0.0';
25
+ module.exports.VERSION = '2.1.2';