зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1458920 - Filter RemoteSettings sync event data r=Gijs,mgoodwin
MozReview-Commit-ID: Hw9CA5W2J26 --HG-- extra : rebase_source : 689aae16d007c19f1d9c73c3be95bd5618b0fe36
This commit is contained in:
Родитель
28f6b114ec
Коммит
2136d2066e
|
@ -68,6 +68,9 @@ The ``sync`` event allows to be notified when the remote settings are changed on
|
|||
}
|
||||
});
|
||||
|
||||
.. important::
|
||||
If one of the event handler fails, the others handlers for the same remote settings collection won't be executed.
|
||||
|
||||
.. note::
|
||||
Currently, the update of remote settings is triggered by the `nsBlocklistService <https://dxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/nsBlocklistService.js>`_ (~ every 24H).
|
||||
|
||||
|
|
|
@ -347,22 +347,34 @@ class RemoteSettingsClient {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
// Read local collection of records.
|
||||
const { data: current } = await collection.list();
|
||||
|
||||
// Handle the obtained records (ie. apply locally).
|
||||
try {
|
||||
// Execute callbacks in order and sequentially.
|
||||
// Handle the obtained records (ie. apply locally through events).
|
||||
// Build the event data list. It should be filtered (ie. by application target)
|
||||
const { created: allCreated, updated: allUpdated, deleted: allDeleted } = syncResult;
|
||||
const [created, deleted, updatedFiltered] = await Promise.all(
|
||||
[allCreated, allDeleted, allUpdated.map(e => e.new)].map(this._filterEntries.bind(this))
|
||||
);
|
||||
// For updates, keep entries whose updated form is matches the target.
|
||||
const updatedFilteredIds = new Set(updatedFiltered.map(e => e.id));
|
||||
const updated = allUpdated.filter(({ new: { id } }) => updatedFilteredIds.has(id));
|
||||
|
||||
// If every changed entry is filtered, we don't even fire the event.
|
||||
if (created.length || updated.length || deleted.length) {
|
||||
// Read local collection of records (also filtered).
|
||||
const { data: allData } = await collection.list();
|
||||
const current = await this._filterEntries(allData);
|
||||
// Fire the event: execute callbacks in order and sequentially.
|
||||
// If one fails everything fails.
|
||||
const { created, updated, deleted } = syncResult;
|
||||
const event = { data: { current, created, updated, deleted } };
|
||||
const callbacks = this._callbacks.get("sync");
|
||||
for (const cb of callbacks) {
|
||||
await cb(event);
|
||||
try {
|
||||
for (const cb of callbacks) {
|
||||
await cb(event);
|
||||
}
|
||||
} catch (e) {
|
||||
reportStatus = UptakeTelemetry.STATUS.APPLY_ERROR;
|
||||
throw e;
|
||||
}
|
||||
} catch (e) {
|
||||
reportStatus = UptakeTelemetry.STATUS.APPLY_ERROR;
|
||||
throw e;
|
||||
}
|
||||
|
||||
// Track last update.
|
||||
|
|
|
@ -175,6 +175,45 @@ add_task(async function test_sends_reload_message_when_blocklist_has_changes() {
|
|||
});
|
||||
add_task(clear_state);
|
||||
|
||||
add_task(async function test_sync_event_data_is_filtered_for_target() {
|
||||
// Here we will synchronize 4 times, the first two to initialize the local DB and
|
||||
// the last two about event filtered data.
|
||||
const timestamp1 = 2000;
|
||||
const timestamp2 = 3000;
|
||||
const timestamp3 = 4000;
|
||||
const timestamp4 = 5000;
|
||||
// Fake a date value obtained from server (used to store a pref, useless here).
|
||||
const fakeServerTime = Date.now();
|
||||
|
||||
for (let {client} of gBlocklistClients) {
|
||||
// Initialize the collection with some data (local is empty, thus no ?_since)
|
||||
await client.maybeSync(timestamp1, fakeServerTime - 30, {loadDump: false});
|
||||
// This will pick the data with ?_since=3000.
|
||||
await client.maybeSync(timestamp2 + 1, fakeServerTime - 20);
|
||||
|
||||
// In ?_since=4000 entries, no target matches. The sync event is not called.
|
||||
let called = false;
|
||||
client.on("sync", e => called = true);
|
||||
await client.maybeSync(timestamp3 + 1, fakeServerTime - 10);
|
||||
equal(called, false, `no sync event for ${client.collectionName}`);
|
||||
|
||||
// In ?_since=5000 entries, only one entry matches.
|
||||
let syncEventData;
|
||||
client.on("sync", e => syncEventData = e.data);
|
||||
await client.maybeSync(timestamp4 + 1, fakeServerTime);
|
||||
const { current, created, updated, deleted } = syncEventData;
|
||||
equal(created.length + updated.length + deleted.length, 1, `event filtered data for ${client.collectionName}`);
|
||||
|
||||
// Since we had entries whose target does not match, the internal storage list
|
||||
// and the event current data should differ.
|
||||
const collection = await client.openCollection();
|
||||
const { data: internalData } = await collection.list();
|
||||
ok(internalData.length > current.length, `event current data for ${client.collectionName}`);
|
||||
}
|
||||
});
|
||||
add_task(clear_state);
|
||||
|
||||
|
||||
// get a response for a given request from sample data
|
||||
function getSampleResponse(req, port) {
|
||||
const responses = {
|
||||
|
@ -322,7 +361,7 @@ function getSampleResponse(req, port) {
|
|||
"versionRange": [{
|
||||
"targetApplication": [],
|
||||
"minVersion": "11.2.202.509",
|
||||
"maxVersion": "11.2.202.539",
|
||||
"maxVersion": "*",
|
||||
"severity": "0",
|
||||
"vulnerabilityStatus": "1"
|
||||
}],
|
||||
|
@ -336,7 +375,6 @@ function getSampleResponse(req, port) {
|
|||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"minVersion": "3.0",
|
||||
"guid": "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
|
||||
"maxVersion": "*"
|
||||
}]
|
||||
}]
|
||||
|
@ -370,6 +408,163 @@ function getSampleResponse(req, port) {
|
|||
"os": "Darwin 11",
|
||||
"featureStatus": "BLOCKED_DEVICE"
|
||||
}]})
|
||||
},
|
||||
"GET:/v1/buckets/blocklists/collections/addons/records?_sort=-last_modified&_since=4000": {
|
||||
"sampleHeaders": [
|
||||
"Access-Control-Allow-Origin: *",
|
||||
"Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
|
||||
"Content-Type: application/json; charset=UTF-8",
|
||||
"Server: waitress",
|
||||
"Etag: \"5000\""
|
||||
],
|
||||
"status": {status: 200, statusText: "OK"},
|
||||
"responseBody": JSON.stringify({"data": [{
|
||||
"last_modified": 4001,
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "some-guid"
|
||||
}],
|
||||
}],
|
||||
"id": "8f03b264-57b7-4263-9b15-ad91b033a034"
|
||||
}]})
|
||||
},
|
||||
"GET:/v1/buckets/blocklists/collections/plugins/records?_sort=-last_modified&_since=4000": {
|
||||
"sampleHeaders": [
|
||||
"Access-Control-Allow-Origin: *",
|
||||
"Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
|
||||
"Content-Type: application/json; charset=UTF-8",
|
||||
"Server: waitress",
|
||||
"Etag: \"5000\""
|
||||
],
|
||||
"status": {status: 200, statusText: "OK"},
|
||||
"responseBody": JSON.stringify({"data": [{
|
||||
"last_modified": 4001,
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "xpcshell@tests.mozilla.org",
|
||||
"minVersion": "0",
|
||||
"maxVersion": "57.*"
|
||||
}]
|
||||
}],
|
||||
"id": "cd3ea0b2-1ba8-4fb6-b242-976a87626116"
|
||||
}]})
|
||||
},
|
||||
"GET:/v1/buckets/blocklists/collections/gfx/records?_sort=-last_modified&_since=4000": {
|
||||
"sampleHeaders": [
|
||||
"Access-Control-Allow-Origin: *",
|
||||
"Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
|
||||
"Content-Type: application/json; charset=UTF-8",
|
||||
"Server: waitress",
|
||||
"Etag: \"5000\""
|
||||
],
|
||||
"status": {status: 200, statusText: "OK"},
|
||||
"responseBody": JSON.stringify({"data": [{
|
||||
"last_modified": 4001,
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "xpcshell@tests.mozilla.org",
|
||||
"minVersion": "99999"
|
||||
}],
|
||||
}],
|
||||
"id": "86771771-e803-4006-95e9-c9275d58b3d1"
|
||||
}]})
|
||||
},
|
||||
"GET:/v1/buckets/blocklists/collections/addons/records?_sort=-last_modified&_since=5000": {
|
||||
"sampleHeaders": [
|
||||
"Access-Control-Allow-Origin: *",
|
||||
"Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
|
||||
"Content-Type: application/json; charset=UTF-8",
|
||||
"Server: waitress",
|
||||
"Etag: \"6000\""
|
||||
],
|
||||
"status": {status: 200, statusText: "OK"},
|
||||
"responseBody": JSON.stringify({"data": [{
|
||||
// delete an entry with non matching target (see above)
|
||||
"last_modified": 5001,
|
||||
"deleted": true,
|
||||
"id": "8f03b264-57b7-4263-9b15-ad91b033a034"
|
||||
}, {
|
||||
// delete entry with matching target (see above)
|
||||
"last_modified": 5002,
|
||||
"deleted": true,
|
||||
"id": "9ccfac91-e463-c30c-f0bd-14143794a8dd"
|
||||
}, {
|
||||
// create an extra non matching
|
||||
"last_modified": 5003,
|
||||
"id": "75b36589-435a-48d4-8ee4-bacee3fb6119",
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "xpcshell@tests.mozilla.org",
|
||||
"minVersion": "0",
|
||||
"maxVersion": "57.*"
|
||||
}]
|
||||
}],
|
||||
}]})
|
||||
},
|
||||
"GET:/v1/buckets/blocklists/collections/plugins/records?_sort=-last_modified&_since=5000": {
|
||||
"sampleHeaders": [
|
||||
"Access-Control-Allow-Origin: *",
|
||||
"Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
|
||||
"Content-Type: application/json; charset=UTF-8",
|
||||
"Server: waitress",
|
||||
"Etag: \"6000\""
|
||||
],
|
||||
"status": {status: 200, statusText: "OK"},
|
||||
"responseBody": JSON.stringify({"data": [{
|
||||
// entry with non matching target (see above)
|
||||
"newAttribute": 42,
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "xpcshell@tests.mozilla.org",
|
||||
"minVersion": "0",
|
||||
"maxVersion": "57.*"
|
||||
}]
|
||||
}],
|
||||
"id": "cd3ea0b2-1ba8-4fb6-b242-976a87626116"
|
||||
}, {
|
||||
// entry with matching target (see above)
|
||||
"newAttribute": 42,
|
||||
"matchFilename": "npViewpoint.dll",
|
||||
"blockID": "p32",
|
||||
"id": "1f48af42-c508-b8ef-b8d5-609d48e4f6c9",
|
||||
"last_modified": 3500,
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "xpcshell@tests.mozilla.org",
|
||||
"minVersion": "3.0",
|
||||
"maxVersion": "*"
|
||||
}]
|
||||
}]
|
||||
}]})
|
||||
},
|
||||
"GET:/v1/buckets/blocklists/collections/gfx/records?_sort=-last_modified&_since=5000": {
|
||||
"sampleHeaders": [
|
||||
"Access-Control-Allow-Origin: *",
|
||||
"Access-Control-Expose-Headers: Retry-After, Content-Length, Alert, Backoff",
|
||||
"Content-Type: application/json; charset=UTF-8",
|
||||
"Server: waitress",
|
||||
"Etag: \"6000\""
|
||||
],
|
||||
"status": {status: 200, statusText: "OK"},
|
||||
"responseBody": JSON.stringify({"data": [{
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "xpcshell@tests.mozilla.org",
|
||||
"minVersion": "0",
|
||||
"maxVersion": "*"
|
||||
}]
|
||||
}],
|
||||
"id": "43031a81-5f36-4eef-9b35-52f0bbeba363"
|
||||
}, {
|
||||
"versionRange": [{
|
||||
"targetApplication": [{
|
||||
"guid": "xpcshell@tests.mozilla.org",
|
||||
"minVersion": "0",
|
||||
"maxVersion": "3"
|
||||
}]
|
||||
}],
|
||||
"id": "75a06bd3-f906-427d-a448-02092ee589fc"
|
||||
}]})
|
||||
}
|
||||
};
|
||||
return responses[`${req.method}:${req.path}?${req.queryString}`] ||
|
||||
|
|
|
@ -449,19 +449,16 @@ add_task(async function test_check_signatures() {
|
|||
|
||||
startHistogram = getUptakeTelemetrySnapshot(TELEMETRY_HISTOGRAM_KEY);
|
||||
|
||||
let retrySyncData;
|
||||
OneCRLBlocklistClient.on("sync", ({ data }) => { retrySyncData = data; });
|
||||
let syncEventSent = false;
|
||||
OneCRLBlocklistClient.on("sync", ({ data }) => { syncEventSent = true; });
|
||||
|
||||
await OneCRLBlocklistClient.maybeSync(5000, startTime);
|
||||
|
||||
endHistogram = getUptakeTelemetrySnapshot(TELEMETRY_HISTOGRAM_KEY);
|
||||
|
||||
// since we only fixed the signature, and no data was changed, the sync result
|
||||
// will be called with empty lists of created/updated/deleted.
|
||||
equal(retrySyncData.current.length, 2);
|
||||
equal(retrySyncData.created.length, 0);
|
||||
equal(retrySyncData.updated.length, 0);
|
||||
equal(retrySyncData.deleted.length, 0);
|
||||
// since we only fixed the signature, and no data was changed, the sync event
|
||||
// was not sent.
|
||||
equal(syncEventSent, false);
|
||||
|
||||
// ensure that the failure count is incremented for a succesful sync with an
|
||||
// (initial) bad signature - only SERVICES_SETTINGS_SYNC_SIG_FAIL should
|
||||
|
@ -493,17 +490,14 @@ add_task(async function test_check_signatures() {
|
|||
|
||||
registerHandlers(badSigGoodOldResponses);
|
||||
|
||||
let oldChangesData;
|
||||
OneCRLBlocklistClient.on("sync", ({ data }) => { oldChangesData = data; });
|
||||
syncEventSent = false;
|
||||
OneCRLBlocklistClient.on("sync", ({ data }) => { syncEventSent = true; });
|
||||
|
||||
await OneCRLBlocklistClient.maybeSync(5000, startTime);
|
||||
|
||||
// Local data was unchanged, since it was never than the one returned by the server.
|
||||
equal(oldChangesData.current.length, 2);
|
||||
equal(oldChangesData.created.length, 0);
|
||||
equal(oldChangesData.updated.length, 0);
|
||||
equal(oldChangesData.deleted.length, 0);
|
||||
|
||||
// Local data was unchanged, since it was never than the one returned by the server,
|
||||
// thus the sync event is not sent.
|
||||
equal(syncEventSent, false);
|
||||
|
||||
const badLocalContentGoodSigResponses = {
|
||||
// In this test, we deliberately serve a bad signature initially. The
|
||||
|
|
Загрузка…
Ссылка в новой задаче