Add database adapter system, production deployment configs, and new dashboard components
- Add PostgreSQL and SQLite database adapters with factory pattern - Add migration script for SQLite to PostgreSQL - Add production Dockerfiles and docker-compose configs - Add deployment documentation and scripts - Add BIA sync dashboard and matching service - Add data completeness configuration and components - Add new dashboard components (BusinessImportanceComparison, ComplexityDynamics, etc.) - Update various services and routes - Remove deprecated management-parameters.json and taxonomy files
This commit is contained in:
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
|
||||
import { logger } from '../services/logger.js';
|
||||
import { clearEffortCalculationConfigCache, getEffortCalculationConfigV25 } from '../services/effortCalculation.js';
|
||||
import type { EffortCalculationConfig, EffortCalculationConfigV25 } from '../config/effortCalculation.js';
|
||||
import type { DataCompletenessConfig } from '../types/index.js';
|
||||
|
||||
// Get __dirname equivalent for ES modules
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -15,6 +16,7 @@ const router = Router();
|
||||
// Path to the configuration files
|
||||
const CONFIG_FILE_PATH = join(__dirname, '../../data/effort-calculation-config.json');
|
||||
const CONFIG_FILE_PATH_V25 = join(__dirname, '../../data/effort-calculation-config-v25.json');
|
||||
const COMPLETENESS_CONFIG_FILE_PATH = join(__dirname, '../../data/data-completeness-config.json');
|
||||
|
||||
/**
|
||||
* Get the current effort calculation configuration (legacy)
|
||||
@@ -122,5 +124,143 @@ router.put('/effort-calculation-v25', async (req: Request, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the data completeness configuration
|
||||
*/
|
||||
router.get('/data-completeness', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// Try to read from JSON file, fallback to default config
|
||||
try {
|
||||
const fileContent = await readFile(COMPLETENESS_CONFIG_FILE_PATH, 'utf-8');
|
||||
const config = JSON.parse(fileContent) as DataCompletenessConfig;
|
||||
res.json(config);
|
||||
} catch (fileError) {
|
||||
// If file doesn't exist, return default config
|
||||
const defaultConfig: DataCompletenessConfig = {
|
||||
metadata: {
|
||||
version: '1.0.0',
|
||||
description: 'Configuration for Data Completeness Score fields',
|
||||
lastUpdated: new Date().toISOString(),
|
||||
},
|
||||
categories: {
|
||||
general: {
|
||||
name: 'General',
|
||||
description: 'General application information fields',
|
||||
fields: [
|
||||
{ name: 'Organisation', fieldPath: 'organisation', enabled: true },
|
||||
{ name: 'ApplicationFunction', fieldPath: 'applicationFunctions', enabled: true },
|
||||
{ name: 'Status', fieldPath: 'status', enabled: true },
|
||||
{ name: 'Business Impact Analyse', fieldPath: 'businessImpactAnalyse', enabled: true },
|
||||
{ name: 'Application Component Hosting Type', fieldPath: 'hostingType', enabled: true },
|
||||
{ name: 'Supplier Product', fieldPath: 'supplierProduct', enabled: true },
|
||||
{ name: 'Business Owner', fieldPath: 'businessOwner', enabled: true },
|
||||
{ name: 'System Owner', fieldPath: 'systemOwner', enabled: true },
|
||||
{ name: 'Functional Application Management', fieldPath: 'functionalApplicationManagement', enabled: true },
|
||||
{ name: 'Technical Application Management', fieldPath: 'technicalApplicationManagement', enabled: true },
|
||||
],
|
||||
},
|
||||
applicationManagement: {
|
||||
name: 'Application Management',
|
||||
description: 'Application management classification fields',
|
||||
fields: [
|
||||
{ name: 'ICT Governance Model', fieldPath: 'governanceModel', enabled: true },
|
||||
{ name: 'Application Management - Application Type', fieldPath: 'applicationType', enabled: true },
|
||||
{ name: 'Application Management - Hosting', fieldPath: 'applicationManagementHosting', enabled: true },
|
||||
{ name: 'Application Management - TAM', fieldPath: 'applicationManagementTAM', enabled: true },
|
||||
{ name: 'Application Management - Dynamics Factor', fieldPath: 'dynamicsFactor', enabled: true },
|
||||
{ name: 'Application Management - Complexity Factor', fieldPath: 'complexityFactor', enabled: true },
|
||||
{ name: 'Application Management - Number of Users', fieldPath: 'numberOfUsers', enabled: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
res.json(defaultConfig);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Failed to get data completeness configuration', error);
|
||||
res.status(500).json({ error: 'Failed to get configuration' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Update the data completeness configuration
|
||||
*/
|
||||
router.put('/data-completeness', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const config = req.body as DataCompletenessConfig;
|
||||
|
||||
// Validate the configuration structure
|
||||
if (!config.categories || !Array.isArray(config.categories)) {
|
||||
res.status(400).json({ error: 'Invalid configuration: categories must be an array' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.categories.length === 0) {
|
||||
res.status(400).json({ error: 'Invalid configuration: must have at least one category' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate each category
|
||||
for (const category of config.categories) {
|
||||
if (!category.id || typeof category.id !== 'string') {
|
||||
res.status(400).json({ error: 'Invalid configuration: each category must have an id' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!category.name || typeof category.name !== 'string') {
|
||||
res.status(400).json({ error: 'Invalid configuration: each category must have a name' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(category.fields)) {
|
||||
res.status(400).json({ error: 'Invalid configuration: category fields must be arrays' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate each field
|
||||
for (const field of category.fields) {
|
||||
if (!field.id || typeof field.id !== 'string') {
|
||||
res.status(400).json({ error: 'Invalid configuration: each field must have an id' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!field.name || typeof field.name !== 'string') {
|
||||
res.status(400).json({ error: 'Invalid configuration: each field must have a name' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!field.fieldPath || typeof field.fieldPath !== 'string') {
|
||||
res.status(400).json({ error: 'Invalid configuration: each field must have a fieldPath' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof field.enabled !== 'boolean') {
|
||||
res.status(400).json({ error: 'Invalid configuration: each field must have an enabled boolean' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update metadata
|
||||
config.metadata = {
|
||||
...config.metadata,
|
||||
lastUpdated: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// Write to JSON file
|
||||
await writeFile(COMPLETENESS_CONFIG_FILE_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
||||
|
||||
// Clear the cache so the new config is loaded on next request
|
||||
const { clearDataCompletenessConfigCache } = await import('../services/dataCompletenessConfig.js');
|
||||
clearDataCompletenessConfigCache();
|
||||
|
||||
logger.info('Data completeness configuration updated');
|
||||
res.json({ success: true, message: 'Configuration saved successfully' });
|
||||
} catch (error) {
|
||||
logger.error('Failed to update data completeness configuration', error);
|
||||
res.status(500).json({ error: 'Failed to save configuration' });
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user