const { audit, json, now, one, run } = require('../db'); const { upsertEmployee } = require('./assignments'); function publicConnection(row) { if (!row) return null; return { id: row.id, name: row.name, tenant: row.tenant, base_url: row.base_url, token_url: row.token_url, client_id: row.client_id, workers_path: row.workers_path, enabled: Boolean(row.enabled), last_sync_at: row.last_sync_at, last_sync_status: row.last_sync_status, configured: Boolean(row.base_url && row.token_url && row.client_id && row.client_secret) }; } function getConnection() { return publicConnection(one('SELECT * FROM workday_connections ORDER BY id LIMIT 1')); } function getPrivateConnection() { return one('SELECT * FROM workday_connections ORDER BY id LIMIT 1'); } function saveConnection(body, userId) { const timestamp = now(); const existing = getPrivateConnection(); const payload = { name: body.name || existing?.name || 'Primary Workday', tenant: body.tenant ?? existing?.tenant ?? '', base_url: body.base_url ?? existing?.base_url ?? '', token_url: body.token_url ?? existing?.token_url ?? '', client_id: body.client_id ?? existing?.client_id ?? '', client_secret: body.client_secret ? body.client_secret : existing?.client_secret ?? '', workers_path: body.workers_path ?? existing?.workers_path ?? '/workers', enabled: body.enabled === undefined ? Number(existing?.enabled || 0) : (body.enabled ? 1 : 0) }; if (existing) { run( `UPDATE workday_connections SET name = ?, tenant = ?, base_url = ?, token_url = ?, client_id = ?, client_secret = ?, workers_path = ?, enabled = ?, updated_at = ? WHERE id = ?`, [payload.name, payload.tenant, payload.base_url, payload.token_url, payload.client_id, payload.client_secret, payload.workers_path, payload.enabled, timestamp, existing.id] ); } else { run( `INSERT INTO workday_connections ( name, tenant, base_url, token_url, client_id, client_secret, workers_path, enabled, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [payload.name, payload.tenant, payload.base_url, payload.token_url, payload.client_id, payload.client_secret, payload.workers_path, payload.enabled, timestamp, timestamp] ); } const saved = getConnection(); audit(userId, 'update', 'workday_connection', saved.id, null, { ...saved, client_secret: '[redacted]' }); return saved; } async function fetchAccessToken(connection) { const basic = Buffer.from(`${connection.client_id}:${connection.client_secret}`).toString('base64'); const response = await fetch(connection.token_url, { method: 'POST', headers: { Authorization: `Basic ${basic}`, 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'client_credentials' }) }); if (!response.ok) { throw new Error(`Workday token request failed: ${response.status}`); } const body = await response.json(); if (!body.access_token) throw new Error('Workday token response did not include an access token'); return body.access_token; } function normalizeWorkers(payload) { const rows = Array.isArray(payload) ? payload : payload.workers || payload.Workers || payload.data || payload.value || []; return rows.map((worker) => ({ workday_worker_id: worker.id || worker.workerId || worker.worker_id || worker.Worker_ID || worker.workerReference?.id, employee_number: worker.employeeNumber || worker.employee_number || worker.Worker_ID || worker.workerNumber, name: worker.name || worker.fullName || worker.Full_Name || worker.descriptor || [worker.firstName, worker.lastName].filter(Boolean).join(' '), email: worker.email || worker.workEmail || worker.primaryWorkEmail || worker.Email, department: worker.department || worker.organization || worker.Department || worker.supervisoryOrganization?.descriptor, location: worker.location || worker.workLocation || worker.Location || worker.location?.descriptor, manager_name: worker.managerName || worker.manager?.descriptor, status: worker.status || (worker.active === false ? 'inactive' : 'active'), source: 'workday', source_payload: worker, last_synced_at: now() })); } async function syncWorkers(userId) { const connection = getPrivateConnection(); if (!connection || !connection.enabled) throw Object.assign(new Error('Workday connection is not enabled'), { status: 400 }); if (!connection.base_url || !connection.token_url || !connection.client_id || !connection.client_secret) { throw Object.assign(new Error('Workday connection is missing URL or OAuth credentials'), { status: 400 }); } const token = await fetchAccessToken(connection); const url = new URL(connection.workers_path || '/workers', connection.base_url); const response = await fetch(url, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' } }); if (!response.ok) { throw new Error(`Workday workers request failed: ${response.status}`); } const workers = normalizeWorkers(await response.json()); for (const worker of workers) upsertEmployee(worker); const timestamp = now(); run('UPDATE workday_connections SET last_sync_at = ?, last_sync_status = ?, updated_at = ? WHERE id = ?', [ timestamp, `Imported ${workers.length} workers`, timestamp, connection.id ]); audit(userId, 'sync', 'workday_workers', connection.id, null, { imported: workers.length }); return { imported: workers.length, workers }; } function importWorkersFromPayload(workers, userId) { const normalized = normalizeWorkers(workers); for (const worker of normalized) upsertEmployee(worker); audit(userId, 'import', 'workday_workers', null, null, { imported: normalized.length, source: 'payload' }); return { imported: normalized.length }; } module.exports = { getConnection, importWorkersFromPayload, saveConnection, syncWorkers };