Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1261 Raise An Issue Not Always Sending Slack Alerts #1263

Merged
merged 2 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions database/firestore.indexes.json
Original file line number Diff line number Diff line change
Expand Up @@ -2096,6 +2096,20 @@
}
]
},
{
"collectionGroup": "bugReports",
"queryScope": "COLLECTION",
"fields": [
{
"fieldPath": "slackMessages.onCreate.sentAt",
"order": "ASCENDING"
},
{
"fieldPath": "slackMessages.onCreate.retries",
"order": "ASCENDING"
}
]
},
{
"collectionGroup": "candidates",
"queryScope": "COLLECTION",
Expand Down
113 changes: 111 additions & 2 deletions functions/actions/bugReports.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { getDocuments } from '../shared/helpers.js';
import config from '../shared/config.js';
import { getDocument, getDocuments, applyUpdates } from '../shared/helpers.js';

export default (db) => {
export default (db, firebase) => {
return {
getBugReportById,
getBugReportByRef,
getBugReportNumberFromIssueTitle,
incrementSlackRetries,
updateBugReportOnSlackSent,
getBugReportsWithFailedSendOnCreate,
updateBugReportOnCreate,
};

async function getBugReportById(id) {
return await getDocument(db.collection('bugReports').doc(id));
}

async function getBugReportByRef(referenceNumber) {
const bugReportsRef = db.collection('bugReports').where('referenceNumber', '==', referenceNumber);
let bugReports = await getDocuments(bugReportsRef);
Expand All @@ -27,4 +37,103 @@ export default (db) => {
const match = issueTitle.match(regex);
return match ? match[0] : match;
}

/**
* Get bugReports who have attempted to send at least one (but not the max number allowed) slack message
* on creation of a github issue but failed
* @returns array
*/
async function getBugReportsWithFailedSendOnCreate() {
try {
const MAX_RETRIES = config.SLACK_MAX_RETRIES ? config.SLACK_MAX_RETRIES : 3;
const bugReportsRef = db.collection('bugReports')
.where('slackMessages.onCreate.retries', '<', MAX_RETRIES)
.where('slackMessages.onCreate.sentAt', '==', null);
return await getDocuments(bugReportsRef);
} catch (error) {
console.error('Error fetching bug reports:', error);
return [];
}
}

/**
* Update the num slack retries based on the action (eg create)
* This number specifies the number of times we've tried to send the slack msg to notify us that an issue was eg created
* @param {*} bugReport
* @param {*} action
* @returns
*/
async function incrementSlackRetries(bugReport, action = 'create') {
const commands = [];
if (action === 'create') {
const bugReportAction = 'onCreate';
let retries = bugReport.slackMessages[bugReportAction].retries;
const data = bugReport;
data.slackMessages.onCreate.retries = ++retries;
const MAX_RETRIES = config.SLACK_MAX_RETRIES ? config.SLACK_MAX_RETRIES : 3;
if (retries < MAX_RETRIES) {
commands.push({
command: 'update',
ref: bugReport.ref,
data: data,
});
}
}
if (commands.length) {
// write to db
const result = await applyUpdates(db, commands);
return result;
}
return true;
}

/**
* Update the slack retry timestamp based on the action (eg create)
* This timestamp specifies the time that the slack msg was sent when the issue was eg created
* @param {*} bugReport
* @param {*} action
* @returns
*/
async function updateBugReportOnSlackSent(bugReport, action = 'create') {
const commands = [];
if (action === 'create') {
const bugReportAction = 'onCreate';
const data = bugReport;
data.slackMessages.onCreate.sentAt = firebase.firestore.FieldValue.serverTimestamp();
commands.push({
command: 'update',
ref: bugReport.ref,
data: data,
});
}
if (commands.length) {
// write to db
const result = await applyUpdates(db, commands);
return result;
}
return true;
}

/**
* When a github issue is created update the bugReport with the issue number and url to the issue
* on github
* @param {*} bugReport
* @param {*} issue
* @returns
*/
async function updateBugReportOnCreate(bugReport, issueNumber, issueUrl) {
const commands = [];
commands.push({
command: 'update',
ref: bugReport.ref,
data: {
zenhubIssueNumber: issueNumber,
githubIssueUrl: issueUrl,
lastUpdatedAt: firebase.firestore.FieldValue.serverTimestamp(),
},
});
// write to db
const result = await applyUpdates(db, commands);
return result;
}
};
23 changes: 22 additions & 1 deletion functions/actions/slack.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import initSlackWebApi from '../shared/slackWebApi.js';
import initUsers from './users.js';
import initBugReports from './bugReports.js';
import initSendSlackNotifications from './zenhub/sendNewIssueSlackNotifications.js';

export default (auth, config, db) => {
export default (auth, config, db, firebase) => {
const slackWebApi = initSlackWebApi(config);
const { updateUser } = initUsers(auth, db);
const { getBugReportsWithFailedSendOnCreate } = initBugReports(db, firebase);
const { sendNewIssueSlackNotifications } = initSendSlackNotifications(config, db, auth, firebase);

return {
lookupSlackUser,
retrySlackMessageOnCreateIssue,
};

/**
Expand All @@ -24,4 +29,20 @@ export default (auth, config, db) => {
}
return memberExists;
}

/**
* Check for slack messages which were not sent when the bugReport was created and resend them
* @param {*} slackChannelId
* @returns
*/
async function retrySlackMessageOnCreateIssue(slackChannelId) {
const bugReports = await getBugReportsWithFailedSendOnCreate();
if (bugReports.length > 0) {
for (const bugReport of bugReports) {
const result = await sendNewIssueSlackNotifications(bugReport, slackChannelId);
return result;
}
}
return true;
}
};
2 changes: 1 addition & 1 deletion functions/actions/zenhub/hooks/onAssignedIssue.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default (config, db, auth) => {
blocksArr.push(addSlackSection(issue, bugReport, assigneeUser, reporterUser));

// Send Slack msg
slack.postBotBlocksMsgToChannel(slackChannelId, blocksArr);
await slack.postBotBlocksMsgToChannel(slackChannelId, blocksArr);
}

function addSlackDivider() {
Expand Down
95 changes: 6 additions & 89 deletions functions/actions/zenhub/hooks/onCreatedIssue.js
Original file line number Diff line number Diff line change
@@ -1,105 +1,22 @@
import initSlack from '../../../shared/slack.js';
import initUsers from '../../users.js';
import initBugReports from '../../bugReports.js';

export default (config, db, auth) => {
export default (config, db, auth, firebase) => {

const slack = initSlack(config);
const { getUser } = initUsers(auth, db);
const { updateBugReportOnCreate } = initBugReports(db, firebase);

return {
onCreatedIssue,
};

/**
* Hook called when an issue is created in github
* Send a message to a Slack channel notifying the team of the bug
* Add key details to the bug report from the issue
*
* @param {object} params
* @param {object} bugReport
* @param {string} slackChannelId
*/
async function onCreatedIssue(params, bugReport, slackChannelId) {

// @TODO: May be worth putting the reporter and userId fields into one object inside the bugReport collection record!?
const reporterUser = await getUser(bugReport.userId);

const issue = {
action: params.action,
url: params.issue.html_url,
id: params.issue.id,
number: params.issue.number,
title: params.issue.title,
};

// Build the slack message
const blocksArr = [];
blocksArr.push(addSlackDivider());
blocksArr.push(addSlackSection1(issue, bugReport, reporterUser));
blocksArr.push(addSlackSection2(bugReport));
if (bugReport.candidate) {
blocksArr.push(addSlackSection3(bugReport));
}

// Send Slack msg
slack.postBotBlocksMsgToChannel(slackChannelId, blocksArr);
}

function addSlackDivider() {
return {
'type': 'divider',
};
}

function addSlackSection1(issue, data, user) {
let text = `The following *${data.criticality}* issue <${issue.url}|#${issue.number}> was raised by <@${user.slackMemberId}>`;
if (data.exercise.referenceNumber) {
text += ` for exercise: *${data.exercise.referenceNumber}*`;
}
else if (data.application.referenceNumber) {
text += ` for application: *${data.application.referenceNumber}*`;
}
return {
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': text,
},
};
}

function addSlackSection2(data) {
return {
'type': 'section',
'text': {
'type': 'mrkdwn',
'text': `Description: ${data.issue}`,
},
};
}

function addSlackSection3(data) {
let fields = [];
// fields.push( {
// 'type': 'mrkdwn',
// 'text': `*Bug Reference Number:*\n${data.referenceNumber}`,
// });
//if (data.candidate) {
fields.push({
'type': 'mrkdwn',
'text': `*Candidate:*\n${data.candidate}`,
});
fields.push({
'type': 'mrkdwn',
'text': `*CPS Device:*\n${data.cpsDevice === '1' ? 'true' : 'false'}`,
});
//}
// fields.push( {
// 'type': 'mrkdwn',
// 'text': `*Link to page:*\n<${data.url}>`,
// });
return {
'type': 'section',
'fields': fields,
};
async function onCreatedIssue(bugReport, issueNumber, issueUrl) {
await updateBugReportOnCreate(bugReport, issueNumber, issueUrl);
}
};
Loading