Add webhook skip diagnostics for approval processing.
Log why REQUEST/CHANGE webhooks are ignored and warn when no pending approvers are found, making notification failures easier to trace. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0b056739e9
commit
449550cb54
2 changed files with 60 additions and 23 deletions
|
|
@ -25,6 +25,10 @@ export function createWebhookRoutes({ config, jiraProcessor, webexService, log }
|
||||||
}
|
}
|
||||||
|
|
||||||
safeProcess(routeName, () => {
|
safeProcess(routeName, () => {
|
||||||
|
logger(
|
||||||
|
routeName,
|
||||||
|
`Accepted webhook event=${req.body.webhookEvent || 'unknown'} issue=${validation.issueKey} project=${req.body.issue?.fields?.project?.key || '?'}`
|
||||||
|
);
|
||||||
logFile(WEBHOOK_LOG_PROVIDERS[routeName] || routeName, req.body);
|
logFile(WEBHOOK_LOG_PROVIDERS[routeName] || routeName, req.body);
|
||||||
jiraProcessor.processJiraTicket(req.body);
|
jiraProcessor.processJiraTicket(req.body);
|
||||||
}, correlationMeta);
|
}, correlationMeta);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ export function createJiraProcessor({ config, requests, jiraService, webexServic
|
||||||
}
|
}
|
||||||
if (approvers.length > 0) {
|
if (approvers.length > 0) {
|
||||||
webexService.sendTicketCard(issueKey, { roomId: approvalRoomId, ...cardPayload });
|
webexService.sendTicketCard(issueKey, { roomId: approvalRoomId, ...cardPayload });
|
||||||
|
} else {
|
||||||
|
logWarn('processJiraTicket', `${issueKey} no pending approvers found after Jira lookup — no Webex messages sent`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => logError('processJiraTicket', `Failed approval notifications for ${issueKey}`, error));
|
.catch(error => logError('processJiraTicket', `Failed approval notifications for ${issueKey}`, error));
|
||||||
|
|
@ -30,54 +32,85 @@ export function createJiraProcessor({ config, requests, jiraService, webexServic
|
||||||
}
|
}
|
||||||
|
|
||||||
const issueKey = jiraTicket.issue.key;
|
const issueKey = jiraTicket.issue.key;
|
||||||
let requestType;
|
const projectKey = jiraTicket.issue.fields?.project?.key;
|
||||||
let transitionId;
|
const requestTypeField = jiraTicket.issue.fields?.customfield_10010;
|
||||||
|
const requestType = requestTypeField?.requestType?.id != null
|
||||||
|
? String(requestTypeField.requestType.id)
|
||||||
|
: undefined;
|
||||||
|
const transitionId = jiraTicket.transition?.transitionId != null
|
||||||
|
? String(jiraTicket.transition.transitionId)
|
||||||
|
: undefined;
|
||||||
|
const webhookEvent = jiraTicket.webhookEvent || 'unknown';
|
||||||
|
|
||||||
logger('processJiraTicket', `Processing ${issueKey}.`);
|
logger(
|
||||||
|
'processJiraTicket',
|
||||||
|
`${issueKey} event=${webhookEvent} project=${projectKey || '?'} requestType=${requestType || '?'} transition=${transitionId || 'none'}`
|
||||||
|
);
|
||||||
|
|
||||||
if (jiraTicket.transition) {
|
if (projectKey !== 'CHANGE' && projectKey !== 'REQUEST') {
|
||||||
logger('processJiraTicket', `${issueKey} Transition: ${jiraTicket.transition.transitionId}.`);
|
logWarn('processJiraTicket', `${issueKey} skipped: project ${projectKey} is not CHANGE or REQUEST`);
|
||||||
const requestTypeField = jiraTicket.issue.fields?.customfield_10010;
|
|
||||||
if (requestTypeField?.requestType?.id) {
|
|
||||||
requestType = requestTypeField.requestType.id;
|
|
||||||
transitionId = jiraTicket.transition.transitionId;
|
|
||||||
} else {
|
|
||||||
logWarn('processJiraTicket', `${issueKey} - missing requestType: ${JSON.stringify(requestTypeField)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!issueKey || !requestType || !requests[requestType]) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger('processJiraTicket', `${issueKey} RequestType: ${requestType} Requests?: ${JSON.stringify(requests[requestType])}`);
|
if (!requestType) {
|
||||||
|
logWarn('processJiraTicket', `${issueKey} skipped: missing request type in customfield_10010`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (requests[requestType].transitions[transitionId]) {
|
if (!requests[requestType]) {
|
||||||
if (jiraTicket.issue.fields.project.key == 'CHANGE') {
|
logWarn(
|
||||||
logger('processJiraTicket', `${issueKey} is a CHANGE.`);
|
'processJiraTicket',
|
||||||
|
`${issueKey} skipped: request type ${requestType} (${requestTypeField.requestType?.name || 'unknown'}) is not configured in requests.json`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger('processJiraTicket', `${issueKey} matched request config: ${JSON.stringify(requests[requestType])}`);
|
||||||
|
|
||||||
|
if (transitionId && requests[requestType].transitions[transitionId]) {
|
||||||
|
logger('processJiraTicket', `${issueKey} matched transition ${transitionId} (${requests[requestType].transitions[transitionId].name})`);
|
||||||
|
if (projectKey == 'CHANGE') {
|
||||||
handleApprovalNotifications(issueKey, jiraTicket, buildChangeApproval);
|
handleApprovalNotifications(issueKey, jiraTicket, buildChangeApproval);
|
||||||
} else if (jiraTicket.issue.fields.project.key == 'REQUEST') {
|
} else {
|
||||||
logger('processJiraTicket', `${issueKey} is a REQUEST.`);
|
|
||||||
handleApprovalNotifications(issueKey, jiraTicket, buildRequestApproval);
|
handleApprovalNotifications(issueKey, jiraTicket, buildRequestApproval);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jiraTicket.changelog) {
|
if (!jiraTicket.changelog) {
|
||||||
|
if (transitionId) {
|
||||||
|
logWarn(
|
||||||
|
'processJiraTicket',
|
||||||
|
`${issueKey} skipped: transition ${transitionId} is not configured for request type ${requestType}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
logWarn('processJiraTicket', `${issueKey} skipped: no transition and no changelog in webhook`);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const approvalStatuses = ['Pending Approval', 'Emergency Approval', 'Waiting for approval'];
|
const approvalStatuses = ['Pending Approval', 'Emergency Approval', 'Waiting for approval'];
|
||||||
|
const statusName = jiraTicket.issue.fields?.status?.name;
|
||||||
|
let approverChange = false;
|
||||||
|
|
||||||
for (const change of jiraTicket.changelog.items) {
|
for (const change of jiraTicket.changelog.items) {
|
||||||
if (change.field != 'Approvers' || !approvalStatuses.includes(jiraTicket.issue.fields.status.name)) {
|
if (change.field != 'Approvers' || !approvalStatuses.includes(statusName)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (jiraTicket.issue.fields.project.key == 'CHANGE') {
|
approverChange = true;
|
||||||
|
if (projectKey == 'CHANGE') {
|
||||||
handleApprovalNotifications(issueKey, jiraTicket, buildChangeApproval);
|
handleApprovalNotifications(issueKey, jiraTicket, buildChangeApproval);
|
||||||
} else {
|
} else {
|
||||||
handleApprovalNotifications(issueKey, jiraTicket, buildRequestApproval);
|
handleApprovalNotifications(issueKey, jiraTicket, buildRequestApproval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!approverChange) {
|
||||||
|
logWarn(
|
||||||
|
'processJiraTicket',
|
||||||
|
`${issueKey} skipped: changelog did not include an Approvers update in an approval status (status=${statusName || '?'})`
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { processJiraTicket };
|
return { processJiraTicket };
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue