Skip to content
This repository was archived by the owner on Apr 3, 2019. It is now read-only.

Commit 34f4390

Browse files
authored
feat(server): implement GET /sms/status
#1681 r=shane-tomlinson
1 parent 3a6101f commit 34f4390

File tree

11 files changed

+544
-134
lines changed

11 files changed

+544
-134
lines changed

config/index.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,10 +587,22 @@ var conf = convict({
587587
format: String,
588588
env: 'SMS_API_SECRET'
589589
},
590+
balanceThreshold: {
591+
doc: 'Minimum balance (in Euros) necessary for status to be deemed "good"',
592+
default: 1,
593+
format: Number,
594+
env: 'SMS_MINIMUM_BALANCE'
595+
},
596+
regions: {
597+
doc: 'Valid ISO 3166-1 alpha-2 country codes for enabled regions',
598+
default: /^(?:US|CA)$/,
599+
format: RegExp,
600+
env: 'SMS_REGIONS'
601+
},
590602
installFirefoxLink: {
591603
doc: 'Link for the installFirefox SMS template',
592-
format: 'url',
593604
default: 'https://mzl.la/1HOd4ec',
605+
format: 'url',
594606
env: 'SMS_INSTALL_FIREFOX_LINK'
595607
}
596608
}

docs/api.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ Since this is a HTTP-based protocol, clients should be prepared to gracefully ha
160160

161161
* Send SMS
162162
* [POST /v1/sms (:lock: sessionToken)](#post-v1sms)
163+
* [GET /v1/sms/status (:lock: sessionToken)](#get-v1smsstatus)
163164

164165
* Miscellaneous
165166
* [POST /v1/get_random_bytes](#post-v1get_random_bytes)
@@ -1568,6 +1569,48 @@ Failing requests may return the following errors:
15681569
* status code 500, errno 132: message rejected
15691570
* status code 500, errno 999: unexpected error
15701571
1572+
## GET /v1/sms/status
1573+
1574+
:lock: HAWK-authenticated with the sessionToken.
1575+
1576+
Returns SMS status for the current user.
1577+
1578+
### Request
1579+
1580+
___Headers___
1581+
1582+
The request must include a Hawk header
1583+
that authenticates the request
1584+
using a `sessionToken`
1585+
received from `/v1/account/create` or `/v1/account/login`.
1586+
1587+
___Example___
1588+
1589+
```sh
1590+
curl -v \
1591+
-X GET \
1592+
-H "Host: api-accounts.dev.lcip.org" \
1593+
-H 'Authorization: Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
1594+
https://api-accounts.dev.lcip.org/v1/sms/status \
1595+
```
1596+
1597+
### Response
1598+
1599+
Successful requests
1600+
will return a `200 OK` response
1601+
with an object
1602+
containing an `ok` property
1603+
indicating the result
1604+
in the JSON body:
1605+
1606+
```json
1607+
{"ok":true}
1608+
```
1609+
1610+
Failing requests may return the following errors:
1611+
1612+
* status code 500, errno 999: unexpected error
1613+
15711614
## POST /v1/get_random_bytes
15721615
15731616
Not HAWK-authenticated.

lib/routes/sms.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
'use strict'
66

7+
const P = require('../promise')
78
const PhoneNumberUtil = require('google-libphonenumber').PhoneNumberUtil
89
const validators = require('./validators')
910

@@ -15,6 +16,9 @@ module.exports = (log, isA, error, config, customs, sms) => {
1516
return []
1617
}
1718

19+
const getGeoData = require('../geodb')(log)
20+
const REGIONS = config.sms.regions
21+
1822
return [
1923
{
2024
method: 'POST',
@@ -98,6 +102,44 @@ module.exports = (log, isA, error, config, customs, sms) => {
98102
return {}
99103
}
100104
}
105+
},
106+
{
107+
method: 'GET',
108+
path: '/sms/status',
109+
config: {
110+
auth: {
111+
strategy: 'sessionToken'
112+
}
113+
},
114+
handler (request, reply) {
115+
log.begin('sms.status', request)
116+
117+
return P.all([ getLocation(), getBalance() ])
118+
.spread(createResponse)
119+
.then(reply, reply)
120+
121+
function getLocation () {
122+
return getGeoData(request.app.clientAddress)
123+
.then(result => REGIONS.test(result.location.countryCode))
124+
.catch(err => {
125+
log.error({ op: 'sms.getGeoData', err: err })
126+
throw error.unexpectedError()
127+
})
128+
}
129+
130+
function getBalance () {
131+
return sms.balance()
132+
.then(balance => balance.isOk)
133+
.catch(err => {
134+
log.error({ op: 'sms.balance', err: err })
135+
throw error.unexpectedError()
136+
})
137+
}
138+
139+
function createResponse (isLocationOk, isBalanceOk) {
140+
return { ok: isLocationOk && isBalanceOk }
141+
}
142+
}
101143
}
102144
]
103145
}

lib/senders/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var P = require('../promise')
88
// This indirection exists to accommodate different config properties
99
// in the old auth mailer. If/when the two config files are merged and
1010
// there's nothing left that imports mailer/config, it is safe to merge
11-
// raw.js and this file into one. Be careful not to mix the arguments
11+
// legacy_index.js and this file into one. Be careful not to mix the args
1212
// up when you do that, they expect config and log in a different order.
1313
var createSenders = require('./legacy_index')
1414

lib/senders/sms.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ module.exports = function (log, translator, templates, smsConfig) {
1616
apiKey: smsConfig.apiKey,
1717
apiSecret: smsConfig.apiSecret
1818
})
19-
var sendSms = P.promisify(nexmo.message.sendSms, nexmo.message)
19+
var sendSms = promisify('sendSms', nexmo.message)
20+
var checkBalance = promisify('checkBalance', nexmo.account)
2021

2122
return {
2223
send: function (phoneNumber, senderId, messageId, acceptLanguage) {
@@ -63,9 +64,27 @@ module.exports = function (log, translator, templates, smsConfig) {
6364
})
6465
}
6566
})
67+
},
68+
69+
balance: function () {
70+
log.trace({ op: 'sms.balance' })
71+
72+
return checkBalance()
73+
.then(function (result) {
74+
var balance = result.value
75+
var isOk = balance >= smsConfig.balanceThreshold
76+
77+
log.info({ op: 'sms.balance.success', balance: balance, isOk: isOk })
78+
79+
return { value: balance, isOk: isOk }
80+
})
6681
}
6782
}
6883

84+
function promisify (methodName, object) {
85+
return P.promisify(object[methodName], object)
86+
}
87+
6988
function getMessage (messageId, acceptLanguage) {
7089
var templateName = TEMPLATE_NAMES.get(messageId)
7190
var template = templates['sms.' + templateName]

0 commit comments

Comments
 (0)