@hamak/smart-data-dico 1.0.3 → 1.1.0

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 (52) hide show
  1. package/backend/dist/server.mjs +82212 -0
  2. package/bin/cli.js +28 -17
  3. package/frontend/dist/assets/index-1b53cffe.js +431 -0
  4. package/frontend/dist/assets/index-fa72a51a.css +1 -0
  5. package/frontend/dist/index.html +15 -0
  6. package/package.json +28 -27
  7. package/backend/package.json +0 -51
  8. package/backend/src/__tests__/integration/api.test.ts +0 -149
  9. package/backend/src/__tests__/setup.ts +0 -24
  10. package/backend/src/__tests__/utils/testUtils.ts +0 -76
  11. package/backend/src/adapters/EntityFileAdapter.ts +0 -154
  12. package/backend/src/adapters/YamlFileInfoEnricher.ts +0 -52
  13. package/backend/src/controllers/authController.ts +0 -131
  14. package/backend/src/controllers/diagramController.ts +0 -143
  15. package/backend/src/controllers/dictionaryController.ts +0 -306
  16. package/backend/src/controllers/importExportController.ts +0 -64
  17. package/backend/src/controllers/perspectiveController.ts +0 -90
  18. package/backend/src/controllers/serviceController.ts +0 -418
  19. package/backend/src/controllers/stereotypeController.ts +0 -59
  20. package/backend/src/controllers/versionController.ts +0 -226
  21. package/backend/src/kernel/config.ts +0 -43
  22. package/backend/src/middleware/auth.ts +0 -128
  23. package/backend/src/middleware/jwtAuth.ts +0 -100
  24. package/backend/src/models/Dictionary.ts +0 -38
  25. package/backend/src/models/EntitySchema.ts +0 -393
  26. package/backend/src/models/__tests__/Dictionary.test.ts +0 -92
  27. package/backend/src/models/__tests__/EntitySchema.test.ts +0 -119
  28. package/backend/src/routes/index.ts +0 -120
  29. package/backend/src/scripts/migrate-to-uuid.ts +0 -24
  30. package/backend/src/server.ts +0 -158
  31. package/backend/src/services/__mocks__/entityService.ts +0 -38
  32. package/backend/src/services/__mocks__/serviceService.ts +0 -88
  33. package/backend/src/services/__mocks__/versionService.ts +0 -38
  34. package/backend/src/services/__tests__/dictionaryService.test.ts +0 -74
  35. package/backend/src/services/diagramService.ts +0 -165
  36. package/backend/src/services/dictionaryService.ts +0 -582
  37. package/backend/src/services/entityService.ts +0 -102
  38. package/backend/src/services/exportService.ts +0 -172
  39. package/backend/src/services/importService.ts +0 -208
  40. package/backend/src/services/perspectiveService.ts +0 -276
  41. package/backend/src/services/qualityService.ts +0 -121
  42. package/backend/src/services/serviceService.ts +0 -763
  43. package/backend/src/services/stereotypeService.ts +0 -98
  44. package/backend/src/services/versionService.ts +0 -135
  45. package/backend/src/setupTests.ts +0 -12
  46. package/backend/src/utils/__mocks__/fileOperations.ts +0 -116
  47. package/backend/src/utils/fileOperations.ts +0 -602
  48. package/backend/src/utils/logger.ts +0 -38
  49. package/backend/src/utils/migration.ts +0 -254
  50. package/backend/src/utils/swagger.ts +0 -358
  51. package/backend/src/utils/uuid.ts +0 -41
  52. package/backend/tsconfig.json +0 -20
@@ -1,92 +0,0 @@
1
- import { Dictionary, DictionaryEntry } from '../Dictionary.js';
2
-
3
- describe('Dictionary Model', () => {
4
- describe('Dictionary Interface', () => {
5
- it('should create a valid Dictionary object', () => {
6
- const dictionary: Dictionary = {
7
- id: 'test-dict-1',
8
- name: 'Test Dictionary',
9
- description: 'A test dictionary',
10
- version: '1.0.0',
11
- createdAt: new Date(),
12
- updatedAt: new Date(),
13
- rootPackage: { id: 'root', name: 'Root', entities: [], subPackages: [] },
14
- };
15
-
16
- expect(dictionary).toBeDefined();
17
- expect(dictionary.id).toBe('test-dict-1');
18
- expect(dictionary.name).toBe('Test Dictionary');
19
- expect(dictionary.description).toBe('A test dictionary');
20
- expect(dictionary.version).toBe('1.0.0');
21
- expect(dictionary.createdAt).toBeInstanceOf(Date);
22
- expect(dictionary.updatedAt).toBeInstanceOf(Date);
23
- });
24
-
25
- it('should create a Dictionary with only required fields', () => {
26
- const dictionary: Dictionary = {
27
- id: 'test-dict-2',
28
- name: 'Minimal Dictionary',
29
- rootPackage: { id: 'root', name: 'Root', entities: [], subPackages: [] },
30
- };
31
-
32
- expect(dictionary).toBeDefined();
33
- expect(dictionary.id).toBe('test-dict-2');
34
- expect(dictionary.name).toBe('Minimal Dictionary');
35
- expect(dictionary.description).toBeUndefined();
36
- expect(dictionary.version).toBeUndefined();
37
- expect(dictionary.createdAt).toBeUndefined();
38
- expect(dictionary.updatedAt).toBeUndefined();
39
- });
40
- });
41
-
42
- describe('DictionaryEntry Interface', () => {
43
- it('should create a valid DictionaryEntry object', () => {
44
- const entry: DictionaryEntry = {
45
- id: 'entry-1',
46
- name: 'Test Entry',
47
- description: 'A test entry',
48
- type: 'string',
49
- format: 'email',
50
- required: true,
51
- defaultValue: 'test@example.com',
52
- examples: ['user@example.com', 'admin@example.com'],
53
- metadata: {
54
- source: 'user',
55
- lastUpdated: '2023-01-01',
56
- },
57
- };
58
-
59
- expect(entry).toBeDefined();
60
- expect(entry.id).toBe('entry-1');
61
- expect(entry.name).toBe('Test Entry');
62
- expect(entry.description).toBe('A test entry');
63
- expect(entry.type).toBe('string');
64
- expect(entry.format).toBe('email');
65
- expect(entry.required).toBe(true);
66
- expect(entry.defaultValue).toBe('test@example.com');
67
- expect(entry.examples).toHaveLength(2);
68
- expect(entry.examples).toContain('user@example.com');
69
- expect(entry.metadata).toHaveProperty('source', 'user');
70
- });
71
-
72
- it('should create a DictionaryEntry with only required fields', () => {
73
- const entry: DictionaryEntry = {
74
- id: 'entry-2',
75
- name: 'Minimal Entry',
76
- description: 'A minimal entry',
77
- type: 'number',
78
- };
79
-
80
- expect(entry).toBeDefined();
81
- expect(entry.id).toBe('entry-2');
82
- expect(entry.name).toBe('Minimal Entry');
83
- expect(entry.description).toBe('A minimal entry');
84
- expect(entry.type).toBe('number');
85
- expect(entry.format).toBeUndefined();
86
- expect(entry.required).toBeUndefined();
87
- expect(entry.defaultValue).toBeUndefined();
88
- expect(entry.examples).toBeUndefined();
89
- expect(entry.metadata).toBeUndefined();
90
- });
91
- });
92
- });
@@ -1,119 +0,0 @@
1
- import { AttributeType, validateEntity } from '../EntitySchema.js';
2
-
3
- describe('EntitySchema', () => {
4
- describe('validateEntity', () => {
5
- it('should validate a valid entity', () => {
6
- const validEntity = {
7
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
8
- name: 'Test Entity',
9
- attributes: [
10
- {
11
- uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694',
12
- name: 'id',
13
- description: 'Primary identifier',
14
- type: AttributeType.STRING,
15
- required: true,
16
- primaryKey: true,
17
- },
18
- {
19
- uuid: 'c5af3719-ee6f-4156-bb1a-e575d345f7a5',
20
- name: 'name',
21
- description: 'Entity name',
22
- type: AttributeType.STRING,
23
- required: true,
24
- },
25
- ],
26
- };
27
-
28
- const result = validateEntity(validEntity);
29
- expect(result.valid).toBe(true);
30
- expect(result.errors).toHaveLength(0);
31
- });
32
-
33
- it('should invalidate an entity with missing required fields', () => {
34
- const invalidEntity = {
35
- // Missing uuid
36
- // Missing name
37
- description: 'A test entity',
38
- attributes: [],
39
- };
40
-
41
- const result = validateEntity(invalidEntity as any);
42
- expect(result.valid).toBe(false);
43
- expect(result.errors.length).toBeGreaterThan(0);
44
- });
45
-
46
- it('should validate an entity with constraints', () => {
47
- const entityWithConstraints = {
48
- uuid: 'd6b0482a-ff70-4267-8c2b-f686e456f8b6',
49
- name: 'Test Entity',
50
- description: 'A test entity',
51
- attributes: [
52
- {
53
- uuid: 'e7c1593b-aa81-4378-9d3c-a797f567a9c7',
54
- name: 'email',
55
- description: 'Email address',
56
- type: AttributeType.STRING,
57
- required: true,
58
- constraints: {
59
- maxLength: 255,
60
- pattern: '^[^@]+@[^@]+$',
61
- format: 'email',
62
- },
63
- },
64
- ],
65
- };
66
-
67
- const result = validateEntity(entityWithConstraints);
68
- expect(result.valid).toBe(true);
69
- expect(result.errors).toHaveLength(0);
70
- });
71
-
72
- it('should invalidate an entity with invalid attribute type', () => {
73
- const entityWithInvalidAttribute = {
74
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
75
- name: 'Test Entity',
76
- description: 'A test entity',
77
- attributes: [
78
- {
79
- uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694',
80
- name: 'id',
81
- description: 'Primary identifier',
82
- type: 'invalid-type', // Invalid type
83
- required: true,
84
- },
85
- ],
86
- };
87
-
88
- const result = validateEntity(entityWithInvalidAttribute as any);
89
- expect(result.valid).toBe(false);
90
- expect(result.errors.length).toBeGreaterThan(0);
91
- });
92
-
93
- it('should validate an entity with metadata entries', () => {
94
- const entityWithMetadata = {
95
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
96
- name: 'Test Entity',
97
- attributes: [
98
- {
99
- uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694',
100
- name: 'id',
101
- description: 'Primary identifier',
102
- type: AttributeType.STRING,
103
- required: true,
104
- metadata: [
105
- { name: 'sensitive', value: true },
106
- ],
107
- },
108
- ],
109
- metadata: [
110
- { name: 'owner', value: 'team-a' },
111
- ],
112
- };
113
-
114
- const result = validateEntity(entityWithMetadata);
115
- expect(result.valid).toBe(true);
116
- expect(result.errors).toHaveLength(0);
117
- });
118
- });
119
- });
@@ -1,120 +0,0 @@
1
- import { Router } from 'express';
2
-
3
- import { getCurrentUser, login } from '../controllers/authController.js';
4
- import { diagramController } from '../controllers/diagramController.js';
5
- import { createDictionary, getDictionaries, getDictionaryById, getDictionaryEntries, getEntityAttributes, getPackageByPath, getPackageHierarchy, getRelatedEntities, getTabularData, saveEntity, listAllPackagesAndEntities, getFlatEntitiesAndAttributes, getEntityHierarchy, createRootPackage, createPackageAtPath, updatePackageAtPath, deletePackageAtPath } from '../controllers/dictionaryController.js';
6
- import { createEntity, deleteEntity, getAllServices, getEntitySchema, getGraphData, getServiceEntities, searchEntities, updateEntity, getPackageRelationships, createRelationship, updateRelationship, deleteRelationship, getImpactAnalysis, getLineage, submitEntity, approveEntity, returnEntity, getEntityComments, addEntityComment, resolveEntityComment } from '../controllers/serviceController.js';
7
- import { getAllStereotypes, getStereotype, createStereotype, updateStereotype, deleteStereotype } from '../controllers/stereotypeController.js';
8
- import { getAllPerspectives, getPerspective, createPerspective, updatePerspective, deletePerspective, resolvePerspective, getPerspectiveGraph, upsertPerspectiveNode } from '../controllers/perspectiveController.js';
9
- import { commitChanges, getCommitHistory, revertToCommit } from '../controllers/versionController.js';
10
- import { importJsonSchema, importSqlDdl, exportJsonSchema, exportMarkdown, getQualityReport } from '../controllers/importExportController.js';
11
- import { authenticate, UserRole } from '../middleware/auth.js';
12
- import { authorizeJwt, verifyToken } from '../middleware/jwtAuth.js';
13
-
14
- const router = Router();
15
-
16
- // API status route
17
- router.get('/api/status', (req, res) => {
18
- res.json({ status: 'operational' });
19
- });
20
-
21
- router.get('/api/packages/hierarchy/:rootPackage', getPackageHierarchy);
22
- router.get('/api/packages/tabular/:rootPackage', getTabularData);
23
-
24
- // Package CRUD routes
25
- router.post('/api/packages', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), createRootPackage);
26
- router.post('/api/packages/:rootPackage/subpackages/*', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), createPackageAtPath);
27
- router.put('/api/packages/:rootPackage/path/*', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), updatePackageAtPath);
28
- router.delete('/api/packages/:rootPackage/path/*', authorizeJwt([UserRole.ADMIN]), deletePackageAtPath);
29
-
30
- router.get('/api/packages/:rootPackage/path/*', getPackageByPath);
31
-
32
- // Auth routes
33
- router.post('/api/auth/login', login);
34
- router.get('/api/auth/me', verifyToken, getCurrentUser);
35
-
36
- // Legacy Dictionary routes
37
- router.get('/api/dictionaries', getDictionaries);
38
- router.post('/api/dictionaries', createDictionary);
39
- router.get('/api/dictionaries/:id', getDictionaryById);
40
- router.get('/api/dictionaries/:id/entries', getDictionaryEntries);
41
- router.get('/api/entities/:microservice/:entityName/attributes', getEntityAttributes);
42
- router.get('/api/entities/:microservice/:entityName/related', getRelatedEntities);
43
- router.post('/api/entities', saveEntity);
44
-
45
- // Data Dictionary/Entity/Package API extensions
46
- router.get('/api/packages/all', listAllPackagesAndEntities);
47
- router.get('/api/entities/flat', getFlatEntitiesAndAttributes);
48
- router.get('/api/entities/hierarchy/:microservice/:entityName', getEntityHierarchy);
49
-
50
- // New Service/Entity API routes
51
- router.get('/api/services', getAllServices);
52
- router.get('/api/services/:service/entities', getServiceEntities);
53
- router.get('/api/services/:service/entities/:entity', getEntitySchema);
54
- router.post('/api/services/:service/entities', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), createEntity);
55
- router.put('/api/services/:service/entities/:entity', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), updateEntity);
56
- router.delete('/api/services/:service/entities/:entity', authorizeJwt([UserRole.ADMIN]), deleteEntity);
57
-
58
- // Entity review workflow
59
- router.post('/api/services/:service/entities/:entity/submit', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), submitEntity);
60
- router.post('/api/services/:service/entities/:entity/approve', authorizeJwt([UserRole.ADMIN]), approveEntity);
61
- router.post('/api/services/:service/entities/:entity/return', authorizeJwt([UserRole.ADMIN]), returnEntity);
62
- router.get('/api/services/:service/entities/:entity/comments', getEntityComments);
63
- router.post('/api/services/:service/entities/:entity/comments', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), addEntityComment);
64
- router.put('/api/services/:service/entities/:entity/comments/:id', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), resolveEntityComment);
65
-
66
- // Package-level relationship CRUD routes
67
- router.get('/api/packages/:packageName/relationships', getPackageRelationships);
68
- router.post('/api/packages/:packageName/relationships', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), createRelationship);
69
- router.put('/api/packages/:packageName/relationships/:uuid', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), updateRelationship);
70
- router.delete('/api/packages/:packageName/relationships/:uuid', authorizeJwt([UserRole.ADMIN]), deleteRelationship);
71
-
72
- // Stereotype API
73
- router.get('/api/stereotypes', getAllStereotypes);
74
- router.get('/api/stereotypes/:id', getStereotype);
75
- router.post('/api/stereotypes', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), createStereotype);
76
- router.put('/api/stereotypes/:id', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), updateStereotype);
77
- router.delete('/api/stereotypes/:id', authorizeJwt([UserRole.ADMIN]), deleteStereotype);
78
-
79
- // Perspective API
80
- router.get('/api/perspectives', getAllPerspectives);
81
- router.get('/api/perspectives/:id', getPerspective);
82
- router.post('/api/perspectives', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), createPerspective);
83
- router.put('/api/perspectives/:id', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), updatePerspective);
84
- router.delete('/api/perspectives/:id', authorizeJwt([UserRole.ADMIN]), deletePerspective);
85
- router.get('/api/perspectives/:id/resolve', resolvePerspective);
86
- router.get('/api/perspectives/:id/graph', getPerspectiveGraph);
87
- router.put('/api/perspectives/:id/nodes', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), upsertPerspectiveNode);
88
-
89
- // Search API
90
- router.get('/api/search', searchEntities);
91
-
92
- // Impact analysis & lineage
93
- router.get('/api/entities/:uuid/impact', getImpactAnalysis);
94
- router.get('/api/entities/:uuid/lineage', getLineage);
95
-
96
- // Import/Export
97
- router.post('/api/import/json-schema', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), importJsonSchema);
98
- router.post('/api/import/sql-ddl', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), importSqlDdl);
99
- router.get('/api/export/json-schema/:service', exportJsonSchema);
100
- router.get('/api/export/markdown/:service', exportMarkdown);
101
-
102
- // Quality
103
- router.get('/api/quality/report', getQualityReport);
104
-
105
- // Graph API for visualization
106
- router.get('/api/graph/:service', getGraphData);
107
-
108
- // Version control API
109
- router.post('/api/commit', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), commitChanges);
110
- router.get('/api/history', getCommitHistory);
111
- router.post('/api/revert', authorizeJwt([UserRole.ADMIN]), revertToCommit);
112
-
113
- // Diagram layout API
114
- router.get('/api/diagrams', diagramController.listDiagramLayouts.bind(diagramController));
115
- router.post('/api/diagrams', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), diagramController.saveDiagramLayout.bind(diagramController));
116
- router.get('/api/diagrams/:id', diagramController.loadDiagramLayout.bind(diagramController));
117
- router.put('/api/diagrams/:id', authorizeJwt([UserRole.ADMIN, UserRole.EDITOR]), diagramController.updateDiagramLayout.bind(diagramController));
118
- router.delete('/api/diagrams/:id', authorizeJwt([UserRole.ADMIN]), diagramController.deleteDiagramLayout.bind(diagramController));
119
-
120
- export default router;
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env ts-node
2
-
3
- import { runAllMigrations } from '../utils/migration.js';
4
- import { logger } from '../utils/logger.js';
5
-
6
- /**
7
- * Migration script to convert existing entities to UUID format
8
- */
9
- async function main() {
10
- try {
11
- logger.info('Starting migration to UUID format...');
12
- await runAllMigrations();
13
- logger.info('Migration completed successfully!');
14
- process.exit(0);
15
- } catch (error) {
16
- logger.error('Migration failed:', error);
17
- process.exit(1);
18
- }
19
- }
20
-
21
- // Run the migration if this script is executed directly
22
- if (require.main === module) {
23
- main();
24
- }
@@ -1,158 +0,0 @@
1
- import express, { Request, Response } from 'express';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import cors from 'cors';
5
- import dotenv from 'dotenv';
6
- import routes from './routes/index.js';
7
- import { setupSwagger } from './utils/swagger.js';
8
- import { logger } from './utils/logger.js';
9
- import { config } from './kernel/config.js';
10
- import { initializeFileSystem, getFileRouter } from './adapters/EntityFileAdapter.js';
11
- import { createYamlFileInfoEnricher } from './adapters/YamlFileInfoEnricher.js';
12
-
13
- // Load environment variables
14
- dotenv.config();
15
-
16
- // Initialize Express app
17
- const app = express();
18
- const port = config.port;
19
-
20
- // Middleware
21
- app.use(cors());
22
- // Access logging middleware
23
- app.use((req: Request, res: Response, next) => {
24
- const start = process.hrtime();
25
- res.on('finish', () => {
26
- const diff = process.hrtime(start);
27
- const executionTimeMs = Number((diff[0] * 1e3 + diff[1] / 1e6).toFixed(2));
28
- logger.info('HTTP Access', {
29
- method: req.method,
30
- path: req.originalUrl,
31
- status: res.statusCode,
32
- executionTimeMs
33
- });
34
- });
35
- next();
36
- });
37
-
38
- app.use(express.json());
39
-
40
- // API welcome route (only in dev — production serves frontend at /)
41
- if (!config.isProduction) {
42
- app.get('/', (req: Request, res: Response) => {
43
- res.json({ message: 'Welcome to the Data Dictionary Management System API' });
44
- });
45
- }
46
-
47
- // Health check endpoint
48
- app.get('/health', (req: Request, res: Response) => {
49
- res.json({ status: 'ok' });
50
- });
51
-
52
- // API routes
53
- app.use(routes);
54
-
55
- // Setup Swagger documentation
56
- setupSwagger(app);
57
-
58
- // =============================================================================
59
- // Framework Filesystem & Git Routes (Phase 1)
60
- // =============================================================================
61
-
62
- async function mountFrameworkRoutes() {
63
- try {
64
- // Initialize filesystem framework
65
- const { enricherRegistry } = await initializeFileSystem();
66
-
67
- // Register YAML entity enricher
68
- const yamlEnricher = createYamlFileInfoEnricher();
69
- enricherRegistry.register(yamlEnricher);
70
-
71
- // Initialize git service and routes
72
- try {
73
- const gitModule = await import('@hamak/ui-remote-git-fs-backend');
74
- const workspaceRoots = new Map<string, string>([
75
- ['dictionaries', config.dataDir],
76
- ]);
77
-
78
- const gitService = gitModule.createGitService(workspaceRoots);
79
- const gitEnricher = gitModule.createGitFileInfoEnricher({
80
- gitService,
81
- workspaceRoots,
82
- });
83
- enricherRegistry.register(gitEnricher);
84
-
85
- const gitRoutes = gitModule.createGitRoutes({ gitService, debug: !config.isProduction });
86
- app.use('/api/git', gitRoutes as any);
87
- app.use('/api/git', gitModule.gitErrorHandler as any);
88
-
89
- logger.info('Git routes mounted at /api/git');
90
- } catch (gitError) {
91
- logger.warn(`Git integration not available: ${gitError}`);
92
- }
93
-
94
- // Mount filesystem routes
95
- const fsRouter = getFileRouter();
96
- app.use('/fs', fsRouter);
97
-
98
- logger.info('Filesystem routes mounted at /fs');
99
- } catch (error) {
100
- logger.warn(`Framework filesystem not initialized: ${error}`);
101
- }
102
- }
103
-
104
- // Mount framework routes (non-blocking — existing routes work regardless)
105
- mountFrameworkRoutes().catch((err) => {
106
- logger.warn(`Failed to mount framework routes: ${err}`);
107
- });
108
-
109
-
110
- // Serve frontend static files in production (BEFORE error handler)
111
- if (config.isProduction) {
112
- // Check multiple possible frontend dist locations
113
- const candidates = [
114
- path.join(process.cwd(), 'public'), // Docker (copied to public/)
115
- path.join(process.cwd(), '..', 'frontend', 'dist'), // npm package / monorepo
116
- path.join(process.cwd(), 'frontend', 'dist'), // alt layout
117
- ];
118
- const publicDir = candidates.find(d => {
119
- try { return fs.statSync(d).isDirectory(); } catch { return false; }
120
- });
121
-
122
- if (publicDir) {
123
- app.use(express.static(publicDir));
124
- // SPA fallback — only for navigation requests, not assets/API
125
- app.get('*', (req, res, next) => {
126
- if (req.path.startsWith('/api') || req.path.startsWith('/fs') || req.path.startsWith('/api-docs') || req.path.includes('.')) {
127
- return next();
128
- }
129
- res.sendFile(path.join(publicDir, 'index.html'));
130
- });
131
- logger.info(`Serving frontend from ${publicDir}`);
132
- } else {
133
- logger.warn('Frontend dist not found. API-only mode.');
134
- }
135
- }
136
-
137
- // Error handling middleware (after static files)
138
- app.use((err: any, req: Request, res: Response, _next: any) => {
139
- logger.error(`Unhandled error: ${err.message}`);
140
- res.status(500).json({
141
- message: 'Internal server error',
142
- error: config.isProduction ? undefined : err.message
143
- });
144
- });
145
-
146
- // Start server only when run directly (not when imported by tests)
147
- const isMainModule = process.argv[1] && (
148
- process.argv[1].endsWith('server.ts') || process.argv[1].endsWith('server.js')
149
- );
150
-
151
- if (isMainModule) {
152
- app.listen(port, () => {
153
- logger.info(`Server running on port ${port}`);
154
- logger.info(`API documentation available at http://localhost:${port}/api-docs`);
155
- });
156
- }
157
-
158
- export default app;
@@ -1,38 +0,0 @@
1
- import { Entity, AttributeType } from '../../models/EntitySchema.js';
2
-
3
- const mockEntities: Record<string, Entity> = {
4
- 'user-service.User': {
5
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
6
- name: 'User',
7
- description: 'User entity',
8
- attributes: [
9
- { uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694', name: 'id', description: 'User ID', type: AttributeType.STRING, required: true, primaryKey: true },
10
- { uuid: 'c5af3719-ee6f-4156-bb1a-e575d345f7a5', name: 'email', description: 'User email', type: AttributeType.STRING, required: true },
11
- ],
12
- } as Entity,
13
- };
14
-
15
- // Mock entity service matching actual EntityService API
16
- class EntityServiceMock {
17
- validateEntity(entity: Entity) {
18
- return { valid: true, errors: [] as string[] };
19
- }
20
-
21
- async validateRelationships(packageName: string, relationships: any[]) {
22
- return { valid: true, errors: [] as string[] };
23
- }
24
-
25
- async saveEntity(entity: Entity, packageName: string) {
26
- return { success: true, errors: [] as string[] };
27
- }
28
-
29
- async getEntity(packageName: string, entityName: string): Promise<Entity | null> {
30
- return mockEntities[`${packageName}.${entityName}`] || null;
31
- }
32
-
33
- async getRelatedEntities(packageName: string, entityName: string): Promise<Entity[]> {
34
- return [];
35
- }
36
- }
37
-
38
- export const entityService = new EntityServiceMock();
@@ -1,88 +0,0 @@
1
- import { AttributeType } from '../../models/EntitySchema.js';
2
-
3
- // Mock service service matching actual ServiceService API
4
- class ServiceServiceMock {
5
- async getAllServices(): Promise<string[]> {
6
- return ['user-service', 'product-service', 'order-service'];
7
- }
8
-
9
- async getServiceEntities(service: string) {
10
- const serviceEntities: Record<string, any[]> = {
11
- 'user-service': [
12
- {
13
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
14
- name: 'User', description: 'User entity',
15
- attributes: [
16
- { uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694', name: 'id', description: 'User ID', type: AttributeType.STRING, required: true, primaryKey: true },
17
- { uuid: 'c5af3719-ee6f-4156-bb1a-e575d345f7a5', name: 'email', description: 'User email', type: AttributeType.STRING, required: true },
18
- ],
19
- },
20
- ],
21
- 'product-service': [],
22
- 'order-service': [],
23
- };
24
- return serviceEntities[service] || [];
25
- }
26
-
27
- async getEntitySchema(service: string, entity: string) {
28
- if (service === 'user-service' && entity === 'User') {
29
- return {
30
- uuid: 'a38d1597-cc4f-4934-bb08-c876c023f693',
31
- name: 'User', description: 'User entity',
32
- attributes: [
33
- { uuid: 'b49e2608-dd5f-4045-aa09-d464c234e694', name: 'id', description: 'User ID', type: AttributeType.STRING, required: true, primaryKey: true },
34
- { uuid: 'c5af3719-ee6f-4156-bb1a-e575d345f7a5', name: 'email', description: 'User email', type: AttributeType.STRING, required: true },
35
- ],
36
- };
37
- }
38
- return null;
39
- }
40
-
41
- async createEntity(service: string, entity: any) {
42
- return { success: true, errors: [] };
43
- }
44
-
45
- async updateEntity(service: string, entity: any) {
46
- return { success: true, errors: [] };
47
- }
48
-
49
- async deleteEntity(service: string, entityName: string) {
50
- return { success: true, errors: [] };
51
- }
52
-
53
- async searchEntities(query: string) {
54
- return [
55
- { entity: 'User', service: 'user-service', matches: ['name', 'description'] },
56
- ];
57
- }
58
-
59
- async getGraphData(service: string) {
60
- return {
61
- nodes: [
62
- { id: 'User', label: 'User', type: 'entity' },
63
- { id: 'Profile', label: 'Profile', type: 'entity' },
64
- ],
65
- edges: [
66
- { source: 'User', target: 'Profile', label: 'hasOne' },
67
- ],
68
- };
69
- }
70
-
71
- async getPackageRelationships(packageName: string) {
72
- return [];
73
- }
74
-
75
- async createRelationship(packageName: string, relationship: any) {
76
- return { success: true, errors: [], relationship };
77
- }
78
-
79
- async updateRelationship(packageName: string, uuid: string, relationship: any) {
80
- return { success: true, errors: [] };
81
- }
82
-
83
- async deleteRelationship(packageName: string, uuid: string) {
84
- return { success: true, errors: [] };
85
- }
86
- }
87
-
88
- export const serviceService = new ServiceServiceMock();
@@ -1,38 +0,0 @@
1
- // Mock version service matching actual VersionService API
2
- class VersionServiceMock {
3
- async commitChanges(message: string) {
4
- return {
5
- success: true,
6
- errors: [] as string[],
7
- commitHash: 'mock-commit-hash-abc123',
8
- timestamp: new Date('2026-01-01T12:00:00Z'),
9
- };
10
- }
11
-
12
- async getCommitHistory(limit: number = 10) {
13
- return [
14
- {
15
- hash: 'mock-commit-1',
16
- message: 'Initial commit',
17
- author: 'Test User',
18
- date: '2023-01-01T12:00:00Z',
19
- },
20
- {
21
- hash: 'mock-commit-2',
22
- message: 'Update User entity',
23
- author: 'Test User',
24
- date: '2023-01-02T12:00:00Z',
25
- },
26
- ];
27
- }
28
-
29
- async revertToCommit(commitHash: string) {
30
- return {
31
- success: true,
32
- errors: [] as string[],
33
- newCommitHash: 'mock-revert-hash-def456',
34
- };
35
- }
36
- }
37
-
38
- export const versionService = new VersionServiceMock();