-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #95 from Plant-for-the-Planet-org/feature/stop-dup…
…licate-alerts Feature/stop duplicate alerts in sms
- Loading branch information
Showing
6 changed files
with
162 additions
and
46 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
apps/server/prisma/migrations/20231027000000_stopAlertUntil_and_lastSMSCreated/migration.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
-- Add stopAlertUntil field to Site table | ||
ALTER TABLE "Site" | ||
ADD COLUMN "stopAlertUntil" TIMESTAMP(3); | ||
|
||
-- Add lastMessageCreated field to Site table | ||
ALTER TABLE "Site" | ||
ADD COLUMN "lastMessageCreated" TIMESTAMP(3); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 74 additions & 21 deletions
95
apps/server/src/Services/Notifications/CreateNotifications.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,85 @@ | ||
import {Prisma} from '@prisma/client'; | ||
import {prisma} from '../../server/db'; | ||
|
||
// Logic | ||
// Execute SQL | ||
|
||
// 1. Join SiteAlert with alertMethod using site and userId: | ||
// 1. **get the oldest unique unprocessed alert for all sites** | ||
// 1. Create notifications for alertMethods | ||
// 1. Create notifications for ‘device’ and ‘webhook’ | ||
// 2. if `site.lastMessageCreated = NULL` or `site.lastMessageCreated < 2 hours ago`create notifications for ‘sms’, ‘whatsapp’, and ‘email’ | ||
// 1. set `site.lastMessageCreated = now` | ||
// 2. For all processed siteAlerts , set `siteAlert.isProcessed` to true | ||
|
||
// Repeat until nothing left to process | ||
|
||
const createNotifications = async () => { | ||
let notificationsCreated = 0; | ||
let totalSiteAlertProcessed = 0; | ||
try { | ||
// In this query, the subquery retrieves all enabled and verified AlertMethods (m) for the user associated with the site. | ||
// Then, a cross join is performed between the SiteAlert table (a) and the AlertMethod subquery (m), ensuring that each siteAlert is paired with all relevant alertMethods. | ||
const notificationCreationQuery = Prisma.sql` | ||
INSERT INTO "Notification" (id, "siteAlertId", "alertMethod", destination, "isDelivered") | ||
SELECT gen_random_uuid(), a.id, m.method, m.destination, false | ||
FROM "SiteAlert" a | ||
INNER JOIN "Site" s ON a."siteId" = s.id | ||
INNER JOIN "AlertMethod" m ON m."userId" = s."userId" | ||
WHERE a."isProcessed" = false AND a."deletedAt" IS NULL AND m."deletedAt" IS NULL AND m."isEnabled" = true AND m."isVerified" = true AND a."eventDate" > CURRENT_TIMESTAMP - INTERVAL '24 hours'`; | ||
|
||
const updateSiteAlertIsProcessedToTrue = Prisma.sql`UPDATE "SiteAlert" SET "isProcessed" = true WHERE "isProcessed" = false AND "deletedAt" IS NULL`; | ||
|
||
// Create Notifications for all unprocessed SiteAlerts and Set all SiteAlert as processed | ||
const results = await prisma.$transaction([ | ||
prisma.$executeRaw(notificationCreationQuery), | ||
prisma.$executeRaw(updateSiteAlertIsProcessedToTrue), | ||
]); | ||
// Since $executeRaw() returns the number of rows affected, the first result of the transaction would be notificationsCreated | ||
notificationsCreated = results[0]; | ||
let moreAlertsToProcess = true; | ||
|
||
while(moreAlertsToProcess){ | ||
const notificationCreationAndUpdate = Prisma.sql` | ||
WITH NotificationsForInsert AS ( | ||
SELECT | ||
a.id AS "siteAlertId", | ||
m.method AS "alertMethod", | ||
m.destination, | ||
a."siteId" | ||
FROM "AlertMethod" m | ||
INNER JOIN "Site" s ON s."userId" = m."userId" | ||
INNER JOIN ( | ||
SELECT DISTINCT ON ("siteId") * | ||
FROM "SiteAlert" | ||
WHERE "isProcessed" = false AND "deletedAt" IS NULL | ||
ORDER BY "siteId", "eventDate" | ||
) a ON a."siteId" = s.id | ||
WHERE | ||
m."deletedAt" IS NULL | ||
AND m."isEnabled" = true | ||
AND m."isVerified" = true | ||
AND ( | ||
((s."lastMessageCreated" IS NULL OR s."lastMessageCreated" < (CURRENT_TIMESTAMP - INTERVAL '2 hours')) AND m."method" IN ('sms', 'whatsapp', 'email')) | ||
OR m."method" IN ('device', 'webhook') | ||
) | ||
), | ||
InsertedNotifications AS ( | ||
INSERT INTO "Notification" (id, "siteAlertId", "alertMethod", destination, "isDelivered") | ||
SELECT | ||
gen_random_uuid(), | ||
"siteAlertId", | ||
"alertMethod", | ||
destination, | ||
false | ||
FROM NotificationsForInsert | ||
RETURNING "siteAlertId", "alertMethod" | ||
), | ||
UpdatedSites AS ( | ||
UPDATE "Site" | ||
SET "lastMessageCreated" = CURRENT_TIMESTAMP | ||
WHERE "id" IN ( | ||
SELECT "siteId" FROM NotificationsForInsert WHERE "alertMethod" IN ('sms', 'whatsapp', 'email') | ||
) | ||
RETURNING "id" | ||
) | ||
UPDATE "SiteAlert" | ||
SET "isProcessed" = true | ||
WHERE "id" IN (SELECT "siteAlertId" FROM InsertedNotifications);`; | ||
|
||
const siteAlertProcessed = await prisma.$executeRaw(notificationCreationAndUpdate); | ||
totalSiteAlertProcessed += siteAlertProcessed | ||
|
||
if(siteAlertProcessed === 0){ | ||
moreAlertsToProcess = false; // No more alerts to process, exit the loop | ||
}else { | ||
await new Promise(resolve => setTimeout(resolve, 200)); // Delay of 1/5 second | ||
} | ||
} | ||
} catch (error) { | ||
console.log(error); | ||
} | ||
return notificationsCreated; | ||
return totalSiteAlertProcessed; | ||
}; | ||
|
||
export default createNotifications; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35c8a77
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
fire-alert – ./
fa.pp.eco
fire-alert.vercel.app
fire-alert-planetapp.vercel.app
firealert.plant-for-the-planet.org
fire-alert-git-develop-planetapp.vercel.app