From 6112b53e1a911a95512a630dba2cc0322a3f7f2a Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Fri, 8 Dec 2023 13:15:48 -0300 Subject: [PATCH 1/5] Only use notifications for submission approved --- site/realm/functions/processNotifications.js | 77 ++++++++----------- .../functions/promoteSubmissionToReport.js | 8 +- 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/site/realm/functions/processNotifications.js b/site/realm/functions/processNotifications.js index 5b495d727c..c3fef8b0e1 100644 --- a/site/realm/functions/processNotifications.js +++ b/site/realm/functions/processNotifications.js @@ -271,69 +271,54 @@ exports = async function () { context.functions.execute('logRollbar', { error }); } - // Notifications to New Promotions try { // Finds all pending notifications to New Promotions - const pendingNotificationsToNewPromotions = await notificationsCollection.find({ processed: false, type: 'submission-promoted' }).toArray(); - - // Gets all incident ids from pending notifications to New Promotions - const pendingNotificationsIncidentIds = pendingNotificationsToNewPromotions.map((notification) => notification.incident_id); + const pendingNotificationsToNewPromotions = await notificationsCollection.find({ processed: false, type: 'submission-promoted' }).toArray();; if (pendingNotificationsToNewPromotions.length > 0) { result += pendingNotificationsToNewPromotions.length; - // Finds all subscriptions to New Promotions for those new incidents - const subscriptionsToNewPromotions = await subscriptionsCollection.find({ type: 'submission-promoted', incident_id: { $in: pendingNotificationsIncidentIds } }).toArray(); - - // Process subscriptions to New Incidents - if (subscriptionsToNewPromotions.length > 0) { - - const userIds = subscriptionsToNewPromotions.map((subscription) => subscription.userId); + const userIds = pendingNotificationsToNewPromotions.map((subscription) => subscription.userId); - const recipients = await getRecipients(userIds); + const recipients = await getRecipients(userIds); - for (const pendingNotification of pendingNotificationsToNewPromotions) { + for (const pendingNotification of pendingNotificationsToNewPromotions) { - // Mark the notification as processed before sending the email - await markNotificationsAsProcessed(notificationsCollection, [pendingNotification]); - - try { - const incident = await incidentsCollection.findOne({ incident_id: pendingNotification.incident_id }); - - //Send email notification - const sendEmailParams = { - recipients, - subject: 'Your submission has been approved!', - dynamicData: { - incidentId: `${incident.incident_id}`, - incidentTitle: incident.title, - incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, - incidentDescription: incident.description, - incidentDate: incident.date, - }, - templateId: 'SubmissionApproved' // Template value from function name sufix from "site/realm/functions/config.json" - }; + // Mark the notification as processed before sending the email + await markNotificationsAsProcessed(notificationsCollection, [pendingNotification]); - await context.functions.execute('sendEmail', sendEmailParams); + try { + const incident = await incidentsCollection.findOne({ incident_id: pendingNotification.incident_id }); + + //Send email notification + const sendEmailParams = { + recipients, + subject: 'Your submission has been approved!', + dynamicData: { + incidentId: `${incident.incident_id}`, + incidentTitle: incident.title, + incidentUrl: `https://incidentdatabase.ai/cite/${incident.incident_id}`, + incidentDescription: incident.description, + incidentDate: incident.date, + }, + templateId: 'SubmissionApproved' // Template value from function name sufix from "site/realm/functions/config.json" + }; + + await context.functions.execute('sendEmail', sendEmailParams); - } catch (error) { - // If there is an error sending the email > Mark the notification as not processed - await markNotificationsAsNotProcessed(notificationsCollection, [pendingNotification]); + } catch (error) { + // If there is an error sending the email > Mark the notification as not processed + await markNotificationsAsNotProcessed(notificationsCollection, [pendingNotification]); - error.message = `[Process Pending Notifications: Submission Promoted]: ${error.message}`; - context.functions.execute('logRollbar', { error }); - } + error.message = `[Process Pending Notifications: Submission Promoted]: ${error.message}`; + context.functions.execute('logRollbar', { error }); } - - console.log(`New Promotions: ${pendingNotificationsToNewPromotions.length} pending notifications were processed.`); - } - else { - // If there are no subscribers to New Incidents (edge case) > Mark all pending notifications as processed - await markNotificationsAsProcessed(notificationsCollection, pendingNotificationsToNewPromotions); } + + console.log(`New Promotions: ${pendingNotificationsToNewPromotions.length} pending notifications were processed.`); } else { console.log('Submission Promoted: No pending notifications to process.'); diff --git a/site/realm/functions/promoteSubmissionToReport.js b/site/realm/functions/promoteSubmissionToReport.js index 0029dde991..11f407a579 100644 --- a/site/realm/functions/promoteSubmissionToReport.js +++ b/site/realm/functions/promoteSubmissionToReport.js @@ -54,16 +54,12 @@ exports = async (input) => { await incidents.insertOne({ ...newIncident, incident_id: BSON.Int32(newIncident.incident_id) }); if (submission.user) { - await subscriptionsCollection.insertOne({ - type: 'submission-promoted', - incident_id: BSON.Int32(newIncident.incident_id), - userId: submission.user - }); await notificationsCollection.insertOne({ type: 'submission-promoted', incident_id: BSON.Int32(newIncident.incident_id), - processed: false + processed: false, + userId: submission.user }); } From aa7d9fc825d71fa4a4ac951766f3155a3a80a7df Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Wed, 13 Dec 2023 15:25:38 -0300 Subject: [PATCH 2/5] Fix tests to adjust to new functionality --- .../e2e/integration/apps/submitted.cy.js | 27 --------- .../unit/functions/processNotifications.cy.js | 58 +++++++------------ .../functions/promoteSubmissionToReport.cy.js | 3 +- .../submissions/SubmissionEditForm.js | 23 -------- 4 files changed, 24 insertions(+), 87 deletions(-) diff --git a/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js b/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js index ee8a00fd68..ce8dee3cdc 100644 --- a/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js +++ b/site/gatsby-site/cypress/e2e/integration/apps/submitted.cy.js @@ -171,21 +171,6 @@ describe('Submitted reports', () => { } ); - cy.conditionalIntercept( - '**/graphql', - (req) => - req.body.operationName == 'UpsertSubscription' && - req.body.variables?.query?.type === SUBSCRIPTION_TYPE.submissionPromoted, - 'UpsertSubscriptionPromoted', - { - data: { - upsertOneSubscription: { - _id: 'dummyIncidentId', - }, - }, - } - ); - cy.get('select[data-cy="promote-select"]').as('dropdown'); cy.get('@dropdown').select('Incident'); @@ -212,18 +197,6 @@ describe('Submitted reports', () => { expect(variables.subscription.userId.link).to.eq(user.userId); }); - cy.wait('@UpsertSubscriptionPromoted') - .its('request.body.variables') - .then((variables) => { - expect(variables.query.type).to.eq(SUBSCRIPTION_TYPE.submissionPromoted); - expect(variables.query.incident_id.incident_id).to.eq(182); - expect(variables.query.userId.userId).to.eq(submission.user.userId); - - expect(variables.subscription.type).to.eq(SUBSCRIPTION_TYPE.submissionPromoted); - expect(variables.subscription.incident_id.link).to.eq(182); - expect(variables.subscription.userId.link).to.eq(submission.user.userId); - }); - cy.contains( '[data-cy="toast"]', 'Successfully promoted submission to Incident 182 and Report 1565' diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications.cy.js index fea695c20b..1ea8e8f18e 100644 --- a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications.cy.js +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications.cy.js @@ -57,6 +57,7 @@ const pendingNotificationsToPromotedIncidents = [ type: SUBSCRIPTION_TYPE.submissionPromoted, incident_id: 217, processed: false, + userId: '63320ce63ec803072c9f5291', }, ]; @@ -101,14 +102,6 @@ const subscriptionsToIncidentUpdates = [ }, ]; -const subscriptionsToPromotedIncidents = [ - { - _id: '6356e39e863169c997309586', - type: SUBSCRIPTION_TYPE.submissionPromoted, - userId: '63320ce63ec803072c9f5291', - }, -]; - const recipients = [ { email: 'test1@email.com', @@ -269,17 +262,17 @@ const stubEverything = () => { .returns({ toArray: () => subscriptionsToIncidentUpdates }); } - const incidentIds = pendingNotificationsToPromotedIncidents.map( - (pendingNotification) => pendingNotification.incident_id - ); - - stub - .withArgs({ - type: SUBSCRIPTION_TYPE.submissionPromoted, - incident_id: { $in: incidentIds }, - }) - .as(`subscriptions.find("${SUBSCRIPTION_TYPE.submissionPromoted}")`) - .returns({ toArray: () => subscriptionsToPromotedIncidents }); + for (const pendingNotification of pendingNotificationsToPromotedIncidents) { + stub + .withArgs({ + type: SUBSCRIPTION_TYPE.submissionPromoted, + incident_id: pendingNotification.incident_id, + }) + .as( + `notifications.find("${SUBSCRIPTION_TYPE.submissionPromoted}", "${pendingNotification.incident_id}")` + ) + .returns({ toArray: () => pendingNotificationsToPromotedIncidents }); + } return stub; })(), @@ -443,27 +436,16 @@ describe('Functions', () => { }); it('New Promotions - Should send pending submissions promoted notifications', () => { - const { notificationsCollection, subscriptionsCollection, incidentsCollection } = - stubEverything(); + const { notificationsCollection, incidentsCollection } = stubEverything(); cy.wrap(processNotifications()).then((result) => { expect(result, 'Notifications processed count').to.be.equal(7); + expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ processed: false, type: SUBSCRIPTION_TYPE.submissionPromoted, }); - expect(subscriptionsCollection.find.getCall(5).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.submissionPromoted, - incident_id: { $in: [217] }, - }); - - for (const subscription of subscriptionsToPromotedIncidents) { - expect(global.context.functions.execute).to.be.calledWith('getUser', { - userId: subscription.userId, - }); - } - for (let i = 0; i < pendingNotificationsToPromotedIncidents.length; i++) { const pendingNotification = pendingNotificationsToPromotedIncidents[i]; @@ -471,7 +453,13 @@ describe('Functions', () => { incident_id: pendingNotification.incident_id, }); - const userIds = subscriptionsToPromotedIncidents.map((subscription) => subscription.userId); + expect(global.context.functions.execute).to.be.calledWith('getUser', { + userId: pendingNotificationsToPromotedIncidents[i].userId, + }); + + const userIds = pendingNotificationsToPromotedIncidents.map( + (subscription) => subscription.userId + ); const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); @@ -778,8 +766,6 @@ describe('Functions', () => { type: SUBSCRIPTION_TYPE.submissionPromoted, }); - expect(global.context.functions.execute).not.to.be.called; - for (let i = 0; i < pendingNotificationsToNewIncidents.length; i++) { const pendingNotification = pendingNotificationsToNewIncidents[i]; @@ -835,7 +821,7 @@ describe('Functions', () => { expect( notificationsCollection.updateOne.getCalls().length, 'Notifications marked as processed count' - ).to.be.equal(7); + ).to.be.equal(8); }); }); }); diff --git a/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js index bc100dea5b..31f0e3c064 100644 --- a/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js +++ b/site/gatsby-site/cypress/e2e/unit/functions/promoteSubmissionToReport.cy.js @@ -260,10 +260,11 @@ describe('Functions', () => { modifiedBy: submission.user, }); - expect(subscriptionsCollection.insertOne.firstCall.args[0]).to.deep.equal({ + expect(notificationsCollection.insertOne.firstCall.args[0]).to.deep.equal({ type: SUBSCRIPTION_TYPE.submissionPromoted, incident_id: 2, userId: 'user1', + processed: false, }); }); }); diff --git a/site/gatsby-site/src/components/submissions/SubmissionEditForm.js b/site/gatsby-site/src/components/submissions/SubmissionEditForm.js index 3fa9785a8e..a408c69103 100644 --- a/site/gatsby-site/src/components/submissions/SubmissionEditForm.js +++ b/site/gatsby-site/src/components/submissions/SubmissionEditForm.js @@ -39,8 +39,6 @@ const SubmissionEditForm = ({ handleSubmit, saving, setSaving, userLoading, user const localizedPath = useLocalizePath(); - const [subscribeToNewSubmissionPromotionMutation] = useMutation(UPSERT_SUBSCRIPTION); - useEffect(() => { if (!isEmpty(touched)) { setSaving(true); @@ -220,27 +218,6 @@ const SubmissionEditForm = ({ handleSubmit, saving, setSaving, userLoading, user await subscribeToNewReports(incident_id); - if (values.user) { - await subscribeToNewSubmissionPromotionMutation({ - variables: { - query: { - type: SUBSCRIPTION_TYPE.submissionPromoted, - userId: { userId: values.user.userId }, - incident_id: { incident_id: incident_id }, - }, - subscription: { - type: SUBSCRIPTION_TYPE.submissionPromoted, - userId: { - link: values.user.userId, - }, - incident_id: { - link: incident_id, - }, - }, - }, - }); - } - addToast({ message: ( From 65286b7bda3545e82676d0d2d715770f5cd323c8 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Wed, 13 Dec 2023 16:01:09 -0300 Subject: [PATCH 3/5] Update notification schema --- .../customData/notifications/relationships.json | 9 ++++++++- .../mongodb-atlas/customData/notifications/schema.json | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/site/realm/data_sources/mongodb-atlas/customData/notifications/relationships.json b/site/realm/data_sources/mongodb-atlas/customData/notifications/relationships.json index 0967ef424b..c26202cf5c 100644 --- a/site/realm/data_sources/mongodb-atlas/customData/notifications/relationships.json +++ b/site/realm/data_sources/mongodb-atlas/customData/notifications/relationships.json @@ -1 +1,8 @@ -{} +{ + "userId": { + "ref": "#/relationship/mongodb-atlas/customData/users", + "source_key": "userId", + "foreign_key": "userId", + "is_list": false + } +} diff --git a/site/realm/data_sources/mongodb-atlas/customData/notifications/schema.json b/site/realm/data_sources/mongodb-atlas/customData/notifications/schema.json index 62d7133b7a..4217efc287 100644 --- a/site/realm/data_sources/mongodb-atlas/customData/notifications/schema.json +++ b/site/realm/data_sources/mongodb-atlas/customData/notifications/schema.json @@ -14,6 +14,9 @@ }, "type": { "bsonType": "string" + }, + "userId": { + "bsonType": "string" } }, "title": "notification" From 04cdd7f9bb0c6a8a72b912d124c292d80552c403 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Tue, 2 Jan 2024 18:11:06 -0300 Subject: [PATCH 4/5] Fix tests --- .../processNewPromotionsNotifications.cy.js | 43 ++++++------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewPromotionsNotifications.cy.js b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewPromotionsNotifications.cy.js index a3f6855336..68b8d6868e 100644 --- a/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewPromotionsNotifications.cy.js +++ b/site/gatsby-site/cypress/e2e/unit/functions/processNotifications/processNewPromotionsNotifications.cy.js @@ -12,12 +12,14 @@ const pendingNotifications = [ type: SUBSCRIPTION_TYPE.submissionPromoted, incident_id: 217, processed: false, + userId: '63320ce63ec803072c9f5291', }, { _id: '63616f82d0db19c07d081401', type: SUBSCRIPTION_TYPE.submissionPromoted, incident_id: 218, processed: false, + userId: '63320ce63ec803072c9f5291', }, //Duplicated pending notification { @@ -25,6 +27,7 @@ const pendingNotifications = [ type: SUBSCRIPTION_TYPE.submissionPromoted, incident_id: 218, processed: false, + userId: '63320ce63ec803072c9f5291', }, ]; @@ -73,9 +76,7 @@ describe('Process New Promotions Pending Notifications', () => { const sendEmailCallArgs = sendEmailCalls[i].args[1]; - const userIds = subscriptions - .filter((s) => s.incident_id === pendingNotification.incident_id) - .map((subscription) => subscription.userId); + const userIds = pendingNotifications.map((n) => n.userId); const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); @@ -107,12 +108,11 @@ describe('Process New Promotions Pending Notifications', () => { }); it('New Promotions - Should send pending submissions promoted notifications', () => { - const { notificationsCollection, subscriptionsCollection, incidentsCollection } = - stubEverything({ - subscriptionType: SUBSCRIPTION_TYPE.submissionPromoted, - pendingNotifications, - subscriptions, - }); + const { notificationsCollection, incidentsCollection } = stubEverything({ + subscriptionType: SUBSCRIPTION_TYPE.submissionPromoted, + pendingNotifications, + subscriptions, + }); cy.wrap(processNotifications()).then(() => { expect(notificationsCollection.find.getCall(3).args[0]).to.deep.equal({ @@ -120,30 +120,20 @@ describe('Process New Promotions Pending Notifications', () => { type: SUBSCRIPTION_TYPE.submissionPromoted, }); - for (const subscription of subscriptions) { + for (const notification of pendingNotifications) { expect(global.context.functions.execute).to.be.calledWith('getUser', { - userId: subscription.userId, + userId: notification.userId, }); } for (let i = 0; i < uniquePendingNotifications.length; i++) { const pendingNotification = uniquePendingNotifications[i]; - expect( - subscriptionsCollection.find.getCall(i).args[0], - 'Get subscriptions for Incident' - ).to.deep.equal({ - type: SUBSCRIPTION_TYPE.submissionPromoted, - incident_id: pendingNotification.incident_id, - }); - expect(incidentsCollection.findOne.getCall(i).args[0], 'Find incident').to.deep.equal({ incident_id: pendingNotification.incident_id, }); - const userIds = subscriptions - .filter((s) => s.incident_id === pendingNotification.incident_id) - .map((subscription) => subscription.userId); + const userIds = pendingNotifications.map((n) => n.userId); const incident = incidents.find((i) => i.incident_id == pendingNotification.incident_id); @@ -176,7 +166,7 @@ describe('Process New Promotions Pending Notifications', () => { }); it('New Promotions - Should mark pending notifications as processed if there are no subscribers', () => { - const { notificationsCollection, subscriptionsCollection } = stubEverything({ + const { notificationsCollection } = stubEverything({ subscriptionType: SUBSCRIPTION_TYPE.submissionPromoted, pendingNotifications, subscriptions: [], @@ -203,16 +193,9 @@ describe('Process New Promotions Pending Notifications', () => { type: SUBSCRIPTION_TYPE.submissionPromoted, }); - expect(global.context.functions.execute).not.to.be.called; - for (let i = 0; i < uniquePendingNotifications.length; i++) { const pendingNotification = uniquePendingNotifications[i]; - expect(subscriptionsCollection.find.getCall(i).args[0]).to.deep.equal({ - type: SUBSCRIPTION_TYPE.submissionPromoted, - incident_id: pendingNotification.incident_id, - }); - expect(notificationsCollection.updateOne.getCall(i).args[0]).to.deep.equal({ _id: pendingNotification._id, }); From a4cec267757486aafd6b5afe2775cb0f226b2f35 Mon Sep 17 00:00:00 2001 From: Clara Youdale Date: Thu, 4 Jan 2024 10:45:37 -0300 Subject: [PATCH 5/5] Use unique user IDs to get recipients --- site/realm/functions/processNotifications.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/site/realm/functions/processNotifications.js b/site/realm/functions/processNotifications.js index 0f9a8c2fdd..bd458a1a79 100644 --- a/site/realm/functions/processNotifications.js +++ b/site/realm/functions/processNotifications.js @@ -4,7 +4,7 @@ const getRecipients = async (userIds) => { for (const userId of userIds) { const userResponse = await context.functions.execute('getUser', { userId }); - if (userResponse?.email && recipients.every(r => r.email !== userResponse.email)) { //Avoid adding duplicated emails + if (userResponse?.email) { recipients.push({ email: userResponse.email, userId, @@ -311,7 +311,9 @@ exports = async function () { const userIds = pendingNotificationsToNewPromotions.map((subscription) => subscription.userId); - const recipients = await getRecipients(userIds); + const uniqueUserIds = [...new Set(userIds)]; + + const recipients = await getRecipients(uniqueUserIds); let uniqueNotifications = [];