@hamak/smart-data-dico 1.0.4 → 1.1.1

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.
Files changed (49) hide show
  1. package/backend/dist/server.mjs +82213 -0
  2. package/bin/cli.js +28 -17
  3. package/package.json +28 -27
  4. package/backend/package.json +0 -51
  5. package/backend/src/__tests__/integration/api.test.ts +0 -149
  6. package/backend/src/__tests__/setup.ts +0 -24
  7. package/backend/src/__tests__/utils/testUtils.ts +0 -76
  8. package/backend/src/adapters/EntityFileAdapter.ts +0 -154
  9. package/backend/src/adapters/YamlFileInfoEnricher.ts +0 -52
  10. package/backend/src/controllers/authController.ts +0 -131
  11. package/backend/src/controllers/diagramController.ts +0 -143
  12. package/backend/src/controllers/dictionaryController.ts +0 -306
  13. package/backend/src/controllers/importExportController.ts +0 -64
  14. package/backend/src/controllers/perspectiveController.ts +0 -90
  15. package/backend/src/controllers/serviceController.ts +0 -418
  16. package/backend/src/controllers/stereotypeController.ts +0 -59
  17. package/backend/src/controllers/versionController.ts +0 -226
  18. package/backend/src/kernel/config.ts +0 -43
  19. package/backend/src/middleware/auth.ts +0 -128
  20. package/backend/src/middleware/jwtAuth.ts +0 -100
  21. package/backend/src/models/Dictionary.ts +0 -38
  22. package/backend/src/models/EntitySchema.ts +0 -393
  23. package/backend/src/models/__tests__/Dictionary.test.ts +0 -92
  24. package/backend/src/models/__tests__/EntitySchema.test.ts +0 -119
  25. package/backend/src/routes/index.ts +0 -120
  26. package/backend/src/scripts/migrate-to-uuid.ts +0 -24
  27. package/backend/src/server.ts +0 -158
  28. package/backend/src/services/__mocks__/entityService.ts +0 -38
  29. package/backend/src/services/__mocks__/serviceService.ts +0 -88
  30. package/backend/src/services/__mocks__/versionService.ts +0 -38
  31. package/backend/src/services/__tests__/dictionaryService.test.ts +0 -74
  32. package/backend/src/services/diagramService.ts +0 -165
  33. package/backend/src/services/dictionaryService.ts +0 -582
  34. package/backend/src/services/entityService.ts +0 -102
  35. package/backend/src/services/exportService.ts +0 -172
  36. package/backend/src/services/importService.ts +0 -208
  37. package/backend/src/services/perspectiveService.ts +0 -276
  38. package/backend/src/services/qualityService.ts +0 -121
  39. package/backend/src/services/serviceService.ts +0 -763
  40. package/backend/src/services/stereotypeService.ts +0 -98
  41. package/backend/src/services/versionService.ts +0 -135
  42. package/backend/src/setupTests.ts +0 -12
  43. package/backend/src/utils/__mocks__/fileOperations.ts +0 -116
  44. package/backend/src/utils/fileOperations.ts +0 -602
  45. package/backend/src/utils/logger.ts +0 -38
  46. package/backend/src/utils/migration.ts +0 -254
  47. package/backend/src/utils/swagger.ts +0 -358
  48. package/backend/src/utils/uuid.ts +0 -41
  49. package/backend/tsconfig.json +0 -20
package/bin/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { fileURLToPath } from 'url';
4
- import { dirname, join, resolve } from 'path';
4
+ import { dirname, join, resolve } from 'node:path';
5
5
  import { existsSync, mkdirSync, cpSync } from 'fs';
6
6
  import { spawn } from 'child_process';
7
7
 
@@ -25,7 +25,7 @@ if (flags.help) {
25
25
 
26
26
  Usage:
27
27
  smart-data-dico [options]
28
- npx smart-data-dico [options]
28
+ npx @hamak/smart-data-dico [options]
29
29
 
30
30
  Options:
31
31
  --port <number> Server port (default: 3001)
@@ -59,14 +59,33 @@ if (!existsSync(dataDir)) {
59
59
  mkdirSync(join(dataDir, 'perspectives'), { recursive: true });
60
60
  }
61
61
 
62
- const serverTs = join(PKG_ROOT, 'backend', 'src', 'server.ts');
63
- const frontendDist = join(PKG_ROOT, 'frontend', 'dist');
64
-
65
- if (!existsSync(serverTs)) {
66
- console.error('Error: Backend source not found.');
62
+ // Determine how to run the server:
63
+ // 1. Bundled (production/npm): single .mjs file, run with node — no deps needed
64
+ // 2. Source (dev): TypeScript source, run with tsx
65
+ const bundledServer = join(PKG_ROOT, 'backend', 'dist', 'server.mjs');
66
+ const sourceServer = join(PKG_ROOT, 'backend', 'src', 'server.ts');
67
+
68
+ let bin, binArgs;
69
+
70
+ if (existsSync(bundledServer)) {
71
+ // Production: bundled server — all deps inlined, just node
72
+ bin = process.execPath; // 'node'
73
+ binArgs = [bundledServer];
74
+ } else if (existsSync(sourceServer)) {
75
+ // Development: run TypeScript source via tsx
76
+ const tsxPaths = [
77
+ join(PKG_ROOT, 'node_modules', '.bin', 'tsx'),
78
+ join(PKG_ROOT, 'backend', 'node_modules', '.bin', 'tsx'),
79
+ ];
80
+ bin = tsxPaths.find(p => existsSync(p)) || 'npx';
81
+ binArgs = bin.endsWith('npx') ? ['tsx', sourceServer] : [sourceServer];
82
+ } else {
83
+ console.error('Error: No server found (neither bundled nor source).');
67
84
  process.exit(1);
68
85
  }
69
86
 
87
+ const frontendDist = join(PKG_ROOT, 'frontend', 'dist');
88
+
70
89
  console.log(`
71
90
  Smart Data Dictionary
72
91
 
@@ -76,16 +95,8 @@ console.log(`
76
95
  Frontend: ${existsSync(frontendDist) ? 'bundled' : 'dev (use frontend dev server on :3000)'}
77
96
  `);
78
97
 
79
- // Find tsx binary — check local node_modules first, then global
80
- const tsxPaths = [
81
- join(PKG_ROOT, 'node_modules', '.bin', 'tsx'),
82
- join(PKG_ROOT, 'backend', 'node_modules', '.bin', 'tsx'),
83
- ];
84
- let tsxBin = tsxPaths.find(p => existsSync(p)) || 'npx';
85
- const tsxArgs = tsxBin === 'npx' ? ['tsx', serverTs] : [serverTs];
86
-
87
- const child = spawn(tsxBin, tsxArgs, {
88
- cwd: join(PKG_ROOT, 'backend'),
98
+ const child = spawn(bin, binArgs, {
99
+ cwd: PKG_ROOT,
89
100
  env: {
90
101
  ...process.env,
91
102
  PORT: port,
package/package.json CHANGED
@@ -1,46 +1,24 @@
1
1
  {
2
2
  "name": "@hamak/smart-data-dico",
3
- "version": "1.0.4",
3
+ "version": "1.1.1",
4
4
  "description": "Collaborative data dictionary management system — model, document, and govern your data landscape",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "smart-data-dico": "bin/cli.js"
8
8
  },
9
9
  "scripts": {
10
- "build": "cd frontend && npm run build",
10
+ "build": "npm run build:frontend && npm run build:backend",
11
+ "build:frontend": "cd frontend && npm run build",
12
+ "build:backend": "node scripts/build-backend.mjs",
11
13
  "start": "node bin/cli.js",
12
14
  "prepublishOnly": "npm run build"
13
15
  },
14
16
  "files": [
15
17
  "bin/",
16
- "backend/src/",
17
- "backend/package.json",
18
- "backend/tsconfig.json",
18
+ "backend/dist/",
19
19
  "frontend/dist/",
20
20
  "data-dictionaries/stereotypes.yaml"
21
21
  ],
22
- "dependencies": {
23
- "tsx": "^4.0.0",
24
- "cors": "^2.8.5",
25
- "dotenv": "^16.3.1",
26
- "express": "^4.18.2",
27
- "jsonschema": "^1.4.1",
28
- "jsonwebtoken": "^9.0.2",
29
- "swagger-jsdoc": "^6.2.8",
30
- "swagger-ui-express": "^5.0.1",
31
- "yaml": "^2.3.1",
32
- "@hamak/filesystem-server-api": "^0.5.2",
33
- "@hamak/filesystem-server-impl": "^0.5.2",
34
- "@hamak/filesystem-server-spi": "^0.5.2",
35
- "@hamak/shared-utils": "^0.5.2",
36
- "@hamak/ui-remote-git-fs-backend": "^0.5.2",
37
- "@types/jsonwebtoken": "^9.0.9",
38
- "@types/cors": "^2.8.13",
39
- "@types/express": "^4.17.17",
40
- "@types/node": "^20.17.50",
41
- "@types/swagger-jsdoc": "^6.0.4",
42
- "@types/swagger-ui-express": "^4.1.8"
43
- },
44
22
  "keywords": [
45
23
  "data-dictionary",
46
24
  "data-modeling",
@@ -57,5 +35,28 @@
57
35
  "repository": {
58
36
  "type": "git",
59
37
  "url": "https://github.com/amah/smart-data-dico.git"
38
+ },
39
+ "devDependencies": {
40
+ "@hamak/filesystem-server-api": "^0.5.2",
41
+ "@hamak/filesystem-server-impl": "^0.5.2",
42
+ "@hamak/filesystem-server-spi": "^0.5.2",
43
+ "@hamak/shared-utils": "^0.5.2",
44
+ "@hamak/ui-remote-git-fs-backend": "^0.5.2",
45
+ "@types/cors": "^2.8.13",
46
+ "@types/express": "^4.17.17",
47
+ "@types/jsonwebtoken": "^9.0.9",
48
+ "@types/node": "^20.17.50",
49
+ "@types/swagger-jsdoc": "^6.0.4",
50
+ "@types/swagger-ui-express": "^4.1.8",
51
+ "cors": "^2.8.5",
52
+ "dotenv": "^16.3.1",
53
+ "esbuild": "^0.27.4",
54
+ "express": "^4.18.2",
55
+ "jsonschema": "^1.4.1",
56
+ "jsonwebtoken": "^9.0.2",
57
+ "swagger-jsdoc": "^6.2.8",
58
+ "swagger-ui-express": "^5.0.1",
59
+ "tsx": "^4.0.0",
60
+ "yaml": "^2.3.1"
60
61
  }
61
62
  }
@@ -1,51 +0,0 @@
1
- {
2
- "name": "data-dictionary-backend",
3
- "version": "0.1.0",
4
- "description": "Backend for Data Dictionary Management System",
5
- "type": "module",
6
- "main": "dist/server.js",
7
- "scripts": {
8
- "start": "node dist/server.js",
9
- "dev": "nodemon --exec tsx src/server.ts",
10
- "build": "tsc",
11
- "test": "jest",
12
- "lint": "eslint . --ext .ts",
13
- "test:coverage": "jest --coverage"
14
- },
15
- "dependencies": {
16
- "@hamak/filesystem-server-api": "^0.5.2",
17
- "@hamak/filesystem-server-impl": "^0.5.2",
18
- "@hamak/filesystem-server-spi": "^0.5.2",
19
- "@hamak/shared-utils": "^0.5.2",
20
- "@hamak/ui-remote-git-fs-backend": "^0.5.2",
21
- "@types/jsonwebtoken": "^9.0.9",
22
- "cors": "^2.8.5",
23
- "dotenv": "^16.3.1",
24
- "express": "^4.18.2",
25
- "jsonschema": "^1.4.1",
26
- "jsonwebtoken": "^9.0.2",
27
- "swagger-jsdoc": "^6.2.8",
28
- "swagger-ui-express": "^5.0.1",
29
- "yaml": "^2.3.1"
30
- },
31
- "devDependencies": {
32
- "@jest/globals": "^30.3.0",
33
- "@types/cors": "^2.8.13",
34
- "@types/express": "^4.17.17",
35
- "@types/jest": "^29.5.3",
36
- "@types/node": "^20.17.50",
37
- "@types/supertest": "^6.0.3",
38
- "@types/swagger-jsdoc": "^6.0.4",
39
- "@types/swagger-ui-express": "^4.1.8",
40
- "@typescript-eslint/eslint-plugin": "^6.0.0",
41
- "@typescript-eslint/parser": "^6.0.0",
42
- "eslint": "^8.45.0",
43
- "jest": "^29.6.2",
44
- "nodemon": "^3.0.1",
45
- "supertest": "^7.1.1",
46
- "ts-jest": "^29.1.1",
47
- "ts-node": "^10.9.1",
48
- "tsx": "^4.21.0",
49
- "typescript": "^5.1.6"
50
- }
51
- }
@@ -1,149 +0,0 @@
1
- import { NextFunction, Request, Response } from 'express';
2
- import request from 'supertest';
3
-
4
- import { UserRole } from '../../middleware/auth.js';
5
- import app from '../../server.js';
6
-
7
- // Extend Request type to include user property
8
- declare global {
9
- namespace Express {
10
- interface Request {
11
- user?: {
12
- id: string;
13
- role: string;
14
- };
15
- }
16
- }
17
- }
18
-
19
- // Mock auth middleware to bypass authentication
20
- jest.mock('../../middleware/auth', () => ({
21
- UserRole: {
22
- ADMIN: 'admin',
23
- EDITOR: 'editor',
24
- VIEWER: 'viewer',
25
- },
26
- authenticate: jest.fn().mockImplementation((_roles: string[]) => (req: Request, _res: Response, next: NextFunction) => {
27
- req.user = { id: 'test-user', role: 'admin' };
28
- next();
29
- }),
30
- }));
31
-
32
- // Mock JWT auth to bypass token verification
33
- jest.mock('../../middleware/jwtAuth', () => ({
34
- verifyToken: jest.fn().mockImplementation((req: Request, _res: Response, next: NextFunction) => {
35
- req.user = { id: 'test-user', role: 'admin' };
36
- next();
37
- }),
38
- authorizeJwt: jest.fn().mockImplementation((_roles: string[]) => (req: Request, _res: Response, next: NextFunction) => {
39
- req.user = { id: 'test-user', role: 'admin' };
40
- next();
41
- }),
42
- }));
43
-
44
- // Mock services (uses __mocks__ directory auto-mocks)
45
- jest.mock('../../services/dictionaryService');
46
- jest.mock('../../services/entityService');
47
- jest.mock('../../services/serviceService');
48
- jest.mock('../../services/versionService');
49
- jest.mock('../../utils/logger');
50
-
51
- describe('API Integration Tests', () => {
52
- describe('Health & Status', () => {
53
- it('should return health status', async () => {
54
- const response = await request(app).get('/health');
55
- expect(response.status).toBe(200);
56
- expect(response.body).toHaveProperty('status', 'ok');
57
- });
58
- });
59
-
60
- describe('Service Endpoints', () => {
61
- it('GET /api/services should return service list', async () => {
62
- const response = await request(app).get('/api/services');
63
- expect(response.status).toBe(200);
64
- expect(response.body).toHaveProperty('data');
65
- expect(Array.isArray(response.body.data)).toBe(true);
66
- expect(response.body.data).toContain('user-service');
67
- });
68
-
69
- it('GET /api/services/:service/entities should return entities', async () => {
70
- const response = await request(app).get('/api/services/user-service/entities');
71
- expect(response.status).toBe(200);
72
- });
73
-
74
- it('GET /api/services/:service/entities/:entity should return entity schema', async () => {
75
- const response = await request(app).get('/api/services/user-service/entities/User');
76
- expect(response.status).toBe(200);
77
- });
78
-
79
- it('POST /api/services/:service/entities should create entity', async () => {
80
- const response = await request(app)
81
- .post('/api/services/user-service/entities')
82
- .send({
83
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
84
- name: 'NewEntity',
85
- description: 'A new entity',
86
- attributes: [{ uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694', name: 'id', description: 'ID', type: 'string', required: true }],
87
- });
88
- expect(response.status).toBe(201);
89
- });
90
-
91
- it('PUT /api/services/:service/entities/:entity should update entity', async () => {
92
- const response = await request(app)
93
- .put('/api/services/user-service/entities/User')
94
- .send({
95
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
96
- name: 'User',
97
- description: 'Updated user',
98
- attributes: [{ uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694', name: 'id', description: 'ID', type: 'string', required: true }],
99
- });
100
- expect(response.status).toBe(200);
101
- });
102
-
103
- it('DELETE /api/services/:service/entities/:entity should delete entity', async () => {
104
- const response = await request(app).delete('/api/services/user-service/entities/User');
105
- expect(response.status).toBe(200);
106
- });
107
- });
108
-
109
- describe('Search & Graph', () => {
110
- it('GET /api/search should return results', async () => {
111
- const response = await request(app).get('/api/search?q=user');
112
- expect(response.status).toBe(200);
113
- });
114
-
115
- it('GET /api/graph/:service should return graph data', async () => {
116
- const response = await request(app).get('/api/graph/user-service');
117
- expect(response.status).toBe(200);
118
- });
119
- });
120
-
121
- describe('Version Control', () => {
122
- it('POST /api/commit should commit changes', async () => {
123
- const response = await request(app)
124
- .post('/api/commit')
125
- .send({ message: 'Test commit' });
126
- expect(response.status).toBe(200);
127
- });
128
-
129
- it('GET /api/history should return commit history', async () => {
130
- const response = await request(app).get('/api/history');
131
- expect(response.status).toBe(200);
132
- });
133
-
134
- it('POST /api/revert should revert to commit', async () => {
135
- const response = await request(app)
136
- .post('/api/revert')
137
- .send({ commitHash: 'mock-commit-1' });
138
- expect(response.status).toBe(200);
139
- });
140
- });
141
-
142
- describe('Relationship Endpoints', () => {
143
- it('GET /api/packages/:packageName/relationships should return relationships', async () => {
144
- const response = await request(app).get('/api/packages/user-service/relationships');
145
- expect(response.status).toBe(200);
146
- expect(response.body).toHaveProperty('data');
147
- });
148
- });
149
- });
@@ -1,24 +0,0 @@
1
- import dotenv from 'dotenv';
2
-
3
- // Load environment variables for testing
4
- dotenv.config({ path: '.env.test' });
5
-
6
- // Mock logger to prevent console output during tests
7
- jest.mock('../utils/logger', () => ({
8
- logger: {
9
- info: jest.fn(),
10
- error: jest.fn(),
11
- warn: jest.fn(),
12
- debug: jest.fn(),
13
- },
14
- }));
15
-
16
- // Global test setup
17
- beforeAll(() => {
18
- // Setup any global test requirements here
19
- });
20
-
21
- // Global test teardown
22
- afterAll(() => {
23
- // Clean up any global test resources here
24
- });
@@ -1,76 +0,0 @@
1
- import { Express } from 'express';
2
- import request from 'supertest';
3
- import fs from 'fs';
4
- import path from 'path';
5
-
6
- /**
7
- * Creates a test client for making HTTP requests to the Express app
8
- * @param app Express application
9
- * @returns Supertest instance
10
- */
11
- export const createTestClient = (app: Express) => {
12
- return request(app);
13
- };
14
-
15
- /**
16
- * Creates a temporary test directory for file operations
17
- * @param dirPath Directory path to create
18
- */
19
- export const createTestDirectory = (dirPath: string): void => {
20
- if (!fs.existsSync(dirPath)) {
21
- fs.mkdirSync(dirPath, { recursive: true });
22
- }
23
- };
24
-
25
- /**
26
- * Removes a test directory and all its contents
27
- * @param dirPath Directory path to remove
28
- */
29
- export const removeTestDirectory = (dirPath: string): void => {
30
- if (fs.existsSync(dirPath)) {
31
- fs.rmSync(dirPath, { recursive: true, force: true });
32
- }
33
- };
34
-
35
- /**
36
- * Creates a test YAML file with the given content
37
- * @param filePath Path to create the file
38
- * @param content Content to write to the file
39
- */
40
- export const createTestYamlFile = (filePath: string, content: string): void => {
41
- const dir = path.dirname(filePath);
42
- if (!fs.existsSync(dir)) {
43
- fs.mkdirSync(dir, { recursive: true });
44
- }
45
- fs.writeFileSync(filePath, content);
46
- };
47
-
48
- /**
49
- * Generates a random entity for testing
50
- * @param overrides Properties to override in the generated entity
51
- * @returns A test entity
52
- */
53
- export const generateTestEntity = (overrides = {}) => {
54
- return {
55
- id: `test-entity-${Date.now()}`,
56
- name: 'TestEntity',
57
- description: 'A test entity for unit tests',
58
- microservice: 'test-service',
59
- version: '1.0.0',
60
- attributes: [
61
- {
62
- name: 'id',
63
- description: 'Primary identifier',
64
- type: 'string',
65
- required: true,
66
- },
67
- {
68
- name: 'name',
69
- description: 'Entity name',
70
- type: 'string',
71
- required: true,
72
- },
73
- ],
74
- ...overrides,
75
- };
76
- };
@@ -1,154 +0,0 @@
1
- /**
2
- * EntityFileAdapter
3
- *
4
- * Wraps @hamak/filesystem-server-impl's WorkspaceManager to provide
5
- * the same API as current fileOperations.ts functions.
6
- * Uses dynamic imports since the framework packages are ESM-only.
7
- */
8
-
9
- import path from 'path';
10
- import YAML from 'yaml';
11
- import { logger } from '../utils/logger.js';
12
- import { Entity, validateEntity } from '../models/EntitySchema.js';
13
- import { generateEntityFilename } from '../utils/uuid.js';
14
- import { config } from '../kernel/config.js';
15
-
16
- // Lazy-loaded framework modules (ESM)
17
- let WorkspaceManager: any;
18
- let FileRouter: any;
19
- let FileInfoEnricherRegistry: any;
20
-
21
- let workspaceManager: any = null;
22
- let enricherRegistry: any = null;
23
- let fileRouter: any = null;
24
-
25
- /**
26
- * Initialize the framework filesystem components.
27
- * Must be called once at startup (async because of ESM dynamic imports).
28
- */
29
- export async function initializeFileSystem(): Promise<{
30
- workspaceManager: any;
31
- fileRouter: any;
32
- enricherRegistry: any;
33
- }> {
34
- if (workspaceManager) {
35
- return { workspaceManager, fileRouter, enricherRegistry };
36
- }
37
-
38
- const fsModule = await import('@hamak/filesystem-server-impl');
39
- WorkspaceManager = fsModule.WorkspaceManager;
40
- FileRouter = fsModule.FileRouter;
41
- FileInfoEnricherRegistry = fsModule.FileInfoEnricherRegistry;
42
-
43
- const baseDirectory = config.dataDir;
44
-
45
- const workspacesConfig: Record<string, string> = {
46
- dictionaries: '.',
47
- };
48
-
49
- workspaceManager = new WorkspaceManager(workspacesConfig, { baseDirectory });
50
- enricherRegistry = new FileInfoEnricherRegistry();
51
- fileRouter = new FileRouter(workspaceManager, { enricherRegistry });
52
-
53
- logger.info('Framework filesystem initialized', { baseDirectory });
54
-
55
- return { workspaceManager, fileRouter, enricherRegistry };
56
- }
57
-
58
- /**
59
- * Get the Express router for the /fs endpoint.
60
- */
61
- export function getFileRouter(): any {
62
- if (!fileRouter) {
63
- throw new Error('FileRouter not initialized. Call initializeFileSystem() first.');
64
- }
65
- return fileRouter.router;
66
- }
67
-
68
- /**
69
- * Get the enricher registry for registering file info enrichers.
70
- */
71
- export function getEnricherRegistry(): any {
72
- if (!enricherRegistry) {
73
- throw new Error('EnricherRegistry not initialized. Call initializeFileSystem() first.');
74
- }
75
- return enricherRegistry;
76
- }
77
-
78
- /**
79
- * Get the workspace manager instance.
80
- */
81
- export function getWorkspaceManager(): any {
82
- if (!workspaceManager) {
83
- throw new Error('WorkspaceManager not initialized. Call initializeFileSystem() first.');
84
- }
85
- return workspaceManager;
86
- }
87
-
88
- /**
89
- * Read an entity file using WorkspaceManager.
90
- */
91
- export async function readEntityViaAdapter(
92
- packageName: string,
93
- entityName: string
94
- ): Promise<Entity | null> {
95
- if (!workspaceManager) {
96
- return null;
97
- }
98
-
99
- try {
100
- const dirPath = `microservices/${packageName}`;
101
- const files = await workspaceManager.listFiles('dictionaries', dirPath);
102
-
103
- for (const file of files) {
104
- if (!file.name.endsWith('.yaml') && !file.name.endsWith('.yml')) {
105
- continue;
106
- }
107
- if (file.name === 'metadata.yaml' || file.name === 'relationships.yaml') {
108
- continue;
109
- }
110
-
111
- const filePath = `${dirPath}/${file.name}`;
112
- const content = await workspaceManager.readFile('dictionaries', filePath);
113
- const entity = YAML.parse(content) as Entity;
114
-
115
- if (entity.name === entityName) {
116
- return entity;
117
- }
118
- }
119
-
120
- return null;
121
- } catch (error) {
122
- logger.error(`EntityFileAdapter.readEntity error: ${error}`);
123
- return null;
124
- }
125
- }
126
-
127
- /**
128
- * Write an entity file using WorkspaceManager.
129
- */
130
- export async function writeEntityViaAdapter(entity: Entity, packageName: string): Promise<boolean> {
131
- if (!workspaceManager) {
132
- return false;
133
- }
134
-
135
- try {
136
- const validation = validateEntity(entity);
137
- if (!validation.valid) {
138
- logger.error(`Invalid entity: ${validation.errors.join(', ')}`);
139
- return false;
140
- }
141
-
142
- const filename = generateEntityFilename(entity.uuid, entity.name);
143
- const filePath = `microservices/${packageName}/${filename}`;
144
- const yamlContent = YAML.stringify(entity);
145
-
146
- await workspaceManager.writeFile('dictionaries', filePath, yamlContent);
147
- logger.info(`Entity written via adapter: ${filePath}`);
148
-
149
- return true;
150
- } catch (error) {
151
- logger.error(`EntityFileAdapter.writeEntity error: ${error}`);
152
- return false;
153
- }
154
- }
@@ -1,52 +0,0 @@
1
- /**
2
- * YamlFileInfoEnricher
3
- *
4
- * FileInfoEnricher that parses .yaml files and adds entity metadata
5
- * (entity name, uuid) to the file info response.
6
- */
7
-
8
- import YAML from 'yaml';
9
- import { logger } from '../utils/logger.js';
10
-
11
- export interface YamlEnrichedData {
12
- entityName?: string;
13
- entityUuid?: string;
14
- }
15
-
16
- /**
17
- * Creates a YAML file info enricher compatible with
18
- * @hamak/filesystem-server-impl's FileInfoEnricherRegistry.
19
- */
20
- export function createYamlFileInfoEnricher() {
21
- return {
22
- name: 'yaml-entity',
23
- extensionKey: 'entity',
24
-
25
- canEnrich(fileInfo: any): boolean {
26
- const name: string = fileInfo?.name || '';
27
- return name.endsWith('.yaml') || name.endsWith('.yml');
28
- },
29
-
30
- async enrich(fileInfo: any, context: any): Promise<YamlEnrichedData | null> {
31
- try {
32
- const content = context?.content;
33
- if (!content) {
34
- return null;
35
- }
36
-
37
- const parsed = YAML.parse(content);
38
- if (!parsed || typeof parsed !== 'object') {
39
- return null;
40
- }
41
-
42
- return {
43
- entityName: parsed.name,
44
- entityUuid: parsed.uuid,
45
- };
46
- } catch (error) {
47
- logger.debug(`YamlFileInfoEnricher: could not parse ${fileInfo?.name}: ${error}`);
48
- return null;
49
- }
50
- },
51
- };
52
- }