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

Commit e2942c2

Browse files
authored
feat(metrics): Log metrics event for sending a tab between devices. (#1700); r=pb,vbudhram,seanmonstar
1 parent 5a1561b commit e2942c2

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

docs/metrics-events.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ are emitted:
150150
|`device.created`|A device record has been created for a Sync account.|
151151
|`device.updated`|Device record is updated on a Sync account.|
152152
|`device.deleted`|Device record has been deleted from a Sync account.|
153+
|`sync.sentTabToDevice`|Device sent a push message for send-tab-to-device feature.|
153154

154155
In redshift,
155156
these events are stored

lib/metrics/events.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ const ACTIVITY_EVENTS = new Set([
1818
'account.verified',
1919
'device.created',
2020
'device.deleted',
21-
'device.updated'
21+
'device.updated',
22+
'sync.sentTabToDevice'
2223
])
2324

2425
// We plan to emit a vast number of flow events to cover all
@@ -30,7 +31,8 @@ const NOT_FLOW_EVENTS = new Set([
3031
'account.deleted',
3132
'device.created',
3233
'device.deleted',
33-
'device.updated'
34+
'device.updated',
35+
'sync.sentTabToDevice'
3436
])
3537

3638
// It's an error if a flow event doesn't have a flow_id

lib/routes/account.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,25 @@ module.exports = function (
13631363
.catch(catchPushError)
13641364
}
13651365
})
1366+
.then(function () {
1367+
// Emit a metrics event for when a user sends tabs between devices.
1368+
// In the future we will aim to get this event directly from sync telemetry,
1369+
// but we're doing it here for now as a quick way to get metrics on the feature.
1370+
if (payload.command === 'sync:collection_changed') {
1371+
// Note that payload schema validation ensures that these properties exist.
1372+
if (payload.data.collections.length === 1 && payload.data.collections[0] === 'clients') {
1373+
var deviceId = undefined
1374+
if (sessionToken.deviceId) {
1375+
deviceId = sessionToken.deviceId.toString('hex')
1376+
}
1377+
return request.emitMetricsEvent('sync.sentTabToDevice', {
1378+
device_id: deviceId,
1379+
service: 'sync',
1380+
uid: stringUid
1381+
})
1382+
}
1383+
}
1384+
})
13661385
.then(
13671386
function () {
13681387
reply({})

test/local/routes/account_devices.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,13 @@ describe('/account/device', function () {
183183
describe('/account/devices/notify', function () {
184184
var config = {}
185185
var uid = uuid.v4('binary')
186+
var deviceId = crypto.randomBytes(16)
187+
var mockLog = mocks.spyLog()
186188
var mockRequest = mocks.mockRequest({
189+
log: mockLog,
187190
credentials: {
188-
uid: uid.toString('hex')
191+
uid: uid,
192+
deviceId: deviceId
189193
}
190194
})
191195
var pushPayload = {
@@ -253,7 +257,7 @@ describe('/account/devices/notify', function () {
253257
assert.equal(mockPush.pushToAllDevices.callCount, 1, 'mockPush.pushToAllDevices was called once')
254258
var args = mockPush.pushToAllDevices.args[0]
255259
assert.equal(args.length, 3, 'mockPush.pushToAllDevices was passed three arguments')
256-
assert.equal(args[0], uid.toString('hex'), 'first argument was the device uid')
260+
assert.equal(args[0], uid, 'first argument was the device uid')
257261
assert.equal(args[1], 'devicesNotify', 'second argument was the devicesNotify reason')
258262
assert.deepEqual(args[2], {
259263
data: Buffer.from(JSON.stringify(pushPayload)),
@@ -266,6 +270,8 @@ describe('/account/devices/notify', function () {
266270

267271
it('specific devices', function () {
268272
mockCustoms.checkAuthenticated.reset()
273+
mockLog.activityEvent.reset()
274+
mockLog.error.reset()
269275
mockRequest.payload = {
270276
to: ['bogusid1', 'bogusid2'],
271277
TTL: 60,
@@ -284,17 +290,48 @@ describe('/account/devices/notify', function () {
284290
assert.equal(mockPush.pushToDevices.callCount, 1, 'mockPush.pushToDevices was called once')
285291
var args = mockPush.pushToDevices.args[0]
286292
assert.equal(args.length, 4, 'mockPush.pushToDevices was passed four arguments')
287-
assert.equal(args[0], uid.toString('hex'), 'first argument was the device uid')
293+
assert.equal(args[0], uid, 'first argument was the device uid')
288294
assert.deepEqual(args[1], ['bogusid1', 'bogusid2'], 'second argument was the list of device ids')
289295
assert.equal(args[2], 'devicesNotify', 'third argument was the devicesNotify reason')
290296
assert.deepEqual(args[3], {
291297
data: Buffer.from(JSON.stringify(pushPayload)),
292298
TTL: 60
293299
}, 'fourth argument was the push options')
300+
assert.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
301+
args = mockLog.activityEvent.args[0]
302+
assert.equal(args.length, 1, 'log.activityEvent was passed one argument')
303+
assert.deepEqual(args[0], {
304+
event: 'sync.sentTabToDevice',
305+
service: 'sync',
306+
userAgent: 'test user-agent',
307+
uid: uid.toString('hex'),
308+
device_id: deviceId.toString('hex')
309+
}, 'event data was correct')
310+
assert.equal(mockLog.error.callCount, 0, 'log.error was not called')
294311
})
295312
})
296313
})
297314

315+
it('does not log activity event for non-send-tab-related messages', function () {
316+
mockPush.pushToDevices.reset()
317+
mockLog.activityEvent.reset()
318+
mockLog.error.reset()
319+
mockRequest.payload = {
320+
to: ['bogusid1', 'bogusid2'],
321+
TTL: 60,
322+
payload: {
323+
isValid: true,
324+
version: 1,
325+
command: 'fxaccounts:password_reset'
326+
}
327+
}
328+
return runTest(route, mockRequest, function (response) {
329+
assert.equal(mockPush.pushToDevices.callCount, 1, 'mockPush.pushToDevices was called once')
330+
assert.equal(mockLog.activityEvent.callCount, 0, 'log.activityEvent was not called')
331+
assert.equal(mockLog.error.callCount, 0, 'log.error was not called')
332+
})
333+
})
334+
298335
it('device driven notifications disabled', function () {
299336
config.deviceNotificationsEnabled = false
300337
mockRequest.payload = {
@@ -367,7 +404,7 @@ describe('/account/device/destroy', function () {
367404
var mockDB = mocks.mockDB()
368405
var mockRequest = mocks.mockRequest({
369406
credentials: {
370-
uid: uid.toString('hex'),
407+
uid: uid
371408
},
372409
log: mockLog,
373410
payload: {
@@ -406,7 +443,7 @@ describe('/account/device/destroy', function () {
406443
assert.equal(args[0], 'device:delete')
407444
assert.equal(args[1], mockRequest)
408445
var details = args[2]
409-
assert.equal(details.uid, uid.toString('hex'))
446+
assert.equal(details.uid, uid)
410447
assert.equal(details.id, deviceId)
411448
assert.ok(Date.now() - details.timestamp < 100)
412449
})

0 commit comments

Comments
 (0)