livrare lot 2
This commit is contained in:
commit
8ecc78e729
763 changed files with 164593 additions and 0 deletions
|
|
@ -0,0 +1,377 @@
|
|||
/**
|
||||
* Dependency Checker Utility
|
||||
*
|
||||
* Verifies if a record can be safely deleted by checking for child references.
|
||||
* Provides warnings and prevents accidental deletion of records with dependencies.
|
||||
*/
|
||||
|
||||
import { query } from '../config/database';
|
||||
|
||||
// Types
|
||||
export interface DependencyRule {
|
||||
childTable: string;
|
||||
childColumn: string;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
export interface DependencyCheckResult {
|
||||
canDelete: boolean;
|
||||
hasChildren: boolean;
|
||||
dependencies: {
|
||||
table: string;
|
||||
displayName: string;
|
||||
count: number;
|
||||
}[];
|
||||
totalChildren: number;
|
||||
warning?: string;
|
||||
}
|
||||
|
||||
export interface DeleteResult {
|
||||
success: boolean;
|
||||
deleted: boolean;
|
||||
message: string;
|
||||
hadDependencies?: boolean;
|
||||
dependencyDetails?: DependencyCheckResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dependency map for all tables that have child references
|
||||
* Key: parent table name
|
||||
* Value: array of child table references
|
||||
*/
|
||||
const DEPENDENCY_MAP: Record<string, DependencyRule[]> = {
|
||||
// Analysis Core
|
||||
'dimension': [{
|
||||
childTable: 'subdimension',
|
||||
childColumn: 'dimension_id',
|
||||
displayName: 'Subdimensiuni'
|
||||
}],
|
||||
|
||||
'subdimension': [{
|
||||
childTable: 'technique',
|
||||
childColumn: 'subdimension_id',
|
||||
displayName: 'Tehnici'
|
||||
}],
|
||||
|
||||
'technique': [
|
||||
{
|
||||
childTable: 'technique_indicator',
|
||||
childColumn: 'technique_id',
|
||||
displayName: 'Indicatori'
|
||||
},
|
||||
{
|
||||
childTable: 'technique_validation_rule',
|
||||
childColumn: 'technique_id',
|
||||
displayName: 'Reguli de validare'
|
||||
}
|
||||
],
|
||||
|
||||
// Source Assessment
|
||||
'platform_modifier': [{
|
||||
childTable: 'platform',
|
||||
childColumn: 'platform_modifier_id',
|
||||
displayName: 'Platforme'
|
||||
}],
|
||||
|
||||
'source_type': [{
|
||||
childTable: 'domain_attribute',
|
||||
childColumn: 'source_type_id',
|
||||
displayName: 'Atribute domeniu'
|
||||
}],
|
||||
|
||||
'source_credibility': [{
|
||||
childTable: 'domain_attribute',
|
||||
childColumn: 'source_credibility_id',
|
||||
displayName: 'Atribute domeniu'
|
||||
}],
|
||||
|
||||
'domain_age_score': [{
|
||||
childTable: 'domain_attribute',
|
||||
childColumn: 'domain_age_score',
|
||||
displayName: 'Atribute domeniu'
|
||||
}],
|
||||
|
||||
'domain_risk_level': [{
|
||||
childTable: 'domain_attribute',
|
||||
childColumn: 'domain_risk_level_id',
|
||||
displayName: 'Atribute domeniu'
|
||||
}],
|
||||
|
||||
'domain_red_flag': [{
|
||||
childTable: 'domain_attribute',
|
||||
childColumn: 'domain_red_flag_id',
|
||||
displayName: 'Atribute domeniu'
|
||||
}],
|
||||
|
||||
'author_classification': [{
|
||||
childTable: 'author',
|
||||
childColumn: 'author_classification_id',
|
||||
displayName: 'Autori'
|
||||
}],
|
||||
|
||||
'author_credibility': [{
|
||||
childTable: 'author',
|
||||
childColumn: 'author_credibility_id',
|
||||
displayName: 'Autori'
|
||||
}],
|
||||
|
||||
// Source hierarchy
|
||||
'source': [
|
||||
{
|
||||
childTable: 'platform',
|
||||
childColumn: 'platform_id',
|
||||
displayName: 'Platforme'
|
||||
},
|
||||
{
|
||||
childTable: 'domain',
|
||||
childColumn: 'domain_id',
|
||||
displayName: 'Domenii'
|
||||
},
|
||||
{
|
||||
childTable: 'author',
|
||||
childColumn: 'author_id',
|
||||
displayName: 'Autori'
|
||||
}
|
||||
],
|
||||
|
||||
'domain': [{
|
||||
childTable: 'domain_attribute',
|
||||
childColumn: 'domain_id',
|
||||
displayName: 'Atribute domeniu'
|
||||
}]
|
||||
};
|
||||
|
||||
/**
|
||||
* Tables that can be deleted without any child checks
|
||||
*/
|
||||
const LEAF_TABLES = new Set([
|
||||
'technique_indicator',
|
||||
'technique_validation_rule',
|
||||
'platform',
|
||||
'domain_attribute',
|
||||
'author',
|
||||
'claim',
|
||||
'claim_type',
|
||||
'confidence',
|
||||
'interpretation',
|
||||
'verdict_category',
|
||||
'risk_mapping',
|
||||
'severity_assessment',
|
||||
'component_weight',
|
||||
'weight_scenario',
|
||||
'multiplier',
|
||||
'source_assessment',
|
||||
'notification',
|
||||
'email_server'
|
||||
]);
|
||||
|
||||
/**
|
||||
* Check if a table has dependency rules defined
|
||||
*/
|
||||
export function hasDependencyRules(tableName: string): boolean {
|
||||
return tableName in DEPENDENCY_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a table is a leaf table (no children possible)
|
||||
*/
|
||||
export function isLeafTable(tableName: string): boolean {
|
||||
return LEAF_TABLES.has(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get dependency rules for a table
|
||||
*/
|
||||
export function getDependencyRules(tableName: string): DependencyRule[] {
|
||||
return DEPENDENCY_MAP[tableName] || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check dependencies for a specific record before deletion
|
||||
*
|
||||
* @param tableName - The table name to check
|
||||
* @param idColumn - The primary key column name
|
||||
* @param idValue - The primary key value
|
||||
* @returns DependencyCheckResult with details about children
|
||||
*/
|
||||
export async function checkDependencies(
|
||||
tableName: string,
|
||||
idColumn: string,
|
||||
idValue: number | string
|
||||
): Promise<DependencyCheckResult> {
|
||||
// If it's a leaf table, it can always be deleted
|
||||
if (isLeafTable(tableName)) {
|
||||
return {
|
||||
canDelete: true,
|
||||
hasChildren: false,
|
||||
dependencies: [],
|
||||
totalChildren: 0
|
||||
};
|
||||
}
|
||||
|
||||
const rules = getDependencyRules(tableName);
|
||||
|
||||
// If no rules defined, assume it can be deleted
|
||||
if (rules.length === 0) {
|
||||
return {
|
||||
canDelete: true,
|
||||
hasChildren: false,
|
||||
dependencies: [],
|
||||
totalChildren: 0
|
||||
};
|
||||
}
|
||||
|
||||
const dependencies: { table: string; displayName: string; count: number }[] = [];
|
||||
let totalChildren = 0;
|
||||
|
||||
// Check each dependency rule
|
||||
for (const rule of rules) {
|
||||
const countQuery = `SELECT COUNT(*) as count FROM ${rule.childTable} WHERE ${rule.childColumn} = $1`;
|
||||
const result = await query<{ count: string }>(countQuery, [idValue]);
|
||||
const count = parseInt(result[0]?.count || '0', 10);
|
||||
|
||||
if (count > 0) {
|
||||
dependencies.push({
|
||||
table: rule.childTable,
|
||||
displayName: rule.displayName,
|
||||
count
|
||||
});
|
||||
totalChildren += count;
|
||||
}
|
||||
}
|
||||
|
||||
const hasChildren = totalChildren > 0;
|
||||
|
||||
return {
|
||||
canDelete: !hasChildren,
|
||||
hasChildren,
|
||||
dependencies,
|
||||
totalChildren,
|
||||
warning: hasChildren
|
||||
? `Nu se poate șterge: există ${totalChildren} înregistrări dependente (${dependencies.map(d => `${d.count} ${d.displayName}`).join(', ')})`
|
||||
: undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe delete function - checks dependencies before deleting
|
||||
*
|
||||
* @param tableName - Table to delete from
|
||||
* @param idColumn - Primary key column name
|
||||
* @param idValue - Primary key value
|
||||
* @param force - If true, attempts to delete even with children (will fail at DB level)
|
||||
* @returns DeleteResult with success status and details
|
||||
*/
|
||||
export async function safeDelete(
|
||||
tableName: string,
|
||||
idColumn: string,
|
||||
idValue: number | string,
|
||||
force: boolean = false
|
||||
): Promise<DeleteResult> {
|
||||
// First check dependencies
|
||||
const depCheck = await checkDependencies(tableName, idColumn, idValue);
|
||||
|
||||
if (depCheck.hasChildren && !force) {
|
||||
return {
|
||||
success: false,
|
||||
deleted: false,
|
||||
message: depCheck.warning || 'Record has dependencies and cannot be deleted',
|
||||
hadDependencies: true,
|
||||
dependencyDetails: depCheck
|
||||
};
|
||||
}
|
||||
|
||||
// Attempt to delete
|
||||
try {
|
||||
const deleteQuery = `DELETE FROM ${tableName} WHERE ${idColumn} = $1 RETURNING *`;
|
||||
const result = await query(deleteQuery, [idValue]);
|
||||
|
||||
if (result.length === 0) {
|
||||
return {
|
||||
success: false,
|
||||
deleted: false,
|
||||
message: 'Înregistrarea nu a fost găsită'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
deleted: true,
|
||||
message: 'Înregistrarea a fost ștearsă cu succes'
|
||||
};
|
||||
} catch (error: any) {
|
||||
// Handle FK violation errors from PostgreSQL
|
||||
if (error.code === '23503') { // foreign_key_violation
|
||||
return {
|
||||
success: false,
|
||||
deleted: false,
|
||||
message: 'Nu se poate șterge: există înregistrări dependente în baza de date',
|
||||
hadDependencies: true
|
||||
};
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a summary of all dependencies for a table (useful for UI display)
|
||||
*/
|
||||
export function getDependencySummary(tableName: string): {
|
||||
hasRules: boolean;
|
||||
isLeaf: boolean;
|
||||
rules: DependencyRule[];
|
||||
canHaveChildren: boolean;
|
||||
} {
|
||||
const isLeaf = isLeafTable(tableName);
|
||||
const rules = getDependencyRules(tableName);
|
||||
|
||||
return {
|
||||
hasRules: rules.length > 0,
|
||||
isLeaf,
|
||||
rules,
|
||||
canHaveChildren: !isLeaf && rules.length > 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch check dependencies for multiple records
|
||||
*/
|
||||
export async function batchCheckDependencies(
|
||||
tableName: string,
|
||||
idColumn: string,
|
||||
idValues: (number | string)[]
|
||||
): Promise<Map<number | string, DependencyCheckResult>> {
|
||||
const results = new Map<number | string, DependencyCheckResult>();
|
||||
|
||||
// For leaf tables, all can be deleted
|
||||
if (isLeafTable(tableName)) {
|
||||
for (const id of idValues) {
|
||||
results.set(id, {
|
||||
canDelete: true,
|
||||
hasChildren: false,
|
||||
dependencies: [],
|
||||
totalChildren: 0
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// Check each record
|
||||
for (const id of idValues) {
|
||||
const check = await checkDependencies(tableName, idColumn, id);
|
||||
results.set(id, check);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
export default {
|
||||
checkDependencies,
|
||||
safeDelete,
|
||||
hasDependencyRules,
|
||||
isLeafTable,
|
||||
getDependencyRules,
|
||||
getDependencySummary,
|
||||
batchCheckDependencies
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue