/** * Schema Configuration routes * * Provides endpoints for configuring which object types should be synced. */ import { Router, Request, Response } from 'express'; import { logger } from '../services/logger.js'; import { requireAuth, requirePermission } from '../middleware/authorization.js'; import { schemaConfigurationService } from '../services/schemaConfigurationService.js'; import { schemaSyncService } from '../services/SchemaSyncService.js'; const router = Router(); // All routes require authentication and manage_settings permission router.use(requireAuth); router.use(requirePermission('manage_settings')); /** * GET /api/schema-configuration/stats * Get configuration statistics */ router.get('/stats', async (req: Request, res: Response) => { try { const stats = await schemaConfigurationService.getConfigurationStats(); res.json(stats); } catch (error) { logger.error('Failed to get configuration stats', error); res.status(500).json({ error: 'Failed to get configuration stats' }); } }); /** * POST /api/schema-configuration/discover * Discover and store all schemas, object types, and attributes from Jira Assets * Uses the unified SchemaSyncService */ router.post('/discover', async (req: Request, res: Response) => { try { logger.info('Schema configuration: Manual schema sync triggered'); const result = await schemaSyncService.syncAll(); if (result.schemasProcessed === 0) { logger.warn('Schema configuration: Sync returned 0 schemas - this might indicate an API issue'); res.status(400).json({ message: 'No schemas found. Please check: 1) JIRA_SERVICE_ACCOUNT_TOKEN is configured correctly, 2) Jira Assets API is accessible, 3) API endpoint /rest/assets/1.0/objectschema/list is available', ...result, }); return; } res.json({ message: 'Schema synchronization completed successfully', schemasDiscovered: result.schemasProcessed, objectTypesDiscovered: result.objectTypesProcessed, attributesDiscovered: result.attributesProcessed, ...result, }); } catch (error) { logger.error('Failed to sync schemas and object types', error); res.status(500).json({ error: 'Failed to sync schemas and object types', details: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined }); } }); /** * GET /api/schema-configuration/object-types * Get all configured object types grouped by schema */ router.get('/object-types', async (req: Request, res: Response) => { try { const schemas = await schemaConfigurationService.getConfiguredObjectTypes(); res.json({ schemas }); } catch (error) { logger.error('Failed to get configured object types', error); res.status(500).json({ error: 'Failed to get configured object types' }); } }); /** * PATCH /api/schema-configuration/object-types/:id/enabled * Enable or disable an object type */ router.patch('/object-types/:id/enabled', async (req: Request, res: Response) => { try { const id = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id; if (!id) { res.status(400).json({ error: 'id parameter required' }); return; } const { enabled } = req.body; if (typeof enabled !== 'boolean') { res.status(400).json({ error: 'enabled must be a boolean' }); return; } await schemaConfigurationService.setObjectTypeEnabled(id, enabled); res.json({ status: 'success', message: `Object type ${id} ${enabled ? 'enabled' : 'disabled'}`, }); } catch (error) { logger.error('Failed to update object type enabled status', error); res.status(500).json({ error: 'Failed to update object type enabled status' }); } }); /** * PATCH /api/schema-configuration/object-types/bulk-enabled * Bulk update enabled status for multiple object types */ router.patch('/object-types/bulk-enabled', async (req: Request, res: Response) => { try { const { updates } = req.body; if (!Array.isArray(updates)) { res.status(400).json({ error: 'updates must be an array' }); return; } // Validate each update for (const update of updates) { if (!update.id || typeof update.enabled !== 'boolean') { res.status(400).json({ error: 'Each update must have id (string) and enabled (boolean)' }); return; } } await schemaConfigurationService.bulkSetObjectTypesEnabled(updates); res.json({ status: 'success', message: `Updated ${updates.length} object types`, }); } catch (error) { logger.error('Failed to bulk update object types', error); res.status(500).json({ error: 'Failed to bulk update object types' }); } }); /** * GET /api/schema-configuration/check * Check if configuration is complete (at least one object type enabled) */ router.get('/check', async (req: Request, res: Response) => { try { const isComplete = await schemaConfigurationService.isConfigurationComplete(); const stats = await schemaConfigurationService.getConfigurationStats(); res.json({ isConfigured: isComplete, stats, }); } catch (error) { logger.error('Failed to check configuration', error); res.status(500).json({ error: 'Failed to check configuration' }); } }); /** * GET /api/schema-configuration/schemas * Get all schemas with their search enabled status */ router.get('/schemas', async (req: Request, res: Response) => { try { const schemas = await schemaConfigurationService.getSchemas(); res.json({ schemas }); } catch (error) { logger.error('Failed to get schemas', error); res.status(500).json({ error: 'Failed to get schemas' }); } }); /** * PATCH /api/schema-configuration/schemas/:schemaId/search-enabled * Enable or disable search for a schema */ router.patch('/schemas/:schemaId/search-enabled', async (req: Request, res: Response) => { try { const schemaId = req.params.schemaId; const { searchEnabled } = req.body; if (typeof searchEnabled !== 'boolean') { res.status(400).json({ error: 'searchEnabled must be a boolean' }); return; } const schemaIdStr = Array.isArray(schemaId) ? schemaId[0] : schemaId; if (!schemaIdStr) { res.status(400).json({ error: 'schemaId parameter required' }); return; } await schemaConfigurationService.setSchemaSearchEnabled(schemaIdStr, searchEnabled); res.json({ status: 'success', message: `Schema ${schemaId} search ${searchEnabled ? 'enabled' : 'disabled'}`, }); } catch (error) { logger.error('Failed to update schema search enabled status', error); res.status(500).json({ error: 'Failed to update schema search enabled status' }); } }); export default router;