@onlineapps/conn-base-hub 1.0.0
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 +6 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +60 -0
- package/coverage/coverage-final.json +2 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +116 -0
- package/coverage/index.js.html +904 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +116 -0
- package/coverage/lcov-report/index.js.html +904 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +145 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/jest.integration.config.js +9 -0
- package/package.json +36 -0
- package/src/config.js +0 -0
- package/src/index.js +274 -0
- package/test/component/hub-integration.test.js +413 -0
- package/test/unit/hub.test.js +351 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Separate Jest config for integration tests
|
|
2
|
+
module.exports = {
|
|
3
|
+
testEnvironment: 'node',
|
|
4
|
+
testMatch: ['**/test/integration/**/*.test.js'],
|
|
5
|
+
testTimeout: 30000, // Longer timeout for integration tests
|
|
6
|
+
globalSetup: './test/integration/setup.js',
|
|
7
|
+
coverageDirectory: 'coverage-integration',
|
|
8
|
+
verbose: true
|
|
9
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onlineapps/conn-base-hub",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Central hub for OA Drive connectors - bundles and integrates all essential connector libraries",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test:unit": "jest --testPathPattern=unit --coverage",
|
|
9
|
+
"test:component": "jest --testPathPattern=component --coverage",
|
|
10
|
+
"test:integration": "jest --config=jest.integration.config.js",
|
|
11
|
+
"test:integration:required": "REQUIRE_SERVICES=true jest --config=jest.integration.config.js",
|
|
12
|
+
"test:watch": "jest --watch",
|
|
13
|
+
"test:coverage": "jest --coverage --coverageReporters=text-lcov html"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"connector",
|
|
17
|
+
"microservice",
|
|
18
|
+
"integration",
|
|
19
|
+
"workflow",
|
|
20
|
+
"mq",
|
|
21
|
+
"registry"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@onlineapps/conn-orch-cookbook": "^2.0.0",
|
|
25
|
+
"@onlineapps/conn-base-logger": "^1.0.0",
|
|
26
|
+
"@onlineapps/conn-infra-mq": "^1.1.0",
|
|
27
|
+
"@onlineapps/conn-orch-registry": "^1.1.4",
|
|
28
|
+
"@onlineapps/conn-base-storage": "^1.0.0",
|
|
29
|
+
"dotenv": "^16.0.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"jest": "^29.7.0"
|
|
33
|
+
},
|
|
34
|
+
"author": "OA Drive Team",
|
|
35
|
+
"license": "ISC"
|
|
36
|
+
}
|
package/src/config.js
ADDED
|
File without changes
|
package/src/index.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @onlineapps/connector-core
|
|
3
|
+
*
|
|
4
|
+
* Core integration library that bundles all essential connectors
|
|
5
|
+
* for OA Drive microservices. Provides a unified interface for:
|
|
6
|
+
* - Message Queue operations
|
|
7
|
+
* - Service Registry registration and discovery
|
|
8
|
+
* - Workflow cookbook processing
|
|
9
|
+
* - Storage operations with MinIO
|
|
10
|
+
* - Centralized logging
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
require('dotenv').config();
|
|
14
|
+
|
|
15
|
+
// Re-export all connectors
|
|
16
|
+
module.exports = {
|
|
17
|
+
// MQ Client for RabbitMQ communication
|
|
18
|
+
MQClient: require('@onlineapps/conn-infra-mq'),
|
|
19
|
+
|
|
20
|
+
// Service Registry client for registration and event consumption
|
|
21
|
+
...require('@onlineapps/conn-orch-registry'),
|
|
22
|
+
|
|
23
|
+
// Cookbook validation and processing
|
|
24
|
+
...require('@onlineapps/conn-orch-cookbook'),
|
|
25
|
+
|
|
26
|
+
// Storage connector for MinIO with fingerprinting
|
|
27
|
+
StorageConnector: require('@onlineapps/conn-base-storage'),
|
|
28
|
+
|
|
29
|
+
// Logger (if available)
|
|
30
|
+
createLogger: (() => {
|
|
31
|
+
try {
|
|
32
|
+
return require('@onlineapps/conn-base-logger');
|
|
33
|
+
} catch (e) {
|
|
34
|
+
// Logger not available, return console as fallback
|
|
35
|
+
return function(config) {
|
|
36
|
+
return {
|
|
37
|
+
info: console.log,
|
|
38
|
+
error: console.error,
|
|
39
|
+
warn: console.warn,
|
|
40
|
+
debug: console.debug,
|
|
41
|
+
api: { info: console.log, error: console.error },
|
|
42
|
+
mq: { info: console.log, error: console.error },
|
|
43
|
+
workflow: { info: console.log, error: console.error },
|
|
44
|
+
registry: { info: console.log, error: console.error },
|
|
45
|
+
close: async () => {}
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
})(),
|
|
50
|
+
|
|
51
|
+
// Convenience factory for creating a fully configured microservice
|
|
52
|
+
createMicroservice: function(config) {
|
|
53
|
+
const { ServiceRegistryClient, MQClient, StorageConnector, createLogger } = module.exports;
|
|
54
|
+
|
|
55
|
+
// Create logger instance
|
|
56
|
+
const logger = createLogger({
|
|
57
|
+
serviceName: config.serviceName,
|
|
58
|
+
version: config.version,
|
|
59
|
+
...config.logger
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
logger,
|
|
64
|
+
// Initialize Registry Client
|
|
65
|
+
registry: config.registry ? new ServiceRegistryClient({
|
|
66
|
+
amqpUrl: config.amqpUrl || process.env.RABBITMQ_URL,
|
|
67
|
+
serviceName: config.serviceName,
|
|
68
|
+
version: config.version || '1.0.0',
|
|
69
|
+
...config.registry
|
|
70
|
+
}) : null,
|
|
71
|
+
|
|
72
|
+
// Initialize MQ Client
|
|
73
|
+
mq: config.mq ? new MQClient({
|
|
74
|
+
type: 'rabbitmq',
|
|
75
|
+
host: config.amqpUrl || process.env.RABBITMQ_URL,
|
|
76
|
+
queue: `${config.serviceName}_queue`,
|
|
77
|
+
...config.mq
|
|
78
|
+
}) : null,
|
|
79
|
+
|
|
80
|
+
// Initialize Storage
|
|
81
|
+
storage: config.storage ? new StorageConnector({
|
|
82
|
+
endPoint: process.env.MINIO_ENDPOINT || 'localhost',
|
|
83
|
+
port: parseInt(process.env.MINIO_PORT || 9000),
|
|
84
|
+
accessKey: process.env.MINIO_ACCESS_KEY || 'minioadmin',
|
|
85
|
+
secretKey: process.env.MINIO_SECRET_KEY || 'minioadmin',
|
|
86
|
+
...config.storage
|
|
87
|
+
}) : null,
|
|
88
|
+
|
|
89
|
+
// Initialize all components
|
|
90
|
+
async init() {
|
|
91
|
+
const results = {};
|
|
92
|
+
|
|
93
|
+
// Initialize Registry
|
|
94
|
+
if (this.registry) {
|
|
95
|
+
await this.registry.initialize();
|
|
96
|
+
|
|
97
|
+
// Optionally subscribe to changes
|
|
98
|
+
if (config.subscribeToRegistry) {
|
|
99
|
+
await this.registry.subscribeToChanges();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Start heartbeat
|
|
103
|
+
this.registry.startHeartbeat();
|
|
104
|
+
results.registry = true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Initialize MQ
|
|
108
|
+
if (this.mq) {
|
|
109
|
+
await this.mq.connect();
|
|
110
|
+
results.mq = true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Initialize Storage
|
|
114
|
+
if (this.storage) {
|
|
115
|
+
await this.storage.initialize();
|
|
116
|
+
results.storage = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return results;
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// Graceful shutdown
|
|
123
|
+
async shutdown() {
|
|
124
|
+
const promises = [];
|
|
125
|
+
|
|
126
|
+
if (this.registry) {
|
|
127
|
+
promises.push(this.registry.dispose());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (this.mq) {
|
|
131
|
+
promises.push(this.mq.disconnect());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (this.logger && this.logger.close) {
|
|
135
|
+
promises.push(this.logger.close());
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
await Promise.all(promises);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// Helper for workflow processing
|
|
144
|
+
WorkflowHelper: {
|
|
145
|
+
/**
|
|
146
|
+
* Create a workflow message
|
|
147
|
+
*/
|
|
148
|
+
createMessage(opts) {
|
|
149
|
+
return {
|
|
150
|
+
workflow_id: opts.workflowId,
|
|
151
|
+
cookbook_name: opts.cookbookName,
|
|
152
|
+
step_index: opts.step || 0,
|
|
153
|
+
data: opts.data || {},
|
|
154
|
+
results: [],
|
|
155
|
+
timestamp: new Date().toISOString()
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Parse a workflow message
|
|
161
|
+
*/
|
|
162
|
+
parseMessage(message) {
|
|
163
|
+
return {
|
|
164
|
+
workflowId: message.workflow_id,
|
|
165
|
+
cookbookName: message.cookbook_name,
|
|
166
|
+
step: message.step_index,
|
|
167
|
+
data: message.data,
|
|
168
|
+
results: message.results
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get next queue from cookbook step
|
|
174
|
+
*/
|
|
175
|
+
getNextQueue(step) {
|
|
176
|
+
if (step.queue) {
|
|
177
|
+
return step.queue;
|
|
178
|
+
}
|
|
179
|
+
return `${step.service}.queue`;
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Validate workflow message
|
|
184
|
+
*/
|
|
185
|
+
isValidMessage(message) {
|
|
186
|
+
return !!(message.workflow_id && message.cookbook_name &&
|
|
187
|
+
typeof message.step_index === 'number' && message.data);
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Add result to workflow message
|
|
192
|
+
*/
|
|
193
|
+
addResult(message, result) {
|
|
194
|
+
return {
|
|
195
|
+
...message,
|
|
196
|
+
results: [...(message.results || []), result],
|
|
197
|
+
last_result: result
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Create error message
|
|
203
|
+
*/
|
|
204
|
+
createErrorMessage(message, error, serviceName) {
|
|
205
|
+
return {
|
|
206
|
+
...message,
|
|
207
|
+
status: 'error',
|
|
208
|
+
error: {
|
|
209
|
+
message: error.message,
|
|
210
|
+
stack: error.stack,
|
|
211
|
+
service: serviceName,
|
|
212
|
+
step: message.step_index
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Process a workflow step
|
|
219
|
+
* @param {Object} message - Workflow message
|
|
220
|
+
* @param {Function} handler - Step handler function
|
|
221
|
+
* @returns {Object} - Updated workflow message
|
|
222
|
+
*/
|
|
223
|
+
async processStep(message, handler) {
|
|
224
|
+
const { workflow_id, current_step, cookbook, context = {} } = message;
|
|
225
|
+
|
|
226
|
+
// Get current step from cookbook
|
|
227
|
+
const step = cookbook?.steps?.[current_step];
|
|
228
|
+
if (!step) {
|
|
229
|
+
throw new Error(`Step ${current_step} not found in cookbook`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Execute handler
|
|
233
|
+
const result = await handler(step, context);
|
|
234
|
+
|
|
235
|
+
// Build response message
|
|
236
|
+
return {
|
|
237
|
+
...message,
|
|
238
|
+
current_step: current_step + 1,
|
|
239
|
+
context: {
|
|
240
|
+
...context,
|
|
241
|
+
[`step_${current_step}_result`]: result
|
|
242
|
+
},
|
|
243
|
+
trace: [
|
|
244
|
+
...(message.trace || []),
|
|
245
|
+
{
|
|
246
|
+
step: current_step,
|
|
247
|
+
service: step.service,
|
|
248
|
+
timestamp: new Date().toISOString(),
|
|
249
|
+
result: result
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
};
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Determine next queue based on workflow state
|
|
257
|
+
* @param {Object} message - Workflow message
|
|
258
|
+
* @returns {string} - Next queue name
|
|
259
|
+
*/
|
|
260
|
+
getNextQueueFromMessage(message) {
|
|
261
|
+
const { current_step, cookbook } = message;
|
|
262
|
+
|
|
263
|
+
if (cookbook && current_step < cookbook.steps.length) {
|
|
264
|
+
const nextStep = cookbook.steps[current_step];
|
|
265
|
+
return `${nextStep.service}.queue`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return 'workflow.completed';
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// For backward compatibility
|
|
274
|
+
module.exports.ConnectorCore = module.exports;
|