@onlineapps/conn-orch-cookbook 2.0.37 โ 2.1.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 -23
- package/package.json +3 -4
- package/src/index.js +26 -86
- package/examples/basicUsage.js +0 -89
package/README.md
CHANGED
|
@@ -52,7 +52,6 @@ const cookbook = require('@onlineapps/conn-orch-cookbook');
|
|
|
52
52
|
// Use specific modules
|
|
53
53
|
const { CookbookExecutor } = cookbook.executor;
|
|
54
54
|
const { CookbookRouter } = cookbook.router;
|
|
55
|
-
const { CookbookGenerator } = cookbook.transformer;
|
|
56
55
|
|
|
57
56
|
// Or use factory functions
|
|
58
57
|
const processor = cookbook.createProcessor({
|
|
@@ -61,6 +60,11 @@ const processor = cookbook.createProcessor({
|
|
|
61
60
|
});
|
|
62
61
|
```
|
|
63
62
|
|
|
63
|
+
> NOTE: the legacy OpenAPI-to-cookbook transformer (`CookbookGenerator`,
|
|
64
|
+
> `ApiSpecAnalyzer`, `StepTranslator`, `ResponseMapper`, `createTransformer`)
|
|
65
|
+
> was removed in v2.1 together with the operations.json-first migration.
|
|
66
|
+
> Cookbooks are authored against [`operations-registry-contract.md`](../../../docs/standards/operations-registry-contract.md) ยง3.
|
|
67
|
+
|
|
64
68
|
## ๐ Included Modules
|
|
65
69
|
|
|
66
70
|
### 1. Core Module (@onlineapps/cookbook-core)
|
|
@@ -75,13 +79,7 @@ const processor = cookbook.createProcessor({
|
|
|
75
79
|
- Variable resolution ($api_input, $steps)
|
|
76
80
|
- Step processing for all types
|
|
77
81
|
|
|
78
|
-
### 3.
|
|
79
|
-
- OpenAPI to cookbook generation
|
|
80
|
-
- API spec analysis
|
|
81
|
-
- Response mapping
|
|
82
|
-
- JSONPath transformations
|
|
83
|
-
|
|
84
|
-
### 4. Router Module (@onlineapps/cookbook-router)
|
|
82
|
+
### 3. Router Module (@onlineapps/cookbook-router)
|
|
85
83
|
- Service discovery
|
|
86
84
|
- Queue management
|
|
87
85
|
- Retry with exponential backoff
|
|
@@ -149,21 +147,6 @@ class WorkflowLauncher {
|
|
|
149
147
|
}
|
|
150
148
|
```
|
|
151
149
|
|
|
152
|
-
### OpenAPI Integration
|
|
153
|
-
|
|
154
|
-
```javascript
|
|
155
|
-
const { CookbookGenerator } = require('@onlineapps/conn-orch-cookbook');
|
|
156
|
-
|
|
157
|
-
const generator = new CookbookGenerator({
|
|
158
|
-
defaultTimeout: 10000,
|
|
159
|
-
defaultRetry: { maxAttempts: 3, delayMs: 2000 }
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Generate cookbook from OpenAPI spec
|
|
163
|
-
const openApiSpec = require('./api-spec.json');
|
|
164
|
-
const cookbook = generator.generate(openApiSpec);
|
|
165
|
-
```
|
|
166
|
-
|
|
167
150
|
## ๐ Cookbook Structure
|
|
168
151
|
|
|
169
152
|
### Basic Example (v2.0 Schema)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-orch-cookbook",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "Complete cookbook toolkit for all services - unified wrapper including core, executor,
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Complete cookbook toolkit for all services - unified wrapper including core, executor, and router functionality (operations.json-first; legacy transformer removed)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -48,9 +48,8 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@onlineapps/cookbook-core": "2.1.16",
|
|
50
50
|
"@onlineapps/cookbook-executor": "1.0.12",
|
|
51
|
-
"@onlineapps/cookbook-router": "1.0.
|
|
51
|
+
"@onlineapps/cookbook-router": "1.0.33",
|
|
52
52
|
"@onlineapps/cookbook-template-helpers": "1.0.11",
|
|
53
|
-
"@onlineapps/cookbook-transformer": "1.1.5",
|
|
54
53
|
"jsonpath": "1.1.1"
|
|
55
54
|
},
|
|
56
55
|
"devDependencies": {
|
package/src/index.js
CHANGED
|
@@ -4,24 +4,26 @@
|
|
|
4
4
|
* @module @onlineapps/conn-orch-cookbook
|
|
5
5
|
* @description Unified cookbook connector for service-wrapper integration.
|
|
6
6
|
* This is a THIN WRAPPER around cookbook-* modules, providing a single
|
|
7
|
-
* unified interface for
|
|
7
|
+
* unified interface for cookbook parsing, validation, execution and routing.
|
|
8
8
|
*
|
|
9
|
-
* NO DUPLICATION: All implementation
|
|
10
|
-
* This connector only re-exports and
|
|
9
|
+
* NO DUPLICATION: All implementation lives in cookbook-* modules.
|
|
10
|
+
* This connector only re-exports and wires them together.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* The legacy OpenAPI-to-cookbook `cookbook-transformer` module was removed
|
|
13
|
+
* together with the operations.json-first contract โ cookbooks are now
|
|
14
|
+
* authored per biz-service against the operations contract.
|
|
15
|
+
*
|
|
16
|
+
* @see /api/docs/standards/operations-registry-contract.md ยง3
|
|
17
|
+
* @see /api/docs/architecture/connector.md
|
|
13
18
|
* @author OA Drive Team
|
|
14
19
|
* @license MIT
|
|
15
20
|
* @since 2.0.0
|
|
16
21
|
*/
|
|
17
22
|
|
|
18
|
-
// Import from shared cookbook modules - NO LOCAL IMPLEMENTATIONS!
|
|
19
23
|
const cookbookCore = require('@onlineapps/cookbook-core');
|
|
20
24
|
const cookbookExecutor = require('@onlineapps/cookbook-executor');
|
|
21
|
-
const cookbookTransformer = require('@onlineapps/cookbook-transformer');
|
|
22
25
|
const cookbookRouter = require('@onlineapps/cookbook-router');
|
|
23
26
|
|
|
24
|
-
// Re-export all from cookbook-core (parser and validator)
|
|
25
27
|
const {
|
|
26
28
|
parseCookbookFromFile,
|
|
27
29
|
parseCookbookFromFileSync,
|
|
@@ -37,7 +39,6 @@ const {
|
|
|
37
39
|
loadSchema
|
|
38
40
|
} = cookbookCore;
|
|
39
41
|
|
|
40
|
-
// Re-export all from cookbook-executor
|
|
41
42
|
const {
|
|
42
43
|
CookbookExecutor,
|
|
43
44
|
ContextManager,
|
|
@@ -45,16 +46,6 @@ const {
|
|
|
45
46
|
VariableResolver
|
|
46
47
|
} = cookbookExecutor;
|
|
47
48
|
|
|
48
|
-
// Re-export all from cookbook-transformer
|
|
49
|
-
const {
|
|
50
|
-
CookbookGenerator,
|
|
51
|
-
ApiSpecAnalyzer,
|
|
52
|
-
StepTranslator,
|
|
53
|
-
ResponseMapper,
|
|
54
|
-
createTransformer
|
|
55
|
-
} = cookbookTransformer;
|
|
56
|
-
|
|
57
|
-
// Re-export all from cookbook-router
|
|
58
49
|
const {
|
|
59
50
|
CookbookRouter,
|
|
60
51
|
ServiceDiscovery,
|
|
@@ -63,33 +54,29 @@ const {
|
|
|
63
54
|
createRouter
|
|
64
55
|
} = cookbookRouter;
|
|
65
56
|
|
|
66
|
-
//
|
|
67
|
-
// NOTE: This package must be present as a dependency of conn-orch-cookbook.
|
|
57
|
+
// NOTE: cookbook-template-helpers must be present as a dependency of conn-orch-cookbook.
|
|
68
58
|
const templateHelpers = require('@onlineapps/cookbook-template-helpers');
|
|
69
59
|
|
|
70
60
|
|
|
71
61
|
/**
|
|
72
62
|
* Create a cookbook processor with all capabilities
|
|
73
|
-
* Factory
|
|
63
|
+
* Factory that wires parser + validator + executor + (optional) router.
|
|
74
64
|
*
|
|
75
65
|
* @function createProcessor
|
|
76
66
|
* @param {Object} [config={}] - Processor configuration
|
|
77
67
|
* @param {Object} [config.executor] - Executor configuration
|
|
78
68
|
* @param {Object} [config.router] - Router configuration
|
|
79
|
-
* @param {Object} [config.transformer] - Transformer configuration
|
|
80
69
|
* @param {Object} [config.mqClient] - Message queue client for router
|
|
81
70
|
* @param {Object} [config.registryClient] - Registry client for router
|
|
82
|
-
* @returns {Object} Processor
|
|
71
|
+
* @returns {Object} Processor with `process(cookbook, input)` + component accessors.
|
|
83
72
|
*
|
|
84
73
|
* @example
|
|
85
74
|
* const processor = createProcessor({
|
|
86
75
|
* executor: { timeout: 30000 },
|
|
87
|
-
* router:
|
|
88
|
-
* mqClient
|
|
89
|
-
* registryClient: registryConnector
|
|
76
|
+
* router: { baseUrl: "http://api.example.com" },
|
|
77
|
+
* mqClient, registryClient
|
|
90
78
|
* });
|
|
91
|
-
*
|
|
92
|
-
* const result = await processor.process(cookbook);
|
|
79
|
+
* const result = await processor.process(cookbook, input);
|
|
93
80
|
*/
|
|
94
81
|
function createProcessor(config = {}) {
|
|
95
82
|
if (!config || typeof config !== 'object') {
|
|
@@ -99,17 +86,13 @@ function createProcessor(config = {}) {
|
|
|
99
86
|
const router = config.mqClient && config.registryClient
|
|
100
87
|
? createRouter(config.mqClient, config.registryClient, config.router)
|
|
101
88
|
: null;
|
|
102
|
-
const transformer = createTransformer(config.transformer);
|
|
103
|
-
const mapper = new ResponseMapper(config.transformer);
|
|
104
89
|
|
|
105
90
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* This connector therefore creates the executor lazily when a cookbook is provided.
|
|
91
|
+
* cookbook-executor validates the cookbook in its constructor, so it is
|
|
92
|
+
* created lazily when a cookbook is provided.
|
|
109
93
|
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* We add `id` = `step_id` ONLY for executor execution to keep runtime behavior predictable.
|
|
94
|
+
* cookbook-executor also expects `step.id` internally. Our V2.1 format
|
|
95
|
+
* uses `step.step_id`. We add `id = step_id` ONLY for execution.
|
|
113
96
|
*/
|
|
114
97
|
const normalizeStepForExecutor = (step) => {
|
|
115
98
|
if (!step || typeof step !== 'object') return step;
|
|
@@ -169,56 +152,30 @@ function createProcessor(config = {}) {
|
|
|
169
152
|
};
|
|
170
153
|
|
|
171
154
|
return {
|
|
172
|
-
/**
|
|
173
|
-
* Process a cookbook through the full pipeline
|
|
174
|
-
* @param {Object|string} cookbook - Cookbook object or file path
|
|
175
|
-
* @param {Object} [context={}] - Execution context
|
|
176
|
-
* @returns {Promise<Object>} Execution result
|
|
177
|
-
*/
|
|
178
155
|
async process(cookbook, input = {}) {
|
|
179
|
-
// Parse if string (file path)
|
|
180
156
|
if (typeof cookbook === 'string') {
|
|
181
157
|
cookbook = await parseCookbookFromFile(cookbook);
|
|
182
158
|
}
|
|
183
159
|
|
|
184
|
-
// Validate
|
|
185
160
|
validateCookbook(cookbook);
|
|
186
|
-
|
|
187
|
-
// Validate all references
|
|
188
161
|
validateAllReferences(cookbook);
|
|
189
162
|
|
|
190
163
|
const cookbookForExecutor = normalizeCookbookForExecutor(cookbook);
|
|
191
164
|
const executor = new CookbookExecutor(cookbookForExecutor, config.executor || {});
|
|
192
165
|
|
|
193
|
-
// Execute
|
|
194
166
|
return executor.execute(input);
|
|
195
167
|
},
|
|
196
168
|
|
|
197
|
-
// Expose components for direct access
|
|
198
169
|
executor: null,
|
|
199
|
-
router
|
|
200
|
-
transformer,
|
|
201
|
-
mapper
|
|
170
|
+
router
|
|
202
171
|
};
|
|
203
172
|
}
|
|
204
173
|
|
|
205
174
|
/**
|
|
206
|
-
* Helper
|
|
207
|
-
* Convenience wrapper around CookbookExecutor
|
|
175
|
+
* Helper: execute a single step.
|
|
176
|
+
* Convenience wrapper around CookbookExecutor.
|
|
208
177
|
*
|
|
209
178
|
* @function executeStep
|
|
210
|
-
* @param {Object} step - Step to execute
|
|
211
|
-
* @param {Object} [context={}] - Execution context
|
|
212
|
-
* @returns {Promise<Object>} Step execution result
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* const result = await executeStep({
|
|
216
|
-
* id: "step1",
|
|
217
|
-
* type: "task",
|
|
218
|
-
* service: "api",
|
|
219
|
-
* action: "GET",
|
|
220
|
-
* endpoint: "/users"
|
|
221
|
-
* });
|
|
222
179
|
*/
|
|
223
180
|
async function executeStep(step, context = {}) {
|
|
224
181
|
if (!step || typeof step !== 'object') {
|
|
@@ -238,25 +195,15 @@ async function executeStep(step, context = {}) {
|
|
|
238
195
|
}
|
|
239
196
|
|
|
240
197
|
/**
|
|
241
|
-
* Helper
|
|
242
|
-
* Convenience wrapper around CookbookExecutor
|
|
198
|
+
* Helper: execute a complete workflow.
|
|
243
199
|
*
|
|
244
200
|
* @function executeWorkflow
|
|
245
|
-
* @param {Object} cookbook - Cookbook to execute
|
|
246
|
-
* @param {Object} [context={}] - Execution context
|
|
247
|
-
* @returns {Promise<Object>} Workflow execution result
|
|
248
|
-
*
|
|
249
|
-
* @example
|
|
250
|
-
* const result = await executeWorkflow(cookbook, {
|
|
251
|
-
* variables: { userId: 123 }
|
|
252
|
-
* });
|
|
253
201
|
*/
|
|
254
202
|
async function executeWorkflow(cookbook, context = {}) {
|
|
255
203
|
const processor = createProcessor();
|
|
256
204
|
return await processor.process(cookbook, context);
|
|
257
205
|
}
|
|
258
206
|
|
|
259
|
-
// Export everything as unified interface
|
|
260
207
|
module.exports = {
|
|
261
208
|
// Parser functions (from cookbook-core)
|
|
262
209
|
parseCookbookFromFile,
|
|
@@ -284,13 +231,6 @@ module.exports = {
|
|
|
284
231
|
StepProcessor,
|
|
285
232
|
VariableResolver,
|
|
286
233
|
|
|
287
|
-
// Transformer classes (from cookbook-transformer)
|
|
288
|
-
CookbookGenerator,
|
|
289
|
-
ApiSpecAnalyzer,
|
|
290
|
-
StepTranslator,
|
|
291
|
-
ResponseMapper,
|
|
292
|
-
createTransformer,
|
|
293
|
-
|
|
294
234
|
// Router classes (from cookbook-router)
|
|
295
235
|
CookbookRouter,
|
|
296
236
|
ServiceDiscovery,
|
|
@@ -298,10 +238,10 @@ module.exports = {
|
|
|
298
238
|
RetryHandler,
|
|
299
239
|
createRouter,
|
|
300
240
|
|
|
301
|
-
// Template helpers
|
|
241
|
+
// Template helpers
|
|
302
242
|
templateHelpers,
|
|
303
243
|
|
|
304
|
-
// Legacy
|
|
244
|
+
// Legacy class shims (thin adapters โ do not grow them)
|
|
305
245
|
CookbookParser: class CookbookParser {
|
|
306
246
|
parse(cookbook) {
|
|
307
247
|
return parseCookbookFromObject(cookbook);
|
|
@@ -325,4 +265,4 @@ module.exports = {
|
|
|
325
265
|
VERSION: '2.0.0',
|
|
326
266
|
COMPATIBLE_CORE_VERSION: cookbookCore.VERSION,
|
|
327
267
|
COMPATIBLE_SCHEMA_VERSION: cookbookCore.SCHEMA_VERSION
|
|
328
|
-
};
|
|
268
|
+
};
|
package/examples/basicUsage.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* basicUsage.js
|
|
3
|
-
*
|
|
4
|
-
* Example demonstrating full lifecycle of ServiceRegistryClient:
|
|
5
|
-
* 1. Load environment variables
|
|
6
|
-
* 2. Instantiate client
|
|
7
|
-
* 3. Initialize (setup queues and start listening for requests)
|
|
8
|
-
* 4. Register event listeners (apiDescriptionRequest, heartbeatSent, apiDescriptionSent, error)
|
|
9
|
-
* 5. Start heartbeat loop
|
|
10
|
-
* 6. Graceful shutdown on process signals
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
function requireEnv(name, description) {
|
|
14
|
-
const value = process.env[name];
|
|
15
|
-
if (!value || String(value).trim() === '') {
|
|
16
|
-
throw new Error(`[conn-orch-cookbook][example] Missing environment variable - ${name} is required. ${description || ''}`);
|
|
17
|
-
}
|
|
18
|
-
return String(value).trim();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const { ServiceRegistryClient, EVENTS } = require('agent-registry-client');
|
|
22
|
-
|
|
23
|
-
// Step 1: Create a new ServiceRegistryClient instance
|
|
24
|
-
const registryClient = new ServiceRegistryClient({
|
|
25
|
-
amqpUrl: requireEnv('AMQP_URL', 'RabbitMQ URL (IPv4-only recommended: amqp://user:pass@127.0.0.1:PORT)'),
|
|
26
|
-
serviceName: requireEnv('SERVICE_NAME', 'Service logical name'),
|
|
27
|
-
version: requireEnv('SERVICE_VERSION', 'Service version (SemVer)'),
|
|
28
|
-
heartbeatInterval: process.env.HEARTBEAT_INTERVAL ? parseInt(process.env.HEARTBEAT_INTERVAL, 10) : undefined,
|
|
29
|
-
apiQueue: process.env.API_QUEUE,
|
|
30
|
-
registryQueue: process.env.REGISTRY_QUEUE
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// Step 2: Register event listeners
|
|
34
|
-
registryClient.on(EVENTS.HEARTBEAT_SENT, (msg) => {
|
|
35
|
-
console.log(`[Heartbeat] Sent at ${msg.timestamp} for ${msg.serviceName}@${msg.version}`);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
registryClient.on(EVENTS.API_DESCRIPTION_REQUEST, async (payload) => {
|
|
39
|
-
console.log(`[API Description Request] for ${payload.serviceName}@${payload.version}`);
|
|
40
|
-
// Load local API description (from file or in-memory)
|
|
41
|
-
const apiDesc = await loadLocalApiDescription();
|
|
42
|
-
await registryClient.sendApiDescription(apiDesc);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
registryClient.on(EVENTS.API_DESCRIPTION_SENT, (msg) => {
|
|
46
|
-
console.log(`[API Description Sent] at ${msg.timestamp} for ${msg.serviceName}@${msg.version}`);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
registryClient.on(EVENTS.ERROR, (err) => {
|
|
50
|
-
console.error('[RegistryClient Error]', err);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
(async () => {
|
|
54
|
-
try {
|
|
55
|
-
// Step 3: Initialize client (setup queues and listeners)
|
|
56
|
-
await registryClient.init();
|
|
57
|
-
console.log('RegistryClient initialized: queues are ready.');
|
|
58
|
-
|
|
59
|
-
// Step 4: Start heartbeat loop
|
|
60
|
-
registryClient.startHeartbeat();
|
|
61
|
-
console.log('Heartbeat loop started.');
|
|
62
|
-
|
|
63
|
-
// Step 5: Graceful shutdown on SIGINT/SIGTERM
|
|
64
|
-
const shutdown = async () => {
|
|
65
|
-
console.log('Shutting down RegistryClient...');
|
|
66
|
-
await registryClient.close();
|
|
67
|
-
process.exit(0);
|
|
68
|
-
};
|
|
69
|
-
process.on('SIGINT', shutdown);
|
|
70
|
-
process.on('SIGTERM', shutdown);
|
|
71
|
-
} catch (err) {
|
|
72
|
-
console.error('Failed to initialize RegistryClient', err);
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
})();
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Helper: Load local API description
|
|
79
|
-
* In a real application, this could read from a JSON file,
|
|
80
|
-
* introspect OpenAPI spec, or build dynamically.
|
|
81
|
-
*/
|
|
82
|
-
async function loadLocalApiDescription() {
|
|
83
|
-
return {
|
|
84
|
-
endpoints: [
|
|
85
|
-
{ path: '/invoices', method: 'POST', description: 'Create a new invoice' },
|
|
86
|
-
{ path: '/invoices/:id', method: 'GET', description: 'Retrieve invoice by ID' }
|
|
87
|
-
]
|
|
88
|
-
};
|
|
89
|
-
}
|