@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,254 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import YAML from 'yaml';
4
- import { logger } from './logger.js';
5
- import { generateUUID, generateEntityFilename } from './uuid.js';
6
- import { Entity, EntityAttribute, EntityRelationship, createEntityWithUUIDs } from '../models/EntitySchema.js';
7
-
8
- const DATA_DICTIONARIES_DIR = path.join(process.cwd(), '..', 'data-dictionaries');
9
-
10
- /**
11
- * Migrates all existing entities to use UUIDs
12
- */
13
- export async function migrateEntitiesToUUID(): Promise<void> {
14
- logger.info('Starting migration of entities to UUID format...');
15
-
16
- const microservicesDir = path.join(DATA_DICTIONARIES_DIR, 'microservices');
17
-
18
- if (!fs.existsSync(microservicesDir)) {
19
- logger.info('No microservices directory found, nothing to migrate');
20
- return;
21
- }
22
-
23
- const microservices = fs.readdirSync(microservicesDir)
24
- .filter(item => fs.statSync(path.join(microservicesDir, item)).isDirectory());
25
-
26
- let migratedCount = 0;
27
- let skippedCount = 0;
28
-
29
- for (const microservice of microservices) {
30
- const microservicePath = path.join(microservicesDir, microservice);
31
- const files = fs.readdirSync(microservicePath)
32
- .filter(file => file.endsWith('.yaml') || file.endsWith('.yml'));
33
-
34
- for (const file of files) {
35
- const filePath = path.join(microservicePath, file);
36
-
37
- try {
38
- const content = fs.readFileSync(filePath, 'utf8');
39
- const entity = YAML.parse(content) as any;
40
-
41
- // Check if entity already has UUID
42
- if (entity.uuid) {
43
- logger.debug(`Entity ${entity.name} already has UUID, skipping`);
44
- skippedCount++;
45
- continue;
46
- }
47
-
48
- // Migrate entity to UUID format
49
- const migratedEntity = migrateEntityToUUID(entity);
50
-
51
- // Write to new UUID-based filename
52
- const newFilename = generateEntityFilename(migratedEntity.uuid, migratedEntity.name);
53
- const newFilePath = path.join(microservicePath, newFilename);
54
-
55
- const yamlContent = YAML.stringify(migratedEntity);
56
- fs.writeFileSync(newFilePath, yamlContent, 'utf8');
57
-
58
- // Remove old file if it has a different name
59
- if (file !== newFilename) {
60
- fs.unlinkSync(filePath);
61
- logger.info(`Migrated ${microservice}/${entity.name}: ${file} -> ${newFilename}`);
62
- } else {
63
- logger.info(`Updated ${microservice}/${entity.name} with UUIDs`);
64
- }
65
-
66
- migratedCount++;
67
-
68
- } catch (error) {
69
- logger.error(`Error migrating entity file ${filePath}: ${error}`);
70
- }
71
- }
72
- }
73
-
74
- logger.info(`Migration completed: ${migratedCount} entities migrated, ${skippedCount} entities skipped`);
75
- }
76
-
77
- /**
78
- * Migrates a single entity to UUID format
79
- */
80
- function migrateEntityToUUID(entity: any): Entity {
81
- // Generate UUID for entity if not present
82
- if (!entity.uuid) {
83
- entity.uuid = generateUUID();
84
- }
85
-
86
- // Migrate attributes
87
- if (entity.attributes) {
88
- entity.attributes = entity.attributes.map((attr: any) => migrateAttributeToUUID(attr));
89
- }
90
-
91
- // Migrate relationships
92
- if (entity.relationships) {
93
- entity.relationships = entity.relationships.map((rel: any) => migrateRelationshipToUUID(rel));
94
- }
95
-
96
- return entity as Entity;
97
- }
98
-
99
- /**
100
- * Migrates a single attribute to UUID format
101
- */
102
- function migrateAttributeToUUID(attr: any): EntityAttribute {
103
- if (!attr.uuid) {
104
- attr.uuid = generateUUID();
105
- }
106
-
107
- // Migrate nested properties if they exist
108
- if (attr.properties) {
109
- attr.properties = Object.fromEntries(
110
- Object.entries(attr.properties).map(([key, prop]: [string, any]) => [
111
- key,
112
- migrateAttributeToUUID(prop)
113
- ])
114
- );
115
- }
116
-
117
- // Migrate items if they exist
118
- if (attr.items) {
119
- attr.items = migrateAttributeToUUID(attr.items);
120
- }
121
-
122
- return attr as EntityAttribute;
123
- }
124
-
125
- /**
126
- * Migrates a single relationship to UUID format
127
- */
128
- function migrateRelationshipToUUID(rel: any): EntityRelationship {
129
- if (!rel.uuid) {
130
- rel.uuid = generateUUID();
131
- }
132
-
133
- return rel as EntityRelationship;
134
- }
135
-
136
- /**
137
- * Migrates diagram layouts to use entity UUIDs instead of IDs
138
- */
139
- export async function migrateDiagramLayoutsToUUID(): Promise<void> {
140
- logger.info('Starting migration of diagram layouts to UUID format...');
141
-
142
- const diagramsDir = path.join(DATA_DICTIONARIES_DIR, 'diagrams');
143
-
144
- if (!fs.existsSync(diagramsDir)) {
145
- logger.info('No diagrams directory found, nothing to migrate');
146
- return;
147
- }
148
-
149
- const files = fs.readdirSync(diagramsDir)
150
- .filter(file => file.endsWith('.json'));
151
-
152
- let migratedCount = 0;
153
-
154
- // First, build a mapping of entity names to UUIDs
155
- const entityNameToUUID = await buildEntityNameToUUIDMapping();
156
-
157
- for (const file of files) {
158
- const filePath = path.join(diagramsDir, file);
159
-
160
- try {
161
- const content = fs.readFileSync(filePath, 'utf8');
162
- const layout = JSON.parse(content);
163
-
164
- // Check if layout already uses UUIDs (UUIDs are 36 characters with dashes)
165
- const entityKeys = Object.keys(layout.entities || {});
166
- const hasUUIDs = entityKeys.some(key => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(key));
167
-
168
- if (hasUUIDs) {
169
- logger.debug(`Diagram layout ${file} already uses UUIDs, skipping`);
170
- continue;
171
- }
172
-
173
- // Migrate entity references from IDs to UUIDs
174
- const newEntities: any = {};
175
-
176
- for (const [entityId, entityData] of Object.entries(layout.entities || {})) {
177
- const uuid = entityNameToUUID.get(entityId);
178
- if (uuid) {
179
- newEntities[uuid] = {
180
- ...(entityData as any),
181
- name: entityId // Add name for readability
182
- };
183
- } else {
184
- logger.warn(`Could not find UUID for entity ID: ${entityId}`);
185
- // Keep the original ID as fallback
186
- newEntities[entityId] = entityData;
187
- }
188
- }
189
-
190
- layout.entities = newEntities;
191
-
192
- // Write back the migrated layout
193
- fs.writeFileSync(filePath, JSON.stringify(layout, null, 2), 'utf8');
194
- logger.info(`Migrated diagram layout: ${file}`);
195
- migratedCount++;
196
-
197
- } catch (error) {
198
- logger.error(`Error migrating diagram layout ${filePath}: ${error}`);
199
- }
200
- }
201
-
202
- logger.info(`Diagram layout migration completed: ${migratedCount} layouts migrated`);
203
- }
204
-
205
- /**
206
- * Builds a mapping from entity names to UUIDs by reading all entity files
207
- */
208
- async function buildEntityNameToUUIDMapping(): Promise<Map<string, string>> {
209
- const mapping = new Map<string, string>();
210
-
211
- const microservicesDir = path.join(DATA_DICTIONARIES_DIR, 'microservices');
212
-
213
- if (!fs.existsSync(microservicesDir)) {
214
- return mapping;
215
- }
216
-
217
- const microservices = fs.readdirSync(microservicesDir)
218
- .filter(item => fs.statSync(path.join(microservicesDir, item)).isDirectory());
219
-
220
- for (const microservice of microservices) {
221
- const microservicePath = path.join(microservicesDir, microservice);
222
- const files = fs.readdirSync(microservicePath)
223
- .filter(file => file.endsWith('.yaml') || file.endsWith('.yml'));
224
-
225
- for (const file of files) {
226
- const filePath = path.join(microservicePath, file);
227
-
228
- try {
229
- const content = fs.readFileSync(filePath, 'utf8');
230
- const entity = YAML.parse(content) as Entity;
231
-
232
- if (entity.uuid && entity.id) {
233
- mapping.set(entity.id, entity.uuid);
234
- }
235
- } catch (error) {
236
- logger.warn(`Error reading entity file for mapping: ${filePath}`);
237
- }
238
- }
239
- }
240
-
241
- return mapping;
242
- }
243
-
244
- /**
245
- * Runs all migrations
246
- */
247
- export async function runAllMigrations(): Promise<void> {
248
- logger.info('Starting all migrations...');
249
-
250
- await migrateEntitiesToUUID();
251
- await migrateDiagramLayoutsToUUID();
252
-
253
- logger.info('All migrations completed');
254
- }
@@ -1,358 +0,0 @@
1
- import swaggerJsdoc from 'swagger-jsdoc';
2
- import swaggerUi from 'swagger-ui-express';
3
- import { Express } from 'express';
4
-
5
- /**
6
- * Swagger definition options
7
- */
8
- const options: swaggerJsdoc.Options = {
9
- definition: {
10
- openapi: '3.0.0',
11
- info: {
12
- title: 'Data Dictionary Management System API',
13
- version: '1.0.0',
14
- description: 'API documentation for the Data Dictionary Management System',
15
- license: {
16
- name: 'MIT',
17
- url: 'https://opensource.org/licenses/MIT',
18
- },
19
- contact: {
20
- name: 'API Support',
21
- email: 'support@datadictionary.com',
22
- },
23
- },
24
- servers: [
25
- {
26
- url: 'http://localhost:3001',
27
- description: 'Development server',
28
- },
29
- ],
30
- components: {
31
- securitySchemes: {
32
- basicAuth: {
33
- type: 'http',
34
- scheme: 'basic',
35
- description: 'Basic authentication with username and password',
36
- },
37
- },
38
- schemas: {
39
- Entity: {
40
- type: 'object',
41
- required: ['id', 'name', 'description', 'microservice', 'version', 'attributes'],
42
- properties: {
43
- id: {
44
- type: 'string',
45
- description: 'Unique identifier for the entity',
46
- },
47
- name: {
48
- type: 'string',
49
- description: 'Name of the entity',
50
- },
51
- description: {
52
- type: 'string',
53
- description: 'Description of the entity',
54
- },
55
- microservice: {
56
- type: 'string',
57
- description: 'Microservice that the entity belongs to',
58
- },
59
- version: {
60
- type: 'string',
61
- description: 'Version of the entity schema',
62
- },
63
- attributes: {
64
- type: 'array',
65
- description: 'List of attributes for the entity',
66
- items: {
67
- $ref: '#/components/schemas/EntityAttribute',
68
- },
69
- },
70
- relationships: {
71
- type: 'array',
72
- description: 'List of relationships for the entity',
73
- items: {
74
- $ref: '#/components/schemas/EntityRelationship',
75
- },
76
- },
77
- metadata: {
78
- type: 'object',
79
- description: 'Additional metadata for the entity',
80
- },
81
- createdAt: {
82
- type: 'string',
83
- format: 'date-time',
84
- description: 'Timestamp when the entity was created',
85
- },
86
- updatedAt: {
87
- type: 'string',
88
- format: 'date-time',
89
- description: 'Timestamp when the entity was last updated',
90
- },
91
- },
92
- },
93
- EntityAttribute: {
94
- type: 'object',
95
- required: ['name', 'description', 'type', 'required'],
96
- properties: {
97
- name: {
98
- type: 'string',
99
- description: 'Name of the attribute',
100
- },
101
- description: {
102
- type: 'string',
103
- description: 'Description of the attribute',
104
- },
105
- type: {
106
- type: 'string',
107
- description: 'Data type of the attribute',
108
- enum: ['string', 'number', 'integer', 'boolean', 'datetime', 'date', 'time', 'enum', 'object', 'array', 'reference'],
109
- },
110
- required: {
111
- type: 'boolean',
112
- description: 'Whether the attribute is required',
113
- },
114
- unique: {
115
- type: 'boolean',
116
- description: 'Whether the attribute value must be unique',
117
- },
118
- defaultValue: {
119
- description: 'Default value for the attribute',
120
- },
121
- examples: {
122
- type: 'array',
123
- description: 'Example values for the attribute',
124
- items: {
125
- type: 'string',
126
- },
127
- },
128
- minLength: {
129
- type: 'integer',
130
- description: 'Minimum length for string attributes',
131
- },
132
- maxLength: {
133
- type: 'integer',
134
- description: 'Maximum length for string attributes',
135
- },
136
- pattern: {
137
- type: 'string',
138
- description: 'Regex pattern for string attributes',
139
- },
140
- format: {
141
- type: 'string',
142
- description: 'Format for the attribute (e.g., email, uuid)',
143
- },
144
- minimum: {
145
- type: 'number',
146
- description: 'Minimum value for number attributes',
147
- },
148
- maximum: {
149
- type: 'number',
150
- description: 'Maximum value for number attributes',
151
- },
152
- precision: {
153
- type: 'integer',
154
- description: 'Precision for number attributes',
155
- },
156
- scale: {
157
- type: 'integer',
158
- description: 'Scale for number attributes',
159
- },
160
- enumValues: {
161
- type: 'array',
162
- description: 'Possible values for enum attributes',
163
- items: {
164
- type: 'string',
165
- },
166
- },
167
- metadata: {
168
- type: 'object',
169
- description: 'Additional metadata for the attribute',
170
- },
171
- },
172
- },
173
- EntityRelationship: {
174
- type: 'object',
175
- required: ['name', 'description', 'type', 'target', 'required'],
176
- properties: {
177
- name: {
178
- type: 'string',
179
- description: 'Name of the relationship',
180
- },
181
- description: {
182
- type: 'string',
183
- description: 'Description of the relationship',
184
- },
185
- type: {
186
- type: 'string',
187
- description: 'Type of the relationship',
188
- enum: ['hasOne', 'hasMany', 'belongsTo', 'manyToMany'],
189
- },
190
- target: {
191
- type: 'string',
192
- description: 'Target entity of the relationship (format: microservice.entity)',
193
- },
194
- inverseName: {
195
- type: 'string',
196
- description: 'Name of the inverse relationship in the target entity',
197
- },
198
- required: {
199
- type: 'boolean',
200
- description: 'Whether the relationship is required',
201
- },
202
- foreignKey: {
203
- type: 'string',
204
- description: 'Foreign key attribute name',
205
- },
206
- metadata: {
207
- type: 'object',
208
- description: 'Additional metadata for the relationship',
209
- },
210
- },
211
- },
212
- GraphData: {
213
- type: 'object',
214
- properties: {
215
- nodes: {
216
- type: 'array',
217
- items: {
218
- $ref: '#/components/schemas/GraphNode',
219
- },
220
- },
221
- edges: {
222
- type: 'array',
223
- items: {
224
- $ref: '#/components/schemas/GraphEdge',
225
- },
226
- },
227
- },
228
- },
229
- GraphNode: {
230
- type: 'object',
231
- properties: {
232
- id: {
233
- type: 'string',
234
- description: 'Unique identifier for the node',
235
- },
236
- label: {
237
- type: 'string',
238
- description: 'Label for the node',
239
- },
240
- type: {
241
- type: 'string',
242
- description: 'Type of the node',
243
- },
244
- service: {
245
- type: 'string',
246
- description: 'Service that the node belongs to',
247
- },
248
- },
249
- },
250
- GraphEdge: {
251
- type: 'object',
252
- properties: {
253
- id: {
254
- type: 'string',
255
- description: 'Unique identifier for the edge',
256
- },
257
- source: {
258
- type: 'string',
259
- description: 'Source node ID',
260
- },
261
- target: {
262
- type: 'string',
263
- description: 'Target node ID',
264
- },
265
- label: {
266
- type: 'string',
267
- description: 'Label for the edge',
268
- },
269
- type: {
270
- type: 'string',
271
- description: 'Type of the edge',
272
- },
273
- },
274
- },
275
- CommitInfo: {
276
- type: 'object',
277
- properties: {
278
- hash: {
279
- type: 'string',
280
- description: 'Commit hash',
281
- },
282
- date: {
283
- type: 'string',
284
- description: 'Commit date',
285
- },
286
- message: {
287
- type: 'string',
288
- description: 'Commit message',
289
- },
290
- author_name: {
291
- type: 'string',
292
- description: 'Author name',
293
- },
294
- author_email: {
295
- type: 'string',
296
- description: 'Author email',
297
- },
298
- },
299
- },
300
- SearchResult: {
301
- type: 'object',
302
- properties: {
303
- type: {
304
- type: 'string',
305
- enum: ['entity', 'attribute'],
306
- description: 'Type of search result',
307
- },
308
- service: {
309
- type: 'string',
310
- description: 'Service name',
311
- },
312
- entityName: {
313
- type: 'string',
314
- description: 'Entity name',
315
- },
316
- attributeName: {
317
- type: 'string',
318
- description: 'Attribute name (for attribute results)',
319
- },
320
- description: {
321
- type: 'string',
322
- description: 'Description of the entity or attribute',
323
- },
324
- path: {
325
- type: 'string',
326
- description: 'Path to the entity or attribute',
327
- },
328
- score: {
329
- type: 'number',
330
- description: 'Search relevance score',
331
- },
332
- },
333
- },
334
- },
335
- },
336
- },
337
- apis: ['./src/routes/*.ts', './src/controllers/*.ts'],
338
- };
339
-
340
- /**
341
- * Swagger specification
342
- */
343
- const swaggerSpec = swaggerJsdoc(options);
344
-
345
- /**
346
- * Configure Swagger middleware for Express
347
- * @param app Express application
348
- */
349
- export const setupSwagger = (app: Express): void => {
350
- // Swagger UI route
351
- app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
352
-
353
- // Swagger JSON route
354
- app.get('/api-docs.json', (req, res) => {
355
- res.setHeader('Content-Type', 'application/json');
356
- res.send(swaggerSpec);
357
- });
358
- };
@@ -1,41 +0,0 @@
1
- import { randomUUID } from 'crypto';
2
-
3
- /**
4
- * Generates a UUID v4
5
- * @returns A new UUID string
6
- */
7
- export function generateUUID(): string {
8
- return randomUUID();
9
- }
10
-
11
- /**
12
- * Validates if a string is a valid UUID
13
- * @param uuid String to validate
14
- * @returns True if valid UUID
15
- */
16
- export function isValidUUID(uuid: string): boolean {
17
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
18
- return uuidRegex.test(uuid);
19
- }
20
-
21
- /**
22
- * Generates a human-readable filename using UUID and name
23
- * @param uuid The UUID of the entity
24
- * @param name The name of the entity
25
- * @returns A filename in format: uuid_name.yaml
26
- */
27
- export function generateEntityFilename(uuid: string, name: string): string {
28
- // Sanitize name for filename (remove special characters, replace spaces with underscores)
29
- const sanitizedName = name.replace(/[^a-zA-Z0-9\s]/g, '').replace(/\s+/g, '_');
30
- return `${uuid}_${sanitizedName}.yaml`;
31
- }
32
-
33
- /**
34
- * Extracts UUID from a filename
35
- * @param filename The filename to extract UUID from
36
- * @returns The UUID or null if not found
37
- */
38
- export function extractUUIDFromFilename(filename: string): string | null {
39
- const match = filename.match(/^([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/i);
40
- return match ? match[1] : null;
41
- }
@@ -1,20 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "lib": ["ES2020"],
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "resolveJsonModule": true,
14
- "sourceMap": true,
15
- "isolatedModules": true,
16
- "types": ["node", "jest"]
17
- },
18
- "include": ["src/**/*"],
19
- "exclude": ["node_modules"]
20
- }