@onlineapps/service-wrapper 2.0.7 → 2.0.9

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,389 @@
1
+ # Service Testing Standard and OpenAPI Generation
2
+
3
+ ## 1. OpenAPI Schema Generation
4
+
5
+ ### Step 1: Analyze Your Service Endpoints
6
+
7
+ List all your service endpoints and their:
8
+ - HTTP methods (GET, POST, PUT, DELETE)
9
+ - Request parameters (query, path, body)
10
+ - Response structures
11
+ - Error responses
12
+
13
+ ### Step 2: Create openapi.yaml
14
+
15
+ Create `openapi.yaml` in your service root directory with this structure:
16
+
17
+ ```yaml
18
+ openapi: 3.0.0
19
+ info:
20
+ title: # Your service name from package.json
21
+ version: # Your service version from package.json
22
+ description: # Brief description of what your service does
23
+
24
+ servers:
25
+ - url: http://localhost:{your-port}
26
+ description: Local development
27
+ - url: http://{container-name}:3000
28
+ description: Docker container
29
+
30
+ paths:
31
+ # Document each endpoint
32
+ /your-endpoint:
33
+ get/post/put/delete:
34
+ summary: # One line description
35
+ operationId: # Unique identifier (e.g., getUsers, createOrder)
36
+ parameters: # If applicable
37
+ requestBody: # If applicable
38
+ responses:
39
+ '200':
40
+ description: # Success response
41
+ content:
42
+ application/json:
43
+ schema:
44
+ # Define or reference schema
45
+ '400':
46
+ # Bad request
47
+ '500':
48
+ # Server error
49
+
50
+ components:
51
+ schemas:
52
+ # Define reusable schemas here
53
+ ```
54
+
55
+ ### Step 3: Generate Schema from Code (Alternative)
56
+
57
+ If you have many endpoints, generate initial schema:
58
+
59
+ ```bash
60
+ # Install openapi generator
61
+ npm install --save-dev @apidevtools/swagger-cli
62
+
63
+ # For Express apps, use express-openapi-generator
64
+ npm install --save-dev express-openapi-generator
65
+
66
+ # Add generation script to package.json
67
+ "scripts": {
68
+ "generate:openapi": "node scripts/generate-openapi.js"
69
+ }
70
+ ```
71
+
72
+ Create `scripts/generate-openapi.js`:
73
+ ```javascript
74
+ const app = require('../src/app');
75
+ const generator = require('express-openapi-generator');
76
+
77
+ generator(app, {
78
+ outputFile: './openapi.yaml',
79
+ info: {
80
+ title: process.env.SERVICE_NAME,
81
+ version: require('../package.json').version
82
+ }
83
+ });
84
+ ```
85
+
86
+ ### Step 4: Validate Your Schema
87
+
88
+ ```bash
89
+ # Install validator
90
+ npm install --save-dev @apidevtools/swagger-parser
91
+
92
+ # Add validation script
93
+ "scripts": {
94
+ "validate:openapi": "swagger-cli validate openapi.yaml"
95
+ }
96
+ ```
97
+
98
+ ## 2. Test Structure
99
+
100
+ ### Directory Structure
101
+
102
+ ```
103
+ /your-service
104
+ /test
105
+ /unit # Unit tests for individual functions
106
+ /integration # Integration tests with mocked dependencies
107
+ /api # API endpoint tests
108
+ /compliance # OpenAPI compliance tests
109
+ test.config.js # Shared test configuration
110
+ ```
111
+
112
+ ### Test Configuration
113
+
114
+ Create `test/test.config.js`:
115
+ ```javascript
116
+ module.exports = {
117
+ // Service configuration for tests
118
+ service: {
119
+ name: process.env.SERVICE_NAME || 'your-service',
120
+ port: process.env.PORT || 3000,
121
+ baseUrl: `http://localhost:${process.env.PORT || 3000}`
122
+ },
123
+
124
+ // Timeouts
125
+ timeouts: {
126
+ unit: 5000,
127
+ integration: 10000,
128
+ api: 15000
129
+ },
130
+
131
+ // Mock data
132
+ mocks: {
133
+ validRequest: { /* valid request data */ },
134
+ invalidRequest: { /* invalid request data */ }
135
+ }
136
+ };
137
+ ```
138
+
139
+ ## 3. Test Implementation
140
+
141
+ ### Unit Tests (`test/unit/`)
142
+
143
+ Test individual functions without external dependencies:
144
+
145
+ ```javascript
146
+ describe('Business Logic', () => {
147
+ test('should process valid input', () => {
148
+ const result = processFunction(validInput);
149
+ expect(result).toBeDefined();
150
+ expect(result.status).toBe('success');
151
+ });
152
+
153
+ test('should handle invalid input', () => {
154
+ expect(() => processFunction(invalidInput)).toThrow();
155
+ });
156
+ });
157
+ ```
158
+
159
+ ### API Tests (`test/api/`)
160
+
161
+ Test actual HTTP endpoints:
162
+
163
+ ```javascript
164
+ const request = require('supertest');
165
+ const app = require('../../src/app');
166
+
167
+ describe('API Endpoints', () => {
168
+ describe('GET /your-endpoint', () => {
169
+ test('should return 200 with valid response', async () => {
170
+ const response = await request(app)
171
+ .get('/your-endpoint')
172
+ .expect(200);
173
+
174
+ expect(response.body).toHaveProperty('expectedField');
175
+ });
176
+
177
+ test('should return 400 for invalid request', async () => {
178
+ const response = await request(app)
179
+ .get('/your-endpoint?invalid=true')
180
+ .expect(400);
181
+
182
+ expect(response.body).toHaveProperty('error');
183
+ });
184
+ });
185
+ });
186
+ ```
187
+
188
+ ### Compliance Tests (`test/compliance/`)
189
+
190
+ Test OpenAPI compliance:
191
+
192
+ ```javascript
193
+ const SwaggerParser = require('@apidevtools/swagger-parser');
194
+ const request = require('supertest');
195
+ const app = require('../../src/app');
196
+ const openApiSchema = require('../../openapi.yaml');
197
+
198
+ describe('OpenAPI Compliance', () => {
199
+ let api;
200
+
201
+ beforeAll(async () => {
202
+ // Validate and parse schema
203
+ api = await SwaggerParser.validate(openApiSchema);
204
+ });
205
+
206
+ test('schema should be valid OpenAPI 3.0', () => {
207
+ expect(api.openapi).toBe('3.0.0');
208
+ });
209
+
210
+ test('all endpoints should match schema', async () => {
211
+ for (const [path, methods] of Object.entries(api.paths)) {
212
+ for (const [method, spec] of Object.entries(methods)) {
213
+ if (['get', 'post', 'put', 'delete'].includes(method)) {
214
+ const response = await request(app)[method](path);
215
+ // Validate response matches schema
216
+ expect(response.status).toBeDefined();
217
+ }
218
+ }
219
+ }
220
+ });
221
+ });
222
+ ```
223
+
224
+ ## 4. NPM Scripts
225
+
226
+ Add to `package.json`:
227
+
228
+ ```json
229
+ {
230
+ "scripts": {
231
+ "test": "npm run test:unit && npm run test:api && npm run test:compliance",
232
+ "test:unit": "jest test/unit",
233
+ "test:integration": "jest test/integration",
234
+ "test:api": "jest test/api",
235
+ "test:compliance": "jest test/compliance",
236
+ "test:coverage": "jest --coverage",
237
+ "validate:openapi": "swagger-cli validate openapi.yaml",
238
+ "generate:openapi": "node scripts/generate-openapi.js",
239
+ "serve:docs": "swagger-ui-express openapi.yaml"
240
+ }
241
+ }
242
+ ```
243
+
244
+ ### Coverage Requirements
245
+
246
+ **Minimum recommended coverage: 80%**
247
+
248
+ Configure Jest coverage thresholds in `package.json`:
249
+
250
+ ```json
251
+ {
252
+ "jest": {
253
+ "coverageThreshold": {
254
+ "global": {
255
+ "branches": 80,
256
+ "functions": 80,
257
+ "lines": 80,
258
+ "statements": 80
259
+ }
260
+ },
261
+ "collectCoverageFrom": [
262
+ "src/**/*.js",
263
+ "!src/**/*.test.js",
264
+ "!src/**/*.spec.js"
265
+ ]
266
+ }
267
+ }
268
+ ```
269
+
270
+ ## 5. Service Wrapper Integration
271
+
272
+ ### Endpoint for OpenAPI Schema
273
+
274
+ Your service must expose its schema:
275
+
276
+ ```javascript
277
+ // In your app.js or routes
278
+ const fs = require('fs');
279
+ const yaml = require('js-yaml');
280
+
281
+ app.get('/openapi', (req, res) => {
282
+ try {
283
+ const doc = yaml.load(fs.readFileSync('./openapi.yaml', 'utf8'));
284
+ res.json(doc);
285
+ } catch (e) {
286
+ res.status(500).json({ error: 'Schema not available' });
287
+ }
288
+ });
289
+ ```
290
+
291
+ ### Validation Middleware
292
+
293
+ Use Service Wrapper's validation:
294
+
295
+ ```javascript
296
+ const { validateRequest } = require('@onlineapps/service-wrapper/middleware');
297
+ const openApiSchema = require('./openapi.yaml');
298
+
299
+ // Apply validation to routes
300
+ app.post('/your-endpoint',
301
+ validateRequest(openApiSchema, '/your-endpoint', 'post'),
302
+ (req, res) => {
303
+ // Request is already validated
304
+ }
305
+ );
306
+ ```
307
+
308
+ ## 6. Continuous Validation
309
+
310
+ ### Pre-commit Hook
311
+
312
+ `.husky/pre-commit`:
313
+ ```bash
314
+ #!/bin/sh
315
+ npm run validate:openapi
316
+ npm run test:compliance
317
+ ```
318
+
319
+ ### CI/CD Pipeline
320
+
321
+ ```yaml
322
+ # .github/workflows/test.yml or gitlab-ci.yml
323
+ test:
324
+ script:
325
+ - npm install
326
+ - npm run validate:openapi
327
+ - npm run test
328
+ - npm run test:coverage
329
+ coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
330
+ ```
331
+
332
+ ## 7. Validation Checklist
333
+
334
+ Before deployment, ensure:
335
+
336
+ - [ ] OpenAPI schema exists at `/openapi.yaml`
337
+ - [ ] Schema validates with `swagger-cli validate`
338
+ - [ ] All endpoints documented in schema
339
+ - [ ] Request/response schemas defined
340
+ - [ ] Error responses documented
341
+ - [ ] Schema version matches package.json
342
+ - [ ] `/openapi` endpoint returns schema
343
+ - [ ] Unit tests pass (>80% coverage)
344
+ - [ ] API tests pass
345
+ - [ ] Compliance tests pass
346
+ - [ ] Service Wrapper can load schema
347
+ - [ ] Validation middleware works
348
+
349
+ ## 8. Common Issues and Solutions
350
+
351
+ ### Issue: Schema doesn't match implementation
352
+ **Solution**: Generate schema from code first, then refine manually
353
+
354
+ ### Issue: Validation too strict
355
+ **Solution**: Use `additionalProperties: true` in schemas during development
356
+
357
+ ### Issue: Complex nested objects
358
+ **Solution**: Break into components and use `$ref`
359
+
360
+ ### Issue: Different environments need different configs
361
+ **Solution**: Use environment variables in schema:
362
+ ```yaml
363
+ servers:
364
+ - url: ${API_BASE_URL}
365
+ description: Configured base URL
366
+ ```
367
+
368
+ ## 9. Testing with conn-e2e-testing
369
+
370
+ The connector testing framework will:
371
+ 1. Load your OpenAPI schema
372
+ 2. Start your service
373
+ 3. Test each endpoint against schema
374
+ 4. Validate requests and responses
375
+ 5. Generate compliance report
376
+
377
+ Ensure your service is ready by:
378
+ 1. Exposing `/openapi` endpoint
379
+ 2. Having all endpoints documented
380
+ 3. Returning proper HTTP status codes
381
+ 4. Using consistent error format
382
+
383
+ ## 10. Resources
384
+
385
+ - [OpenAPI 3.0 Specification](https://swagger.io/specification/)
386
+ - [JSON Schema Validation](https://json-schema.org/draft/2019-09/json-schema-validation.html)
387
+ - [swagger-cli Documentation](https://apitools.dev/swagger-cli/)
388
+ - Service Wrapper documentation: `/shared/connector/service-wrapper/README.md`
389
+ - Testing examples: `/shared/connector/conn-e2e-testing/examples/`
@@ -0,0 +1,218 @@
1
+ # Service Wrapper Architecture
2
+
3
+ > **NOTE: This document describes initial architecture concepts. For the production-ready decision, see [FINAL_ARCHITECTURE.md](./FINAL_ARCHITECTURE.md)**
4
+
5
+ ## Definition
6
+
7
+ Service Wrapper is a **collection of connectors** that provides all components necessary for a business service to function within the system.
8
+
9
+ ## Core Principles
10
+
11
+ 1. **Component Collection** - Wrapper is NOT a framework but a set of reusable components
12
+ 2. **Central Management** - All services use the same wrapper version for consistency
13
+ 3. **Version Independence** - Each service can pin its wrapper version as needed
14
+ 4. **Duplicate by Design** - Each service has its own wrapper instance (not shared)
15
+
16
+ ## Architecture Pattern: Enhanced Service
17
+
18
+ ```javascript
19
+ // Business service enhanced with wrapper components
20
+ const express = require('express');
21
+ const { MQConnector, MonitoringConnector, HealthConnector } = require('@onlineapps/service-wrapper');
22
+
23
+ class EnhancedService {
24
+ constructor() {
25
+ // Core business service
26
+ this.app = express();
27
+ this.setupBusinessRoutes();
28
+
29
+ // Wrapper components
30
+ this.mq = new MQConnector(this);
31
+ this.monitoring = new MonitoringConnector(this);
32
+ this.health = new HealthConnector(this);
33
+ }
34
+
35
+ async start() {
36
+ // 1. Start HTTP server
37
+ await this.app.listen(this.config.port);
38
+
39
+ // 2. Connect to MQ
40
+ await this.mq.connect();
41
+ await this.mq.consume('workflow.init', this.handleWorkflow.bind(this));
42
+ await this.mq.consume(`${this.config.name}.workflow`, this.handleServiceMessage.bind(this));
43
+
44
+ // 3. Start monitoring
45
+ await this.monitoring.start();
46
+
47
+ // 4. Register health checks
48
+ this.health.register('/health', this.checkHealth.bind(this));
49
+ }
50
+
51
+ handleWorkflow(message) {
52
+ // Wrapper handles workflow routing
53
+ const operation = this.config.operations[message.step.operation];
54
+
55
+ // Call own HTTP endpoint
56
+ return this.callHttp(operation.endpoint, message.input);
57
+ }
58
+
59
+ callHttp(endpoint, data) {
60
+ // Internal HTTP call to business logic
61
+ return fetch(`http://localhost:${this.config.port}${endpoint}`, {
62
+ method: 'POST',
63
+ body: JSON.stringify(data)
64
+ });
65
+ }
66
+ }
67
+ ```
68
+
69
+ ## Component Responsibilities
70
+
71
+ ### MQ Connector
72
+ - Listen on `workflow.init` queue
73
+ - Listen on service-specific queues
74
+ - Route messages between services
75
+ - Handle dead letter queues
76
+
77
+ ### Monitoring Connector
78
+ - Collect metrics
79
+ - Export to monitoring system
80
+ - Track request/response times
81
+ - Monitor queue depth
82
+
83
+ ### Health Connector
84
+ - Provide `/health` endpoint
85
+ - Check service dependencies
86
+ - Report readiness status
87
+ - Aggregate component health
88
+
89
+ ### Registry Connector
90
+ - Register service on startup
91
+ - Send heartbeat
92
+ - Update capabilities
93
+ - Handle deregistration
94
+
95
+ ## Single Process Architecture
96
+
97
+ **Decision:** Use single process with embedded wrapper components.
98
+
99
+ ### Rationale:
100
+ 1. **Simplicity** - One deployment unit
101
+ 2. **Efficiency** - No IPC overhead
102
+ 3. **Atomicity** - Components share state
103
+ 4. **Debugging** - Single log stream
104
+
105
+ ### Implementation:
106
+ ```javascript
107
+ // service/index.js
108
+ const ServiceWrapper = require('@onlineapps/service-wrapper');
109
+ const app = require('./src/app'); // Express app
110
+ const config = require('./conn-config/config.json');
111
+ const operations = require('./conn-config/operations.json');
112
+
113
+ // Single process startup
114
+ async function start() {
115
+ // Business service
116
+ const server = app.listen(config.port);
117
+
118
+ // Wrapper components
119
+ const wrapper = new ServiceWrapper({
120
+ app,
121
+ server,
122
+ config,
123
+ operations
124
+ });
125
+
126
+ await wrapper.initialize();
127
+ console.log(`Service ${config.name} running with wrapper components`);
128
+ }
129
+
130
+ start().catch(console.error);
131
+ ```
132
+
133
+ ## Configuration Pattern
134
+
135
+ ### operations.json
136
+ ```json
137
+ {
138
+ "operations": {
139
+ "operation-name": {
140
+ "endpoint": "/api/operation",
141
+ "method": "POST",
142
+ "input": { "schema": "..." },
143
+ "output": { "schema": "..." }
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### config.json
150
+ ```json
151
+ {
152
+ "service": {
153
+ "name": "my-service",
154
+ "port": 3000
155
+ },
156
+ "wrapper": {
157
+ "mq": {
158
+ "url": "${RABBITMQ_URL}"
159
+ },
160
+ "monitoring": {
161
+ "enabled": true
162
+ }
163
+ }
164
+ }
165
+ ```
166
+
167
+ ## Health Check Strategy
168
+
169
+ Health checks are **provided by wrapper** but **implemented by service**:
170
+
171
+ ```javascript
172
+ // Wrapper provides endpoint
173
+ app.get('/health', healthConnector.handler);
174
+
175
+ // Service implements check
176
+ class MyService {
177
+ checkHealth() {
178
+ return {
179
+ status: this.isHealthy() ? 'healthy' : 'unhealthy',
180
+ database: this.db.isConnected(),
181
+ dependencies: this.checkDependencies()
182
+ };
183
+ }
184
+ }
185
+ ```
186
+
187
+ ## Migration Path
188
+
189
+ ### From Standalone Service:
190
+ 1. Add `@onlineapps/service-wrapper` dependency
191
+ 2. Create `conn-config/operations.json`
192
+ 3. Wrap service startup with ServiceWrapper
193
+ 4. Remove duplicate infrastructure code
194
+
195
+ ### From Two-Process Architecture:
196
+ 1. Merge wrapper.js into service index.js
197
+ 2. Remove separate wrapper process
198
+ 3. Consolidate configuration
199
+ 4. Single deployment unit
200
+
201
+ ## Benefits
202
+
203
+ 1. **No Duplication** - Common components in wrapper
204
+ 2. **Central Updates** - Update wrapper version for all services
205
+ 3. **Consistent Behavior** - All services work the same way
206
+ 4. **Simple Deployment** - One process per service
207
+ 5. **Clear Separation** - Business logic vs infrastructure
208
+
209
+ ## Drawbacks
210
+
211
+ 1. **Tight Coupling** - Service depends on wrapper version
212
+ 2. **Restart Required** - Any change restarts everything
213
+ 3. **Mixed Dependencies** - Business and infrastructure together
214
+ 4. **Testing Complexity** - Must mock wrapper components
215
+
216
+ ## Conclusion
217
+
218
+ Service Wrapper as a **component collection** in a **single process** provides the best balance of simplicity, efficiency, and maintainability for our microservices architecture.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/service-wrapper",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -25,13 +25,14 @@
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@onlineapps/conn-base-cache": "^1.0.0",
28
- "@onlineapps/conn-base-logger": "^1.0.0",
28
+ "@onlineapps/conn-base-monitoring": "^1.0.0",
29
29
  "@onlineapps/conn-infra-error-handler": "^1.0.0",
30
30
  "@onlineapps/conn-infra-mq": "^1.1.0",
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.4",
35
+ "@onlineapps/monitoring-core": "^1.0.0"
35
36
  },
36
37
  "devDependencies": {
37
38
  "express": "^5.1.0",