-
Notifications
You must be signed in to change notification settings - Fork 21
feat(devices): Devices capabilities #320
Conversation
philbooth
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eoger, I think this looks good! Shout if you disagree with any of the comments, I seem to be having an opinionated day today!
.gitignore
Outdated
| @@ -1,3 +1,4 @@ | |||
| .DS_Store | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, but IIRC we prefer to keep platform-specific ignores out of the project .gitignore and ask people to add it to their own ~/.gitignore_global instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, please use a global git ignore
| const index = CAPABILITIES.indexOf(c) | ||
| if (index !== -1) { | ||
| acc.push({sql: ADD_CAPABILITY, params: [uid, deviceId, index]}) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder whether it might be more helpful to clients to fail the request completely if they submit an unrecognised capability, rather than dropping it silently?
Or do you envisage that validation taking place in the auth server? If that's the case it seems unfortunate that we have to define the valid capability strings in two separate repos. Instead, maybe the auth server could take responsibility for mapping between the capability string and its enumeration, leaving this repo just handling integers?
| deviceId BINARY(16) NOT NULL, | ||
| capability TINYINT UNSIGNED NOT NULL, | ||
| PRIMARY KEY (uid, deviceId, capability), | ||
| FOREIGN KEY (uid, deviceId) REFERENCES devices(uid, id) ON DELETE CASCADE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sigh, I should have used ON DELETE CASCADE on the devices table itself. And unverifiedTokens. 🤦♂️
lib/db/mysql.js
Outdated
| return query(connection, sql, params) | ||
| .then( | ||
| function (result) { | ||
| log.trace('MySql.write', { sql, result }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This trace message should be MySql.writeMultiple presumably?
lib/db/mysql.js
Outdated
| function (result) { | ||
| log.trace('MySql.write', { sql, result }) | ||
| if (resultHandler) { | ||
| return resultHandler(i, result) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking out loud, ignore me if it's rubbish, but...
Instead of passing in one resultHandler and then giving it an index so it knows which query it's being invoked for, would it be a little bit cleaner if resultHandler was a property of the objects inside the queries array? So each query can have a dedicated result handler that doesn't need to check the value of i?
lib/db/mysql.js
Outdated
| } | ||
|
|
||
| // Select : devices d, sessionTokens s, accounts a | ||
| // Since MySQL doesn't have an ARRAY type, we store arrays as comma-separated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More bike-shedding, but this comment reads a little bit inaccurately to me. Unless I misunderstand, we're not storing them as comma-separated values, they're stored as rows in devicesCapabilities. They're just returned from queries as comma-separated values, right?
lib/db/mysql.js
Outdated
| } | ||
|
|
||
| // Enum capability name -> capability id (index) | ||
| const CAPABILITIES = ['pushbox'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also define a const CAPABILITY_IDS at this point, containing the inverse mapping from string to integer? It would save us having to rely on indexOf in the rest of the code, and could be generated with CAPABILITIES.reduce so no need to actually define anything really.
lib/db/mysql.js
Outdated
| const capabilitiesQueries = [] | ||
| if (deviceInfo.hasOwnProperty('capabilities')) { | ||
| capabilitiesQueries.push({sql: PURGE_CAPABILITIES, params: [uid, deviceId]}) | ||
| addLegalCapabilities(uid, deviceId, deviceInfo, capabilitiesQueries) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bike-shedding but I think, on balance, I don't like this pattern of passing in the array and modifying it (or not) inside addLegalCapabilities. It's kind of small-scale action-at-a-distance.
Would it be cleaner if addLegalCapabilities always returns a fresh array and the caller can then be explicit about joining them with capabilitiesQueries.concat if it needs to?
lib/db/schema/patch-073-074.sql
Outdated
| IN `inCapability` TINYINT UNSIGNED | ||
| ) | ||
| BEGIN | ||
| INSERT INTO devicesCapabilities( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even more bike-shedding (sorry)... are we sure about the plural on the devices part here? deviceCapabilities sounds more natural to me.
48753a3 to
2e41a11
Compare
|
Thank you @philbooth that was really useful feedback! |
| ['totp-2fa', 2] // TOTP code | ||
| ]) | ||
|
|
||
| // If you modify one of these maps, modify the other. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine for now but I think, if the capabilities list gets longer in the future, we should definitely switch to generating one of these programmatically so that there's only one definition to maintain.
philbooth
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Splendid! Let's also add the new error information to the docs for createDevice and updateDevice, then get this merged!
| "callbackAuthKey": "w3b14Zjc-Afj2SDOLOyong", | ||
| "callbackIsExpired": false | ||
| "callbackIsExpired": false, | ||
| "capabilities": ["pushbox"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also document the new error response in the section below?
| "callbackAuthKey": "w3b14Zjc-Afj2SDOLOyong", | ||
| "callbackIsExpired": false | ||
| "callbackIsExpired": false, | ||
| "capabilities": ["pushbox"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also document the new error response in the section below?
| * `callbackAuthKey` (string): | ||
| Auth key for push service | ||
| * `capabilities` (array): | ||
| Array of strings describing the current device capabilities |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also document the new error under Rejects with?
| * `callbackAuthKey` (string): | ||
| Auth key for push service | ||
| * `capabilities` (array): | ||
| Array of strings describing the current device capabilities |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also document the new error under Rejects with?
2e41a11 to
9577f83
Compare
|
All done, thank you! |
Connects to mozilla/fxa-auth-server#2320 (please read for context).
devicesanddevicesCapabilitiesat the same time.Since MySQL doesn't have an Array type, we pull the device capabilities using a
GROUP_CONCATand convert them to an array in JS.writeMultiplemethod).CAPABILITIESarray inmysql.js.device.capabilitiesis supplied inupdateDevice, it is implied that we are replacing the current device capabilities.