Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(#9467): quick fixes to RapidPro #9559

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions api/src/services/rapidpro.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
});
};

Expand Down
10 changes: 8 additions & 2 deletions api/tests/mocha/services/rapidpro.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
]);
});
});
Expand Down
16 changes: 12 additions & 4 deletions tests/e2e/default/sms/rapidpro.wdio-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
]
});

Expand All @@ -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])
Expand All @@ -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({
Expand All @@ -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);
Expand All @@ -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 () => {
Expand Down