Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
team-friendo
signalboost
Commits
4dfdfc6a
Verified
Commit
4dfdfc6a
authored
Jun 29, 2020
by
Mari
Committed by
aguestuser
Sep 07, 2020
Browse files
[215] add recycleablePhoneNumber model/repository
parent
14eb1261
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
222 additions
and
98 deletions
+222
-98
app/api/routes.js
app/api/routes.js
+1
-1
app/db/index.js
app/db/index.js
+3
-0
app/db/migrations/20200623002750-create-recycleablePhoneNumbers-table.js
...ns/20200623002750-create-recycleablePhoneNumbers-table.js
+31
-0
app/db/models/recycleablePhoneNumber.js
app/db/models/recycleablePhoneNumber.js
+17
-0
app/db/repositories/recycleablePhoneNumber.js
app/db/repositories/recycleablePhoneNumber.js
+12
-0
app/registrar/phoneNumber/index.js
app/registrar/phoneNumber/index.js
+2
-1
app/registrar/phoneNumber/recycle.js
app/registrar/phoneNumber/recycle.js
+24
-14
test/unit/db/models/recycleablePhoneNumber.spec.js
test/unit/db/models/recycleablePhoneNumber.spec.js
+54
-0
test/unit/db/repositories/recycleablePhoneNumber.spec.js
test/unit/db/repositories/recycleablePhoneNumber.spec.js
+33
-0
test/unit/registrar/phoneNumber/recycle.spec.js
test/unit/registrar/phoneNumber/recycle.spec.js
+45
-82
No files found.
app/api/routes.js
View file @
4dfdfc6a
...
...
@@ -69,7 +69,7 @@ const routesOf = async router => {
router
.
post
(
'
/phoneNumbers/recycle
'
,
async
ctx
=>
{
const
{
phoneNumbers
}
=
ctx
.
request
.
body
const
result
=
await
phoneNumberService
.
recycle
({
const
result
=
await
phoneNumberService
.
enqueueRecycleablePhoneNumber
({
phoneNumbers
,
})
merge
(
ctx
,
{
status
:
httpStatusOfMany
(
result
),
body
:
result
})
...
...
app/db/index.js
View file @
4dfdfc6a
...
...
@@ -10,6 +10,8 @@ const { membershipOf } = require('./models/membership')
const
{
messageCountOf
}
=
require
(
'
./models/messageCount
'
)
const
{
phoneNumberOf
}
=
require
(
'
./models/phoneNumber
'
)
const
{
smsSenderOf
}
=
require
(
'
./models/smsSender
'
)
const
{
recycleablePhoneNumberOf
}
=
require
(
'
./models/recycleablePhoneNumber
'
)
const
{
wait
}
=
require
(
'
../util
'
)
const
{
maxConnectionAttempts
,
connectionInterval
}
=
config
...
...
@@ -28,6 +30,7 @@ const run = async () => {
membership
:
membershipOf
(
sequelize
,
Sequelize
),
messageCount
:
messageCountOf
(
sequelize
,
Sequelize
),
phoneNumber
:
phoneNumberOf
(
sequelize
,
Sequelize
),
recycleablePhoneNumber
:
recycleablePhoneNumberOf
(
sequelize
,
Sequelize
),
smsSender
:
smsSenderOf
(
sequelize
,
Sequelize
),
}
...
...
app/db/migrations/20200623002750-create-recycleablePhoneNumbers-table.js
0 → 100644
View file @
4dfdfc6a
'
use strict
'
;
module
.
exports
=
{
up
:
(
queryInterface
,
Sequelize
)
=>
{
return
queryInterface
.
createTable
(
'
recycleablePhoneNumbers
'
,
{
channelPhoneNumber
:
{
type
:
Sequelize
.
STRING
,
primaryKey
:
true
,
allowNull
:
false
,
unique
:
true
,
},
whenEnqueued
:
{
type
:
Sequelize
.
DATE
,
allowNull
:
false
,
},
createdAt
:
{
type
:
Sequelize
.
DATE
,
allowNull
:
false
,
defaultValue
:
Sequelize
.
NOW
,
},
updatedAt
:
{
type
:
Sequelize
.
DATE
,
allowNull
:
false
,
defaultValue
:
Sequelize
.
NOW
,
},
});
},
down
:
(
queryInterface
,
Sequelize
)
=>
{
return
queryInterface
.
dropTable
(
'
recycleablePhoneNumbers
'
);
}
};
app/db/models/recycleablePhoneNumber.js
0 → 100644
View file @
4dfdfc6a
const
recycleablePhoneNumberOf
=
(
sequelize
,
DataTypes
)
=>
{
const
recycleablePhoneNumber
=
sequelize
.
define
(
'
recycleablePhoneNumber
'
,
{
channelPhoneNumber
:
{
type
:
DataTypes
.
STRING
,
primaryKey
:
true
,
allowNull
:
false
,
unique
:
true
,
},
whenEnqueued
:
{
type
:
DataTypes
.
DATE
,
allowNull
:
false
,
},
})
return
recycleablePhoneNumber
}
module
.
exports
=
{
recycleablePhoneNumberOf
}
app/db/repositories/recycleablePhoneNumber.js
0 → 100644
View file @
4dfdfc6a
const
app
=
require
(
'
../..
'
)
const
enqueue
=
channelPhoneNumber
=>
app
.
db
.
recycleablePhoneNumber
.
create
({
channelPhoneNumber
,
whenEnqueued
:
new
Date
().
toISOString
(),
})
const
dequeue
=
channelPhoneNumber
=>
app
.
db
.
recycleablePhoneNumber
.
destroy
({
where
:
{
channelPhoneNumber
}
})
module
.
exports
=
{
enqueue
,
dequeue
}
app/registrar/phoneNumber/index.js
View file @
4dfdfc6a
...
...
@@ -4,7 +4,7 @@ const { errors } = require('./common')
const
{
destroy
}
=
require
(
'
./destroy
'
)
const
{
list
}
=
require
(
'
./present
'
)
const
{
provisionN
}
=
require
(
'
./provision
'
)
const
{
recycle
}
=
require
(
'
./recycle
'
)
const
{
enqueueRecycleablePhoneNumber
,
recycle
}
=
require
(
'
./recycle
'
)
const
{
register
,
registerAllPurchased
,
registerAllUnregistered
}
=
require
(
'
./register
'
)
const
{
handleSms
}
=
require
(
'
./sms
'
)
const
{
purchase
,
purchaseN
}
=
require
(
'
./purchase
'
)
...
...
@@ -22,6 +22,7 @@ module.exports = {
provisionN
,
purchase
,
purchaseN
,
enqueueRecycleablePhoneNumber
,
recycle
,
register
,
registerAllPurchased
,
...
...
app/registrar/phoneNumber/recycle.js
View file @
4dfdfc6a
const
channelRepository
=
require
(
'
../../db/repositories/channel
'
)
const
phoneNumberRepository
=
require
(
'
../../db/repositories/phoneNumber
'
)
const
recycleablePhoneNumberRepository
=
require
(
'
../../db/repositories/recycleablePhoneNumber
'
)
const
eventRepository
=
require
(
'
../../db/repositories/event
'
)
const
common
=
require
(
'
./common
'
)
const
{
defaultLanguage
}
=
require
(
'
../../config
'
)
...
...
@@ -8,30 +9,39 @@ const { eventTypes } = require('../../db/models/event')
const
{
sdMessageOf
}
=
require
(
'
../../signal/constants
'
)
const
{
messagesIn
}
=
require
(
'
../../dispatcher/strings/messages
'
)
// ({
Database, Socket,
string}) -> SignalboostStatus
const
recycle
=
async
({
phoneNumbers
})
=>
{
// ({ string
}) -> SignalboostStatus
const
enqueueRecycleablePhoneNumber
=
async
({
phoneNumbers
})
=>
{
return
await
Promise
.
all
(
phoneNumbers
.
split
(
'
,
'
).
map
(
async
phoneNumber
=>
{
const
channel
=
await
channelRepository
.
findDeep
(
phoneNumber
)
if
(
channel
)
{
return
notifyMembers
(
channel
)
.
then
(()
=>
common
.
destroyChannel
(
channel
))
.
then
(()
=>
eventRepository
.
log
(
eventTypes
.
CHANNEL_DESTROYED
,
phoneNumber
))
.
then
(()
=>
recordStatusChange
(
phoneNumber
,
common
.
statuses
.
VERIFIED
))
.
then
(
phoneNumberStatus
=>
({
status
:
'
SUCCESS
'
,
data
:
phoneNumberStatus
}))
.
catch
(
err
=>
handleRecycleFailure
(
err
,
phoneNumber
))
}
else
{
try
{
const
channel
=
await
channelRepository
.
find
(
phoneNumber
)
return
recycleablePhoneNumberRepository
.
enqueue
(
channel
.
phoneNumber
)
}
catch
(
e
)
{
return
{
status
:
'
ERROR
'
,
message
:
`Channel not found for
${
phoneNumber
}
`
}
}
}),
)
}
// (string) -> SignalboostStatus
const
recycle
=
async
channelPhoneNumber
=>
{
const
channel
=
await
channelRepository
.
findDeep
(
channelPhoneNumber
)
if
(
channel
)
{
return
notifyMembers
(
channel
)
.
then
(()
=>
common
.
destroyChannel
(
channel
))
.
then
(()
=>
eventRepository
.
log
(
eventTypes
.
CHANNEL_DESTROYED
,
channelPhoneNumber
))
.
then
(()
=>
recordStatusChange
(
channelPhoneNumber
,
common
.
statuses
.
VERIFIED
))
.
then
(
phoneNumberStatus
=>
({
status
:
'
SUCCESS
'
,
data
:
phoneNumberStatus
}))
.
catch
(
err
=>
handleRecycleFailure
(
err
,
channelPhoneNumber
))
}
else
{
return
{
status
:
'
ERROR
'
,
message
:
`Channel not found for
${
channelPhoneNumber
}
`
}
}
}
/********************
* HELPER FUNCTIONS
********************/
// (
Database, Socket,
Channel) -> Channel
// (Channel) -> Channel
const
notifyMembers
=
async
channel
=>
{
const
memberPhoneNumbers
=
channelRepository
.
getMemberPhoneNumbers
(
channel
)
await
signal
.
broadcastMessage
(
...
...
@@ -58,4 +68,4 @@ const handleRecycleFailure = async (err, phoneNumber) => {
}
}
module
.
exports
=
{
recycle
}
module
.
exports
=
{
enqueueRecycleablePhoneNumber
,
recycle
}
test/unit/db/models/recycleablePhoneNumber.spec.js
0 → 100644
View file @
4dfdfc6a
import
{
expect
}
from
'
chai
'
import
{
describe
,
it
,
before
,
after
}
from
'
mocha
'
import
{
run
}
from
'
../../../../app/db/index
'
import
{
genPhoneNumber
}
from
'
../../../support/factories/phoneNumber
'
describe
(
'
recycleablePhoneNumber model
'
,
()
=>
{
let
db
let
channelPhoneNumber
=
genPhoneNumber
()
before
(
async
()
=>
{
db
=
await
run
()
})
after
(
async
()
=>
{
await
db
.
recycleablePhoneNumber
.
destroy
({
where
:
{}
})
await
db
.
stop
()
})
it
(
'
has the correct fields
'
,
async
()
=>
{
const
enqueuedChannelNumber
=
await
db
.
recycleablePhoneNumber
.
create
({
channelPhoneNumber
,
whenEnqueued
:
new
Date
().
toISOString
(),
})
expect
(
enqueuedChannelNumber
.
channelPhoneNumber
).
to
.
be
.
a
(
'
string
'
)
expect
(
enqueuedChannelNumber
.
whenEnqueued
).
to
.
be
.
a
(
'
Date
'
)
})
describe
(
'
validations
'
,
()
=>
{
it
(
'
requires a channelPhoneNumber
'
,
async
()
=>
{
const
err
=
await
db
.
recycleablePhoneNumber
.
create
({
whenEnqueued
:
new
Date
().
toISOString
()
})
.
catch
(
e
=>
e
)
expect
(
err
.
message
).
to
.
include
(
'
channelPhoneNumber cannot be null
'
)
})
it
(
'
requires a timestamp for whenEnqueued
'
,
async
()
=>
{
const
err
=
await
db
.
recycleablePhoneNumber
.
create
({
channelPhoneNumber
:
genPhoneNumber
()
})
.
catch
(
e
=>
e
)
expect
(
err
.
message
).
to
.
include
(
'
whenEnqueued cannot be null
'
)
})
it
(
"
doesn't allow the same phone number to be enqueued twice
"
,
async
()
=>
{
const
err
=
await
db
.
recycleablePhoneNumber
.
create
({
channelPhoneNumber
,
whenEnqueued
:
new
Date
().
toISOString
(),
})
.
catch
(
e
=>
JSON
.
stringify
(
e
.
errors
[
0
]))
expect
(
err
).
to
.
include
(
'
channelPhoneNumber must be unique
'
)
})
})
})
test/unit/db/repositories/recycleablePhoneNumber.spec.js
0 → 100644
View file @
4dfdfc6a
import
{
describe
,
it
,
before
,
after
,
beforeEach
}
from
'
mocha
'
import
{
expect
}
from
'
chai
'
import
{
genPhoneNumber
}
from
'
../../../support/factories/phoneNumber
'
import
recycleablePhoneNumberRepository
from
'
../../../../app/db/repositories/recycleablePhoneNumber
'
import
app
from
'
../../../../app
'
import
testApp
from
'
../../../support/testApp
'
import
dbService
from
'
../../../../app/db
'
describe
(
'
recycleablePhoneNumber repository
'
,
()
=>
{
let
db
,
recycleablePhoneNumberCount
,
channelPhoneNumber
before
(
async
()
=>
{
db
=
(
await
app
.
run
({
...
testApp
,
db
:
dbService
})).
db
channelPhoneNumber
=
genPhoneNumber
()
})
beforeEach
(
async
()
=>
{
recycleablePhoneNumberCount
=
await
db
.
recycleablePhoneNumber
.
count
()
})
after
(
async
()
=>
await
app
.
stop
())
it
(
'
enqueues a channel for recycling
'
,
async
()
=>
{
let
enqueuedChannel
=
await
recycleablePhoneNumberRepository
.
enqueue
(
channelPhoneNumber
)
expect
(
enqueuedChannel
.
channelPhoneNumber
).
to
.
eql
(
channelPhoneNumber
)
expect
(
await
db
.
recycleablePhoneNumber
.
count
()).
to
.
eql
(
recycleablePhoneNumberCount
+
1
)
})
it
(
'
dequeues a channel for recycling
'
,
async
()
=>
{
await
recycleablePhoneNumberRepository
.
dequeue
(
channelPhoneNumber
)
expect
(
await
db
.
recycleablePhoneNumber
.
count
()).
to
.
eql
(
recycleablePhoneNumberCount
-
1
)
})
})
test/unit/registrar/phoneNumber/recycle.spec.js
View file @
4dfdfc6a
import
{
expect
}
from
'
chai
'
import
{
afterEach
,
beforeEach
,
describe
,
it
}
from
'
mocha
'
import
phoneNumberService
from
'
../../../../app/registrar/phoneNumber
'
import
{
enqueueRecycleablePhoneNumber
,
recycle
}
from
'
../../../../app/registrar/phoneNumber
'
import
sinon
from
'
sinon
'
import
phoneNumberRepository
from
'
../../../../app/db/repositories/phoneNumber
'
import
channelRepository
from
'
../../../../app/db/repositories/channel
'
...
...
@@ -10,7 +10,7 @@ import common from '../../../../app/registrar/phoneNumber/common'
import
{
eventTypes
}
from
'
../../../../app/db/models/event
'
describe
(
'
phone number services -- recycle module
'
,
()
=>
{
const
phoneNumbers
=
'
+11111111111
,
+12222222222
'
const
phoneNumbers
=
[
'
+11111111111
'
,
'
+12222222222
'
]
let
updatePhoneNumberStub
,
broadcastMessageStub
,
findChannelStub
,
...
...
@@ -68,27 +68,21 @@ describe('phone number services -- recycle module', () => {
broadcastMessageStub
.
callsFake
(()
=>
Promise
.
reject
(
'
Failed to broadcast message
'
))
describe
(
'
recycling phone numbers
'
,
()
=>
{
describe
(
'
when phone number
s
do not
exist in
channel
s db
'
,
()
=>
{
describe
(
'
when
the
phone number do
es
not
belong to a valid
channel
'
,
()
=>
{
beforeEach
(
async
()
=>
{
findChannelStub
.
returns
(
Promise
.
resolve
(
null
))
})
it
(
'
returns a channel not found status
'
,
async
()
=>
{
const
response
=
await
phoneNumberService
.
recycle
({
phoneNumbers
})
expect
(
response
).
to
.
eql
([
{
message
:
'
Channel not found for +11111111111
'
,
status
:
'
ERROR
'
,
},
{
message
:
'
Channel not found for +12222222222
'
,
status
:
'
ERROR
'
,
},
])
const
response
=
await
recycle
(
phoneNumbers
[
0
])
expect
(
response
).
to
.
eql
({
message
:
'
Channel not found for +11111111111
'
,
status
:
'
ERROR
'
,
})
})
})
describe
(
'
when phone number
s
do
exist in
channel
s db
'
,
()
=>
{
describe
(
'
when
the
phone number do
es belong to a valid
channel
'
,
()
=>
{
beforeEach
(
async
()
=>
{
findChannelStub
.
returns
(
Promise
.
resolve
({}))
})
...
...
@@ -104,20 +98,13 @@ describe('phone number services -- recycle module', () => {
})
it
(
'
returns a failed status
'
,
async
()
=>
{
const
response
=
await
phoneNumberService
.
recycle
({
phoneNumbers
})
expect
(
response
).
to
.
eql
([
{
message
:
'
Failed to recycle channel for +11111111111. Error: Failed to broadcast message
'
,
status
:
'
ERROR
'
,
},
{
message
:
'
Failed to recycle channel for +12222222222. Error: Failed to broadcast message
'
,
status
:
'
ERROR
'
,
},
])
const
response
=
await
recycle
(
phoneNumbers
[
0
])
expect
(
response
).
to
.
eql
({
message
:
'
Failed to recycle channel for +11111111111. Error: Failed to broadcast message
'
,
status
:
'
ERROR
'
,
})
})
})
...
...
@@ -137,9 +124,9 @@ describe('phone number services -- recycle module', () => {
})
it
(
'
notifies the members of the channel of destruction
'
,
async
()
=>
{
await
phoneNumberService
.
recycle
(
{
phoneNumbers
}
)
await
recycle
(
phoneNumbers
[
0
]
)
expect
(
broadcastMessageStub
.
callCount
).
to
.
eql
(
2
)
expect
(
broadcastMessageStub
.
callCount
).
to
.
eql
(
1
)
})
it
(
'
adds a CHANNEL_DESTROYED event to the event log
'
,
async
()
=>
{
...
...
@@ -152,7 +139,7 @@ describe('phone number services -- recycle module', () => {
})
it
(
'
updates the phone number record to verified
'
,
async
()
=>
{
await
phoneNumberService
.
recycle
(
{
phoneNumbers
}
)
await
recycle
(
phoneNumbers
[
0
]
)
expect
(
updatePhoneNumberStub
.
getCall
(
0
).
args
).
to
.
eql
([
'
+11111111111
'
,
...
...
@@ -161,30 +148,21 @@ describe('phone number services -- recycle module', () => {
})
it
(
'
successfully destroys the channel
'
,
async
()
=>
{
await
phoneNumberService
.
recycle
(
{
phoneNumbers
}
)
await
recycle
(
phoneNumbers
[
0
]
)
expect
(
destroyChannelSpy
.
callCount
).
to
.
eql
(
2
)
expect
(
destroyChannelSpy
.
callCount
).
to
.
eql
(
1
)
})
it
(
'
returns successful recycled phone number statuses
'
,
async
()
=>
{
const
response
=
await
phoneNumberService
.
recycle
({
phoneNumbers
})
expect
(
response
).
to
.
eql
([
{
data
:
{
phoneNumber
:
'
+11111111111
'
,
status
:
'
VERIFIED
'
,
},
status
:
'
SUCCESS
'
,
},
{
data
:
{
phoneNumber
:
'
+12222222222
'
,
status
:
'
VERIFIED
'
,
},
status
:
'
SUCCESS
'
,
const
response
=
await
recycle
(
phoneNumbers
[
0
])
expect
(
response
).
to
.
eql
({
data
:
{
phoneNumber
:
'
+11111111111
'
,
status
:
'
VERIFIED
'
,
},
])
status
:
'
SUCCESS
'
,
})
})
})
...
...
@@ -194,20 +172,13 @@ describe('phone number services -- recycle module', () => {
})
it
(
'
returns a failed status
'
,
async
()
=>
{
const
response
=
await
phoneNumberService
.
recycle
(
{
phoneNumbers
}
)
const
response
=
await
recycle
(
phoneNumbers
[
0
]
)
expect
(
response
).
to
.
eql
([
{
message
:
'
Failed to recycle channel for +11111111111. Error: DB phoneNumber update failure
'
,
status
:
'
ERROR
'
,
},
{
message
:
'
Failed to recycle channel for +12222222222. Error: DB phoneNumber update failure
'
,
status
:
'
ERROR
'
,
},
])
expect
(
response
).
to
.
eql
({
message
:
'
Failed to recycle channel for +11111111111. Error: DB phoneNumber update failure
'
,
status
:
'
ERROR
'
,
})
})
})
})
...
...
@@ -220,15 +191,15 @@ describe('phone number services -- recycle module', () => {
})
it
(
'
notifies the correct instance maintainers
'
,
async
()
=>
{
await
phoneNumberService
.
recycle
(
{
phoneNumbers
}
)
await
recycle
(
phoneNumbers
[
0
]
)
expect
(
broadcastMessageStub
.
getCall
(
2
).
args
[
0
]).
to
.
eql
([
'
+16154804259
'
,
'
+12345678910
'
])
expect
(
broadcastMessageStub
.
getCall
(
1
).
args
[
0
]).
to
.
eql
([
'
+16154804259
'
,
'
+12345678910
'
])
})
it
(
'
notifies the instance maintainers with a channel failure message
'
,
async
()
=>
{
await
phoneNumberService
.
recycle
(
{
phoneNumbers
}
)
await
recycle
(
phoneNumbers
[
0
]
)
expect
(
broadcastMessageStub
.
getCall
(
2
).
args
[
1
]).
to
.
eql
({
expect
(
broadcastMessageStub
.
getCall
(
1
).
args
[
1
]).
to
.
eql
({
messageBody
:
'
Failed to recycle channel for phone number: +11111111111
'
,
type
:
'
send
'
,
username
:
'
+15555555555
'
,
...
...
@@ -236,20 +207,12 @@ describe('phone number services -- recycle module', () => {
})
it
(
'
returns a failed status
'
,
async
()
=>
{
const
response
=
await
phoneNumberService
.
recycle
({
phoneNumbers
})
expect
(
response
).
to
.
eql
([
{
message
:
'
Failed to recycle channel for +11111111111. Error: Failed to destroy channel
'
,
status
:
'
ERROR
'
,
},
{
message
:
'
Failed to recycle channel for +12222222222. Error: Failed to destroy channel
'
,
status
:
'
ERROR
'
,
},
])
const
response
=
await
recycle
(
phoneNumbers
[
0
])
expect
(
response
).
to
.
eql
({
message
:
'
Failed to recycle channel for +11111111111. Error: Failed to destroy channel
'
,
status
:
'
ERROR
'
,
})
})
})
})
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment