@hamak/smart-data-dico 1.0.4 → 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.
- package/backend/dist/server.mjs +82212 -0
- package/bin/cli.js +28 -17
- package/package.json +28 -27
- package/backend/package.json +0 -51
- package/backend/src/__tests__/integration/api.test.ts +0 -149
- package/backend/src/__tests__/setup.ts +0 -24
- package/backend/src/__tests__/utils/testUtils.ts +0 -76
- package/backend/src/adapters/EntityFileAdapter.ts +0 -154
- package/backend/src/adapters/YamlFileInfoEnricher.ts +0 -52
- package/backend/src/controllers/authController.ts +0 -131
- package/backend/src/controllers/diagramController.ts +0 -143
- package/backend/src/controllers/dictionaryController.ts +0 -306
- package/backend/src/controllers/importExportController.ts +0 -64
- package/backend/src/controllers/perspectiveController.ts +0 -90
- package/backend/src/controllers/serviceController.ts +0 -418
- package/backend/src/controllers/stereotypeController.ts +0 -59
- package/backend/src/controllers/versionController.ts +0 -226
- package/backend/src/kernel/config.ts +0 -43
- package/backend/src/middleware/auth.ts +0 -128
- package/backend/src/middleware/jwtAuth.ts +0 -100
- package/backend/src/models/Dictionary.ts +0 -38
- package/backend/src/models/EntitySchema.ts +0 -393
- package/backend/src/models/__tests__/Dictionary.test.ts +0 -92
- package/backend/src/models/__tests__/EntitySchema.test.ts +0 -119
- package/backend/src/routes/index.ts +0 -120
- package/backend/src/scripts/migrate-to-uuid.ts +0 -24
- package/backend/src/server.ts +0 -158
- package/backend/src/services/__mocks__/entityService.ts +0 -38
- package/backend/src/services/__mocks__/serviceService.ts +0 -88
- package/backend/src/services/__mocks__/versionService.ts +0 -38
- package/backend/src/services/__tests__/dictionaryService.test.ts +0 -74
- package/backend/src/services/diagramService.ts +0 -165
- package/backend/src/services/dictionaryService.ts +0 -582
- package/backend/src/services/entityService.ts +0 -102
- package/backend/src/services/exportService.ts +0 -172
- package/backend/src/services/importService.ts +0 -208
- package/backend/src/services/perspectiveService.ts +0 -276
- package/backend/src/services/qualityService.ts +0 -121
- package/backend/src/services/serviceService.ts +0 -763
- package/backend/src/services/stereotypeService.ts +0 -98
- package/backend/src/services/versionService.ts +0 -135
- package/backend/src/setupTests.ts +0 -12
- package/backend/src/utils/__mocks__/fileOperations.ts +0 -116
- package/backend/src/utils/fileOperations.ts +0 -602
- package/backend/src/utils/logger.ts +0 -38
- package/backend/src/utils/migration.ts +0 -254
- package/backend/src/utils/swagger.ts +0 -358
- package/backend/src/utils/uuid.ts +0 -41
- package/backend/tsconfig.json +0 -20
|
@@ -1,602 +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 { Entity, Relationship, Perspective, ReviewComment, validateEntity } from '../models/EntitySchema.js';
|
|
6
|
-
import { Dictionary } from '../models/Dictionary.js';
|
|
7
|
-
import { generateEntityFilename, extractUUIDFromFilename } from './uuid.js';
|
|
8
|
-
import { config } from '../kernel/config.js';
|
|
9
|
-
|
|
10
|
-
// Base directory for data dictionaries
|
|
11
|
-
const DATA_DICTIONARIES_DIR = config.dataDir;
|
|
12
|
-
|
|
13
|
-
// Lazy-loaded git service from @hamak/ui-remote-git-fs-backend
|
|
14
|
-
let gitServiceInstance: any = null;
|
|
15
|
-
|
|
16
|
-
async function getGitService() {
|
|
17
|
-
if (gitServiceInstance) return gitServiceInstance;
|
|
18
|
-
try {
|
|
19
|
-
const gitModule = await import('@hamak/ui-remote-git-fs-backend');
|
|
20
|
-
const workspaceRoots = new Map<string, string>([
|
|
21
|
-
['dictionaries', DATA_DICTIONARIES_DIR],
|
|
22
|
-
]);
|
|
23
|
-
gitServiceInstance = gitModule.createGitService(workspaceRoots);
|
|
24
|
-
return gitServiceInstance;
|
|
25
|
-
} catch {
|
|
26
|
-
logger.warn('Git service not available');
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Ensures the data dictionaries directory structure exists
|
|
33
|
-
*/
|
|
34
|
-
export async function ensureDirectoryStructure(): Promise<void> {
|
|
35
|
-
const baseDir = DATA_DICTIONARIES_DIR;
|
|
36
|
-
const microservicesDir = path.join(baseDir, 'microservices');
|
|
37
|
-
|
|
38
|
-
if (!fs.existsSync(baseDir)) {
|
|
39
|
-
fs.mkdirSync(baseDir, { recursive: true });
|
|
40
|
-
logger.info(`Created base directory: ${baseDir}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (!fs.existsSync(microservicesDir)) {
|
|
44
|
-
fs.mkdirSync(microservicesDir, { recursive: true });
|
|
45
|
-
logger.info(`Created microservices directory: ${microservicesDir}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const perspectivesDir = path.join(baseDir, 'perspectives');
|
|
49
|
-
if (!fs.existsSync(perspectivesDir)) {
|
|
50
|
-
fs.mkdirSync(perspectivesDir, { recursive: true });
|
|
51
|
-
logger.info(`Created perspectives directory: ${perspectivesDir}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Reads an entity from a YAML file
|
|
57
|
-
* @param packageName Package (service) name
|
|
58
|
-
* @param entityName Entity name
|
|
59
|
-
*/
|
|
60
|
-
export async function readEntityFile(packageName: string, entityName: string): Promise<Entity | null> {
|
|
61
|
-
const startTime = process.hrtime();
|
|
62
|
-
try {
|
|
63
|
-
const packagePath = path.join(DATA_DICTIONARIES_DIR, 'microservices', packageName);
|
|
64
|
-
|
|
65
|
-
if (!fs.existsSync(packagePath)) {
|
|
66
|
-
logger.warn(`Package directory not found: ${packagePath}`);
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Try to find file by name (could be UUID-based or legacy name-based)
|
|
71
|
-
const files = fs.readdirSync(packagePath)
|
|
72
|
-
.filter(file => file.endsWith('.yaml') || file.endsWith('.yml'));
|
|
73
|
-
|
|
74
|
-
let filePath: string | null = null;
|
|
75
|
-
|
|
76
|
-
// First try legacy naming convention
|
|
77
|
-
const legacyPath = path.join(packagePath, `${entityName}.yaml`);
|
|
78
|
-
if (fs.existsSync(legacyPath)) {
|
|
79
|
-
filePath = legacyPath;
|
|
80
|
-
} else {
|
|
81
|
-
// Try to find by UUID-based filename that contains the entity name
|
|
82
|
-
for (const file of files) {
|
|
83
|
-
if (file === 'metadata.yaml' || file === 'relationships.yaml') continue;
|
|
84
|
-
const fullPath = path.join(packagePath, file);
|
|
85
|
-
try {
|
|
86
|
-
const content = fs.readFileSync(fullPath, 'utf8');
|
|
87
|
-
const entity = YAML.parse(content) as Entity;
|
|
88
|
-
if (entity.name === entityName) {
|
|
89
|
-
filePath = fullPath;
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!filePath) {
|
|
99
|
-
logger.warn(`Entity file not found: ${packageName}.${entityName}`);
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const readStartTime = process.hrtime();
|
|
104
|
-
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
105
|
-
const readEndTime = process.hrtime(readStartTime);
|
|
106
|
-
const readTimeMs = Number((readEndTime[0] * 1e3 + readEndTime[1] / 1e6).toFixed(2));
|
|
107
|
-
|
|
108
|
-
const parseStartTime = process.hrtime();
|
|
109
|
-
const entity = YAML.parse(fileContent) as Entity;
|
|
110
|
-
const parseEndTime = process.hrtime(parseStartTime);
|
|
111
|
-
const parseTimeMs = Number((parseEndTime[0] * 1e3 + parseEndTime[1] / 1e6).toFixed(2));
|
|
112
|
-
|
|
113
|
-
const endTime = process.hrtime(startTime);
|
|
114
|
-
const totalTimeMs = Number((endTime[0] * 1e3 + endTime[1] / 1e6).toFixed(2));
|
|
115
|
-
|
|
116
|
-
logger.debug(`Read entity ${packageName}.${entityName}: ${totalTimeMs}ms (read: ${readTimeMs}ms, parse: ${parseTimeMs}ms)`);
|
|
117
|
-
|
|
118
|
-
return entity;
|
|
119
|
-
} catch (error) {
|
|
120
|
-
logger.error(`Error reading entity file: ${error}`);
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Writes an entity to a YAML file
|
|
127
|
-
* @param entity Entity to write
|
|
128
|
-
* @param packageName Package name (directory under microservices/)
|
|
129
|
-
*/
|
|
130
|
-
export async function writeEntityFile(entity: Entity, packageName?: string): Promise<boolean> {
|
|
131
|
-
try {
|
|
132
|
-
const validation = validateEntity(entity);
|
|
133
|
-
if (!validation.valid) {
|
|
134
|
-
logger.error(`Invalid entity: ${validation.errors.join(', ')}`);
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Use packageName parameter or fall back to a default
|
|
139
|
-
const pkgName = packageName;
|
|
140
|
-
if (!pkgName) {
|
|
141
|
-
logger.error('Package name is required to write entity file');
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const packageDir = path.join(DATA_DICTIONARIES_DIR, 'microservices', pkgName);
|
|
146
|
-
|
|
147
|
-
if (!fs.existsSync(packageDir)) {
|
|
148
|
-
fs.mkdirSync(packageDir, { recursive: true });
|
|
149
|
-
logger.info(`Created package directory: ${packageDir}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Check if there's an existing file for this entity (by name) to remove it
|
|
153
|
-
const existingFiles = fs.readdirSync(packageDir)
|
|
154
|
-
.filter(file => (file.endsWith('.yaml') || file.endsWith('.yml')) && file !== 'metadata.yaml' && file !== 'relationships.yaml');
|
|
155
|
-
|
|
156
|
-
for (const file of existingFiles) {
|
|
157
|
-
const fullPath = path.join(packageDir, file);
|
|
158
|
-
try {
|
|
159
|
-
const content = fs.readFileSync(fullPath, 'utf8');
|
|
160
|
-
const existingEntity = YAML.parse(content) as Entity;
|
|
161
|
-
if (existingEntity.name === entity.name) {
|
|
162
|
-
const newFilename = generateEntityFilename(entity.uuid, entity.name);
|
|
163
|
-
if (file !== newFilename) {
|
|
164
|
-
fs.unlinkSync(fullPath);
|
|
165
|
-
logger.info(`Removed old entity file: ${fullPath}`);
|
|
166
|
-
}
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
} catch (error) {
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const filename = generateEntityFilename(entity.uuid, entity.name);
|
|
175
|
-
const filePath = path.join(packageDir, filename);
|
|
176
|
-
const yamlContent = YAML.stringify(entity);
|
|
177
|
-
|
|
178
|
-
fs.writeFileSync(filePath, yamlContent, 'utf8');
|
|
179
|
-
logger.info(`Entity written to file: ${filePath}`);
|
|
180
|
-
|
|
181
|
-
try {
|
|
182
|
-
await commitChanges(filePath, `Updated entity: ${entity.name} (${entity.uuid})`);
|
|
183
|
-
} catch (gitError) {
|
|
184
|
-
logger.warn(`Git operations failed: ${gitError}`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return true;
|
|
188
|
-
} catch (error) {
|
|
189
|
-
logger.error(`Error writing entity file: ${error}`);
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Reads relationships from a package's relationships.yaml file
|
|
196
|
-
*/
|
|
197
|
-
export async function readRelationshipsFile(packagePath: string): Promise<Relationship[]> {
|
|
198
|
-
try {
|
|
199
|
-
const filePath = path.join(packagePath, 'relationships.yaml');
|
|
200
|
-
if (!fs.existsSync(filePath)) {
|
|
201
|
-
return [];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
205
|
-
const parsed = YAML.parse(content);
|
|
206
|
-
|
|
207
|
-
if (!parsed || !Array.isArray(parsed)) {
|
|
208
|
-
return [];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return parsed as Relationship[];
|
|
212
|
-
} catch (error) {
|
|
213
|
-
logger.error(`Error reading relationships file: ${error}`);
|
|
214
|
-
return [];
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Writes relationships to a package's relationships.yaml file
|
|
220
|
-
*/
|
|
221
|
-
export async function writeRelationshipsFile(packagePath: string, relationships: Relationship[]): Promise<boolean> {
|
|
222
|
-
try {
|
|
223
|
-
if (!fs.existsSync(packagePath)) {
|
|
224
|
-
fs.mkdirSync(packagePath, { recursive: true });
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const filePath = path.join(packagePath, 'relationships.yaml');
|
|
228
|
-
const yamlContent = YAML.stringify(relationships);
|
|
229
|
-
|
|
230
|
-
fs.writeFileSync(filePath, yamlContent, 'utf8');
|
|
231
|
-
logger.info(`Relationships written to file: ${filePath}`);
|
|
232
|
-
|
|
233
|
-
try {
|
|
234
|
-
await commitChanges(filePath, `Updated relationships in ${path.basename(packagePath)}`);
|
|
235
|
-
} catch (gitError) {
|
|
236
|
-
logger.warn(`Git operations failed: ${gitError}`);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return true;
|
|
240
|
-
} catch (error) {
|
|
241
|
-
logger.error(`Error writing relationships file: ${error}`);
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Gets the full path for a package directory
|
|
248
|
-
*/
|
|
249
|
-
export function getPackagePath(packageName: string): string {
|
|
250
|
-
return path.join(DATA_DICTIONARIES_DIR, 'microservices', packageName);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Lists all available entities across all packages
|
|
255
|
-
*/
|
|
256
|
-
export async function listAllEntities(): Promise<Array<{ microservice: string; name: string; path: string }>> {
|
|
257
|
-
try {
|
|
258
|
-
const microservicesDir = path.join(DATA_DICTIONARIES_DIR, 'microservices');
|
|
259
|
-
const entities: Array<{ microservice: string; name: string; path: string }> = [];
|
|
260
|
-
|
|
261
|
-
if (!fs.existsSync(microservicesDir)) {
|
|
262
|
-
return entities;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const microservices = fs.readdirSync(microservicesDir)
|
|
266
|
-
.filter((item: string) => fs.statSync(path.join(microservicesDir, item)).isDirectory());
|
|
267
|
-
|
|
268
|
-
for (const microservice of microservices) {
|
|
269
|
-
const microservicePath = path.join(microservicesDir, microservice);
|
|
270
|
-
const files = fs.readdirSync(microservicePath)
|
|
271
|
-
.filter((file: string) => (file.endsWith('.yaml') || file.endsWith('.yml')) && file !== 'metadata.yaml' && file !== 'relationships.yaml' && !file.endsWith('.comments.yaml'));
|
|
272
|
-
|
|
273
|
-
for (const file of files) {
|
|
274
|
-
const name = path.basename(file, path.extname(file));
|
|
275
|
-
entities.push({
|
|
276
|
-
microservice,
|
|
277
|
-
name,
|
|
278
|
-
path: path.join(microservicePath, file)
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return entities;
|
|
284
|
-
} catch (error) {
|
|
285
|
-
logger.error(`Error listing entities: ${error}`);
|
|
286
|
-
return [];
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Lists all entities for a specific package
|
|
292
|
-
*/
|
|
293
|
-
export async function listMicroserviceEntities(microservice: string): Promise<string[]> {
|
|
294
|
-
const startTime = process.hrtime();
|
|
295
|
-
try {
|
|
296
|
-
const microservicePath = path.join(DATA_DICTIONARIES_DIR, 'microservices', microservice);
|
|
297
|
-
|
|
298
|
-
if (!fs.existsSync(microservicePath)) {
|
|
299
|
-
return [];
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const readStartTime = process.hrtime();
|
|
303
|
-
const allFiles = fs.readdirSync(microservicePath);
|
|
304
|
-
const readEndTime = process.hrtime(readStartTime);
|
|
305
|
-
const readTimeMs = Number((readEndTime[0] * 1e3 + readEndTime[1] / 1e6).toFixed(2));
|
|
306
|
-
|
|
307
|
-
const processStartTime = process.hrtime();
|
|
308
|
-
const files = allFiles
|
|
309
|
-
.filter((file: string) => (file.endsWith('.yaml') || file.endsWith('.yml')) && file !== 'metadata.yaml' && file !== 'relationships.yaml' && !file.endsWith('.comments.yaml'))
|
|
310
|
-
.map((file: string) => path.basename(file, path.extname(file)));
|
|
311
|
-
const processEndTime = process.hrtime(processStartTime);
|
|
312
|
-
const processTimeMs = Number((processEndTime[0] * 1e3 + processEndTime[1] / 1e6).toFixed(2));
|
|
313
|
-
|
|
314
|
-
const endTime = process.hrtime(startTime);
|
|
315
|
-
const totalTimeMs = Number((endTime[0] * 1e3 + endTime[1] / 1e6).toFixed(2));
|
|
316
|
-
|
|
317
|
-
logger.debug(`Listed ${files.length} entities for ${microservice}: ${totalTimeMs}ms (read: ${readTimeMs}ms, process: ${processTimeMs}ms)`);
|
|
318
|
-
|
|
319
|
-
return files;
|
|
320
|
-
} catch (error) {
|
|
321
|
-
logger.error(`Error listing microservice entities: ${error}`);
|
|
322
|
-
return [];
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Lists all available microservices
|
|
328
|
-
*/
|
|
329
|
-
export async function listMicroservices(): Promise<string[]> {
|
|
330
|
-
try {
|
|
331
|
-
const microservicesDir = path.join(DATA_DICTIONARIES_DIR, 'microservices');
|
|
332
|
-
|
|
333
|
-
if (!fs.existsSync(microservicesDir)) {
|
|
334
|
-
return [];
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const microservices = fs.readdirSync(microservicesDir)
|
|
338
|
-
.filter((item: string) => fs.statSync(path.join(microservicesDir, item)).isDirectory());
|
|
339
|
-
|
|
340
|
-
return microservices;
|
|
341
|
-
} catch (error) {
|
|
342
|
-
logger.error(`Error listing microservices: ${error}`);
|
|
343
|
-
return [];
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Commits changes to git via @hamak/ui-remote-git-fs-backend
|
|
349
|
-
*/
|
|
350
|
-
async function commitChanges(filePath: string, message: string): Promise<void> {
|
|
351
|
-
if (!config.git.autoCommit) return;
|
|
352
|
-
|
|
353
|
-
try {
|
|
354
|
-
const gitService = await getGitService();
|
|
355
|
-
if (!gitService) {
|
|
356
|
-
logger.warn('Git service not available, skipping commit');
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
await gitService.commit('dictionaries', '.', { message, paths: [filePath] });
|
|
361
|
-
logger.info(`Changes committed: ${message}`);
|
|
362
|
-
} catch (error) {
|
|
363
|
-
logger.error(`Git error: ${error}`);
|
|
364
|
-
throw error;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Deletes an entity file
|
|
370
|
-
*/
|
|
371
|
-
export async function deleteEntityFile(microservice: string, entityName: string): Promise<boolean> {
|
|
372
|
-
try {
|
|
373
|
-
const microservicePath = path.join(DATA_DICTIONARIES_DIR, 'microservices', microservice);
|
|
374
|
-
|
|
375
|
-
if (!fs.existsSync(microservicePath)) {
|
|
376
|
-
logger.warn(`Package directory not found: ${microservicePath}`);
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const files = fs.readdirSync(microservicePath)
|
|
381
|
-
.filter(file => (file.endsWith('.yaml') || file.endsWith('.yml')) && file !== 'metadata.yaml' && file !== 'relationships.yaml');
|
|
382
|
-
|
|
383
|
-
let filePath: string | null = null;
|
|
384
|
-
|
|
385
|
-
const legacyPath = path.join(microservicePath, `${entityName}.yaml`);
|
|
386
|
-
if (fs.existsSync(legacyPath)) {
|
|
387
|
-
filePath = legacyPath;
|
|
388
|
-
} else {
|
|
389
|
-
for (const file of files) {
|
|
390
|
-
const fullPath = path.join(microservicePath, file);
|
|
391
|
-
try {
|
|
392
|
-
const content = fs.readFileSync(fullPath, 'utf8');
|
|
393
|
-
const entity = YAML.parse(content) as Entity;
|
|
394
|
-
if (entity.name === entityName) {
|
|
395
|
-
filePath = fullPath;
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
} catch (error) {
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
if (!filePath) {
|
|
405
|
-
logger.warn(`Entity file not found for deletion: ${microservice}.${entityName}`);
|
|
406
|
-
return false;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
fs.unlinkSync(filePath);
|
|
410
|
-
logger.info(`Entity file deleted: ${filePath}`);
|
|
411
|
-
|
|
412
|
-
try {
|
|
413
|
-
await commitChanges(filePath, `Deleted entity: ${entityName}`);
|
|
414
|
-
} catch (gitError) {
|
|
415
|
-
logger.warn(`Git operations failed: ${gitError}`);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
return true;
|
|
419
|
-
} catch (error) {
|
|
420
|
-
logger.error(`Error deleting entity file: ${error}`);
|
|
421
|
-
return false;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Writes dictionary metadata to a YAML file
|
|
427
|
-
*/
|
|
428
|
-
export async function writeDictionaryMetadata(dictionary: Dictionary): Promise<boolean> {
|
|
429
|
-
try {
|
|
430
|
-
const dictionaryDir = path.join(DATA_DICTIONARIES_DIR, dictionary.id);
|
|
431
|
-
|
|
432
|
-
if (!fs.existsSync(dictionaryDir)) {
|
|
433
|
-
fs.mkdirSync(dictionaryDir, { recursive: true });
|
|
434
|
-
logger.info(`Created dictionary directory: ${dictionaryDir}`);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const filePath = path.join(dictionaryDir, 'metadata.yaml');
|
|
438
|
-
|
|
439
|
-
const metadata = {
|
|
440
|
-
id: dictionary.id,
|
|
441
|
-
name: dictionary.name,
|
|
442
|
-
description: dictionary.description,
|
|
443
|
-
metadataDefinitions: dictionary.metadataDefinitions,
|
|
444
|
-
createdAt: dictionary.createdAt,
|
|
445
|
-
updatedAt: dictionary.updatedAt
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
const yamlContent = YAML.stringify(metadata);
|
|
449
|
-
|
|
450
|
-
fs.writeFileSync(filePath, yamlContent, 'utf8');
|
|
451
|
-
logger.info(`Dictionary metadata written to file: ${filePath}`);
|
|
452
|
-
|
|
453
|
-
try {
|
|
454
|
-
await commitChanges(filePath, `Updated dictionary metadata: ${dictionary.name}`);
|
|
455
|
-
} catch (gitError) {
|
|
456
|
-
logger.warn(`Git operations failed: ${gitError}`);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return true;
|
|
460
|
-
} catch (error) {
|
|
461
|
-
logger.error(`Error writing dictionary metadata: ${error}`);
|
|
462
|
-
return false;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Lists all available dictionaries
|
|
468
|
-
*/
|
|
469
|
-
export async function listAllDictionaries(): Promise<string[]> {
|
|
470
|
-
try {
|
|
471
|
-
const baseDir = DATA_DICTIONARIES_DIR;
|
|
472
|
-
const dictionaries: string[] = [];
|
|
473
|
-
|
|
474
|
-
if (!fs.existsSync(baseDir)) {
|
|
475
|
-
return [];
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
const items = fs.readdirSync(baseDir);
|
|
479
|
-
|
|
480
|
-
for (const item of items) {
|
|
481
|
-
const itemPath = path.join(baseDir, item);
|
|
482
|
-
|
|
483
|
-
if (fs.statSync(itemPath).isDirectory()) {
|
|
484
|
-
if (item === 'microservices') {
|
|
485
|
-
const microservices = await listMicroservices();
|
|
486
|
-
dictionaries.push(...microservices);
|
|
487
|
-
} else {
|
|
488
|
-
const metadataPath = path.join(itemPath, 'metadata.yaml');
|
|
489
|
-
if (fs.existsSync(metadataPath)) {
|
|
490
|
-
dictionaries.push(item);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return dictionaries;
|
|
497
|
-
} catch (error) {
|
|
498
|
-
logger.error(`Error listing all dictionaries: ${error}`);
|
|
499
|
-
return [];
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// --- Perspective file operations ---
|
|
504
|
-
|
|
505
|
-
const PERSPECTIVES_DIR = path.join(DATA_DICTIONARIES_DIR, 'perspectives');
|
|
506
|
-
|
|
507
|
-
export async function listPerspectives(): Promise<Perspective[]> {
|
|
508
|
-
try {
|
|
509
|
-
if (!fs.existsSync(PERSPECTIVES_DIR)) return [];
|
|
510
|
-
const files = fs.readdirSync(PERSPECTIVES_DIR).filter(f => f.endsWith('.yaml'));
|
|
511
|
-
return files.map(f => {
|
|
512
|
-
const content = fs.readFileSync(path.join(PERSPECTIVES_DIR, f), 'utf8');
|
|
513
|
-
return YAML.parse(content) as Perspective;
|
|
514
|
-
}).filter(Boolean);
|
|
515
|
-
} catch (error) {
|
|
516
|
-
logger.error(`Error listing perspectives: ${error}`);
|
|
517
|
-
return [];
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
export async function readPerspectiveFile(uuid: string): Promise<Perspective | null> {
|
|
522
|
-
try {
|
|
523
|
-
const filePath = path.join(PERSPECTIVES_DIR, `${uuid}.yaml`);
|
|
524
|
-
if (!fs.existsSync(filePath)) return null;
|
|
525
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
526
|
-
return YAML.parse(content) as Perspective;
|
|
527
|
-
} catch (error) {
|
|
528
|
-
logger.error(`Error reading perspective ${uuid}: ${error}`);
|
|
529
|
-
return null;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
export async function writePerspectiveFile(perspective: Perspective): Promise<boolean> {
|
|
534
|
-
try {
|
|
535
|
-
if (!fs.existsSync(PERSPECTIVES_DIR)) {
|
|
536
|
-
fs.mkdirSync(PERSPECTIVES_DIR, { recursive: true });
|
|
537
|
-
}
|
|
538
|
-
const filePath = path.join(PERSPECTIVES_DIR, `${perspective.uuid}.yaml`);
|
|
539
|
-
fs.writeFileSync(filePath, YAML.stringify(perspective), 'utf8');
|
|
540
|
-
return true;
|
|
541
|
-
} catch (error) {
|
|
542
|
-
logger.error(`Error writing perspective: ${error}`);
|
|
543
|
-
return false;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
export async function deletePerspectiveFile(uuid: string): Promise<boolean> {
|
|
548
|
-
try {
|
|
549
|
-
const filePath = path.join(PERSPECTIVES_DIR, `${uuid}.yaml`);
|
|
550
|
-
if (!fs.existsSync(filePath)) return false;
|
|
551
|
-
fs.unlinkSync(filePath);
|
|
552
|
-
return true;
|
|
553
|
-
} catch (error) {
|
|
554
|
-
logger.error(`Error deleting perspective ${uuid}: ${error}`);
|
|
555
|
-
return false;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Collects all relationships from all packages for cross-service BFS traversal.
|
|
561
|
-
*/
|
|
562
|
-
export async function getAllRelationships(): Promise<{ packageName: string; relationships: Relationship[] }[]> {
|
|
563
|
-
const result: { packageName: string; relationships: Relationship[] }[] = [];
|
|
564
|
-
try {
|
|
565
|
-
const microservices = await listMicroservices();
|
|
566
|
-
for (const ms of microservices) {
|
|
567
|
-
const pkgPath = path.join(DATA_DICTIONARIES_DIR, 'microservices', ms);
|
|
568
|
-
const rels = await readRelationshipsFile(pkgPath);
|
|
569
|
-
if (rels.length > 0) {
|
|
570
|
-
result.push({ packageName: ms, relationships: rels });
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
} catch (error) {
|
|
574
|
-
logger.error(`Error collecting all relationships: ${error}`);
|
|
575
|
-
}
|
|
576
|
-
return result;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
// --- Review comment file operations ---
|
|
580
|
-
|
|
581
|
-
export async function readComments(service: string, entityUuid: string): Promise<ReviewComment[]> {
|
|
582
|
-
try {
|
|
583
|
-
const filePath = path.join(DATA_DICTIONARIES_DIR, 'microservices', service, `${entityUuid}.comments.yaml`);
|
|
584
|
-
if (!fs.existsSync(filePath)) return [];
|
|
585
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
586
|
-
return YAML.parse(content) || [];
|
|
587
|
-
} catch (error) {
|
|
588
|
-
logger.error(`Error reading comments: ${error}`);
|
|
589
|
-
return [];
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
export async function writeComments(service: string, entityUuid: string, comments: ReviewComment[]): Promise<boolean> {
|
|
594
|
-
try {
|
|
595
|
-
const filePath = path.join(DATA_DICTIONARIES_DIR, 'microservices', service, `${entityUuid}.comments.yaml`);
|
|
596
|
-
fs.writeFileSync(filePath, YAML.stringify(comments), 'utf8');
|
|
597
|
-
return true;
|
|
598
|
-
} catch (error) {
|
|
599
|
-
logger.error(`Error writing comments: ${error}`);
|
|
600
|
-
return false;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// Simple logger utility
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Log levels
|
|
5
|
-
*/
|
|
6
|
-
export enum LogLevel {
|
|
7
|
-
ERROR = 'ERROR',
|
|
8
|
-
WARN = 'WARN',
|
|
9
|
-
INFO = 'INFO',
|
|
10
|
-
DEBUG = 'DEBUG'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Simple logger function
|
|
15
|
-
* @param level Log level
|
|
16
|
-
* @param message Message to log
|
|
17
|
-
* @param meta Additional metadata
|
|
18
|
-
*/
|
|
19
|
-
export function log(level: LogLevel, message: string, meta?: any): void {
|
|
20
|
-
const timestamp = new Date().toISOString();
|
|
21
|
-
const logEntry = {
|
|
22
|
-
timestamp,
|
|
23
|
-
level,
|
|
24
|
-
message,
|
|
25
|
-
...(meta && { meta })
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
// In production, this would be replaced with a proper logging solution
|
|
29
|
-
console.log(JSON.stringify(logEntry));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Convenience methods
|
|
33
|
-
export const logger = {
|
|
34
|
-
error: (message: string, meta?: any) => log(LogLevel.ERROR, message, meta),
|
|
35
|
-
warn: (message: string, meta?: any) => log(LogLevel.WARN, message, meta),
|
|
36
|
-
info: (message: string, meta?: any) => log(LogLevel.INFO, message, meta),
|
|
37
|
-
debug: (message: string, meta?: any) => log(LogLevel.DEBUG, message, meta)
|
|
38
|
-
};
|