Bug 872758 - Delete all documents on FHR upload; r=gps

This commit is contained in:
Stefan Mirea 2013-06-21 10:30:30 -07:00
parent 7b3c0db518
commit 182a158660
5 changed files with 107 additions and 46 deletions

View File

@ -114,9 +114,9 @@ BagheeraClient.prototype = Object.freeze({
* @param options
* (object) Extra options to control behavior. Recognized properties:
*
* deleteID -- (string) Old document ID to delete as part of
* deleteIDs -- (array) Old document IDs to delete as part of
* upload. If not specified, no old documents will be deleted as
* part of upload. The string value is typically a UUID in hex
* part of upload. The array values are typically UUIDs in hex
* form.
*
* telemetryCompressed -- (string) Telemetry histogram to record
@ -161,12 +161,16 @@ BagheeraClient.prototype = Object.freeze({
request.loadFlags = this._loadFlags;
request.timeout = this.DEFAULT_TIMEOUT_MSEC;
let deleteID;
// Since API changed, throw on old API usage.
if ("deleteID" in options) {
throw new Error("API has changed, use (array) deleteIDs instead");
}
if (options.deleteID) {
deleteID = options.deleteID;
this._log.debug("Will delete " + deleteID);
request.setHeader("X-Obsolete-Document", options.deleteID);
let deleteIDs;
if (options.deleteIDs && options.deleteIDs.length > 0) {
deleteIDs = options.deleteIDs;
this._log.debug("Will delete " + deleteIDs.join(", "));
request.setHeader("X-Obsolete-Document", deleteIDs.join(","));
}
let deferred = Promise.defer();
@ -190,7 +194,7 @@ BagheeraClient.prototype = Object.freeze({
let result = new BagheeraClientRequestResult();
result.namespace = namespace;
result.id = id;
result.deleteID = deleteID;
result.deleteIDs = deleteIDs ? deleteIDs.slice(0) : null;
request.onComplete = this._onComplete.bind(this, request, deferred, result);
request.post(data);

View File

@ -274,7 +274,9 @@ BagheeraServer.prototype = {
if (request.hasHeader("X-Obsolete-Document")) {
let obsolete = request.getHeader("X-Obsolete-Document");
this._log.info("Deleting from X-Obsolete-Document header: " + obsolete);
delete this.namespaces[namespace][obsolete];
for (let obsolete_id of obsolete.split(",")) {
delete this.namespaces[namespace][obsolete_id];
}
}
response.setStatusLine(request.httpVersion, 201, "Created");

View File

@ -6,7 +6,8 @@
Cu.import("resource://services-common/bagheeraclient.js");
Cu.import("resource://services-common/rest.js");
Cu.import("resource://testing-common/services-common/bagheeraserver.js");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
const PORT = 8080;
@ -72,20 +73,55 @@ add_test(function test_post_json_bad_data() {
});
});
add_test(function test_post_json_delete_obsolete() {
add_task(function test_post_delete_multiple_obsolete_documents () {
let [client, server] = getClientAndServer();
server.createNamespace("foo");
server.setDocument("foo", "obsolete", "Old payload");
let namespace = "foo";
let documents = [
[namespace, "one", "{v:1}"],
[namespace, "two", "{v:2}"],
[namespace, "three", "{v:3}"],
[namespace, "four", "{v:4}"],
];
let promise = client.uploadJSON("foo", "new", {foo: "bar"}, {deleteID: "obsolete"});
promise.then(function onSuccess(result) {
try {
// create initial documents
server.createNamespace(namespace);
for (let [ns, id, payload] of documents) {
server.setDocument(ns, id, payload);
do_check_true(server.hasDocument(ns, id));
}
// Test uploading with deleting some documents.
let deleteIDs = [0, 1].map((no) => { return documents[no][1]; });
let result = yield client.uploadJSON(namespace, "new-1", {foo: "bar"}, {deleteIDs: deleteIDs});
do_check_true(result.transportSuccess);
do_check_true(result.serverSuccess);
do_check_true(server.hasDocument("foo", "new"));
do_check_false(server.hasDocument("foo", "obsolete"));
do_check_true(server.hasDocument(namespace, "new-1"));
for (let id of deleteIDs) {
do_check_false(server.hasDocument(namespace, id));
}
// Check if the documents that were not staged for deletion are still there.
for (let [,id,] of documents) {
if (deleteIDs.indexOf(id) == -1) {
do_check_true(server.hasDocument(namespace, id));
}
}
server.stop(run_next_test);
});
// Test upload without deleting documents.
let ids = Object.keys(server.namespaces[namespace]);
result = yield client.uploadJSON(namespace, "new-2", {foo: "bar"});
do_check_true(result.transportSuccess);
do_check_true(result.serverSuccess);
do_check_true(server.hasDocument(namespace, "new-2"));
// Check to see if all the original documents are still there.
for (let id of ids) {
do_check_true(deleteIDs.indexOf(id) !== -1 || server.hasDocument(namespace, id));
}
} finally {
let deferred = Promise.defer();
server.stop(deferred.resolve.bind(deferred));
yield deferred.promise;
}
});
add_test(function test_delete_document() {
@ -102,4 +138,4 @@ add_test(function test_delete_document() {
server.stop(run_next_test);
});
});
});

View File

@ -174,8 +174,17 @@ HealthReporterState.prototype = Object.freeze({
},
removeRemoteID: function (id) {
this._log.warn("Removing document from remote ID list: " + id);
let filtered = this._s.remoteIDs.filter((x) => x != id);
return this.removeRemoteIDs(id ? [id] : []);
},
removeRemoteIDs: function (ids) {
if (!ids || !ids.length) {
this._log.warn("No IDs passed for removal.");
return Promise.resolve();
}
this._log.warn("Removing documents from remote ID list: " + ids);
let filtered = this._s.remoteIDs.filter((x) => ids.indexOf(x) === -1);
if (filtered.length == this._s.remoteIDs.length) {
return Promise.resolve();
@ -192,13 +201,17 @@ HealthReporterState.prototype = Object.freeze({
},
updateLastPingAndRemoveRemoteID: function (date, id) {
if (!id) {
return this.updateLastPingAndRemoveRemoteIDs(date, id ? [id] : []);
},
updateLastPingAndRemoveRemoteIDs: function (date, ids) {
if (!ids) {
return this.setLastPingDate(date);
}
this._log.info("Recording last ping time and deleted remote document.");
this._s.lastPingTime = date.getTime();
return this.removeRemoteID(id);
return this.removeRemoteIDs(ids);
},
_migratePrefs: function () {
@ -1338,10 +1351,10 @@ HealthReporter.prototype = Object.freeze({
if (isDelete) {
this._log.warn("Marking delete as successful.");
yield this._state.removeRemoteID(result.id);
yield this._state.removeRemoteIDs([result.id]);
} else {
this._log.warn("Marking upload as successful.");
yield this._state.updateLastPingAndRemoveRemoteID(date, result.deleteID);
yield this._state.updateLastPingAndRemoveRemoteIDs(date, result.deleteIDs);
}
request.onSubmissionSuccess(this._now());
@ -1388,7 +1401,7 @@ HealthReporter.prototype = Object.freeze({
let result;
try {
let options = {
deleteID: lastID,
deleteIDs: this._state.remoteIDs.filter((x) => { return x != id; }),
telemetryCompressed: TELEMETRY_PAYLOAD_SIZE_COMPRESSED,
};
result = yield client.uploadJSON(this.serverNamespace, id, payload,

View File

@ -647,11 +647,15 @@ add_task(function test_data_submission_success() {
let now = new Date();
let request = new DataSubmissionRequest(deferred, now);
reporter._state.addRemoteID("foo");
reporter.requestDataUpload(request);
yield deferred.promise;
do_check_eq(request.state, request.SUBMISSION_SUCCESS);
do_check_true(reporter.lastPingDate.getTime() > 0);
do_check_true(reporter.haveRemoteData());
for (let remoteID of reporter._state.remoteIDs) {
do_check_neq(remoteID, "foo");
}
// Ensure data from providers made it to payload.
let o = yield reporter.getJSONPayload(true);
@ -660,7 +664,7 @@ add_task(function test_data_submission_success() {
let data = yield getHealthReportProviderValues(reporter, now);
do_check_eq(data._v, 1);
do_check_eq(data.firstDocumentUploadAttempt, 1);
do_check_eq(data.continuationUploadAttempt, 1);
do_check_eq(data.uploadSuccess, 1);
do_check_eq(Object.keys(data).length, 3);
@ -1009,36 +1013,39 @@ add_task(function test_state_invalid_json() {
add_task(function test_state_multiple_remote_ids() {
let [reporter, server] = yield getReporterAndServer("state_multiple_remote_ids");
let documents = [
[reporter.serverNamespace, "one", "{v:1}"],
[reporter.serverNamespace, "two", "{v:2}"],
];
let now = new Date(Date.now() - 5000);
server.setDocument(reporter.serverNamespace, "id1", "foo");
server.setDocument(reporter.serverNamespace, "id2", "bar");
try {
yield reporter._state.addRemoteID("id1");
yield reporter._state.addRemoteID("id2");
for (let [ns, id, payload] of documents) {
server.setDocument(ns, id, payload);
do_check_true(server.hasDocument(ns, id));
yield reporter._state.addRemoteID(id);
do_check_eq(reporter._state.remoteIDs.indexOf(id), reporter._state.remoteIDs.length - 1);
}
yield reporter._state.setLastPingDate(now);
do_check_eq(reporter._state.remoteIDs.length, 2);
do_check_eq(reporter._state.remoteIDs[0], "id1");
do_check_eq(reporter._state.remoteIDs[1], "id2");
do_check_eq(reporter.lastSubmitID, "id1");
do_check_eq(reporter.lastSubmitID, documents[0][1]);
let deferred = Promise.defer();
let request = new DataSubmissionRequest(deferred, now);
reporter.requestDataUpload(request);
yield deferred.promise;
do_check_eq(reporter._state.remoteIDs.length, 2);
do_check_eq(reporter._state.remoteIDs[0], "id2");
do_check_eq(reporter._state.remoteIDs.length, 1);
for (let [,id,] of documents) {
do_check_eq(reporter._state.remoteIDs.indexOf(id), -1);
do_check_false(server.hasDocument(reporter.serverNamespace, id));
}
do_check_true(reporter.lastPingDate.getTime() > now.getTime());
do_check_false(server.hasDocument(reporter.serverNamespace, "id1"));
do_check_true(server.hasDocument(reporter.serverNamespace, "id2"));
let o = CommonUtils.readJSON(reporter._state._filename);
do_check_eq(reporter._state.remoteIDs.length, 2);
do_check_eq(reporter._state.remoteIDs[0], "id2");
do_check_eq(reporter._state.remoteIDs[1], reporter._state.remoteIDs[1]);
let o = yield CommonUtils.readJSON(reporter._state._filename);
do_check_eq(o.remoteIDs.length, 1);
do_check_eq(o.remoteIDs[0], reporter._state.remoteIDs[0]);
do_check_eq(o.lastPingTime, reporter.lastPingDate.getTime());
} finally {
yield shutdownServer(server);
reporter._shutdown();
@ -1076,4 +1083,3 @@ add_task(function test_state_downgrade_upgrade() {
reporter._shutdown();
}
});