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

Commit

Permalink
feat(server): implement GET /sms/status
Browse files Browse the repository at this point in the history
#1681

r=shane-tomlinson
  • Loading branch information
philbooth authored Mar 1, 2017
1 parent 3a6101f commit 34f4390
Show file tree
Hide file tree
Showing 11 changed files with 544 additions and 134 deletions.
14 changes: 13 additions & 1 deletion config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -587,10 +587,22 @@ var conf = convict({
format: String,
env: 'SMS_API_SECRET'
},
balanceThreshold: {
doc: 'Minimum balance (in Euros) necessary for status to be deemed "good"',
default: 1,
format: Number,
env: 'SMS_MINIMUM_BALANCE'
},
regions: {
doc: 'Valid ISO 3166-1 alpha-2 country codes for enabled regions',
default: /^(?:US|CA)$/,
format: RegExp,
env: 'SMS_REGIONS'
},
installFirefoxLink: {
doc: 'Link for the installFirefox SMS template',
format: 'url',
default: 'https://mzl.la/1HOd4ec',
format: 'url',
env: 'SMS_INSTALL_FIREFOX_LINK'
}
}
Expand Down
43 changes: 43 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ Since this is a HTTP-based protocol, clients should be prepared to gracefully ha

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

* Miscellaneous
* [POST /v1/get_random_bytes](#post-v1get_random_bytes)
Expand Down Expand Up @@ -1568,6 +1569,48 @@ Failing requests may return the following errors:
* status code 500, errno 132: message rejected
* status code 500, errno 999: unexpected error
## GET /v1/sms/status
:lock: HAWK-authenticated with the sessionToken.
Returns SMS status for the current user.
### Request
___Headers___
The request must include a Hawk header
that authenticates the request
using a `sessionToken`
received from `/v1/account/create` or `/v1/account/login`.
___Example___
```sh
curl -v \
-X GET \
-H "Host: api-accounts.dev.lcip.org" \
-H 'Authorization: Hawk id="d4c5b1e3f5791ef83896c27519979b93a45e6d0da34c7509c5632ac35b28b48d", ts="1373391043", nonce="ohQjqb", hash="vBODPWhDhiRWM4tmI9qp+np+3aoqEFzdGuGk0h7bh9w=", mac="LAnpP3P2PXelC6hUoUaHP72nCqY5Iibaa3eeiGBqIIU="' \
https://api-accounts.dev.lcip.org/v1/sms/status \
```
### Response
Successful requests
will return a `200 OK` response
with an object
containing an `ok` property
indicating the result
in the JSON body:
```json
{"ok":true}
```
Failing requests may return the following errors:
* status code 500, errno 999: unexpected error
## POST /v1/get_random_bytes
Not HAWK-authenticated.
Expand Down
42 changes: 42 additions & 0 deletions lib/routes/sms.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

'use strict'

const P = require('../promise')
const PhoneNumberUtil = require('google-libphonenumber').PhoneNumberUtil
const validators = require('./validators')

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

const getGeoData = require('../geodb')(log)
const REGIONS = config.sms.regions

return [
{
method: 'POST',
Expand Down Expand Up @@ -98,6 +102,44 @@ module.exports = (log, isA, error, config, customs, sms) => {
return {}
}
}
},
{
method: 'GET',
path: '/sms/status',
config: {
auth: {
strategy: 'sessionToken'
}
},
handler (request, reply) {
log.begin('sms.status', request)

return P.all([ getLocation(), getBalance() ])
.spread(createResponse)
.then(reply, reply)

function getLocation () {
return getGeoData(request.app.clientAddress)
.then(result => REGIONS.test(result.location.countryCode))
.catch(err => {
log.error({ op: 'sms.getGeoData', err: err })
throw error.unexpectedError()
})
}

function getBalance () {
return sms.balance()
.then(balance => balance.isOk)
.catch(err => {
log.error({ op: 'sms.balance', err: err })
throw error.unexpectedError()
})
}

function createResponse (isLocationOk, isBalanceOk) {
return { ok: isLocationOk && isBalanceOk }
}
}
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion lib/senders/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var P = require('../promise')
// This indirection exists to accommodate different config properties
// in the old auth mailer. If/when the two config files are merged and
// there's nothing left that imports mailer/config, it is safe to merge
// raw.js and this file into one. Be careful not to mix the arguments
// legacy_index.js and this file into one. Be careful not to mix the args
// up when you do that, they expect config and log in a different order.
var createSenders = require('./legacy_index')

Expand Down
21 changes: 20 additions & 1 deletion lib/senders/sms.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module.exports = function (log, translator, templates, smsConfig) {
apiKey: smsConfig.apiKey,
apiSecret: smsConfig.apiSecret
})
var sendSms = P.promisify(nexmo.message.sendSms, nexmo.message)
var sendSms = promisify('sendSms', nexmo.message)
var checkBalance = promisify('checkBalance', nexmo.account)

return {
send: function (phoneNumber, senderId, messageId, acceptLanguage) {
Expand Down Expand Up @@ -63,9 +64,27 @@ module.exports = function (log, translator, templates, smsConfig) {
})
}
})
},

balance: function () {
log.trace({ op: 'sms.balance' })

return checkBalance()
.then(function (result) {
var balance = result.value
var isOk = balance >= smsConfig.balanceThreshold

log.info({ op: 'sms.balance.success', balance: balance, isOk: isOk })

return { value: balance, isOk: isOk }
})
}
}

function promisify (methodName, object) {
return P.promisify(object[methodName], object)
}

function getMessage (messageId, acceptLanguage) {
var templateName = TEMPLATE_NAMES.get(messageId)
var template = templates['sms.' + templateName]
Expand Down
Loading

0 comments on commit 34f4390

Please sign in to comment.