/** * Mappings Routes * Manage value mappings */ const express = require('express'); module.exports = (pool) => { const router = express.Router(); // List all mappings for a source router.get('/source/:source_name', async (req, res, next) => { try { const { rule_name } = req.query; let query = 'SELECT * FROM mappings WHERE source_name = $1'; const params = [req.params.source_name]; if (rule_name) { query += ' AND rule_name = $2'; params.push(rule_name); } query += ' ORDER BY rule_name, input_value'; const result = await pool.query(query, params); res.json(result.rows); } catch (err) { next(err); } }); // Get unmapped values router.get('/source/:source_name/unmapped', async (req, res, next) => { try { const { rule_name } = req.query; const result = await pool.query( 'SELECT * FROM get_unmapped_values($1, $2)', [req.params.source_name, rule_name || null] ); res.json(result.rows); } catch (err) { next(err); } }); // Get single mapping router.get('/:id', async (req, res, next) => { try { const result = await pool.query( 'SELECT * FROM mappings WHERE id = $1', [req.params.id] ); if (result.rows.length === 0) { return res.status(404).json({ error: 'Mapping not found' }); } res.json(result.rows[0]); } catch (err) { next(err); } }); // Create mapping router.post('/', async (req, res, next) => { try { const { source_name, rule_name, input_value, output } = req.body; if (!source_name || !rule_name || !input_value || !output) { return res.status(400).json({ error: 'Missing required fields: source_name, rule_name, input_value, output' }); } const result = await pool.query( `INSERT INTO mappings (source_name, rule_name, input_value, output) VALUES ($1, $2, $3, $4) RETURNING *`, [source_name, rule_name, JSON.stringify(input_value), JSON.stringify(output)] ); res.status(201).json(result.rows[0]); } catch (err) { if (err.code === '23505') { // Unique violation return res.status(409).json({ error: 'Mapping already exists' }); } if (err.code === '23503') { // Foreign key violation return res.status(404).json({ error: 'Source or rule not found' }); } next(err); } }); // Bulk create mappings router.post('/bulk', async (req, res, next) => { const client = await pool.connect(); try { const { mappings } = req.body; if (!Array.isArray(mappings)) { return res.status(400).json({ error: 'Expected array of mappings' }); } await client.query('BEGIN'); const results = []; for (const mapping of mappings) { const { source_name, rule_name, input_value, output } = mapping; const result = await client.query( `INSERT INTO mappings (source_name, rule_name, input_value, output) VALUES ($1, $2, $3, $4) ON CONFLICT (source_name, rule_name, input_value) DO UPDATE SET output = EXCLUDED.output RETURNING *`, [source_name, rule_name, JSON.stringify(input_value), JSON.stringify(output)] ); results.push(result.rows[0]); } await client.query('COMMIT'); res.status(201).json({ count: results.length, mappings: results }); } catch (err) { await client.query('ROLLBACK'); next(err); } finally { client.release(); } }); // Update mapping router.put('/:id', async (req, res, next) => { try { const { input_value, output } = req.body; const result = await pool.query( `UPDATE mappings SET input_value = COALESCE($2, input_value), output = COALESCE($3, output) WHERE id = $1 RETURNING *`, [req.params.id, input_value, output ? JSON.stringify(output) : null] ); if (result.rows.length === 0) { return res.status(404).json({ error: 'Mapping not found' }); } res.json(result.rows[0]); } catch (err) { next(err); } }); // Delete mapping router.delete('/:id', async (req, res, next) => { try { const result = await pool.query( 'DELETE FROM mappings WHERE id = $1 RETURNING id', [req.params.id] ); if (result.rows.length === 0) { return res.status(404).json({ error: 'Mapping not found' }); } res.json({ success: true, deleted: result.rows[0].id }); } catch (err) { next(err); } }); return router; };