Initial commit: ZiRA Classification Tool for Zuyderland CMDB
This commit is contained in:
217
backend/src/routes/applications.ts
Normal file
217
backend/src/routes/applications.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { dataService } from '../services/dataService.js';
|
||||
import { databaseService } from '../services/database.js';
|
||||
import { logger } from '../services/logger.js';
|
||||
import { calculateRequiredEffortApplicationManagement, calculateRequiredEffortApplicationManagementWithBreakdown } from '../services/effortCalculation.js';
|
||||
import type { SearchFilters, ApplicationUpdateRequest, ReferenceValue, ClassificationResult, ApplicationDetails, ApplicationStatus } from '../types/index.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Search applications with filters
|
||||
router.post('/search', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { filters, page = 1, pageSize = 25 } = req.body as {
|
||||
filters: SearchFilters;
|
||||
page?: number;
|
||||
pageSize?: number;
|
||||
};
|
||||
|
||||
const result = await dataService.searchApplications(filters, page, pageSize);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
logger.error('Failed to search applications', error);
|
||||
res.status(500).json({ error: 'Failed to search applications' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get team dashboard data
|
||||
router.get('/team-dashboard', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const excludedStatusesParam = req.query.excludedStatuses as string | undefined;
|
||||
let excludedStatuses: ApplicationStatus[] = [];
|
||||
|
||||
if (excludedStatusesParam && excludedStatusesParam.trim().length > 0) {
|
||||
// Parse comma-separated statuses
|
||||
excludedStatuses = excludedStatusesParam
|
||||
.split(',')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0) as ApplicationStatus[];
|
||||
} else {
|
||||
// Default to excluding 'Closed' and 'Deprecated' if not specified
|
||||
excludedStatuses = ['Closed', 'Deprecated'];
|
||||
}
|
||||
|
||||
const data = await dataService.getTeamDashboardData(excludedStatuses);
|
||||
res.json(data);
|
||||
} catch (error) {
|
||||
logger.error('Failed to get team dashboard data', error);
|
||||
res.status(500).json({ error: 'Failed to get team dashboard data' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get application by ID
|
||||
router.get('/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
// Don't treat special routes as application IDs
|
||||
if (id === 'team-dashboard' || id === 'calculate-effort' || id === 'search') {
|
||||
res.status(404).json({ error: 'Route not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const application = await dataService.getApplicationById(id);
|
||||
|
||||
if (!application) {
|
||||
res.status(404).json({ error: 'Application not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(application);
|
||||
} catch (error) {
|
||||
logger.error('Failed to get application', error);
|
||||
res.status(500).json({ error: 'Failed to get application' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update application
|
||||
router.put('/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const updates = req.body as {
|
||||
applicationFunctions?: ReferenceValue[];
|
||||
dynamicsFactor?: ReferenceValue;
|
||||
complexityFactor?: ReferenceValue;
|
||||
numberOfUsers?: ReferenceValue;
|
||||
governanceModel?: ReferenceValue;
|
||||
source?: 'AI_ACCEPTED' | 'AI_MODIFIED' | 'MANUAL';
|
||||
};
|
||||
|
||||
const application = await dataService.getApplicationById(id);
|
||||
if (!application) {
|
||||
res.status(404).json({ error: 'Application not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Build changes object for history
|
||||
const changes: ClassificationResult['changes'] = {};
|
||||
if (updates.applicationFunctions) {
|
||||
changes.applicationFunctions = {
|
||||
from: application.applicationFunctions,
|
||||
to: updates.applicationFunctions,
|
||||
};
|
||||
}
|
||||
if (updates.dynamicsFactor) {
|
||||
changes.dynamicsFactor = {
|
||||
from: application.dynamicsFactor,
|
||||
to: updates.dynamicsFactor,
|
||||
};
|
||||
}
|
||||
if (updates.complexityFactor) {
|
||||
changes.complexityFactor = {
|
||||
from: application.complexityFactor,
|
||||
to: updates.complexityFactor,
|
||||
};
|
||||
}
|
||||
if (updates.numberOfUsers) {
|
||||
changes.numberOfUsers = {
|
||||
from: application.numberOfUsers,
|
||||
to: updates.numberOfUsers,
|
||||
};
|
||||
}
|
||||
if (updates.governanceModel) {
|
||||
changes.governanceModel = {
|
||||
from: application.governanceModel,
|
||||
to: updates.governanceModel,
|
||||
};
|
||||
}
|
||||
|
||||
const success = await dataService.updateApplication(id, updates);
|
||||
|
||||
if (success) {
|
||||
// Save to classification history
|
||||
const classificationResult: ClassificationResult = {
|
||||
applicationId: id,
|
||||
applicationName: application.name,
|
||||
changes,
|
||||
source: updates.source || 'MANUAL',
|
||||
timestamp: new Date(),
|
||||
};
|
||||
databaseService.saveClassificationResult(classificationResult);
|
||||
|
||||
const updatedApp = await dataService.getApplicationById(id);
|
||||
res.json(updatedApp);
|
||||
} else {
|
||||
res.status(500).json({ error: 'Failed to update application' });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to update application', error);
|
||||
res.status(500).json({ error: 'Failed to update application' });
|
||||
}
|
||||
});
|
||||
|
||||
// Calculate FTE effort for an application (real-time calculation without saving)
|
||||
router.post('/calculate-effort', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const applicationData = req.body as Partial<ApplicationDetails>;
|
||||
|
||||
// Build a complete ApplicationDetails object with defaults
|
||||
const application: ApplicationDetails = {
|
||||
id: applicationData.id || '',
|
||||
key: applicationData.key || '',
|
||||
name: applicationData.name || '',
|
||||
searchReference: applicationData.searchReference || null,
|
||||
description: applicationData.description || null,
|
||||
supplierProduct: applicationData.supplierProduct || null,
|
||||
organisation: applicationData.organisation || null,
|
||||
hostingType: applicationData.hostingType || null,
|
||||
status: applicationData.status || null,
|
||||
businessImportance: applicationData.businessImportance || null,
|
||||
businessImpactAnalyse: applicationData.businessImpactAnalyse || null,
|
||||
systemOwner: applicationData.systemOwner || null,
|
||||
businessOwner: applicationData.businessOwner || null,
|
||||
functionalApplicationManagement: applicationData.functionalApplicationManagement || null,
|
||||
technicalApplicationManagement: applicationData.technicalApplicationManagement || null,
|
||||
technicalApplicationManagementPrimary: applicationData.technicalApplicationManagementPrimary || null,
|
||||
technicalApplicationManagementSecondary: applicationData.technicalApplicationManagementSecondary || null,
|
||||
medischeTechniek: applicationData.medischeTechniek || false,
|
||||
applicationFunctions: applicationData.applicationFunctions || [],
|
||||
dynamicsFactor: applicationData.dynamicsFactor || null,
|
||||
complexityFactor: applicationData.complexityFactor || null,
|
||||
numberOfUsers: applicationData.numberOfUsers || null,
|
||||
governanceModel: applicationData.governanceModel || null,
|
||||
applicationCluster: applicationData.applicationCluster || null,
|
||||
applicationType: applicationData.applicationType || null,
|
||||
platform: applicationData.platform || null,
|
||||
requiredEffortApplicationManagement: null,
|
||||
overrideFTE: applicationData.overrideFTE || null,
|
||||
applicationManagementHosting: applicationData.applicationManagementHosting || null,
|
||||
applicationManagementTAM: applicationData.applicationManagementTAM || null,
|
||||
technischeArchitectuur: applicationData.technischeArchitectuur || null,
|
||||
};
|
||||
|
||||
const result = calculateRequiredEffortApplicationManagementWithBreakdown(application);
|
||||
|
||||
res.json({
|
||||
requiredEffortApplicationManagement: result.finalEffort,
|
||||
breakdown: result.breakdown,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to calculate effort', error);
|
||||
res.status(500).json({ error: 'Failed to calculate effort' });
|
||||
}
|
||||
});
|
||||
|
||||
// Get application classification history
|
||||
router.get('/:id/history', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const history = databaseService.getClassificationsByApplicationId(id);
|
||||
res.json(history);
|
||||
} catch (error) {
|
||||
logger.error('Failed to get classification history', error);
|
||||
res.status(500).json({ error: 'Failed to get classification history' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user