Stabilize logging, correlation IDs, service-account Jira auth, and direct approver reminder DMs while splitting the monolith into focused modules with Compose-based deployment. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
4.9 KiB
JavaScript
121 lines
4.9 KiB
JavaScript
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,
|
|
};
|
|
}
|