@hamak/smart-data-dico 1.0.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/README.md +292 -0
- package/backend/package.json +51 -0
- package/backend/src/__tests__/integration/api.test.ts +149 -0
- package/backend/src/__tests__/setup.ts +24 -0
- package/backend/src/__tests__/utils/testUtils.ts +76 -0
- package/backend/src/adapters/EntityFileAdapter.ts +154 -0
- package/backend/src/adapters/YamlFileInfoEnricher.ts +52 -0
- package/backend/src/controllers/authController.ts +131 -0
- package/backend/src/controllers/diagramController.ts +143 -0
- package/backend/src/controllers/dictionaryController.ts +306 -0
- package/backend/src/controllers/importExportController.ts +64 -0
- package/backend/src/controllers/perspectiveController.ts +90 -0
- package/backend/src/controllers/serviceController.ts +418 -0
- package/backend/src/controllers/stereotypeController.ts +59 -0
- package/backend/src/controllers/versionController.ts +226 -0
- package/backend/src/kernel/config.ts +43 -0
- package/backend/src/middleware/auth.ts +128 -0
- package/backend/src/middleware/jwtAuth.ts +100 -0
- package/backend/src/models/Dictionary.ts +38 -0
- package/backend/src/models/EntitySchema.ts +393 -0
- package/backend/src/models/__tests__/Dictionary.test.ts +92 -0
- package/backend/src/models/__tests__/EntitySchema.test.ts +119 -0
- package/backend/src/routes/index.ts +120 -0
- package/backend/src/scripts/migrate-to-uuid.ts +24 -0
- package/backend/src/server.ts +140 -0
- package/backend/src/services/__mocks__/entityService.ts +38 -0
- package/backend/src/services/__mocks__/serviceService.ts +88 -0
- package/backend/src/services/__mocks__/versionService.ts +38 -0
- package/backend/src/services/__tests__/dictionaryService.test.ts +74 -0
- package/backend/src/services/diagramService.ts +165 -0
- package/backend/src/services/dictionaryService.ts +582 -0
- package/backend/src/services/entityService.ts +102 -0
- package/backend/src/services/exportService.ts +172 -0
- package/backend/src/services/importService.ts +208 -0
- package/backend/src/services/perspectiveService.ts +276 -0
- package/backend/src/services/qualityService.ts +121 -0
- package/backend/src/services/serviceService.ts +763 -0
- package/backend/src/services/stereotypeService.ts +98 -0
- package/backend/src/services/versionService.ts +135 -0
- package/backend/src/setupTests.ts +12 -0
- package/backend/src/utils/__mocks__/fileOperations.ts +116 -0
- package/backend/src/utils/fileOperations.ts +602 -0
- package/backend/src/utils/logger.ts +38 -0
- package/backend/src/utils/migration.ts +254 -0
- package/backend/src/utils/swagger.ts +358 -0
- package/backend/src/utils/uuid.ts +41 -0
- package/backend/tsconfig.json +20 -0
- package/bin/cli.js +129 -0
- package/data-dictionaries/stereotypes.yaml +91 -0
- package/package.json +42 -0
|
@@ -0,0 +1,154 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import jwt, { SignOptions } from 'jsonwebtoken';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
import { User, UserRole } from '../middleware/auth.js';
|
|
5
|
+
|
|
6
|
+
// Mock user database - imported from auth middleware
|
|
7
|
+
// In a real application, this would be in a separate database module
|
|
8
|
+
const users: Record<string, { password: string; user: User }> = {
|
|
9
|
+
'admin': {
|
|
10
|
+
password: 'admin123',
|
|
11
|
+
user: {
|
|
12
|
+
id: '1',
|
|
13
|
+
username: 'admin',
|
|
14
|
+
role: UserRole.ADMIN
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
'editor': {
|
|
18
|
+
password: 'editor123',
|
|
19
|
+
user: {
|
|
20
|
+
id: '2',
|
|
21
|
+
username: 'editor',
|
|
22
|
+
role: UserRole.EDITOR
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
'viewer': {
|
|
26
|
+
password: 'viewer123',
|
|
27
|
+
user: {
|
|
28
|
+
id: '3',
|
|
29
|
+
username: 'viewer',
|
|
30
|
+
role: UserRole.VIEWER
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// JWT secret key - should be in environment variables in production
|
|
36
|
+
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key';
|
|
37
|
+
// Token expiration time (default: 24 hours)
|
|
38
|
+
const TOKEN_EXPIRATION = process.env.TOKEN_EXPIRATION || '24h';
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Login controller
|
|
42
|
+
* Validates username and password, generates JWT token
|
|
43
|
+
* @param req Express request
|
|
44
|
+
* @param res Express response
|
|
45
|
+
*/
|
|
46
|
+
export const login = (req: Request, res: Response) => {
|
|
47
|
+
try {
|
|
48
|
+
const { username, password } = req.body;
|
|
49
|
+
|
|
50
|
+
// Validate request body
|
|
51
|
+
if (!username || !password) {
|
|
52
|
+
return res.status(400).json({
|
|
53
|
+
message: 'Bad request',
|
|
54
|
+
error: 'Username and password are required'
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Validate credentials
|
|
59
|
+
const userRecord = users[username];
|
|
60
|
+
|
|
61
|
+
if (!userRecord || userRecord.password !== password) {
|
|
62
|
+
logger.info(`Failed login attempt for user: ${username}`);
|
|
63
|
+
return res.status(401).json({
|
|
64
|
+
message: 'Authentication failed',
|
|
65
|
+
error: 'Invalid username or password'
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Generate JWT token
|
|
70
|
+
const payload = {
|
|
71
|
+
id: userRecord.user.id,
|
|
72
|
+
username: userRecord.user.username,
|
|
73
|
+
role: userRecord.user.role
|
|
74
|
+
};
|
|
75
|
+
const options: SignOptions = { expiresIn: TOKEN_EXPIRATION as any };
|
|
76
|
+
const token = jwt.sign(payload, JWT_SECRET, options);
|
|
77
|
+
|
|
78
|
+
logger.info(`User logged in: ${username}`);
|
|
79
|
+
|
|
80
|
+
// Return token and user info
|
|
81
|
+
return res.status(200).json({
|
|
82
|
+
message: 'Authentication successful',
|
|
83
|
+
token,
|
|
84
|
+
user: {
|
|
85
|
+
id: userRecord.user.id,
|
|
86
|
+
username: userRecord.user.username,
|
|
87
|
+
role: userRecord.user.role
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
} catch (error: any) {
|
|
91
|
+
logger.error(`Login error: ${error.message}`);
|
|
92
|
+
return res.status(500).json({
|
|
93
|
+
message: 'Internal server error',
|
|
94
|
+
error: process.env.NODE_ENV === 'production' ? undefined : error.message
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get current user controller
|
|
101
|
+
* Returns user information based on JWT token
|
|
102
|
+
* @param req Express request
|
|
103
|
+
* @param res Express response
|
|
104
|
+
*/
|
|
105
|
+
export const getCurrentUser = (req: Request, res: Response) => {
|
|
106
|
+
try {
|
|
107
|
+
// User should be attached to request by JWT middleware
|
|
108
|
+
const user = (req as any).user;
|
|
109
|
+
|
|
110
|
+
if (!user) {
|
|
111
|
+
return res.status(401).json({
|
|
112
|
+
message: 'Authentication required',
|
|
113
|
+
error: 'User not authenticated'
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return res.status(200).json({
|
|
118
|
+
user: {
|
|
119
|
+
id: user.id,
|
|
120
|
+
username: user.username,
|
|
121
|
+
role: user.role
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
logger.error(`Get current user error: ${error.message}`);
|
|
126
|
+
return res.status(500).json({
|
|
127
|
+
message: 'Internal server error',
|
|
128
|
+
error: process.env.NODE_ENV === 'production' ? undefined : error.message
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { diagramService, DiagramLayout } from '../services/diagramService.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
export class DiagramController {
|
|
6
|
+
async saveDiagramLayout(req: Request, res: Response): Promise<void> {
|
|
7
|
+
try {
|
|
8
|
+
const layoutData = req.body as Omit<DiagramLayout, 'createdAt' | 'updatedAt'>;
|
|
9
|
+
|
|
10
|
+
if (!layoutData.id || !layoutData.name) {
|
|
11
|
+
res.status(400).json({
|
|
12
|
+
message: 'Missing required fields: id and name are required'
|
|
13
|
+
});
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const savedLayout = await diagramService.saveDiagramLayout(layoutData);
|
|
18
|
+
|
|
19
|
+
res.status(201).json({
|
|
20
|
+
message: 'Diagram layout saved successfully',
|
|
21
|
+
data: savedLayout
|
|
22
|
+
});
|
|
23
|
+
} catch (error) {
|
|
24
|
+
logger.error('Error in saveDiagramLayout:', error);
|
|
25
|
+
res.status(500).json({
|
|
26
|
+
message: 'Failed to save diagram layout',
|
|
27
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async loadDiagramLayout(req: Request, res: Response): Promise<void> {
|
|
33
|
+
try {
|
|
34
|
+
const { id } = req.params;
|
|
35
|
+
|
|
36
|
+
if (!id) {
|
|
37
|
+
res.status(400).json({
|
|
38
|
+
message: 'Diagram layout ID is required'
|
|
39
|
+
});
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const layout = await diagramService.loadDiagramLayout(id);
|
|
44
|
+
|
|
45
|
+
if (!layout) {
|
|
46
|
+
res.status(404).json({
|
|
47
|
+
message: 'Diagram layout not found'
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
res.json({
|
|
53
|
+
message: 'Diagram layout loaded successfully',
|
|
54
|
+
data: layout
|
|
55
|
+
});
|
|
56
|
+
} catch (error) {
|
|
57
|
+
logger.error('Error in loadDiagramLayout:', error);
|
|
58
|
+
res.status(500).json({
|
|
59
|
+
message: 'Failed to load diagram layout',
|
|
60
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async updateDiagramLayout(req: Request, res: Response): Promise<void> {
|
|
66
|
+
try {
|
|
67
|
+
const { id } = req.params;
|
|
68
|
+
const updates = req.body;
|
|
69
|
+
|
|
70
|
+
if (!id) {
|
|
71
|
+
res.status(400).json({
|
|
72
|
+
message: 'Diagram layout ID is required'
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const updatedLayout = await diagramService.updateDiagramLayout(id, updates);
|
|
78
|
+
|
|
79
|
+
res.json({
|
|
80
|
+
message: 'Diagram layout updated successfully',
|
|
81
|
+
data: updatedLayout
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.error('Error in updateDiagramLayout:', error);
|
|
85
|
+
if (error instanceof Error && error.message === 'Diagram layout not found') {
|
|
86
|
+
res.status(404).json({
|
|
87
|
+
message: 'Diagram layout not found'
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
res.status(500).json({
|
|
91
|
+
message: 'Failed to update diagram layout',
|
|
92
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async deleteDiagramLayout(req: Request, res: Response): Promise<void> {
|
|
99
|
+
try {
|
|
100
|
+
const { id } = req.params;
|
|
101
|
+
|
|
102
|
+
if (!id) {
|
|
103
|
+
res.status(400).json({
|
|
104
|
+
message: 'Diagram layout ID is required'
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await diagramService.deleteDiagramLayout(id);
|
|
110
|
+
|
|
111
|
+
res.json({
|
|
112
|
+
message: 'Diagram layout deleted successfully'
|
|
113
|
+
});
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logger.error('Error in deleteDiagramLayout:', error);
|
|
116
|
+
res.status(500).json({
|
|
117
|
+
message: 'Failed to delete diagram layout',
|
|
118
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async listDiagramLayouts(req: Request, res: Response): Promise<void> {
|
|
124
|
+
try {
|
|
125
|
+
const { service } = req.query;
|
|
126
|
+
|
|
127
|
+
const layouts = await diagramService.listDiagramLayouts(service as string);
|
|
128
|
+
|
|
129
|
+
res.json({
|
|
130
|
+
message: 'Diagram layouts retrieved successfully',
|
|
131
|
+
data: layouts
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
logger.error('Error in listDiagramLayouts:', error);
|
|
135
|
+
res.status(500).json({
|
|
136
|
+
message: 'Failed to list diagram layouts',
|
|
137
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const diagramController = new DiagramController();
|