@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,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/`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/service-wrapper",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -24,17 +24,19 @@
24
24
  "author": "OA Drive Team",
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
- "@onlineapps/conn-base-logger": "^1.0.0",
27
+ "@onlineapps/conn-base-cache": "^1.0.0",
28
+ "@onlineapps/conn-base-monitoring": "file:../conn-base-monitoring/onlineapps-conn-base-monitoring-1.0.0.tgz",
29
+ "@onlineapps/conn-infra-error-handler": "^1.0.0",
28
30
  "@onlineapps/conn-infra-mq": "^1.1.0",
29
- "@onlineapps/conn-orch-registry": "^1.1.4",
31
+ "@onlineapps/conn-orch-api-mapper": "^1.0.0",
30
32
  "@onlineapps/conn-orch-cookbook": "^2.0.0",
31
33
  "@onlineapps/conn-orch-orchestrator": "^1.0.1",
32
- "@onlineapps/conn-orch-api-mapper": "^1.0.0",
33
- "@onlineapps/conn-base-cache": "^1.0.0",
34
- "@onlineapps/conn-infra-error-handler": "^1.0.0"
34
+ "@onlineapps/conn-orch-registry": "^1.1.4",
35
+ "@onlineapps/monitoring-core": "file:../../../api/shared/monitoring-core/onlineapps-monitoring-core-1.0.0.tgz"
35
36
  },
36
37
  "devDependencies": {
37
- "jest": "^29.5.0",
38
+ "express": "^5.1.0",
39
+ "jest": "^29.7.0",
38
40
  "jsdoc": "^4.0.2",
39
41
  "jsdoc-to-markdown": "^8.0.0"
40
42
  },
@@ -13,7 +13,7 @@
13
13
  // Import connectors
14
14
  const MQConnector = require('@onlineapps/conn-infra-mq');
15
15
  const RegistryConnector = require('@onlineapps/conn-orch-registry');
16
- const LoggerConnector = require('@onlineapps/conn-base-logger');
16
+ const MonitoringConnector = require('@onlineapps/conn-base-monitoring');
17
17
  const OrchestratorConnector = require('@onlineapps/conn-orch-orchestrator');
18
18
  const ApiMapperConnector = require('@onlineapps/conn-orch-api-mapper');
19
19
  const CookbookConnector = require('@onlineapps/conn-orch-cookbook');
@@ -69,7 +69,8 @@ class ServiceWrapper {
69
69
  this.config = options.config || {};
70
70
 
71
71
  // Initialize connectors
72
- this.logger = LoggerConnector.create(this.serviceName);
72
+ this.logger = MonitoringConnector; // Singleton, will be initialized later
73
+ this.monitoring = MonitoringConnector; // Also expose as monitoring for clarity
73
74
  this.mqClient = null;
74
75
  this.registryClient = null;
75
76
  this.orchestrator = null;
@@ -109,11 +110,14 @@ class ServiceWrapper {
109
110
  */
110
111
  async start() {
111
112
  if (this.isRunning) {
112
- this.logger.warn('Service wrapper already running');
113
+ console.warn('Service wrapper already running');
113
114
  return;
114
115
  }
115
116
 
116
117
  try {
118
+ // Initialize monitoring first
119
+ await this.monitoring.init({ serviceName: this.serviceName });
120
+
117
121
  this.logger.info(`Starting service wrapper for ${this.serviceName}`);
118
122
 
119
123
  // 1. Initialize infrastructure connectors
@@ -298,8 +302,37 @@ class ServiceWrapper {
298
302
  // Subscribe to workflow messages
299
303
  await this.mqClient.consume(
300
304
  queueName,
301
- async (message) => {
305
+ async (rawMessage) => {
302
306
  try {
307
+ // Parse message content if it's a buffer (AMQP message)
308
+ let message;
309
+ if (rawMessage && rawMessage.content) {
310
+ // This is an AMQP message with content buffer
311
+ const messageContent = rawMessage.content.toString();
312
+ try {
313
+ message = JSON.parse(messageContent);
314
+ } catch (parseError) {
315
+ this.logger.error('Failed to parse message content', {
316
+ error: parseError.message,
317
+ content: messageContent
318
+ });
319
+ throw parseError;
320
+ }
321
+ } else {
322
+ // Already parsed or direct message
323
+ message = rawMessage;
324
+ }
325
+
326
+ // DEBUG: Log the actual message structure
327
+ this.logger.info('DEBUG: Received message structure', {
328
+ workflow_id: message.workflow_id,
329
+ has_cookbook: !!message.cookbook,
330
+ cookbook_name: message.cookbook?.name,
331
+ cookbook_steps: message.cookbook?.steps?.length,
332
+ first_step: message.cookbook?.steps?.[0],
333
+ current_step: message.current_step
334
+ });
335
+
303
336
  // Delegate ALL processing to orchestrator
304
337
  const result = await this.orchestrator.processWorkflowMessage(
305
338
  message,
@@ -314,8 +347,8 @@ class ServiceWrapper {
314
347
 
315
348
  } catch (error) {
316
349
  this.logger.error('Message processing failed', {
317
- workflow_id: message.workflow_id,
318
- step: message.current_step,
350
+ workflow_id: message?.workflow_id || 'unknown',
351
+ step: message?.current_step || 'unknown',
319
352
  error: error.message
320
353
  });
321
354
  }