Skip to content

Commit 158c575

Browse files
authored
Merge ce2c80c into 26bdd0b
2 parents 26bdd0b + ce2c80c commit 158c575

File tree

2 files changed

+149
-11
lines changed

2 files changed

+149
-11
lines changed

src/server/handlers/notificationHandler.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,13 @@ async function onReceiveNotification(subscription, messageTime) {
4343
if (subscription.rcWebhookList && subscription.rcWebhookList.length > 0) {
4444
await Promise.all(subscription.rcWebhookList.map(async rcWebhook => {
4545
try {
46-
await sendAdaptiveCardMessage(rcWebhook.uri, messageCard);
47-
// TODO: make webhook inactive when webhook is not found
48-
// if (response.data.error && response.data.error.indexOf('Webhook not found') >= -1) {
49-
// }
46+
const response = await sendAdaptiveCardMessage(rcWebhook.uri, messageCard);
47+
if (response.data.error && response.data.error.indexOf('Webhook not found') >= -1) {
48+
subscription.rcWebhookList = [{
49+
...rcWebhook,
50+
active: false,
51+
}].concat(subscription.rcWebhookList.filter((webhook) => webhook.id !== rcWebhook.id));
52+
}
5053
} catch (e) {
5154
console.error('Error sending message to RC Webhook:');
5255
console.error(e);
@@ -59,8 +62,20 @@ async function onReceiveNotification(subscription, messageTime) {
5962
);
6063
}
6164
}));
62-
subscription.messageReceivedAt = new Date(messageTime);
63-
await subscription.save();
65+
if (
66+
subscription.rcWebhookList &&
67+
subscription.rcWebhookList.length > 0 &&
68+
subscription.rcWebhookList.filter(rcWebhook => rcWebhook.active).length === 0
69+
) {
70+
// webhook is deleted
71+
await googleClient.deleteWatch(subscription.formId, subscription.id);
72+
await subscription.destroy();
73+
user.subscriptions = user.subscriptions.filter(sub => sub.id !== subscription.id);
74+
await user.save();
75+
} else {
76+
subscription.messageReceivedAt = new Date(messageTime);
77+
await subscription.save();
78+
}
6479
}
6580

6681
exports.onReceiveNotification = onReceiveNotification;

tests/notification.test.js

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('Notification', () => {
2424
const mockMessagePublishTime = '2021-03-31T01:34:08.053Z';
2525
let subscription;
2626

27-
beforeAll(async () => {
27+
beforeEach(async () => {
2828
// Mock data on subscriptions table
2929
subscription = await Subscription.create({
3030
id: mockWatchId,
@@ -38,7 +38,7 @@ describe('Notification', () => {
3838
});
3939
});
4040

41-
afterAll(async () => {
41+
afterEach(async () => {
4242
// Clean up
4343
await Subscription.destroy({
4444
where: {
@@ -254,10 +254,10 @@ describe('Notification', () => {
254254
...subscription.rcWebhookList,
255255
{
256256
id: 'otherRcWebhookId',
257-
uri: `${mockDomain}/webhook/${mockRCWebhookId}`,
257+
uri: `${mockDomain}/webhook/otherRcWebhookId`,
258258
active: true,
259259
},
260-
]
260+
];
261261
await subscription.save();
262262
const googleFormScope = nock('https://forms.googleapis.com')
263263
.get(`/v1/forms/${mockFormId}`)
@@ -269,8 +269,58 @@ describe('Notification', () => {
269269
.post(mockRcWebhookEndpoint)
270270
.reply(200, { result: 'OK' });
271271
const webhookScope1 = nock(mockDomain)
272-
.post(`/webhook/${mockRCWebhookId}`)
272+
.post(`/webhook/otherRcWebhookId`)
273+
.reply(200, { result: 'OK' });
274+
let requestBody = null;
275+
webhookScope.once('request', ({ headers: requestHeaders }, interceptor, reqBody) => {
276+
requestBody = JSON.parse(reqBody);
277+
});
278+
let requestBody1 = null;
279+
webhookScope1.once('request', ({ headers: requestHeaders }, interceptor, reqBody) => {
280+
requestBody1 = JSON.parse(reqBody);
281+
});
282+
const res = await request(server).post('/notification').send({
283+
message: {
284+
attributes: {
285+
watchId: mockWatchId,
286+
},
287+
publishTime: mockMessagePublishTime,
288+
},
289+
});
290+
expect(res.status).toEqual(200);
291+
expect(res.body.result).toEqual('ok');
292+
expect(requestBody.attachments[0].type).toContain('AdaptiveCard');
293+
expect(requestBody1.attachments[0].type).toContain('AdaptiveCard');
294+
const newSubscription = await Subscription.findByPk(mockWatchId);
295+
expect((new Date(newSubscription.messageReceivedAt)).getTime()).toEqual((new Date(mockMessagePublishTime)).getTime());
296+
googleFormScope.done();
297+
googleFormResponseScope.done();
298+
webhookScope.done();
299+
webhookScope1.done();
300+
});
301+
302+
it('should set webhook inactive when webhook response with not found', async () => {
303+
subscription.rcWebhookList = [
304+
...subscription.rcWebhookList,
305+
{
306+
id: 'otherRcWebhookId',
307+
uri: `${mockDomain}/webhook/otherRcWebhookId`,
308+
active: true,
309+
},
310+
]
311+
await subscription.save();
312+
const googleFormScope = nock('https://forms.googleapis.com')
313+
.get(`/v1/forms/${mockFormId}`)
314+
.reply(200, formData);
315+
const googleFormResponseScope = nock('https://forms.googleapis.com')
316+
.get(uri => uri.includes(`/v1/forms/${mockFormId}/responses`))
317+
.reply(200, formResponsesData);
318+
const webhookScope = nock(mockDomain)
319+
.post(mockRcWebhookEndpoint)
273320
.reply(200, { result: 'OK' });
321+
const webhookScope1 = nock(mockDomain)
322+
.post(`/webhook/otherRcWebhookId`)
323+
.reply(200, { error: `Webhook not found! otherRcWebhookId` });
274324
let requestBody = null;
275325
webhookScope.once('request', ({ headers: requestHeaders }, interceptor, reqBody) => {
276326
requestBody = JSON.parse(reqBody);
@@ -293,10 +343,83 @@ describe('Notification', () => {
293343
expect(requestBody1.attachments[0].type).toContain('AdaptiveCard');
294344
const newSubscription = await Subscription.findByPk(mockWatchId);
295345
expect((new Date(newSubscription.messageReceivedAt)).getTime()).toEqual((new Date(mockMessagePublishTime)).getTime());
346+
expect(newSubscription.rcWebhookList.length).toEqual(2);
347+
expect(newSubscription.rcWebhookList.find(item => item.id === 'otherRcWebhookId').active).toEqual(false);
348+
googleFormScope.done();
349+
googleFormResponseScope.done();
350+
webhookScope.done();
351+
webhookScope1.done();
352+
});
353+
354+
it('should destroy subscription when all webhooks inactive', async () => {
355+
subscription.rcWebhookList = [
356+
...subscription.rcWebhookList,
357+
{
358+
id: 'otherRcWebhookId',
359+
uri: `${mockDomain}/webhook/otherRcWebhookId`,
360+
active: true,
361+
},
362+
]
363+
await subscription.save();
364+
user.subscriptions = [{
365+
id: mockWatchId,
366+
rcWebhookId: mockRCWebhookId,
367+
formId: mockFormId,
368+
}, {
369+
id: mockWatchId,
370+
rcWebhookId: 'otherRcWebhookId',
371+
formId: mockFormId,
372+
}, {
373+
id: 'otherWatchId',
374+
rcWebhookId: mockRCWebhookId,
375+
formId: 'otherFormId',
376+
}];
377+
await user.save();
378+
const googleFormScope = nock('https://forms.googleapis.com')
379+
.get(`/v1/forms/${mockFormId}`)
380+
.reply(200, formData);
381+
const googleFormResponseScope = nock('https://forms.googleapis.com')
382+
.get(uri => uri.includes(`/v1/forms/${mockFormId}/responses`))
383+
.reply(200, formResponsesData);
384+
const googleFormWatchDeleteScope = nock('https://forms.googleapis.com')
385+
.delete(`/v1/forms/${mockFormId}/watches/${mockWatchId}`)
386+
.reply(200);
387+
const webhookScope = nock(mockDomain)
388+
.post(mockRcWebhookEndpoint)
389+
.reply(200, { error: `Webhook not found! ${mockRCWebhookId}` });
390+
const webhookScope1 = nock(mockDomain)
391+
.post(`/webhook/otherRcWebhookId`)
392+
.reply(200, { error: `Webhook not found! otherRcWebhookId` });
393+
let requestBody = null;
394+
webhookScope.once('request', ({ headers: requestHeaders }, interceptor, reqBody) => {
395+
requestBody = JSON.parse(reqBody);
396+
});
397+
let requestBody1 = null;
398+
webhookScope1.once('request', ({ headers: requestHeaders }, interceptor, reqBody) => {
399+
requestBody1 = JSON.parse(reqBody);
400+
});
401+
const res = await request(server).post('/notification').send({
402+
message: {
403+
attributes: {
404+
watchId: mockWatchId,
405+
},
406+
publishTime: mockMessagePublishTime,
407+
},
408+
});
409+
expect(res.status).toEqual(200);
410+
expect(res.body.result).toEqual('ok');
411+
expect(requestBody.attachments[0].type).toContain('AdaptiveCard');
412+
expect(requestBody1.attachments[0].type).toContain('AdaptiveCard');
413+
const newSubscription = await Subscription.findByPk(mockWatchId);
414+
expect(!!newSubscription).toEqual(false);
415+
const newUser = await User.findByPk(mockUserId);
416+
expect(newUser.subscriptions.length).toEqual(1);
417+
expect(newUser.subscriptions[0].id).toEqual('otherWatchId');
296418
googleFormScope.done();
297419
googleFormResponseScope.done();
298420
webhookScope.done();
299421
webhookScope1.done();
422+
googleFormWatchDeleteScope.done();
300423
});
301424

302425
it('should send card to webhook uri when rcWebhookList is empty', async () => {

0 commit comments

Comments
 (0)