@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 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 OpenAPI spec Your service
39
+ This package operations.json Your service
40
40
  Handles all Defines API Pure business
41
- infrastructure interface logic only
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. **OpenAPI specification** - Describes your API
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. **Environment variables** - For configuration
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
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "cookbook": {
3
+ "version": "1.0",
4
+ "description": "Single operation example",
5
+ "steps": [
6
+ {
7
+ "service": "hello-service",
8
+ "operation": "good-day",
9
+ "input": {
10
+ "name": "Igor"
11
+ }
12
+ }
13
+ ]
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/service-wrapper",
3
- "version": "2.0.13",
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.4",
34
+ "@onlineapps/conn-orch-registry": "^1.1.13",
35
35
  "@onlineapps/monitoring-core": "^1.0.0"
36
36
  },
37
37
  "devDependencies": {
@@ -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
- await this.registryClient.register(serviceInfo);
278
- console.log(`Service registered: ${serviceName}`);
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
- const heartbeatInterval = this.config.wrapper?.registry?.heartbeatInterval || 30000;
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
- * Subscribe to workflow queues
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 _subscribeToQueues() {
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
  */