@onlineapps/service-wrapper 2.0.13 → 2.0.16
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 +4 -4
- package/docs/MIGRATION_FROM_OPENAPI.md +309 -0
- package/docs/STANDARDS_OVERVIEW.md +226 -0
- package/examples/README.md +170 -0
- package/examples/cookbook-file-output.json +16 -0
- package/examples/cookbook-multi-step.json +23 -0
- package/examples/cookbook-single-operation.json +15 -0
- package/package.json +2 -2
- package/src/ServiceWrapper.js +80 -7
- /package/docs/{API_STRUCTURE_STANDARD.md → archived-2025-09-29/API_STRUCTURE_STANDARD.md} +0 -0
- /package/docs/{ARCHITECTURE_DECISION.md → archived-2025-09-29/ARCHITECTURE_DECISION.md} +0 -0
- /package/docs/{HANDLER_VS_HTTP_COMPARISON.md → archived-2025-09-29/HANDLER_VS_HTTP_COMPARISON.md} +0 -0
- /package/docs/{INSTALLATION_GUIDE.md → archived-2025-09-29/INSTALLATION_GUIDE.md} +0 -0
- /package/docs/{OPERATIONS_SCHEMA.md → archived-2025-09-29/OPERATIONS_SCHEMA.md} +0 -0
- /package/docs/{PERFORMANCE.md → archived-2025-09-29/PERFORMANCE.md} +0 -0
- /package/docs/{PROCESS_FLOWS.md → archived-2025-09-29/PROCESS_FLOWS.md} +0 -0
- /package/docs/{SERVICE_TESTING_STANDARD.md → archived-2025-09-29/SERVICE_TESTING_STANDARD.md} +0 -0
- /package/docs/{WRAPPER_ARCHITECTURE.md → archived-2025-09-29/WRAPPER_ARCHITECTURE.md} +0 -0
package/README.md
CHANGED
|
@@ -36,9 +36,9 @@ This package provides the infrastructure layer that sits between:
|
|
|
36
36
|
```
|
|
37
37
|
[RabbitMQ Message] → [Service Wrapper] → [Service API Call] → [Business Logic]
|
|
38
38
|
↑ ↑ ↑
|
|
39
|
-
This package
|
|
39
|
+
This package operations.json Your service
|
|
40
40
|
Handles all Defines API Pure business
|
|
41
|
-
infrastructure
|
|
41
|
+
infrastructure mapping logic only
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
## Usage
|
|
@@ -180,9 +180,9 @@ This follows Docker/Kubernetes best practices for configuration management while
|
|
|
180
180
|
## Service Requirements
|
|
181
181
|
|
|
182
182
|
Your service needs:
|
|
183
|
-
1. **
|
|
183
|
+
1. **Operations specification** - Describes your API operations ([Operations Standard](/docs/OPERATIONS_STANDARD.md))
|
|
184
184
|
2. **Express app** - With business logic endpoints
|
|
185
|
-
3. **
|
|
185
|
+
3. **Configuration files** - Service config and environment variables
|
|
186
186
|
|
|
187
187
|
Your service should NOT have:
|
|
188
188
|
- Workflow processing code
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Migration from OpenAPI to Operations Standard
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This guide helps you migrate existing services from OpenAPI specifications to the simpler Operations Standard.
|
|
5
|
+
|
|
6
|
+
## Why Migrate?
|
|
7
|
+
|
|
8
|
+
### Problems with OpenAPI
|
|
9
|
+
- **Over-engineered** for internal microservices
|
|
10
|
+
- **Complex** specification (100+ lines for simple operations)
|
|
11
|
+
- **Slow parsing** and validation overhead
|
|
12
|
+
- **Designed for external APIs**, not internal workflows
|
|
13
|
+
- **Poor fit** for cookbook-based orchestration
|
|
14
|
+
|
|
15
|
+
### Benefits of Operations Standard
|
|
16
|
+
- ✅ **Simpler** - 10x less configuration
|
|
17
|
+
- ✅ **Faster** - Direct JSON parsing
|
|
18
|
+
- ✅ **Focused** - Only what workflows need
|
|
19
|
+
- ✅ **Maintainable** - Easy to read and update
|
|
20
|
+
- ✅ **Flexible** - Can generate OpenAPI if needed later
|
|
21
|
+
|
|
22
|
+
## Migration Steps
|
|
23
|
+
|
|
24
|
+
### Step 1: Create operations.json
|
|
25
|
+
|
|
26
|
+
Transform your OpenAPI spec into Operations format:
|
|
27
|
+
|
|
28
|
+
#### Before (OpenAPI - openapi.json):
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"openapi": "3.0.0",
|
|
32
|
+
"info": {
|
|
33
|
+
"title": "Hello Service",
|
|
34
|
+
"version": "1.0.0"
|
|
35
|
+
},
|
|
36
|
+
"paths": {
|
|
37
|
+
"/api/good-day": {
|
|
38
|
+
"post": {
|
|
39
|
+
"operationId": "goodDay",
|
|
40
|
+
"summary": "Generate good day greeting",
|
|
41
|
+
"requestBody": {
|
|
42
|
+
"required": true,
|
|
43
|
+
"content": {
|
|
44
|
+
"application/json": {
|
|
45
|
+
"schema": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"required": ["name"],
|
|
48
|
+
"properties": {
|
|
49
|
+
"name": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"minLength": 1,
|
|
52
|
+
"maxLength": 100
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"responses": {
|
|
60
|
+
"200": {
|
|
61
|
+
"description": "Success",
|
|
62
|
+
"content": {
|
|
63
|
+
"application/json": {
|
|
64
|
+
"schema": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"message": { "type": "string" },
|
|
68
|
+
"timestamp": { "type": "string", "format": "date-time" }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
#### After (Operations - operations.json):
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"operations": {
|
|
85
|
+
"good-day": {
|
|
86
|
+
"description": "Generate good day greeting",
|
|
87
|
+
"endpoint": "/api/good-day",
|
|
88
|
+
"method": "POST",
|
|
89
|
+
"input": {
|
|
90
|
+
"name": {
|
|
91
|
+
"type": "string",
|
|
92
|
+
"required": true,
|
|
93
|
+
"minLength": 1,
|
|
94
|
+
"maxLength": 100
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"output": {
|
|
98
|
+
"message": { "type": "string" },
|
|
99
|
+
"timestamp": { "type": "datetime" }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Result**: ~50 lines → ~15 lines (70% reduction)
|
|
107
|
+
|
|
108
|
+
### Step 2: Update Directory Structure
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Old structure
|
|
112
|
+
services/my-service/
|
|
113
|
+
├── connector-config/
|
|
114
|
+
│ ├── manifest.json
|
|
115
|
+
│ └── openapi.json
|
|
116
|
+
|
|
117
|
+
# New structure
|
|
118
|
+
services/my-service/
|
|
119
|
+
├── conn-config/
|
|
120
|
+
│ ├── config.json
|
|
121
|
+
│ └── operations.json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Move configuration:
|
|
125
|
+
```bash
|
|
126
|
+
cd services/my-service
|
|
127
|
+
mkdir -p conn-config
|
|
128
|
+
|
|
129
|
+
# If you have existing config, merge it
|
|
130
|
+
mv connector-config/manifest.json conn-config/config.json
|
|
131
|
+
|
|
132
|
+
# Create operations.json (see conversion below)
|
|
133
|
+
# Delete old directory
|
|
134
|
+
rm -rf connector-config/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Step 3: Convert OpenAPI to Operations
|
|
138
|
+
|
|
139
|
+
Use this mapping:
|
|
140
|
+
|
|
141
|
+
| OpenAPI | Operations |
|
|
142
|
+
|---------|-----------|
|
|
143
|
+
| `paths[path][method].operationId` | `operations[operation-name]` |
|
|
144
|
+
| `paths[path]` | `endpoint` |
|
|
145
|
+
| `requestBody.content.schema.properties` | `input` |
|
|
146
|
+
| `responses.200.content.schema.properties` | `output` |
|
|
147
|
+
| `schema.required[]` | `field.required: true` |
|
|
148
|
+
| `schema.minLength` | `minLength` |
|
|
149
|
+
| `schema.maxLength` | `maxLength` |
|
|
150
|
+
|
|
151
|
+
### Step 4: Update ServiceWrapper Initialization
|
|
152
|
+
|
|
153
|
+
#### Before:
|
|
154
|
+
```javascript
|
|
155
|
+
const { ServiceWrapper } = require('@onlineapps/service-wrapper');
|
|
156
|
+
const openApiSpec = require('./connector-config/openapi.json');
|
|
157
|
+
|
|
158
|
+
const wrapper = new ServiceWrapper({
|
|
159
|
+
app,
|
|
160
|
+
server,
|
|
161
|
+
config,
|
|
162
|
+
openApiSpec // OLD
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### After:
|
|
167
|
+
```javascript
|
|
168
|
+
const { ServiceWrapper } = require('@onlineapps/service-wrapper');
|
|
169
|
+
const operations = require('./conn-config/operations.json');
|
|
170
|
+
|
|
171
|
+
const wrapper = new ServiceWrapper({
|
|
172
|
+
app,
|
|
173
|
+
server,
|
|
174
|
+
config,
|
|
175
|
+
operations // NEW
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Step 5: Update Specification Endpoint
|
|
180
|
+
|
|
181
|
+
#### Before:
|
|
182
|
+
```javascript
|
|
183
|
+
app.get('/specification', (req, res) => {
|
|
184
|
+
const openapi = require('./connector-config/openapi.json');
|
|
185
|
+
res.json(openapi);
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### After:
|
|
190
|
+
```javascript
|
|
191
|
+
app.get('/specification', (req, res) => {
|
|
192
|
+
const operations = require('./conn-config/operations.json');
|
|
193
|
+
res.json(operations);
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Step 6: Update Documentation References
|
|
198
|
+
|
|
199
|
+
Update all references:
|
|
200
|
+
- `connector-config/` → `conn-config/`
|
|
201
|
+
- `openapi.json` → `operations.json`
|
|
202
|
+
- "OpenAPI specification" → "Operations specification"
|
|
203
|
+
|
|
204
|
+
Files to check:
|
|
205
|
+
- README.md
|
|
206
|
+
- docker-compose.yml comments
|
|
207
|
+
- Test files
|
|
208
|
+
- Documentation
|
|
209
|
+
|
|
210
|
+
### Step 7: Test
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# 1. Check specification endpoint
|
|
214
|
+
curl http://localhost:PORT/specification | jq .
|
|
215
|
+
|
|
216
|
+
# 2. Verify operations are listed
|
|
217
|
+
curl http://localhost:PORT/specification | jq '.operations | keys'
|
|
218
|
+
|
|
219
|
+
# 3. Test each operation
|
|
220
|
+
curl -X POST http://localhost:PORT/api/good-day \
|
|
221
|
+
-H "Content-Type: application/json" \
|
|
222
|
+
-d '{"name": "Test"}'
|
|
223
|
+
|
|
224
|
+
# 4. Run test suite
|
|
225
|
+
npm test
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Conversion Script
|
|
229
|
+
|
|
230
|
+
You can use this Node.js script to help convert OpenAPI to Operations:
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
// convert-openapi-to-operations.js
|
|
234
|
+
const fs = require('fs');
|
|
235
|
+
|
|
236
|
+
function convertOpenAPIToOperations(openapi) {
|
|
237
|
+
const operations = {};
|
|
238
|
+
|
|
239
|
+
Object.entries(openapi.paths || {}).forEach(([path, methods]) => {
|
|
240
|
+
Object.entries(methods).forEach(([method, spec]) => {
|
|
241
|
+
const operationId = spec.operationId || path.split('/').pop();
|
|
242
|
+
|
|
243
|
+
operations[operationId] = {
|
|
244
|
+
description: spec.summary || spec.description,
|
|
245
|
+
endpoint: path,
|
|
246
|
+
method: method.toUpperCase(),
|
|
247
|
+
input: convertSchema(spec.requestBody?.content?.['application/json']?.schema),
|
|
248
|
+
output: convertSchema(spec.responses?.['200']?.content?.['application/json']?.schema)
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return { operations };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function convertSchema(schema) {
|
|
257
|
+
if (!schema?.properties) return {};
|
|
258
|
+
|
|
259
|
+
const result = {};
|
|
260
|
+
Object.entries(schema.properties).forEach(([name, prop]) => {
|
|
261
|
+
result[name] = {
|
|
262
|
+
type: prop.type,
|
|
263
|
+
required: schema.required?.includes(name) || false,
|
|
264
|
+
...(prop.minLength && { minLength: prop.minLength }),
|
|
265
|
+
...(prop.maxLength && { maxLength: prop.maxLength }),
|
|
266
|
+
...(prop.description && { description: prop.description })
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Usage
|
|
274
|
+
const openapi = require('./connector-config/openapi.json');
|
|
275
|
+
const operations = convertOpenAPIToOperations(openapi);
|
|
276
|
+
fs.writeFileSync('./conn-config/operations.json', JSON.stringify(operations, null, 2));
|
|
277
|
+
console.log('Converted to operations.json');
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Validation
|
|
281
|
+
|
|
282
|
+
After migration, verify:
|
|
283
|
+
|
|
284
|
+
- [ ] `conn-config/operations.json` exists
|
|
285
|
+
- [ ] All operations have required fields (description, endpoint, method)
|
|
286
|
+
- [ ] Input/output schemas defined
|
|
287
|
+
- [ ] ServiceWrapper initializes without errors
|
|
288
|
+
- [ ] `/specification` endpoint returns operations
|
|
289
|
+
- [ ] All API endpoints work
|
|
290
|
+
- [ ] Tests pass
|
|
291
|
+
- [ ] Documentation updated
|
|
292
|
+
|
|
293
|
+
## Rollback
|
|
294
|
+
|
|
295
|
+
If you need to rollback:
|
|
296
|
+
|
|
297
|
+
1. Keep old `connector-config/` directory temporarily
|
|
298
|
+
2. Restore old ServiceWrapper initialization
|
|
299
|
+
3. Update references back
|
|
300
|
+
|
|
301
|
+
## Need Help?
|
|
302
|
+
|
|
303
|
+
- See [Operations Standard](/docs/OPERATIONS_STANDARD.md)
|
|
304
|
+
- Check [hello-service](/services/hello-service/) as reference
|
|
305
|
+
- Review [Service Template](/docs/SERVICE_TEMPLATE.md)
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
*Operations Standard simplifies service definitions while maintaining all necessary functionality for workflow orchestration.*
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Service Wrapper Standards Overview
|
|
2
|
+
|
|
3
|
+
## All Standards in One Place
|
|
4
|
+
|
|
5
|
+
This document provides a comprehensive overview of all standards related to Service Wrapper and microservice development.
|
|
6
|
+
|
|
7
|
+
## 📋 Standards Documents
|
|
8
|
+
|
|
9
|
+
### 1. API Structure Standard
|
|
10
|
+
**Location:** [API_STRUCTURE_STANDARD.md](./API_STRUCTURE_STANDARD.md)
|
|
11
|
+
|
|
12
|
+
**Key Points:**
|
|
13
|
+
- URL structure: `/`, `/health`, `/status`, `/specification`, `/api/*`
|
|
14
|
+
- Service discovery endpoint returns metadata
|
|
15
|
+
- Operations endpoint at `/specification`
|
|
16
|
+
- Business logic under `/api/*`
|
|
17
|
+
- No versioning in URLs
|
|
18
|
+
|
|
19
|
+
### 2. Operations Schema Standard
|
|
20
|
+
**Location:** [OPERATIONS_SCHEMA.md](./OPERATIONS_SCHEMA.md)
|
|
21
|
+
|
|
22
|
+
**Key Points:**
|
|
23
|
+
- Replaces OpenAPI for internal services
|
|
24
|
+
- Located in `/conn-config/operations.json`
|
|
25
|
+
- Defines input/output schemas for operations
|
|
26
|
+
- Direct mapping to Cookbook workflow steps
|
|
27
|
+
- Supports both internal and external APIs
|
|
28
|
+
|
|
29
|
+
### 3. Configuration Standard
|
|
30
|
+
**Location:** [CONFIGURATION_GUIDE.md](./CONFIGURATION_GUIDE.md)
|
|
31
|
+
|
|
32
|
+
**Key Points:**
|
|
33
|
+
- Configuration in `/conn-config/` directory
|
|
34
|
+
- Three files: `config.json`, `operations.json`, `middleware.json`
|
|
35
|
+
- Environment variable format: `${VAR:default}`
|
|
36
|
+
- Parsed by ServiceWrapper directly
|
|
37
|
+
|
|
38
|
+
### 4. Service Testing Standard
|
|
39
|
+
**Location:** [SERVICE_TESTING_STANDARD.md](./SERVICE_TESTING_STANDARD.md)
|
|
40
|
+
|
|
41
|
+
**Key Points:**
|
|
42
|
+
- Unit tests for business logic
|
|
43
|
+
- Component tests for connectors
|
|
44
|
+
- Integration tests for MQ flows
|
|
45
|
+
- E2E tests for complete workflows
|
|
46
|
+
- Mock strategies for external dependencies
|
|
47
|
+
|
|
48
|
+
### 5. Architecture Standards
|
|
49
|
+
**Location:** [FINAL_ARCHITECTURE.md](./FINAL_ARCHITECTURE.md)
|
|
50
|
+
|
|
51
|
+
**Key Points:**
|
|
52
|
+
- Single-process architecture
|
|
53
|
+
- ServiceWrapper as thin orchestration layer
|
|
54
|
+
- Specialized connectors for infrastructure
|
|
55
|
+
- HTTP-only communication pattern
|
|
56
|
+
- No direct handler calls
|
|
57
|
+
|
|
58
|
+
## 🏗️ Directory Structure Standards
|
|
59
|
+
|
|
60
|
+
### Service Directory Layout
|
|
61
|
+
```
|
|
62
|
+
service-name/
|
|
63
|
+
├── src/ # Business logic only
|
|
64
|
+
│ ├── app.js # Express application
|
|
65
|
+
│ └── routes/ # API endpoints
|
|
66
|
+
├── conn-config/ # Configuration
|
|
67
|
+
│ ├── config.json # Service configuration
|
|
68
|
+
│ ├── operations.json # Operations specification
|
|
69
|
+
│ └── middleware.json # Middleware configuration
|
|
70
|
+
├── test/ # Tests
|
|
71
|
+
│ ├── unit/ # Business logic tests
|
|
72
|
+
│ ├── integration/ # API tests
|
|
73
|
+
│ └── e2e/ # Full flow tests
|
|
74
|
+
├── index.js # Service entry point
|
|
75
|
+
├── Dockerfile # Container definition
|
|
76
|
+
└── package.json # Dependencies
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 🔌 Connector Standards
|
|
80
|
+
|
|
81
|
+
### Naming Convention
|
|
82
|
+
- Base connectors: `conn-base-*` (cache, monitoring)
|
|
83
|
+
- Infrastructure: `conn-infra-*` (mq, error-handler)
|
|
84
|
+
- Orchestration: `conn-orch-*` (registry, cookbook, orchestrator)
|
|
85
|
+
|
|
86
|
+
### Package Naming
|
|
87
|
+
- npm scope: `@onlineapps/`
|
|
88
|
+
- Full name: `@onlineapps/conn-{layer}-{function}`
|
|
89
|
+
|
|
90
|
+
## 📦 Package Standards
|
|
91
|
+
|
|
92
|
+
### Version Management
|
|
93
|
+
- Semantic versioning (MAJOR.MINOR.PATCH)
|
|
94
|
+
- Breaking changes increment MAJOR
|
|
95
|
+
- New features increment MINOR
|
|
96
|
+
- Bug fixes increment PATCH
|
|
97
|
+
|
|
98
|
+
### Publishing
|
|
99
|
+
- Always test locally before publishing
|
|
100
|
+
- Document breaking changes in changelog
|
|
101
|
+
- Update dependent packages after publishing
|
|
102
|
+
|
|
103
|
+
## 🔧 Environment Variables
|
|
104
|
+
|
|
105
|
+
### Standard Variables
|
|
106
|
+
```bash
|
|
107
|
+
# Service Configuration
|
|
108
|
+
PORT=3000 # Service port
|
|
109
|
+
NODE_ENV=production # Environment
|
|
110
|
+
|
|
111
|
+
# Infrastructure
|
|
112
|
+
RABBITMQ_URL=amqp://... # Message queue
|
|
113
|
+
REGISTRY_URL=http://... # Service registry
|
|
114
|
+
REDIS_URL=redis://... # Cache
|
|
115
|
+
|
|
116
|
+
# Features
|
|
117
|
+
MQ_ENABLED=true # Enable MQ integration
|
|
118
|
+
REGISTRY_ENABLED=true # Enable registry
|
|
119
|
+
MONITORING_ENABLED=true # Enable monitoring
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Format in config.json
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"port": "${PORT:3000}",
|
|
126
|
+
"rabbitmq": "${RABBITMQ_URL:amqp://localhost:5672}"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 📝 Documentation Standards
|
|
131
|
+
|
|
132
|
+
### Required Documentation
|
|
133
|
+
1. **README.md** - Overview and usage
|
|
134
|
+
2. **API.md** - API documentation (auto-generated)
|
|
135
|
+
3. **Configuration docs** - In `/docs` directory
|
|
136
|
+
4. **Inline comments** - Only when necessary
|
|
137
|
+
|
|
138
|
+
### Documentation Style
|
|
139
|
+
- Clear, concise explanations
|
|
140
|
+
- Examples for complex concepts
|
|
141
|
+
- Links to related documents
|
|
142
|
+
- No duplication - reference source of truth
|
|
143
|
+
|
|
144
|
+
## 🚀 Deployment Standards
|
|
145
|
+
|
|
146
|
+
### Docker
|
|
147
|
+
- Alpine-based images for size
|
|
148
|
+
- Non-root user for security
|
|
149
|
+
- Health checks included
|
|
150
|
+
- Multi-stage builds for optimization
|
|
151
|
+
|
|
152
|
+
### Health Checks
|
|
153
|
+
- Endpoint at `/health`
|
|
154
|
+
- Returns `{"status": "healthy", "timestamp": "..."}`
|
|
155
|
+
- Checked every 30 seconds
|
|
156
|
+
- 3 retries before marking unhealthy
|
|
157
|
+
|
|
158
|
+
## 🔄 Workflow Standards
|
|
159
|
+
|
|
160
|
+
### Cookbook Integration
|
|
161
|
+
- Operations map to cookbook steps
|
|
162
|
+
- Context passed between steps
|
|
163
|
+
- Error handling with retries
|
|
164
|
+
- Dead letter queue for failures
|
|
165
|
+
|
|
166
|
+
### Message Queue
|
|
167
|
+
- Queue naming: `{service-name}.workflow`
|
|
168
|
+
- Prefetch limit: 10 messages
|
|
169
|
+
- Acknowledgment after processing
|
|
170
|
+
- Automatic retries with backoff
|
|
171
|
+
|
|
172
|
+
## ✅ Code Quality Standards
|
|
173
|
+
|
|
174
|
+
### Best Practices
|
|
175
|
+
- No hardcoded values
|
|
176
|
+
- Configuration from environment
|
|
177
|
+
- Proper error handling
|
|
178
|
+
- Logging at appropriate levels
|
|
179
|
+
- Clean separation of concerns
|
|
180
|
+
|
|
181
|
+
### What Services Should NOT Have
|
|
182
|
+
- Workflow processing code
|
|
183
|
+
- Message queue operations
|
|
184
|
+
- Service registration logic
|
|
185
|
+
- Infrastructure connectors
|
|
186
|
+
- Environment variable parsing
|
|
187
|
+
|
|
188
|
+
## 📊 Monitoring Standards
|
|
189
|
+
|
|
190
|
+
### Metrics
|
|
191
|
+
- Request count and duration
|
|
192
|
+
- Error rates
|
|
193
|
+
- Queue depth
|
|
194
|
+
- Memory usage
|
|
195
|
+
- CPU utilization
|
|
196
|
+
|
|
197
|
+
### Logging
|
|
198
|
+
- Structured JSON logs
|
|
199
|
+
- Correlation IDs for tracing
|
|
200
|
+
- Appropriate log levels
|
|
201
|
+
- No sensitive data in logs
|
|
202
|
+
|
|
203
|
+
## 🔐 Security Standards
|
|
204
|
+
|
|
205
|
+
### Configuration
|
|
206
|
+
- Secrets from environment only
|
|
207
|
+
- No credentials in code
|
|
208
|
+
- No credentials in config files
|
|
209
|
+
- Secure defaults
|
|
210
|
+
|
|
211
|
+
### Communication
|
|
212
|
+
- HTTPS in production
|
|
213
|
+
- Authentication tokens
|
|
214
|
+
- Request validation
|
|
215
|
+
- Rate limiting
|
|
216
|
+
|
|
217
|
+
## 📚 References
|
|
218
|
+
|
|
219
|
+
- [Installation Guide](./INSTALLATION_GUIDE.md) - How to set up services
|
|
220
|
+
- [Performance Guide](./PERFORMANCE.md) - Optimization tips
|
|
221
|
+
- [Process Flows](./PROCESS_FLOWS.md) - How everything works together
|
|
222
|
+
- [Architecture Decision](./ARCHITECTURE_DECISION.md) - Why these choices
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
*This document serves as the single source of truth for all Service Wrapper standards.*
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Service Wrapper - Cookbook Examples
|
|
2
|
+
|
|
3
|
+
Example cookbooks demonstrating service-wrapper workflow orchestration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
These examples show how cookbooks are processed by service-wrapper to orchestrate operations across microservices.
|
|
8
|
+
|
|
9
|
+
## Example Cookbooks
|
|
10
|
+
|
|
11
|
+
### [cookbook-single-operation.json](cookbook-single-operation.json)
|
|
12
|
+
Basic single-operation workflow.
|
|
13
|
+
|
|
14
|
+
**Features:**
|
|
15
|
+
- Single service call
|
|
16
|
+
- Simple string output
|
|
17
|
+
- Direct JSON response
|
|
18
|
+
|
|
19
|
+
**Expected Output:**
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"message": "Good day, Igor! Welcome to our service!",
|
|
23
|
+
"timestamp": "2025-09-30T12:00:00Z",
|
|
24
|
+
"service": "hello-service"
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### [cookbook-file-output.json](cookbook-file-output.json)
|
|
31
|
+
Operation generating file output stored in MinIO.
|
|
32
|
+
|
|
33
|
+
**Features:**
|
|
34
|
+
- File generation (HTML/Markdown)
|
|
35
|
+
- MinIO storage reference
|
|
36
|
+
- `outputRef` for file retrieval
|
|
37
|
+
|
|
38
|
+
**Expected Output:**
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"certificate": "<!DOCTYPE html>...",
|
|
42
|
+
"outputRef": "cert-7d4e8f9a.html",
|
|
43
|
+
"timestamp": "2025-09-30T12:00:00Z"
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### [cookbook-multi-step.json](cookbook-multi-step.json)
|
|
50
|
+
Multi-step workflow orchestration.
|
|
51
|
+
|
|
52
|
+
**Features:**
|
|
53
|
+
- Sequential step execution
|
|
54
|
+
- Output passing between steps
|
|
55
|
+
- Multiple operations in one workflow
|
|
56
|
+
|
|
57
|
+
**Expected Output:**
|
|
58
|
+
Each step output accumulated in final workflow result.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Testing Workflows
|
|
63
|
+
|
|
64
|
+
### Via API Gateway (Production-like)
|
|
65
|
+
```bash
|
|
66
|
+
curl -X POST http://localhost:33000/workflow \
|
|
67
|
+
-H "Content-Type: application/json" \
|
|
68
|
+
-d @examples/cookbook-single-operation.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Direct Service Test (Development)
|
|
72
|
+
```bash
|
|
73
|
+
# Test hello-service directly
|
|
74
|
+
curl -X POST http://localhost:33199/api/good-day \
|
|
75
|
+
-H "Content-Type: application/json" \
|
|
76
|
+
-d '{"name": "Igor"}'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Via RabbitMQ (Integration Test)
|
|
80
|
+
```bash
|
|
81
|
+
# Publish directly to workflow queue
|
|
82
|
+
node scripts/publish-workflow.js examples/cookbook-single-operation.json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Workflow Response Format
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"workflowId": "wf-2025-09-30-abc123",
|
|
90
|
+
"status": "completed",
|
|
91
|
+
"startTime": "2025-09-30T12:00:00Z",
|
|
92
|
+
"endTime": "2025-09-30T12:00:01Z",
|
|
93
|
+
"steps": [
|
|
94
|
+
{
|
|
95
|
+
"service": "hello-service",
|
|
96
|
+
"operation": "good-day",
|
|
97
|
+
"input": {...},
|
|
98
|
+
"output": {...},
|
|
99
|
+
"processingTime": 45,
|
|
100
|
+
"status": "success"
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Creating Custom Cookbooks
|
|
107
|
+
|
|
108
|
+
### 1. Define Operation in Service
|
|
109
|
+
|
|
110
|
+
Add to `conn-config/operations.json`:
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"my-operation": {
|
|
114
|
+
"description": "Custom operation",
|
|
115
|
+
"endpoint": "/api/my-operation",
|
|
116
|
+
"method": "POST",
|
|
117
|
+
"input": {
|
|
118
|
+
"param1": {
|
|
119
|
+
"type": "string",
|
|
120
|
+
"required": true
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"output": {
|
|
124
|
+
"result": {
|
|
125
|
+
"type": "string"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 2. Implement Service Endpoint
|
|
133
|
+
|
|
134
|
+
In service `src/routes/api.routes.js`:
|
|
135
|
+
```javascript
|
|
136
|
+
router.post('/my-operation', (req, res) => {
|
|
137
|
+
const { param1 } = req.body;
|
|
138
|
+
// Business logic
|
|
139
|
+
res.json({ result: "..." });
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 3. Create Cookbook
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"cookbook": {
|
|
148
|
+
"version": "1.0",
|
|
149
|
+
"steps": [{
|
|
150
|
+
"service": "your-service",
|
|
151
|
+
"operation": "my-operation",
|
|
152
|
+
"input": { "param1": "value" }
|
|
153
|
+
}]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 4. Test
|
|
159
|
+
```bash
|
|
160
|
+
curl -X POST http://localhost:33000/workflow \
|
|
161
|
+
-H "Content-Type: application/json" \
|
|
162
|
+
-d @your-cookbook.json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Related Documentation
|
|
166
|
+
|
|
167
|
+
- [Operations Standard](/docs/OPERATIONS_STANDARD.md)
|
|
168
|
+
- [Service Wrapper Configuration](../docs/CONFIGURATION_GUIDE.md)
|
|
169
|
+
- [Workflow Module](/docs/modules/workflow.md)
|
|
170
|
+
- [Hello Service Examples](/services/hello-service/docs/WORKFLOW_EXECUTION.md)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cookbook": {
|
|
3
|
+
"version": "1.0",
|
|
4
|
+
"description": "Operation with file output (MinIO storage)",
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"service": "hello-service",
|
|
8
|
+
"operation": "hello-cert",
|
|
9
|
+
"input": {
|
|
10
|
+
"name": "Igor",
|
|
11
|
+
"format": "html"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cookbook": {
|
|
3
|
+
"version": "1.0",
|
|
4
|
+
"description": "Multi-step workflow across services",
|
|
5
|
+
"steps": [
|
|
6
|
+
{
|
|
7
|
+
"service": "hello-service",
|
|
8
|
+
"operation": "good-day",
|
|
9
|
+
"input": {
|
|
10
|
+
"name": "Igor"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"service": "hello-service",
|
|
15
|
+
"operation": "hello-cert",
|
|
16
|
+
"input": {
|
|
17
|
+
"name": "Igor",
|
|
18
|
+
"format": "md"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.16",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@onlineapps/conn-orch-api-mapper": "^1.0.0",
|
|
32
32
|
"@onlineapps/conn-orch-cookbook": "^2.0.0",
|
|
33
33
|
"@onlineapps/conn-orch-orchestrator": "^1.0.1",
|
|
34
|
-
"@onlineapps/conn-orch-registry": "^1.1.
|
|
34
|
+
"@onlineapps/conn-orch-registry": "^1.1.13",
|
|
35
35
|
"@onlineapps/monitoring-core": "^1.0.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
package/src/ServiceWrapper.js
CHANGED
|
@@ -34,6 +34,7 @@ class ServiceWrapper {
|
|
|
34
34
|
* @param {Object} options.server - HTTP server instance
|
|
35
35
|
* @param {Object} options.config - Service and wrapper configuration
|
|
36
36
|
* @param {Object} options.operations - Operations schema
|
|
37
|
+
* @param {Object} [options.validationProof=null] - Optional validation proof {hash, data}
|
|
37
38
|
*/
|
|
38
39
|
constructor(options = {}) {
|
|
39
40
|
this._validateOptions(options);
|
|
@@ -43,6 +44,7 @@ class ServiceWrapper {
|
|
|
43
44
|
this.server = options.server;
|
|
44
45
|
this.config = this._processConfig(options.config);
|
|
45
46
|
this.operations = options.operations;
|
|
47
|
+
this.validationProof = options.validationProof || null;
|
|
46
48
|
|
|
47
49
|
// Initialize connector placeholders
|
|
48
50
|
this.mqClient = null;
|
|
@@ -165,9 +167,10 @@ class ServiceWrapper {
|
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
// 6. Initialize orchestrator for workflow processing
|
|
170
|
+
// NOTE: Orchestrator is prepared but workflow listeners will be started
|
|
171
|
+
// ONLY after receiving certificate from Registry (see _initializeRegistry)
|
|
168
172
|
if (this.mqClient) {
|
|
169
173
|
await this._initializeOrchestrator();
|
|
170
|
-
await this._subscribeToQueues();
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
this.isInitialized = true;
|
|
@@ -259,10 +262,16 @@ class ServiceWrapper {
|
|
|
259
262
|
amqpUrl: mqUrl,
|
|
260
263
|
serviceName: serviceName,
|
|
261
264
|
version: this.config.service?.version || '1.0.0',
|
|
265
|
+
registryQueue: 'registry.register', // Use correct queue name
|
|
262
266
|
registryUrl: registryUrl,
|
|
263
|
-
logger: this.logger || console
|
|
267
|
+
logger: this.logger || console,
|
|
268
|
+
validationProof: this.validationProof // Inject validation proof
|
|
264
269
|
});
|
|
265
270
|
|
|
271
|
+
// Initialize registry client connection
|
|
272
|
+
await this.registryClient.init();
|
|
273
|
+
console.log('Registry client initialized');
|
|
274
|
+
|
|
266
275
|
// Register service
|
|
267
276
|
const serviceInfo = {
|
|
268
277
|
name: serviceName,
|
|
@@ -274,11 +283,39 @@ class ServiceWrapper {
|
|
|
274
283
|
}
|
|
275
284
|
};
|
|
276
285
|
|
|
277
|
-
|
|
278
|
-
|
|
286
|
+
// Validation proof is handled by RegistryClient.register() - no need to load here
|
|
287
|
+
// RegistryClient loads proof during init() and includes it in registration message
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
const registrationResult = await this.registryClient.register(serviceInfo);
|
|
291
|
+
console.log(`Service registered: ${serviceName}`, registrationResult);
|
|
292
|
+
|
|
293
|
+
// Store certificate if received
|
|
294
|
+
if (registrationResult.certificate) {
|
|
295
|
+
this.certificate = registrationResult.certificate;
|
|
296
|
+
console.log(`✓ Certificate received: ${registrationResult.certificate.certificateId}`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// CRITICAL: Only start workflow listeners after successful registration with certificate
|
|
300
|
+
if (registrationResult.success && registrationResult.certificate) {
|
|
301
|
+
console.log('✓ Certificate validated, starting workflow listeners...');
|
|
302
|
+
await this._startWorkflowListeners();
|
|
303
|
+
} else {
|
|
304
|
+
console.warn('⚠ Registration succeeded but no certificate received - workflow listeners NOT started');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error(`Service registration failed: ${error.message}`);
|
|
309
|
+
throw new Error(`Failed to register service ${serviceName}: ${error.message}`);
|
|
310
|
+
}
|
|
279
311
|
|
|
280
312
|
// Start heartbeat
|
|
281
|
-
|
|
313
|
+
// Priority: ENV variable > config > default (10s)
|
|
314
|
+
const heartbeatInterval = parseInt(process.env.HEARTBEAT_INTERVAL) ||
|
|
315
|
+
this.config.wrapper?.registry?.heartbeatInterval ||
|
|
316
|
+
10000;
|
|
317
|
+
console.log(`[ServiceWrapper] Heartbeat interval set to ${heartbeatInterval}ms`);
|
|
318
|
+
|
|
282
319
|
this.heartbeatTimer = setInterval(async () => {
|
|
283
320
|
try {
|
|
284
321
|
await this.registryClient.sendHeartbeat(serviceName);
|
|
@@ -385,10 +422,11 @@ class ServiceWrapper {
|
|
|
385
422
|
}
|
|
386
423
|
|
|
387
424
|
/**
|
|
388
|
-
*
|
|
425
|
+
* Start workflow listeners - ONLY called after receiving certificate from Registry
|
|
426
|
+
* CRITICAL: Unregistered services MUST NOT consume workflow messages
|
|
389
427
|
* @private
|
|
390
428
|
*/
|
|
391
|
-
async
|
|
429
|
+
async _startWorkflowListeners() {
|
|
392
430
|
const serviceName = this.config.service?.name || 'unnamed-service';
|
|
393
431
|
|
|
394
432
|
// Subscribe to workflow.init queue (for all services)
|
|
@@ -558,6 +596,41 @@ class ServiceWrapper {
|
|
|
558
596
|
}
|
|
559
597
|
}
|
|
560
598
|
|
|
599
|
+
/**
|
|
600
|
+
* Load validation proof from file
|
|
601
|
+
* @returns {Object|null} Validation proof or null if not found
|
|
602
|
+
*/
|
|
603
|
+
loadValidationProof() {
|
|
604
|
+
const fs = require('fs');
|
|
605
|
+
const path = require('path');
|
|
606
|
+
|
|
607
|
+
try {
|
|
608
|
+
// Look for .validation-proof.json in service root (process.cwd())
|
|
609
|
+
const proofPath = path.join(process.cwd(), '.validation-proof.json');
|
|
610
|
+
|
|
611
|
+
if (!fs.existsSync(proofPath)) {
|
|
612
|
+
console.warn('Validation proof not found:', proofPath);
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const proofData = fs.readFileSync(proofPath, 'utf8');
|
|
617
|
+
const proof = JSON.parse(proofData);
|
|
618
|
+
|
|
619
|
+
// Basic validation
|
|
620
|
+
if (!proof.validationProof || !proof.validationData) {
|
|
621
|
+
console.warn('Invalid validation proof format');
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
console.log('✓ Validation proof loaded:', proof.validationProof.substring(0, 16) + '...');
|
|
626
|
+
return proof;
|
|
627
|
+
|
|
628
|
+
} catch (error) {
|
|
629
|
+
console.warn('Failed to load validation proof:', error.message);
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
561
634
|
/**
|
|
562
635
|
* Get wrapper status
|
|
563
636
|
*/
|
|
File without changes
|
|
File without changes
|
/package/docs/{HANDLER_VS_HTTP_COMPARISON.md → archived-2025-09-29/HANDLER_VS_HTTP_COMPARISON.md}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/package/docs/{SERVICE_TESTING_STANDARD.md → archived-2025-09-29/SERVICE_TESTING_STANDARD.md}
RENAMED
|
File without changes
|
|
File without changes
|