Verified Commit e172e9f4 authored by aguestuser's avatar aguestuser

[215] fix localization bug in `phoneNumberRegistrar.destroy`

* was sending destruction notification in default lang (EN) to all recipients
* should send notification in reach recipient's lang
* to fix, provide new impl of `notifyMembersExcept`
parent f336460c
import { Op } from 'sequelize'
const app = require('../../../app')
const { Op } = require('sequelize')
const { loggerOf } = require('../../util')
const { memberTypes } = require('./membership')
const { map } = require('lodash')
const {
signal: { supportPhoneNumber },
} = require('../../config')
......@@ -94,6 +94,10 @@ const isSysadmin = async phoneNumber => {
// all selectors assume you are operating on an already deeply-fetched channel (with all nested attrs avail)
const getMemberPhoneNumbers = channel => (channel.memberships || []).map(m => m.memberPhoneNumber)
const getMembersExcept = (channel, members) => {
const phoneNumbersToExclude = new Set(map(members, 'memberPhoneNumber'))
return channel.memberships.filter(m => !phoneNumbersToExclude.has(m.memberPhoneNumber))
}
const getMemberPhoneNumbersExcept = (channel, phoneNumbers) =>
getMemberPhoneNumbers(channel).filter(pn => !phoneNumbers.includes(pn))
......@@ -121,6 +125,7 @@ module.exports = {
getAdminMemberships,
getAdminPhoneNumbers,
getMemberPhoneNumbers,
getMembersExcept,
getMemberPhoneNumbersExcept,
getSubscriberMemberships,
getSubscriberPhoneNumbers,
......
......@@ -14,11 +14,10 @@ const notificationKeys = {
CHANNEL_REDEEMED: 'channelRedeemed',
}
// (Database, Socket, Channel, String, String) -> Promise<Array<string>>
const notifyMembersExcept = async (channel, message, sender) => {
if (channel == null) return
const memberPhoneNumbers = channelRepository.getMemberPhoneNumbersExcept(channel, [sender])
await signal.broadcastMessage(memberPhoneNumbers, sdMessageOf(channel, message))
// (Channel, String, String) -> Promise<Array<string>>
const notifyMembersExcept = async (channel, sender, notificationKey) => {
const recipients = channelRepository.getMembersExcept(channel, [sender])
return _notifyMany(channel, notificationKey, recipients)
}
// (string) -> Promise<Array<string>>
......
......@@ -3,12 +3,14 @@ const phoneNumberRepository = require('../../db/repositories/phoneNumber')
const { defaultLanguage } = require('../../config')
const common = require('./common')
const notifier = require('../../notifier')
const { notificationKeys } = notifier
const signal = require('../../signal')
const { messagesIn } = require('../../dispatcher/strings/messages')
const channelRepository = require('../../db/repositories/channel')
const eventRepository = require('../../db/repositories/event')
const logger = require('../logger')
const fs = require('fs-extra')
const { eventTypes } = require('../../db/models/event')
const {
signal: { keystorePath },
......@@ -26,17 +28,13 @@ const destroy = async ({ phoneNumber, sender }) => {
if (phoneNumberRecord) {
await phoneNumberRepository.destroy(phoneNumber, tx)
await releasePhoneNumber(phoneNumber)
await releasePhoneNumber(phoneNumberRecord)
}
if (channel) {
await channelRepository.destroy(channel.phoneNumber, tx)
await eventRepository.log(eventTypes.CHANNEL_DESTROYED, phoneNumber, tx)
await notifier.notifyMembersExcept(
channel,
messagesIn(defaultLanguage).notifications.channelDestroyed,
sender,
)
await notifier.notifyMembersExcept(channel, sender, notificationKeys.CHANNEL_DESTROYED)
}
await signal.unsubscribe(phoneNumber)
......@@ -52,7 +50,7 @@ const destroy = async ({ phoneNumber, sender }) => {
// HELPERS
// (DB, string) -> Promise<void>
// (string) -> Promise<void>
const deleteSignalKeystore = async phoneNumber => {
try {
await fs.remove(`${keystorePath}/${phoneNumber}`)
......@@ -63,10 +61,10 @@ const deleteSignalKeystore = async phoneNumber => {
}
// (PhoneNumberInstance) -> Promise<void>
const releasePhoneNumber = async phoneNumber => {
const releasePhoneNumber = async phoneNumberRecord => {
try {
const twilioClient = common.getTwilioClient()
await twilioClient.incomingPhoneNumbers(phoneNumber.twilioSid).remove()
await twilioClient.incomingPhoneNumbers(phoneNumberRecord.twilioSid).remove()
} catch (error) {
return error.status === 404
? Promise.resolve()
......@@ -74,7 +72,7 @@ const releasePhoneNumber = async phoneNumber => {
}
}
// (String, DB, Socket, String) -> SignalboostStatus
// (Error, string) -> SignalboostStatus
const handleDestroyFailure = async (err, phoneNumber) => {
logger.log(`Error destroying channel: ${phoneNumber}:`)
logger.error(err)
......
......@@ -3,6 +3,7 @@ import { describe, it, beforeEach, afterEach } from 'mocha'
import sinon from 'sinon'
import channelRepository from '../../app/db/repositories/channel'
import signal from '../../app/signal'
import { map } from 'lodash'
import { deepChannelFactory } from '../support/factories/channel'
import {
adminMembershipFactory,
......@@ -36,7 +37,7 @@ describe('notifier module', () => {
it('sends a notification to each admin in their language', async () => {
await notifier.notifyAdmins(channel, notificationKeys.CHANNEL_RECYCLED)
expect(sendMessageStub.callCount).to.eql(2)
expect(sendMessageStub.getCalls().map(x => x.args)).to.have.deep.members([
expect(map(sendMessageStub.getCalls(), 'args')).to.have.deep.members([
[
channel.memberships[0].memberPhoneNumber,
sdMessageOf(channel, messagesIn('DE').notifications[notificationKeys.CHANNEL_RECYCLED]),
......@@ -53,7 +54,7 @@ describe('notifier module', () => {
it('sends a notification to each member in their language', async () => {
await notifier.notifyMembers(channel, notificationKeys.CHANNEL_DESTROYED)
expect(sendMessageStub.callCount).to.eql(4)
expect(sendMessageStub.getCalls().map(x => x.args)).to.have.deep.members([
expect(map(sendMessageStub.getCalls(), 'args')).to.have.deep.members([
[
channel.memberships[0].memberPhoneNumber,
sdMessageOf(channel, messagesIn('DE').notifications[notificationKeys.CHANNEL_DESTROYED]),
......@@ -74,6 +75,30 @@ describe('notifier module', () => {
})
})
describe('#notifyMembersExcept', () => {
it('sends a language-appropriate notification to all members except the sender', async () => {
await notifier.notifyMembersExcept(
channel,
channel.memberships[0],
notificationKeys.CHANNEL_DESTROYED,
)
expect(map(sendMessageStub.getCalls(), 'args')).to.have.deep.members([
[
channel.memberships[1].memberPhoneNumber,
sdMessageOf(channel, messagesIn('FR').notifications[notificationKeys.CHANNEL_DESTROYED]),
],
[
channel.memberships[2].memberPhoneNumber,
sdMessageOf(channel, messagesIn('ES').notifications[notificationKeys.CHANNEL_DESTROYED]),
],
[
channel.memberships[3].memberPhoneNumber,
sdMessageOf(channel, messagesIn('DE').notifications[notificationKeys.CHANNEL_DESTROYED]),
],
])
})
})
describe('#notifyMaintainers', () => {
it('sends an untranslated notification to sysadmins of the instance', async () => {
await notifier.notifyMaintainers('foo')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment