Skip to content

Commit

Permalink
chore: rework Nagware to send emails and/or Slack notifications on ev…
Browse files Browse the repository at this point in the history
…ery valid detection but at a decreased cadence
  • Loading branch information
craigzour committed Feb 7, 2024
1 parent 4bd949c commit 3b8b332
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 46 deletions.
4 changes: 2 additions & 2 deletions aws/lambdas/cloudwatch.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ resource "aws_cloudwatch_event_rule" "form_archiver_lambda_trigger" {

resource "aws_cloudwatch_event_rule" "nagware_lambda_trigger" {
name = "nagware-lambda-trigger"
description = "Fires every business day at 5am EST"
schedule_expression = "cron(0 10 ? * MON-FRI *)" # 5 AM EST = 10 AM UTC ; every Monday through Friday
description = "Fires every Tuesday, Thursday and Sunday at 5am EST"
schedule_expression = "cron(0 10 * * TUE,THU,SUN *)" # 5 AM EST = 10 AM UTC ; every Tuesday, Thursday and Sunday
}

resource "aws_cloudwatch_event_target" "reliability_dlq_lambda_trigger" {
Expand Down
106 changes: 64 additions & 42 deletions aws/lambdas/code/nagware/nagware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,45 @@ import { getTemplateInfo } from "./lib/templates.js";
import { notifyFormOwner } from "./lib/emailNotification.js";
import { Handler } from "aws-lambda";

const ENABLED_IN_STAGING = true;
type NotificationSettings = {
shouldSendEmail: boolean;
shouldSendSlackNotification: boolean;
};

type NagwareDetection = {
formTimestamp: number;
formId: string;
formName: string;
owners: { name: string; email: string; }[]
};

export const handler: Handler = async () => {
try {
const oldestFormResponseByFormID = await findOldestFormResponseByFormID();

const isSunday = new Date().getDay() == 0; // 0 is Sunday

await nagOrDelete(oldestFormResponseByFormID, { shouldSendEmail: isSunday == false, shouldSendSlackNotification: isSunday });

return {
statusCode: "SUCCESS",
};
} catch (error) {
// Error Message will be sent to slack
console.error(
JSON.stringify({
level: "error",
msg: "Failed to run Nagware.",
error: (error as Error).message,
})
);

return {
statusCode: "ERROR",
error: (error as Error).message,
};
}
};

async function findOldestFormResponseByFormID() {
const unsavedFormResponses = await retrieveFormResponsesOver28DaysOld("New");
Expand All @@ -28,35 +66,24 @@ async function findOldestFormResponseByFormID() {
return Object.values(reduceResult);
}

async function nagOrDelete(oldestFormResponseByFormID: { formID: string; createdAt: number }[]) {
async function nagOrDelete(oldestFormResponseByFormID: { formID: string; createdAt: number }[], notificationSettings: NotificationSettings) {
for (const formResponse of oldestFormResponseByFormID) {
const diffMs = Math.abs(Date.now() - formResponse.createdAt);
const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));

try {
const templateInfo = await getTemplateInfo(formResponse.formID);

if (templateInfo.isPublished) {
if (diffDays > 45) {
console.warn(
JSON.stringify({
level: "warn",
msg: `
*Form*\n
Identifier: ${formResponse.formID}\n
Name: ${templateInfo.formName}
\n*Owner(s)*\n
${templateInfo.owners.map((owner) => `${owner.name} (${owner.email})`).join("\n")}
\n*Oldest response*\n
${diffDays} days since submission
`,
})
);
} else {
if (notificationSettings.shouldSendEmail) {
for (const owner of templateInfo.owners) {
await notifyFormOwner(formResponse.formID, templateInfo.formName, owner.email);
}
}

logNagwareDetection({
formTimestamp: formResponse.createdAt,
formId: formResponse.formID,
formName: templateInfo.formName,
owners: templateInfo.owners,
}, notificationSettings.shouldSendSlackNotification);
} else {
// Delete form response if form is not published and older than 28 days
await deleteOldTestResponses(formResponse.formID);
Expand All @@ -75,29 +102,24 @@ ${diffDays} days since submission
}
}

export const handler: Handler = async () => {
try {
if (!ENABLED_IN_STAGING && process.env.ENVIRONMENT === "staging") return;
function logNagwareDetection(nagwareDetection: NagwareDetection, shouldSendSlackNotification: boolean) {
const diffMs = Math.abs(Date.now() - nagwareDetection.formTimestamp);
const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));

const oldestFormResponseByFormID = await findOldestFormResponseByFormID();
await nagOrDelete(oldestFormResponseByFormID);

return {
statusCode: "SUCCESS",
};
} catch (error) {
// Error Message will be sent to slack
console.error(
if (diffDays > 45) {
console.warn(
JSON.stringify({
level: "error",
msg: "Failed to run Nagware.",
error: (error as Error).message,
level: shouldSendSlackNotification ? "warn" : "info",
msg: `
*Form*\n
Identifier: ${nagwareDetection.formId}\n
Name: ${nagwareDetection.formName}
\n*Owner(s)*\n
${nagwareDetection.owners.map((owner) => `${owner.name} (${owner.email})`).join("\n")}
\n*Oldest response*\n
${diffDays} days since submission
`,
})
);

return {
statusCode: "ERROR",
error: (error as Error).message,
};
}
};
}
3 changes: 1 addition & 2 deletions aws/lambdas/nagware.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ resource "aws_lambda_function" "nagware" {
source_code_hash = data.archive_file.nagware_code.output_base64sha256

runtime = "nodejs18.x"

environment {
variables = {
ENVIRONMENT = var.env
Expand All @@ -38,7 +38,6 @@ resource "aws_lambda_function" "nagware" {
DB_NAME = var.rds_db_name
NOTIFY_API_KEY = var.notify_api_key_secret_arn
TEMPLATE_ID = var.gc_template_id
SNS_ERROR_TOPIC_ARN = var.sns_topic_alert_critical_arn
LOCALSTACK = var.localstack_hosted
}
}
Expand Down

0 comments on commit 3b8b332

Please sign in to comment.