didi-lot2-backend/backend/services/orchestration-layer/didiFramework/src/routes/validation-rules.ts
2026-07-10 03:39:53 -07:00

359 lines
11 KiB
TypeScript

/**
* Validation Rules Routes - FULL CRUD
*
* Validation rules are leaf nodes - no children, can be deleted directly
* technique -> technique_validation_rule
*/
import { Router, Request, Response } from 'express';
import { query, queryOne, transaction } from '../config/database';
import { TechniqueValidationRule, ApiResponse } from '../types';
import { PoolClient } from 'pg';
import { internalError } from '../config/error-response';
const router = Router();
// Parameter type for validation rules
const PARAMETER_TYPE_VALIDATION_RULE = 5;
// Helper: Create parameter entry
const createParameter = async (client: PoolClient): Promise<number> => {
const maxResult = await client.query('SELECT COALESCE(MAX(parameter_id), 0) + 1 as next_id FROM parameter');
const nextParamId = maxResult.rows[0].next_id;
await client.query(`
INSERT INTO parameter (parameter_id, parameter_type, valid_from, valid_to, created_date, updated_date)
VALUES ($1, $2, '2026-01-01', '2999-01-01', CURRENT_DATE, CURRENT_DATE)
`, [nextParamId, PARAMETER_TYPE_VALIDATION_RULE]);
return nextParamId;
};
// Helper: Get next rule_id
const getNextRuleId = async (client: PoolClient): Promise<number> => {
const result = await client.query('SELECT COALESCE(MAX(technique_valid_rule_id), 0) + 1 as next_id FROM technique_validation_rule');
return result.rows[0].next_id;
};
// GET all validation rules
router.get('/', async (req: Request, res: Response) => {
try {
const rules = await query<TechniqueValidationRule>(
'SELECT * FROM technique_validation_rule ORDER BY technique_id, technique_valid_rule_id'
);
res.json({
success: true,
data: rules,
count: rules.length
} as ApiResponse<TechniqueValidationRule[]>);
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
} as ApiResponse<never>);
}
});
// GET validation rules with technique info
router.get('/with-techniques', async (req: Request, res: Response) => {
try {
const rules = await query<TechniqueValidationRule & { technique_name: string }>(`
SELECT r.*, t.technique_name
FROM technique_validation_rule r
JOIN technique t ON r.technique_id = t.technique_id
ORDER BY r.technique_id, r.technique_valid_rule_id
`);
res.json({
success: true,
data: rules,
count: rules.length
});
} catch (error) {
internalError(res, error);
}
});
// GET validation rules by technique_id
router.get('/by-technique/:techniqueId', async (req: Request, res: Response) => {
try {
const rules = await query<TechniqueValidationRule>(
'SELECT * FROM technique_validation_rule WHERE technique_id = $1 ORDER BY technique_valid_rule_id',
[req.params.techniqueId]
);
res.json({
success: true,
data: rules,
count: rules.length
} as ApiResponse<TechniqueValidationRule[]>);
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
} as ApiResponse<never>);
}
});
// GET stats — declarat înainte de '/:id', altfel 'stats' e capturat ca :id
router.get('/stats', async (req: Request, res: Response) => {
try {
const stats = await query<any>(`
SELECT
(SELECT COUNT(*) FROM technique) as total_techniques,
(SELECT COUNT(DISTINCT technique_id) FROM technique_validation_rule) as techniques_with_rules,
(SELECT COUNT(*) FROM technique_validation_rule) as total_rules,
(SELECT COUNT(*) FROM technique WHERE technique_id NOT IN (SELECT DISTINCT technique_id FROM technique_validation_rule)) as techniques_missing_rules
`);
res.json({
success: true,
data: stats[0]
});
} catch (error) {
internalError(res, error);
}
});
// GET single validation rule by ID
router.get('/:id', async (req: Request, res: Response) => {
try {
const rule = await queryOne<TechniqueValidationRule>(
'SELECT * FROM technique_validation_rule WHERE technique_valid_rule_id = $1',
[req.params.id]
);
if (!rule) {
return res.status(404).json({
success: false,
error: 'Regula de validare nu a fost găsită'
});
}
res.json({
success: true,
data: rule
});
} catch (error) {
internalError(res, error);
}
});
// POST - Create single validation rule
router.post('/', async (req: Request, res: Response) => {
try {
const { technique_id, rule_name, rule_value, description } = req.body;
// Validation
if (!technique_id || !rule_name || !rule_value) {
return res.status(400).json({
success: false,
error: 'Câmpuri obligatorii: technique_id, rule_name, rule_value'
});
}
// Verify technique exists
const technique = await queryOne('SELECT technique_id FROM technique WHERE technique_id = $1', [technique_id]);
if (!technique) {
return res.status(400).json({
success: false,
error: 'Tehnica specificată nu există'
});
}
const result = await transaction(async (client) => {
// Create parameter entry
const parameterId = await createParameter(client);
// Get next rule ID
const ruleId = await getNextRuleId(client);
// Insert rule
const insertResult = await client.query(`
INSERT INTO technique_validation_rule (technique_valid_rule_id, technique_id, rule_name, rule_value, description, parameter_id)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
`, [ruleId, technique_id, rule_name, rule_value, description || '', parameterId]);
return insertResult.rows[0];
});
res.status(201).json({
success: true,
data: result,
message: 'Regula de validare a fost creată cu succes'
});
} catch (error) {
internalError(res, error);
}
});
// POST /bulk-for-technique - Create multiple rules for a single technique
router.post('/bulk-for-technique/:techniqueId', async (req: Request, res: Response) => {
try {
const techniqueId = parseInt(req.params.techniqueId);
const { rules } = req.body;
if (!rules || !Array.isArray(rules) || rules.length === 0) {
return res.status(400).json({
success: false,
error: 'Câmp obligatoriu: rules (array de {rule_name, rule_value, description})'
});
}
// Verify technique exists
const technique = await queryOne('SELECT technique_id FROM technique WHERE technique_id = $1', [techniqueId]);
if (!technique) {
return res.status(400).json({
success: false,
error: 'Tehnica specificată nu există'
});
}
const result = await transaction(async (client) => {
const created: any[] = [];
for (const rule of rules) {
if (!rule.rule_name || !rule.rule_value) {
throw new Error(`Regulă invalidă: ${JSON.stringify(rule)}. Obligatoriu: rule_name, rule_value`);
}
// Create parameter entry
const parameterId = await createParameter(client);
// Get next rule ID
const ruleId = await getNextRuleId(client);
// Insert rule
const insertResult = await client.query(`
INSERT INTO technique_validation_rule (technique_valid_rule_id, technique_id, rule_name, rule_value, description, parameter_id)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
`, [ruleId, techniqueId, rule.rule_name, rule.rule_value, rule.description || '', parameterId]);
created.push(insertResult.rows[0]);
}
return created;
});
res.status(201).json({
success: true,
data: result,
count: result.length,
message: `${result.length} reguli de validare au fost create cu succes`
});
} catch (error) {
internalError(res, error);
}
});
// PUT - Update validation rule
router.put('/:id', async (req: Request, res: Response) => {
try {
const id = req.params.id;
const { technique_id, rule_name, rule_value, description } = req.body;
// Check if rule exists
const existing = await queryOne<TechniqueValidationRule>(
'SELECT * FROM technique_validation_rule WHERE technique_valid_rule_id = $1',
[id]
);
if (!existing) {
return res.status(404).json({
success: false,
error: 'Regula de validare nu a fost găsită'
});
}
// If changing technique_id, verify it exists
if (technique_id) {
const technique = await queryOne('SELECT technique_id FROM technique WHERE technique_id = $1', [technique_id]);
if (!technique) {
return res.status(400).json({
success: false,
error: 'Tehnica specificată nu există'
});
}
}
const result = await queryOne<TechniqueValidationRule>(`
UPDATE technique_validation_rule
SET technique_id = COALESCE($1, technique_id),
rule_name = COALESCE($2, rule_name),
rule_value = COALESCE($3, rule_value),
description = COALESCE($4, description)
WHERE technique_valid_rule_id = $5
RETURNING *
`, [technique_id, rule_name, rule_value, description, id]);
res.json({
success: true,
data: result,
message: 'Regula de validare a fost actualizată cu succes'
});
} catch (error) {
internalError(res, error);
}
});
// DELETE - Delete single validation rule
router.delete('/:id', async (req: Request, res: Response) => {
try {
const id = req.params.id;
// Check if rule exists
const existing = await queryOne<TechniqueValidationRule>(
'SELECT * FROM technique_validation_rule WHERE technique_valid_rule_id = $1',
[id]
);
if (!existing) {
return res.status(404).json({
success: false,
error: 'Regula de validare nu a fost găsită'
});
}
// Delete (leaf node - no children to check)
await query('DELETE FROM technique_validation_rule WHERE technique_valid_rule_id = $1', [id]);
res.json({
success: true,
message: 'Regula de validare a fost ștearsă cu succes',
deleted: true
});
} catch (error) {
internalError(res, error);
}
});
// DELETE all validation rules for a technique
router.delete('/by-technique/:techniqueId', async (req: Request, res: Response) => {
try {
const techniqueId = req.params.techniqueId;
// Verify technique exists
const technique = await queryOne('SELECT technique_id FROM technique WHERE technique_id = $1', [techniqueId]);
if (!technique) {
return res.status(404).json({
success: false,
error: 'Tehnica specificată nu există'
});
}
const result = await query<TechniqueValidationRule>(
'DELETE FROM technique_validation_rule WHERE technique_id = $1 RETURNING *',
[techniqueId]
);
res.json({
success: true,
data: result,
count: result.length,
message: `${result.length} reguli de validare au fost șterse pentru tehnica ${techniqueId}`
});
} catch (error) {
internalError(res, error);
}
});
export default router;