Bug 1074688 - Part 2 Add Join/Refresh/Leave room functions to the mozLoop API. r=mikedeboer

This commit is contained in:
Mark Banner 2014-11-06 20:52:16 +00:00
parent ed25bd2ef7
commit 4eb7ef3cb9
2 changed files with 137 additions and 1 deletions

View File

@ -18,6 +18,9 @@ XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
this.EXPORTED_SYMBOLS = ["LoopRooms", "roomsPushNotification"];
// The maximum number of clients that we support currently.
const CLIENT_MAX_SIZE = 2;
const roomsPushNotification = function(version, channelID) {
return LoopRoomsInternal.onNotification(version, channelID);
};
@ -271,6 +274,81 @@ let LoopRoomsInternal = {
}, error => callback(error)).catch(error => callback(error));
},
/**
* Internal function to handle POSTs to a room.
*
* @param {String} roomToken The room token.
* @param {Object} postData The data to post to the room.
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
_postToRoom(roomToken, postData, callback) {
let url = "/rooms/" + encodeURIComponent(roomToken);
MozLoopService.hawkRequest(this.sessionType, url, "POST", postData).then(response => {
// Delete doesn't have a body return.
var joinData = response.body ? JSON.parse(response.body) : {};
callback(null, joinData);
}, error => callback(error)).catch(error => callback(error));
},
/**
* Joins a room
*
* @param {String} roomToken The room token.
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
join: function(roomToken, callback) {
this._postToRoom(roomToken, {
action: "join",
displayName: MozLoopService.userProfile.email,
clientMaxSize: CLIENT_MAX_SIZE
}, callback);
},
/**
* Refreshes a room
*
* @param {String} roomToken The room token.
* @param {String} sessionToken The session token for the session that has been
* joined
* @param {Function} callback Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
refreshMembership: function(roomToken, sessionToken, callback) {
this._postToRoom(roomToken, {
action: "refresh",
sessionToken: sessionToken
}, callback);
},
/**
* Leaves a room. Although this is an sync function, no data is returned
* from the server.
*
* @param {String} roomToken The room token.
* @param {String} sessionToken The session token for the session that has been
* joined
* @param {Function} callback Optional. Function that will be invoked once the operation
* finished. The first argument passed will be an
* `Error` object or `null`.
*/
leave: function(roomToken, sessionToken, callback) {
if (!callback) {
callback = function(error) {
if (error) {
MozLoopService.log.error(error);
}
};
}
this._postToRoom(roomToken, {
action: "leave",
sessionToken: sessionToken
}, callback);
},
/**
* Callback used to indicate changes to rooms data on the LoopServer.
@ -322,6 +400,19 @@ this.LoopRooms = {
return LoopRoomsInternal.delete(roomToken, callback);
},
join: function(roomToken, callback) {
return LoopRoomsInternal.join(roomToken, callback);
},
refreshMembership: function(roomToken, sessionToken, callback) {
return LoopRoomsInternal.refreshMembership(roomToken, sessionToken,
callback);
},
leave: function(roomToken, sessionToken, callback) {
return LoopRoomsInternal.leave(roomToken, sessionToken, callback);
},
promise: function(method, ...params) {
return new Promise((resolve, reject) => {
this[method](...params, (error, result) => {

View File

@ -212,7 +212,16 @@ add_task(function* setup_server() {
// Add a request handler for each room in the list.
[...kRooms.values()].forEach(function(room) {
loopServer.registerPathHandler("/rooms/" + encodeURIComponent(room.roomToken), (req, res) => {
returnRoomDetails(res, room.roomName);
if (req.method == "POST") {
let body = CommonUtils.readBytesFromInputStream(req.bodyInputStream);
let data = JSON.parse(body);
res.setStatusLine(null, 200, "OK");
res.write(JSON.stringify(data));
res.processAsync();
res.finish();
} else {
returnRoomDetails(res, room.roomName);
}
});
});
@ -321,6 +330,39 @@ add_task(function* test_roomUpdates() {
yield waitForCondition(() => Object.getOwnPropertyNames(gExpectedJoins).length === 0);
});
// Test if joining a room works as expected.
add_task(function* test_joinRoom() {
// We need these set up for getting the email address.
Services.prefs.setCharPref("loop.fxa_oauth.profile", JSON.stringify({
email: "fake@invalid.com"
}));
Services.prefs.setCharPref("loop.fxa_oauth.tokendata", JSON.stringify({
token_type: "bearer"
}));
let roomToken = "_nxD4V4FflQ";
let joinedData = yield LoopRooms.promise("join", roomToken);
Assert.equal(joinedData.action, "join");
Assert.equal(joinedData.displayName, "fake@invalid.com");
});
// Test if refreshing a room works as expected.
add_task(function* test_refreshMembership() {
let roomToken = "_nxD4V4FflQ";
let refreshedData = yield LoopRooms.promise("refreshMembership", roomToken,
"fakeSessionToken");
Assert.equal(refreshedData.action, "refresh");
Assert.equal(refreshedData.sessionToken, "fakeSessionToken");
});
// Test if leaving a room works as expected.
add_task(function* test_leaveRoom() {
let roomToken = "_nxD4V4FflQ";
let leaveData = yield LoopRooms.promise("leave", roomToken, "fakeLeaveSessionToken");
Assert.equal(leaveData.action, "leave");
Assert.equal(leaveData.sessionToken, "fakeLeaveSessionToken");
});
// Test if the event emitter implementation doesn't leak and is working as expected.
add_task(function* () {
Assert.strictEqual(gExpectedAdds.length, 0, "No room additions should be expected anymore");
@ -339,6 +381,9 @@ function run_test() {
// Revert original Chat.open implementation
Chat.open = openChatOrig;
Services.prefs.clearUserPref("loop.fxa_oauth.profile");
Services.prefs.clearUserPref("loop.fxa_oauth.tokendata");
LoopRooms.off("add", onRoomAdded);
LoopRooms.off("update", onRoomUpdated);
LoopRooms.off("joined", onRoomJoined);