@onlineapps/service-wrapper 2.0.6 → 2.0.8

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.
@@ -0,0 +1,363 @@
1
+ # Operations Schema Specification
2
+
3
+ ## Overview
4
+
5
+ The Operations Schema replaces OpenAPI for internal microservices, providing a simpler, more focused specification for service operations that map directly to Cookbook workflow steps.
6
+
7
+ ## Why Operations Schema?
8
+
9
+ - **Simpler than OpenAPI** - Only what's needed for workflow orchestration
10
+ - **Direct Cookbook mapping** - Operations correspond 1:1 with workflow steps
11
+ - **Flexible execution** - Supports both direct function calls and HTTP
12
+ - **Extensible** - Easy to add support for external APIs (REST, GraphQL, gRPC)
13
+
14
+ ## File Location
15
+
16
+ Operations are defined in `/conn-config/operations.json` for each service.
17
+
18
+ ## Schema Structure
19
+
20
+ ### Basic Operation (Internal Service)
21
+
22
+ ```json
23
+ {
24
+ "operations": {
25
+ "operation-name": {
26
+ "description": "Human-readable description",
27
+ "handler": "path.to.handler.function",
28
+ "endpoint": "/api/operation-name",
29
+ "method": "POST",
30
+ "input": {
31
+ "field1": {
32
+ "type": "string",
33
+ "required": true,
34
+ "description": "Field description"
35
+ },
36
+ "field2": {
37
+ "type": "number",
38
+ "default": 0
39
+ }
40
+ },
41
+ "output": {
42
+ "result": {
43
+ "type": "string",
44
+ "description": "Result description"
45
+ },
46
+ "timestamp": {
47
+ "type": "datetime"
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ ### External REST API Operation
56
+
57
+ ```json
58
+ {
59
+ "operations": {
60
+ "send-email": {
61
+ "description": "Send email via SendGrid",
62
+ "handler": "handlers.email.send",
63
+ "external": {
64
+ "type": "rest",
65
+ "endpoint": "https://api.sendgrid.com/v3/mail/send",
66
+ "method": "POST",
67
+ "headers": {
68
+ "Authorization": "Bearer ${SENDGRID_API_KEY}",
69
+ "Content-Type": "application/json"
70
+ },
71
+ "mapping": {
72
+ "input": {
73
+ "personalizations[0].to[0].email": "$.recipient",
74
+ "from.email": "${SENDER_EMAIL}",
75
+ "subject": "$.subject",
76
+ "content[0].value": "$.body"
77
+ },
78
+ "output": {
79
+ "messageId": "$.x-message-id",
80
+ "status": "$.status"
81
+ }
82
+ }
83
+ },
84
+ "input": {
85
+ "recipient": { "type": "string", "required": true },
86
+ "subject": { "type": "string", "required": true },
87
+ "body": { "type": "string", "required": true }
88
+ },
89
+ "output": {
90
+ "messageId": "string",
91
+ "status": "string"
92
+ }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### GraphQL Operation
99
+
100
+ ```json
101
+ {
102
+ "operations": {
103
+ "get-user-repos": {
104
+ "description": "Get user repositories from GitHub",
105
+ "external": {
106
+ "type": "graphql",
107
+ "endpoint": "https://api.github.com/graphql",
108
+ "headers": {
109
+ "Authorization": "Bearer ${GITHUB_TOKEN}"
110
+ },
111
+ "query": "query($login: String!) { user(login: $login) { repositories(first: 10) { nodes { name, description } } } }",
112
+ "variables": {
113
+ "login": "$.username"
114
+ },
115
+ "mapping": {
116
+ "output": {
117
+ "repositories": "$.data.user.repositories.nodes"
118
+ }
119
+ }
120
+ },
121
+ "input": {
122
+ "username": { "type": "string", "required": true }
123
+ },
124
+ "output": {
125
+ "repositories": { "type": "array" }
126
+ }
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ ## Field Types
133
+
134
+ Supported types for input/output fields:
135
+
136
+ - `string` - Text data
137
+ - `number` - Numeric value (integer or float)
138
+ - `boolean` - True/false
139
+ - `object` - Nested object structure
140
+ - `array` - Array of items
141
+ - `datetime` - ISO 8601 datetime string
142
+ - `date` - ISO 8601 date string
143
+ - `email` - Email address (validated)
144
+ - `url` - URL (validated)
145
+ - `uuid` - UUID v4 (validated)
146
+
147
+ ## Execution Modes
148
+
149
+ Services can run in different execution modes, configured in `config.json`:
150
+
151
+ ### Direct Mode (Internal Services)
152
+ ```json
153
+ {
154
+ "service": {
155
+ "execution": {
156
+ "mode": "direct",
157
+ "handler": "./src/handlers"
158
+ }
159
+ }
160
+ }
161
+ ```
162
+ - Service Wrapper calls handlers directly
163
+ - No HTTP overhead
164
+ - 50% memory savings
165
+ - Single process
166
+
167
+ ### HTTP Mode (Default/External)
168
+ ```json
169
+ {
170
+ "service": {
171
+ "execution": {
172
+ "mode": "http",
173
+ "baseUrl": "${SERVICE_URL:http://localhost:3000}"
174
+ }
175
+ }
176
+ }
177
+ ```
178
+ - Service Wrapper makes HTTP calls
179
+ - Service runs separately
180
+ - Better for testing
181
+ - Required for external APIs
182
+
183
+ ## Cookbook Integration
184
+
185
+ Operations map directly to Cookbook workflow steps:
186
+
187
+ ### Cookbook Step
188
+ ```json
189
+ {
190
+ "step_id": "greet_user",
191
+ "type": "service_call",
192
+ "service": "hello-service",
193
+ "operation": "good-day",
194
+ "input": {
195
+ "name": "$.user_name"
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### Corresponding Operation
201
+ ```json
202
+ {
203
+ "operations": {
204
+ "good-day": {
205
+ "description": "Generate good day greeting",
206
+ "handler": "handlers.greetings.goodDay",
207
+ "endpoint": "/api/good-day",
208
+ "input": {
209
+ "name": { "type": "string", "required": true }
210
+ },
211
+ "output": {
212
+ "message": "string",
213
+ "timestamp": "datetime"
214
+ }
215
+ }
216
+ }
217
+ }
218
+ ```
219
+
220
+ ## Handler Implementation
221
+
222
+ ### Direct Mode Handler
223
+ ```javascript
224
+ // handlers/greetings.js
225
+ exports.goodDay = async (input) => {
226
+ const { name } = input;
227
+ return {
228
+ message: `Good day, ${name}!`,
229
+ timestamp: new Date().toISOString()
230
+ };
231
+ };
232
+ ```
233
+
234
+ ### HTTP Mode Endpoint
235
+ ```javascript
236
+ // src/app.js
237
+ app.post('/api/good-day', async (req, res) => {
238
+ const { name } = req.body;
239
+ res.json({
240
+ message: `Good day, ${name}!`,
241
+ timestamp: new Date().toISOString()
242
+ });
243
+ });
244
+ ```
245
+
246
+ ## Validation
247
+
248
+ Input/output validation is automatic based on the schema:
249
+
250
+ ```javascript
251
+ // Service Wrapper validates automatically
252
+ const operation = operations['good-day'];
253
+ validateInput(input, operation.input); // Throws if invalid
254
+ const result = await executeOperation(operation, input);
255
+ validateOutput(result, operation.output); // Throws if invalid
256
+ ```
257
+
258
+ ## Migration from OpenAPI
259
+
260
+ ### Before (OpenAPI)
261
+ ```yaml
262
+ paths:
263
+ /v1/good-day:
264
+ post:
265
+ operationId: createGoodDay
266
+ requestBody:
267
+ content:
268
+ application/json:
269
+ schema:
270
+ $ref: '#/components/schemas/GreetingRequest'
271
+ responses:
272
+ '200':
273
+ content:
274
+ application/json:
275
+ schema:
276
+ $ref: '#/components/schemas/GreetingResponse'
277
+ ```
278
+
279
+ ### After (Operations Schema)
280
+ ```json
281
+ {
282
+ "operations": {
283
+ "good-day": {
284
+ "handler": "handlers.greetings.goodDay",
285
+ "endpoint": "/api/good-day",
286
+ "input": {
287
+ "name": { "type": "string", "required": true }
288
+ },
289
+ "output": {
290
+ "message": "string",
291
+ "timestamp": "datetime"
292
+ }
293
+ }
294
+ }
295
+ }
296
+ ```
297
+
298
+ ## Best Practices
299
+
300
+ 1. **Keep operations focused** - One operation = one business action
301
+ 2. **Use descriptive names** - Operation names should be self-explanatory
302
+ 3. **Document fields** - Add descriptions to complex fields
303
+ 4. **Validate strictly** - Mark required fields appropriately
304
+ 5. **Handle errors gracefully** - Define error responses in output
305
+ 6. **Use environment variables** - For URLs, keys, and configuration
306
+ 7. **Test both modes** - Ensure operations work in both direct and HTTP mode
307
+
308
+ ## Service Wrapper Integration
309
+
310
+ The Service Wrapper automatically:
311
+ - Loads operations.json on startup
312
+ - Routes Cookbook steps to operations
313
+ - Validates input/output
314
+ - Handles execution mode (direct/HTTP)
315
+ - Manages external API calls
316
+ - Provides metrics and logging
317
+
318
+ ## Example Service Structure
319
+
320
+ ```
321
+ hello-service/
322
+ ├── conn-config/
323
+ │ ├── config.json # Service configuration
324
+ │ ├── operations.json # Operations schema
325
+ │ └── middleware.json # Middleware config
326
+ ├── src/
327
+ │ ├── app.js # Express app (for HTTP mode)
328
+ │ └── handlers/ # Handler functions (for direct mode)
329
+ │ └── greetings.js
330
+ └── package.json
331
+ ```
332
+
333
+ ## Testing Operations
334
+
335
+ ```javascript
336
+ // Test operation directly
337
+ const operations = require('./conn-config/operations.json');
338
+ const handlers = require('./src/handlers');
339
+
340
+ test('good-day operation', async () => {
341
+ const input = { name: 'World' };
342
+ const result = await handlers.greetings.goodDay(input);
343
+
344
+ expect(result.message).toBe('Good day, World!');
345
+ expect(result.timestamp).toBeDefined();
346
+ });
347
+ ```
348
+
349
+ ## Future Extensions
350
+
351
+ The schema is designed to be extensible:
352
+
353
+ - **WebSocket operations** - Real-time communication
354
+ - **Message queue operations** - Direct MQ publishing
355
+ - **Database operations** - Direct DB queries
356
+ - **File operations** - S3, MinIO uploads
357
+ - **Batch operations** - Bulk processing
358
+
359
+ Each extension follows the same pattern:
360
+ 1. Define operation in operations.json
361
+ 2. Specify type in `external` block
362
+ 3. Map inputs/outputs
363
+ 4. Service Wrapper handles execution