feat(profile): send push notifications after a profile update

This commit is contained in:
Edouard Oger 2017-03-16 17:23:22 -04:00
Родитель c90719a041
Коммит 2e8342093c
6 изменённых файлов: 190 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var config = require('../config').getProperties()
var log = require('../lib/log')(config.log.level, 'profile-server-messaging')
var error = require('../lib/error')
var Token = require('../lib/tokens')(log, config)
var SQSReceiver = require('../lib/sqs')(log)
var profileUpdates = require('../lib/profile/updates')(log)
var push = require('../lib/push')
var DB = require('../lib/db')(
config,
log,
error,
Token.SessionToken,
Token.KeyFetchToken,
Token.AccountResetToken,
Token.PasswordForgotToken,
Token.PasswordChangeToken
)
var profileUpdatesQueue = new SQSReceiver(config.profileServerMessaging.region, [
config.profileServerMessaging.profileUpdatesQueueUrl
])
DB.connect(config[config.db.backend])
.then(
function (db) {
profileUpdates(profileUpdatesQueue, push(log, db, config))
}
)

Просмотреть файл

@ -373,6 +373,20 @@ var conf = convict({
default: ''
}
},
profileServerMessaging: {
region: {
doc: 'The region where the queues live',
format: String,
env: 'PROFILE_MESSAGING_REGION',
default: ''
},
profileUpdatesQueueUrl: {
doc: 'The queue URL to use (should include https://sqs.<region>.amazonaws.com/<account-id>/<queue-name>)',
format: String,
env: 'PROFILE_UPDATES_QUEUE_URL',
default: ''
}
},
verificationReminders: {
rate: {
doc: 'Rate of users getting the verification reminder. If "0" then the feature is disabled. If "1" all users get it.',

Просмотреть файл

@ -6,6 +6,7 @@
"anyOf":[
{ "$ref":"#/definitions/deviceConnected" },
{ "$ref":"#/definitions/deviceDisconnected" },
{ "$ref":"#/definitions/profileUpdated" },
{ "$ref":"#/definitions/collectionsChanged" },
{ "$ref":"#/definitions/passwordChanged" },
{ "$ref":"#/definitions/passwordReset" }
@ -77,6 +78,24 @@
},
"additionalProperties": false
},
"profileUpdated":{
"required":[
"version",
"command"
],
"properties":{
"version":{
"type":"integer"
},
"command":{
"type":"string",
"enum":[
"fxaccounts:profile_updated"
]
}
},
"additionalProperties": false
},
"collectionsChanged":{
"required":[
"version",

33
lib/profile/updates.js Normal file
Просмотреть файл

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const P = require('./../promise')
module.exports = function (log) {
return function start(messageQueue, push) {
function handleProfileUpdated(message) {
return new P(resolve => {
const uid = Buffer.from(message.uid, 'hex')
resolve(push.notifyProfileUpdated(uid))
})
.catch(function(err) {
log.error({ op: 'handleProfileUpdated', err: err })
})
.then(function () {
// We always delete the message, we are not really mission critical
message.del()
})
}
messageQueue.on('data', handleProfileUpdated)
messageQueue.start()
return {
messageQueue: messageQueue,
handleProfileUpdated: handleProfileUpdated
}
}
}

Просмотреть файл

@ -15,6 +15,7 @@ var PUSH_PAYLOAD_SCHEMA_VERSION = 1
var PUSH_COMMANDS = {
DEVICE_CONNECTED: 'fxaccounts:device_connected',
DEVICE_DISCONNECTED: 'fxaccounts:device_disconnected',
PROFILE_UPDATED: 'fxaccounts:profile_updated',
PASSWORD_CHANGED: 'fxaccounts:password_changed',
PASSWORD_RESET: 'fxaccounts:password_reset'
}
@ -76,6 +77,14 @@ var reasonToEvents = {
noCallback: 'push.device_disconnected.no_push_callback',
noKeys: 'push.device_disconnected.data_but_no_keys'
},
profileUpdated: {
send: 'push.profile_updated.send',
success: 'push.profile_updated.success',
resetSettings: 'push.profile_updated.reset_settings',
failed: 'push.profile_updated.failed',
noCallback: 'push.profile_updated.no_push_callback',
noKeys: 'push.profile_updated.data_but_no_keys'
},
devicesNotify: {
send: 'push.devices_notify.send',
success: 'push.devices_notify.success',
@ -198,6 +207,21 @@ module.exports = function (log, db, config) {
return this.pushToDevice(uid, idToDisconnect, 'deviceDisconnected', options)
},
/**
* Notifies all devices that a the profile attached to the account was updated
*
* @param uid
* @promise
*/
notifyProfileUpdated: function notifyProfileUpdated(uid) {
var data = Buffer.from(JSON.stringify({
version: PUSH_PAYLOAD_SCHEMA_VERSION,
command: PUSH_COMMANDS.PROFILE_UPDATED
}))
var options = { data: data }
return this.pushToAllDevices(uid, 'profileUpdated', options)
},
/**
* Notifies a set of devices that the password was changed
*

Просмотреть файл

@ -0,0 +1,67 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const assert = require('insist')
const EventEmitter = require('events').EventEmitter
const sinon = require('sinon')
const spyLog = require('../../mocks').spyLog
const profileUpdates = require('../../../lib/profile/updates')
const P = require('../../../lib/promise')
const mockDeliveryQueue = new EventEmitter()
mockDeliveryQueue.start = function start() {}
function mockMessage(msg) {
msg.del = sinon.spy()
return msg
}
var pushShouldThrow = false
const mockPush = {
notifyProfileUpdated: sinon.spy(() => {
if (pushShouldThrow) {
throw new Error('oops')
}
return P.resolve()
})
}
function mockProfileUpdates(log) {
return profileUpdates(log)(mockDeliveryQueue, mockPush)
}
describe('profile updates', () => {
it(
'should log errors',
() => {
pushShouldThrow = true
const mockLog = spyLog()
return mockProfileUpdates(mockLog).handleProfileUpdated(mockMessage({
uid: 'bogusuid'
})).then(() => {
assert.equal(mockPush.notifyProfileUpdated.callCount, 1)
assert.equal(mockLog.messages.length, 1)
pushShouldThrow = false
})
}
)
it(
'should send push notifications',
() => {
const mockLog = spyLog()
const uid = '1e2122ba'
return mockProfileUpdates(mockLog).handleProfileUpdated(mockMessage({
uid: uid
})).then(function () {
assert.equal(mockLog.messages.length, 0)
assert.equal(mockPush.notifyProfileUpdated.callCount, 2)
var args = mockPush.notifyProfileUpdated.getCall(1).args
assert.deepEqual(args[0], Buffer.from(uid, 'hex'))
})
}
)
})