From 68a3b582c0f5a0c33be121ab53e5b5edac9217ef Mon Sep 17 00:00:00 2001 From: Keyrxng <106303466+Keyrxng@users.noreply.github.com> Date: Tue, 24 Sep 2024 14:44:36 +0100 Subject: [PATCH] chore: use only the timeline events --- src/helpers/task-deadline.ts | 35 ++++++++++++++------ src/helpers/task-metadata.ts | 58 ++++++++++---------------------- src/helpers/task-update.ts | 30 ++++++++--------- tests/main.test.ts | 64 +++++++++++++++++++++--------------- 4 files changed, 94 insertions(+), 93 deletions(-) diff --git a/src/helpers/task-deadline.ts b/src/helpers/task-deadline.ts index 555fb07..c7449af 100644 --- a/src/helpers/task-deadline.ts +++ b/src/helpers/task-deadline.ts @@ -3,14 +3,22 @@ import { Context } from "../types/context"; import { ListIssueForRepo } from "../types/github-types"; import { getAssigneesActivityForIssue } from "./get-assignee-activity"; +/** + * Retrieves the deadline with the threshold for the issue. + * + * Uses `startPlusLabelDuration` to set a base deadline and then checks for any activity that has happened after that. + * + * If activity if detected after the deadline, it will adjust the `deadlineWithThreshold` to the most recent activity. + * + * Recent activity is determined by the `eventWhitelist`. + */ export async function getDeadlineWithThreshold( context: Context, metadata: { - taskDeadline: string; + startPlusLabelDuration: string | null; taskAssignees: number[] | undefined; }, - issue: ListIssueForRepo, - lastCheck: DateTime + issue: ListIssueForRepo ) { const { logger, @@ -26,19 +34,18 @@ export async function getDeadlineWithThreshold( }); } - const deadline = DateTime.fromISO(metadata.taskDeadline); - const now = DateTime.now(); - - if (!deadline.isValid && !lastCheck.isValid) { - logger.error(`Invalid date found on ${issue.html_url}`); + const deadline = DateTime.fromISO(metadata.startPlusLabelDuration || issue.created_at); + if (!deadline.isValid) { + logger.error(`Invalid deadline date found on ${issue.html_url}`); return false; } + // activity which has happened after either: A) issue start + time label duration or B) just issue creation date const activity = (await getAssigneesActivityForIssue(context, issue, assigneeIds)).filter((o) => { if (!o.created_at) { return false; } - return DateTime.fromISO(o.created_at) > lastCheck; + return DateTime.fromISO(o.created_at) >= deadline; }); const filteredActivity = activity.filter((o) => { @@ -48,14 +55,22 @@ export async function getDeadlineWithThreshold( return eventWhitelist.includes(o.event); }); + // adding the buffer onto the already established issueStart + timeLabelDuration let deadlineWithThreshold = deadline.plus({ milliseconds: disqualification }); let reminderWithThreshold = deadline.plus({ milliseconds: warning }); + // if there is any activity that has happened after the deadline, we need to adjust the deadlineWithThreshold if (filteredActivity?.length) { + // use the most recent activity or the intial deadline const lastActivity = filteredActivity[0].created_at ? DateTime.fromISO(filteredActivity[0].created_at) : deadline; + if (!lastActivity.isValid) { + logger.error(`Invalid date found on last activity for ${issue.html_url}`); + return false; + } + // take the last activity and add the buffer onto it deadlineWithThreshold = lastActivity.plus({ milliseconds: disqualification }); reminderWithThreshold = lastActivity.plus({ milliseconds: warning }); } - return { deadlineWithThreshold, reminderWithThreshold, now }; + return { deadlineWithThreshold, reminderWithThreshold }; } diff --git a/src/helpers/task-metadata.ts b/src/helpers/task-metadata.ts index 5adb685..254c199 100644 --- a/src/helpers/task-metadata.ts +++ b/src/helpers/task-metadata.ts @@ -1,40 +1,22 @@ import { DateTime } from "luxon"; import { Context } from "../types/context"; -import { ListCommentsForIssue, ListForOrg, ListIssueForRepo } from "../types/github-types"; +import { ListForOrg, ListIssueForRepo } from "../types/github-types"; import ms from "ms"; -export async function getTaskMetadata( +/** + * Retrieves assignment events from the timeline of an issue and calculates the deadline based on the time label. + * + * It does not care about previous updates, comments or other events that might have happened on the issue. + * + * It returns who is assigned and the initial calculated deadline (start + time label duration). + */ +export async function getTaskAssignmentDetails( context: Context, repo: ListForOrg["data"][0], issue: ListIssueForRepo -): Promise<{ metadata: { taskDeadline: string; taskAssignees: number[] }; lastCheck: DateTime } | false> { +): Promise<{ startPlusLabelDuration: string; taskAssignees: number[] } | false> { const { logger, octokit } = context; - const comments = (await octokit.paginate(octokit.rest.issues.listComments, { - owner: repo.owner.login, - repo: repo.name, - issue_number: issue.number, - per_page: 100, - })) as ListCommentsForIssue[]; - - const botComments = comments.filter((o) => o.user?.type === "Bot"); - // Has the bot assigned them, typically via the `/start` command - const assignmentRegex = /Ubiquity - Assignment - start -/gi; - const botAssignmentComments = botComments - .filter((o) => assignmentRegex.test(o?.body || "")) - .sort((a, b) => DateTime.fromISO(b.created_at).toMillis() - DateTime.fromISO(a.created_at).toMillis()); - - // Has the bot previously reminded them? - const botFollowup = /