import fetch from 'node-fetch'; export function createWebexService(config, log) { const { logger, logError } = log; function handleWebexResponse(context, issueKey, response, responseText) { if (!response.ok) { logError(context, `Webex API error for ${issueKey} (HTTP ${response.status})`, responseText); return false; } logger(context, `Successfully sent notification for ${issueKey}`); return true; } function sendTicketCard(issueKey, body) { const requestOptions = { method: 'POST', headers: { Authorization: `Bearer ${config.webex.token}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), redirect: 'follow', }; fetch('https://webexapis.com/v1/messages', requestOptions) .then(response => response.text().then(text => ({ response, text }))) .then(({ response, text }) => handleWebexResponse('sendTicketCard', issueKey, response, text)) .catch(error => logError('sendTicketCard', `Network error sending to Webex for ${issueKey}`, error)); } function sendWebexMessage(body) { return new Promise(function (resolve, reject) { const requestOptions = { method: 'POST', headers: { Authorization: `Bearer ${config.webex.token}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), redirect: 'follow', }; fetch('https://webexapis.com/v1/messages', requestOptions) .then(response => response.text().then(text => ({ response, text }))) .then(({ response, text }) => { if (!response.ok) { return reject(new Error(`Webex API error (HTTP ${response.status}): ${text}`)); } try { resolve(JSON.parse(text)); } catch { reject(new Error(`Invalid JSON response from Webex: ${text}`)); } }) .catch(error => reject(error)); }); } function sendHighPriorityMessage(body) { const requestOptions = { method: 'POST', headers: { Authorization: `Bearer ${config.jiraNotifier.token}`, 'Content-Type': 'application/json', }, body: JSON.stringify(body), redirect: 'follow', }; fetch('https://webexapis.com/v1/messages', requestOptions) .then(response => response.text().then(text => ({ response, text }))) .then(({ response, text }) => handleWebexResponse('sendHighPriorityMessage', body.roomId || 'unknown', response, text)) .catch(error => logError('sendHighPriorityMessage', 'Network error sending to Webex', error)); } function processHighSeverity(jiraTicket, roomId) { const jiraKey = jiraTicket.issue.key; if (jiraTicket.webhookEvent == 'comment_created' && jiraTicket.comment) { const action = jiraTicket.comment.created == jiraTicket.comment.updated ? 'added a comment' : 'edited a comment'; const userName = jiraTicket.comment.updateAuthor.displayName; const projectKey = jiraTicket.issue.fields.project.name; const comment = jiraTicket.comment.body; sendHighPriorityMessage({ markdown: `${userName} ${action} on the [${jiraKey}](https://aeo.atlassian.net/browse/${jiraKey}) issue in the ${projectKey} project to read as follows:\n${comment}`, roomId, }); } if (jiraTicket.webhookEvent == 'jira:issue_updated' && jiraTicket.changelog) { const userName = jiraTicket.user.displayName; const statuses = ['status', 'Severity', 'priority', 'Severity (migrated)']; let text = ''; let addDescription = 0; for (const change of jiraTicket.changelog.items) { if (statuses.includes(change.field)) { text += `${userName} changed ${change.field} from ${change.fromString} to ${change.toString} for issue [${jiraKey}](https://aeo.atlassian.net/browse/${jiraKey}).\n`; } if (change.field == 'Severity' || change.field == 'priority') { addDescription = 1; } } if (addDescription > 0) { text += `Description / Details: ${jiraTicket.issue.fields.description}\n`; } log.logDebug('processHighSeverity', `Sending high-priority update for ${jiraKey}`); sendHighPriorityMessage({ markdown: text, roomId }); } } return { sendTicketCard, sendWebexMessage, sendHighPriorityMessage, processHighSeverity, }; }