@onlineapps/conn-orch-validator 2.0.29 → 2.0.30
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-orch-validator",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.30",
|
|
4
4
|
"description": "Validation orchestrator for OA Drive microservices - coordinates validation across all layers (base, infra, orch, business)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -136,7 +136,7 @@ class ValidationOrchestrator {
|
|
|
136
136
|
const packageFile = path.join(this.serviceRoot, 'package.json');
|
|
137
137
|
const dockerFile = path.join(this.serviceRoot, 'Dockerfile');
|
|
138
138
|
const dockerComposeFile = path.join(this.serviceRoot, 'docker-compose.yml');
|
|
139
|
-
const
|
|
139
|
+
const envTemplateFile = path.join(this.serviceRoot, '..', '..', 'config', 'env-templates', `${path.basename(this.serviceRoot)}.env`);
|
|
140
140
|
|
|
141
141
|
if (!fs.existsSync(configFile)) {
|
|
142
142
|
throw new Error('config.json not found');
|
|
@@ -154,8 +154,8 @@ class ValidationOrchestrator {
|
|
|
154
154
|
if (fs.existsSync(dockerComposeFile)) {
|
|
155
155
|
infra.dockerCompose = fs.readFileSync(dockerComposeFile, 'utf8');
|
|
156
156
|
}
|
|
157
|
-
if (fs.existsSync(
|
|
158
|
-
infra.
|
|
157
|
+
if (fs.existsSync(envTemplateFile)) {
|
|
158
|
+
infra.envTemplate = fs.readFileSync(envTemplateFile, 'utf8');
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// Extract @onlineapps/* dependencies
|
|
@@ -15,6 +15,28 @@
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Try reading from new path first (config/service/), fall back to legacy (conn-config/).
|
|
20
|
+
* @param {string} root - Service root
|
|
21
|
+
* @param {string} filename - e.g. 'config.json'
|
|
22
|
+
* @returns {string|null} File contents or null
|
|
23
|
+
*/
|
|
24
|
+
function readConfigFile(root, filename) {
|
|
25
|
+
const candidates = [
|
|
26
|
+
path.join(root, 'config', 'service', filename),
|
|
27
|
+
path.join(root, 'conn-config', filename)
|
|
28
|
+
];
|
|
29
|
+
for (const p of candidates) {
|
|
30
|
+
try { return fs.readFileSync(p, 'utf-8'); } catch { /* try next */ }
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function hasConfigDir(root) {
|
|
36
|
+
return fs.existsSync(path.join(root, 'config', 'service')) ||
|
|
37
|
+
fs.existsSync(path.join(root, 'conn-config'));
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
const STANDARD_LEVELS = [
|
|
19
41
|
{
|
|
20
42
|
level: 'v1.0',
|
|
@@ -25,13 +47,13 @@ const STANDARD_LEVELS = [
|
|
|
25
47
|
const has = (p) => fs.existsSync(path.join(root, p));
|
|
26
48
|
const read = (p) => { try { return fs.readFileSync(path.join(root, p), 'utf-8'); } catch { return null; } };
|
|
27
49
|
|
|
28
|
-
results.push({ passed:
|
|
50
|
+
results.push({ passed: hasConfigDir(root), id: 'dir_conn_config', message: 'config/service/ or conn-config/ directory' });
|
|
29
51
|
results.push({ passed: has('src'), id: 'dir_src', message: 'src/ directory' });
|
|
30
52
|
results.push({ passed: has('tests'), id: 'dir_tests', message: 'tests/ directory' });
|
|
31
53
|
results.push({ passed: has('src/app.js'), id: 'file_app', message: 'src/app.js' });
|
|
32
54
|
results.push({ passed: has('index.js'), id: 'file_index', message: 'index.js entry point' });
|
|
33
55
|
|
|
34
|
-
const configRaw =
|
|
56
|
+
const configRaw = readConfigFile(root, 'config.json');
|
|
35
57
|
let configValid = false;
|
|
36
58
|
if (configRaw) {
|
|
37
59
|
try {
|
|
@@ -41,7 +63,7 @@ const STANDARD_LEVELS = [
|
|
|
41
63
|
}
|
|
42
64
|
results.push({ passed: configValid, id: 'config_valid', message: 'Valid config.json with service.name + service.port' });
|
|
43
65
|
|
|
44
|
-
const opsRaw =
|
|
66
|
+
const opsRaw = readConfigFile(root, 'operations.json');
|
|
45
67
|
let opsValid = false;
|
|
46
68
|
if (opsRaw) {
|
|
47
69
|
try {
|
|
@@ -69,9 +91,7 @@ const STANDARD_LEVELS = [
|
|
|
69
91
|
name: 'Multitenancy Standard',
|
|
70
92
|
since: '2026-03-01',
|
|
71
93
|
checks: (root) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
const configRaw = read('conn-config/config.json');
|
|
94
|
+
const configRaw = readConfigFile(root, 'config.json');
|
|
75
95
|
let hasTenantContext = false;
|
|
76
96
|
if (configRaw) {
|
|
77
97
|
try {
|
|
@@ -224,11 +244,22 @@ class ServiceStructureValidator {
|
|
|
224
244
|
*/
|
|
225
245
|
validateDirectoryStructure() {
|
|
226
246
|
const requiredDirs = [
|
|
227
|
-
{ path: 'conn-config', description: 'Configuration directory' },
|
|
228
247
|
{ path: 'src', description: 'Source code directory' },
|
|
229
248
|
{ path: 'tests', description: 'Tests directory' }
|
|
230
249
|
];
|
|
231
250
|
|
|
251
|
+
if (!hasConfigDir(this.serviceRoot)) {
|
|
252
|
+
this.errors.push({
|
|
253
|
+
type: 'MISSING_DIRECTORY',
|
|
254
|
+
path: 'config/service/ (or conn-config/)',
|
|
255
|
+
message: 'Required directory missing: config/service/ (or legacy conn-config/)',
|
|
256
|
+
description: 'Configuration directory',
|
|
257
|
+
fix: 'Create directory: mkdir -p config/service'
|
|
258
|
+
});
|
|
259
|
+
} else {
|
|
260
|
+
this.info.push('✓ Found Configuration directory');
|
|
261
|
+
}
|
|
262
|
+
|
|
232
263
|
const recommendedDirs = [
|
|
233
264
|
{ path: 'tests/unit', description: 'Unit tests' },
|
|
234
265
|
{ path: 'tests/integration', description: 'Integration tests' },
|
|
@@ -268,48 +299,46 @@ class ServiceStructureValidator {
|
|
|
268
299
|
* Validate configuration files
|
|
269
300
|
*/
|
|
270
301
|
validateConfigurationFiles() {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (!fs.existsSync(configPath)) {
|
|
302
|
+
const configRaw = readConfigFile(this.serviceRoot, 'config.json');
|
|
303
|
+
if (!configRaw) {
|
|
274
304
|
this.errors.push({
|
|
275
305
|
type: 'MISSING_CONFIG',
|
|
276
|
-
path: 'conn-config/config.json',
|
|
277
|
-
message: 'Service configuration missing: conn-config/config.json',
|
|
306
|
+
path: 'config/service/config.json (or conn-config/config.json)',
|
|
307
|
+
message: 'Service configuration missing: config/service/config.json (or conn-config/config.json)',
|
|
278
308
|
fix: 'Create config.json with service metadata. See: /docs/standards/SERVICE_TEMPLATE.md'
|
|
279
309
|
});
|
|
280
310
|
} else {
|
|
281
311
|
try {
|
|
282
|
-
const config = JSON.parse(
|
|
312
|
+
const config = JSON.parse(configRaw);
|
|
283
313
|
this.validateConfigStructure(config);
|
|
284
314
|
this.info.push('✓ Found valid config.json');
|
|
285
315
|
} catch (error) {
|
|
286
316
|
this.errors.push({
|
|
287
317
|
type: 'INVALID_CONFIG',
|
|
288
|
-
path: '
|
|
318
|
+
path: 'config.json',
|
|
289
319
|
message: `Invalid JSON in config.json: ${error.message}`,
|
|
290
320
|
fix: 'Fix JSON syntax errors in config.json'
|
|
291
321
|
});
|
|
292
322
|
}
|
|
293
323
|
}
|
|
294
324
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (!fs.existsSync(operationsPath)) {
|
|
325
|
+
const opsRaw = readConfigFile(this.serviceRoot, 'operations.json');
|
|
326
|
+
if (!opsRaw) {
|
|
298
327
|
this.errors.push({
|
|
299
328
|
type: 'MISSING_OPERATIONS',
|
|
300
|
-
path: 'conn-config/operations.json',
|
|
301
|
-
message: 'Operations specification missing: conn-config/operations.json',
|
|
329
|
+
path: 'config/service/operations.json (or conn-config/operations.json)',
|
|
330
|
+
message: 'Operations specification missing: config/service/operations.json (or conn-config/operations.json)',
|
|
302
331
|
fix: 'Create operations.json. See: /docs/standards/OPERATIONS.md'
|
|
303
332
|
});
|
|
304
333
|
} else {
|
|
305
334
|
try {
|
|
306
|
-
const operations = JSON.parse(
|
|
335
|
+
const operations = JSON.parse(opsRaw);
|
|
307
336
|
this.validateOperationsStructure(operations);
|
|
308
337
|
this.info.push('✓ Found valid operations.json');
|
|
309
338
|
} catch (error) {
|
|
310
339
|
this.errors.push({
|
|
311
340
|
type: 'INVALID_OPERATIONS',
|
|
312
|
-
path: '
|
|
341
|
+
path: 'operations.json',
|
|
313
342
|
message: `Invalid JSON in operations.json: ${error.message}`,
|
|
314
343
|
fix: 'Fix JSON syntax errors in operations.json'
|
|
315
344
|
});
|
|
@@ -338,7 +367,7 @@ class ServiceStructureValidator {
|
|
|
338
367
|
if (!value) {
|
|
339
368
|
this.errors.push({
|
|
340
369
|
type: 'MISSING_CONFIG_FIELD',
|
|
341
|
-
path: '
|
|
370
|
+
path: 'config/service/config.json',
|
|
342
371
|
field: field,
|
|
343
372
|
message: `Required field missing in config.json: ${field}`,
|
|
344
373
|
fix: `Add "${field}" to config.json`
|
|
@@ -351,7 +380,7 @@ class ServiceStructureValidator {
|
|
|
351
380
|
if (!/^[a-z][a-z0-9-]*$/.test(config.service.name)) {
|
|
352
381
|
this.warnings.push({
|
|
353
382
|
type: 'INVALID_SERVICE_NAME',
|
|
354
|
-
path: '
|
|
383
|
+
path: 'config/service/config.json',
|
|
355
384
|
field: 'service.name',
|
|
356
385
|
value: config.service.name,
|
|
357
386
|
message: `Service name should be lowercase kebab-case: ${config.service.name}`,
|
|
@@ -387,7 +416,7 @@ class ServiceStructureValidator {
|
|
|
387
416
|
if (!operations.operations) {
|
|
388
417
|
this.errors.push({
|
|
389
418
|
type: 'INVALID_OPERATIONS_STRUCTURE',
|
|
390
|
-
path: '
|
|
419
|
+
path: 'config/service/operations.json',
|
|
391
420
|
message: 'operations.json must have "operations" key',
|
|
392
421
|
fix: 'Wrap operations in {"operations": {...}}. See: /docs/standards/OPERATIONS.md'
|
|
393
422
|
});
|
|
@@ -398,7 +427,7 @@ class ServiceStructureValidator {
|
|
|
398
427
|
if (typeof ops !== 'object' || Array.isArray(ops)) {
|
|
399
428
|
this.errors.push({
|
|
400
429
|
type: 'INVALID_OPERATIONS_TYPE',
|
|
401
|
-
path: '
|
|
430
|
+
path: 'config/service/operations.json',
|
|
402
431
|
message: 'operations must be an object',
|
|
403
432
|
fix: 'operations should be key-value pairs: {"operation-name": {...}}'
|
|
404
433
|
});
|
|
@@ -408,7 +437,7 @@ class ServiceStructureValidator {
|
|
|
408
437
|
if (Object.keys(ops).length === 0) {
|
|
409
438
|
this.warnings.push({
|
|
410
439
|
type: 'NO_OPERATIONS',
|
|
411
|
-
path: '
|
|
440
|
+
path: 'config/service/operations.json',
|
|
412
441
|
message: 'No operations defined',
|
|
413
442
|
fix: 'Add at least one operation to operations.json'
|
|
414
443
|
});
|
|
@@ -431,7 +460,7 @@ class ServiceStructureValidator {
|
|
|
431
460
|
if (!spec[field]) {
|
|
432
461
|
this.errors.push({
|
|
433
462
|
type: 'MISSING_OPERATION_FIELD',
|
|
434
|
-
path: '
|
|
463
|
+
path: 'config/service/operations.json',
|
|
435
464
|
operation: name,
|
|
436
465
|
field: field,
|
|
437
466
|
message: `Operation "${name}" missing required field: ${field}`,
|
|
@@ -444,7 +473,7 @@ class ServiceStructureValidator {
|
|
|
444
473
|
if (spec.endpoint && !spec.endpoint.startsWith('/')) {
|
|
445
474
|
this.errors.push({
|
|
446
475
|
type: 'INVALID_ENDPOINT',
|
|
447
|
-
path: '
|
|
476
|
+
path: 'config/service/operations.json',
|
|
448
477
|
operation: name,
|
|
449
478
|
field: 'endpoint',
|
|
450
479
|
value: spec.endpoint,
|
|
@@ -458,7 +487,7 @@ class ServiceStructureValidator {
|
|
|
458
487
|
if (spec.method && !validMethods.includes(spec.method)) {
|
|
459
488
|
this.errors.push({
|
|
460
489
|
type: 'INVALID_HTTP_METHOD',
|
|
461
|
-
path: '
|
|
490
|
+
path: 'config/service/operations.json',
|
|
462
491
|
operation: name,
|
|
463
492
|
field: 'method',
|
|
464
493
|
value: spec.method,
|