From 6b2b9859dd830ff2c7092089a11462b08312c881 Mon Sep 17 00:00:00 2001 From: Diana Barsan Date: Fri, 18 Oct 2024 11:28:21 +0300 Subject: [PATCH] quick fixes to rapidpro --- api/src/services/rapidpro.js | 10 ++++++++-- api/tests/mocha/services/rapidpro.spec.js | 10 ++++++++-- tests/e2e/default/sms/rapidpro.wdio-spec.js | 16 ++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/api/src/services/rapidpro.js b/api/src/services/rapidpro.js index 20fb991182d..e1703f5979b 100644 --- a/api/src/services/rapidpro.js +++ b/api/src/services/rapidpro.js @@ -57,7 +57,7 @@ const getRequestOptions = ({ apiToken, host }={}) => ({ }, }); -const remoteStatusToLocalState = (result) => result.status && STATUS_MAP[result.status]; +const remoteStatusToLocalState = (result) => (result.status && STATUS_MAP[result.status]) || STATUS_MAP.queued; /** * @typedef {Object} stateUpdate @@ -107,8 +107,14 @@ const sendMessage = (credentials, message) => { return getStateUpdate(state, message.id, result.id); }) .catch(err => { - // ignore error, sending the message will be retried later logger.error(`Error thrown when trying to send message: %o`, err); + if (err?.statusCode === 400) { + // source https://rapidpro.io/api/v2/ + // 400: The request failed due to invalid parameters. + // Do not retry with the same values, and the body of the response will contain details. + return getStateUpdate(STATUS_MAP.failed, message.id, undefined); + } + // ignore error, sending the message will be retried later }); }; diff --git a/api/tests/mocha/services/rapidpro.spec.js b/api/tests/mocha/services/rapidpro.spec.js index d7ef91d6a1f..a8f6ea22e97 100644 --- a/api/tests/mocha/services/rapidpro.spec.js +++ b/api/tests/mocha/services/rapidpro.spec.js @@ -129,20 +129,26 @@ describe('RapidPro SMS Gateway', () => { { to: 'phone2', content: 'message2', id: 'two' }, { to: 'phone3', content: 'message3', id: 'three' }, { to: 'phone4', content: 'message4', id: 'four' }, + { to: 'phone5', content: 'message5', id: 'five' }, + { to: 'phone6', content: 'message6', id: 'six' }, ]; sinon.stub(request, 'post') .withArgs(sinon.match({ body: { text: 'message1' } })).resolves({ id: 'broadcast1', status: 'queued' }) .withArgs(sinon.match({ body: { text: 'message2' } })).resolves() .withArgs(sinon.match({ body: { text: 'message3' } })).rejects({ some: 'error' }) - .withArgs(sinon.match({ body: { text: 'message4' } })).resolves({ id: 'broadcast4', status: 'queued' }); + .withArgs(sinon.match({ body: { text: 'message4' } })).resolves({ id: 'broadcast4', status: 'queued' }) + .withArgs(sinon.match({ body: { text: 'message5' } })).rejects({ statusCode: 400 }) + .withArgs(sinon.match({ body: { text: 'message6' } })).resolves({ id: 'broadcast6' }); return service.send(messages).then((result) => { - expect(request.post.callCount).to.equal(4); + expect(request.post.callCount).to.equal(6); expect(result).to.deep.equal([ { messageId: 'one', gatewayRef: 'broadcast1', state: 'received-by-gateway', details: 'Queued' }, { messageId: 'four', gatewayRef: 'broadcast4', state: 'received-by-gateway', details: 'Queued' }, + { messageId: 'five', gatewayRef: undefined, state: 'failed', details: 'Failed' }, + { messageId: 'six', gatewayRef: 'broadcast6', state: 'received-by-gateway', details: 'Queued' }, ]); }); }); diff --git a/tests/e2e/default/sms/rapidpro.wdio-spec.js b/tests/e2e/default/sms/rapidpro.wdio-spec.js index eb8424a7fcb..fd5816a026c 100644 --- a/tests/e2e/default/sms/rapidpro.wdio-spec.js +++ b/tests/e2e/default/sms/rapidpro.wdio-spec.js @@ -281,6 +281,12 @@ describe('RapidPro SMS Gateway', () => { group: 3, type: 'ANC Reminders LMP', }, + { + messages: [{ to: 'phone6', message: 'message6', uuid: 'uuid6' }], + state: 'pending', + group: 3, + type: 'ANC Reminders LMP', + }, ] }); @@ -304,7 +310,7 @@ describe('RapidPro SMS Gateway', () => { await setOutgoingKey(); await utils.saveDoc(pregnancyReportWithTasks); - await browser.waitUntil(() => broadcastsEndpointRequests.length === 4, 1200); + await browser.waitUntil(() => broadcastsEndpointRequests.length === 5, 1200); const bodies = broadcastsEndpointRequests .map(item => item[0]) @@ -314,16 +320,17 @@ describe('RapidPro SMS Gateway', () => { verifyPhoneAndMessage(bodies[1], 3); verifyPhoneAndMessage(bodies[2], 4); verifyPhoneAndMessage(bodies[3], 5); + verifyPhoneAndMessage(bodies[4], 6); const headers = broadcastsEndpointRequests.map(item => item[1]); - expect(headers.length).to.equal(4); + expect(headers.length).to.equal(5); headers.forEach(header => expect(header.authorization).to.equal(`Token ${OUTGOING_KEY}`)); }); it('should set correct states from broadcast api', async () => { await setOutgoingKey(); - const statuses = ['queued', '', 'queued', 'sent', 'failed']; + const statuses = ['queued', '', 'queued', 'sent', 'failed', undefined]; broadcastsResult = (req, res) => { const idx = req.body.text.replace('message', ''); res.json({ @@ -334,7 +341,7 @@ describe('RapidPro SMS Gateway', () => { const { rev } = await utils.saveDoc(pregnancyReportWithTasks); const revNumber = parseInt(rev); - await browser.waitUntil(() => broadcastsEndpointRequests.length === 4, 1200); + await browser.waitUntil(() => broadcastsEndpointRequests.length === 5, 1200); await utils.waitForDocRev([{ id: pregnancyReportWithTasks._id, rev: revNumber + 1 }]); const report = await utils.getDoc(pregnancyReportWithTasks._id); @@ -343,6 +350,7 @@ describe('RapidPro SMS Gateway', () => { verifyGatewayRefAndState(report.scheduled_tasks[0], 'broadcast3', 'received-by-gateway'); verifyGatewayRefAndState(report.scheduled_tasks[1], 'broadcast4', 'sent'); verifyGatewayRefAndState(report.scheduled_tasks[2], 'broadcast5', 'failed'); + verifyGatewayRefAndState(report.scheduled_tasks[3], 'broadcast6', 'received-by-gateway'); }); it('should update the states correctly from messages api', async () => {