diff --git a/services/settings/docs/index.rst b/services/settings/docs/index.rst index 21576a0cd878..d6f123e52d74 100644 --- a/services/settings/docs/index.rst +++ b/services/settings/docs/index.rst @@ -324,6 +324,8 @@ The polling for changes process sends two notifications that observers can regis * ``remote-settings:changes-poll-start``: Polling for changes is starting. triggered either by the scheduled timer or a push broadcast. * ``remote-settings:changes-poll-end``: Polling for changes has ended +* ``remote-settings:sync-error``: A synchronization error occured. Notification subject provides information about the error and affected + collection in the ``wrappedJSObject`` attribute. .. code-block:: javascript diff --git a/services/settings/remote-settings.js b/services/settings/remote-settings.js index 6d0185faf9fa..43075973515a 100644 --- a/services/settings/remote-settings.js +++ b/services/settings/remote-settings.js @@ -342,6 +342,11 @@ function remoteSettingsFunction() { UptakeTelemetry.STATUS.SYNC_ERROR, syncTelemetryArgs ); + // Notify potential observers of the error. + Services.obs.notifyObservers( + { wrappedJSObject: { error: firstError } }, + "remote-settings:sync-error" + ); // Rethrow the first observed error throw firstError; } diff --git a/services/settings/test/unit/test_remote_settings_poll.js b/services/settings/test/unit/test_remote_settings_poll.js index 3aa02cd40253..806fbbe980c5 100644 --- a/services/settings/test/unit/test_remote_settings_poll.js +++ b/services/settings/test/unit/test_remote_settings_poll.js @@ -723,34 +723,34 @@ add_task(async function test_client_error() { TELEMETRY_HISTOGRAM_SYNC_KEY ); + const collectionDetails = { + id: "b6ba7fab-a40a-4d03-a4af-6b627f3c5b36", + last_modified: 42, + host: "localhost", + bucket: "main", + collection: "some-entry", + }; server.registerPathHandler( CHANGES_PATH, - serveChangesEntries(10000, [ - { - id: "b6ba7fab-a40a-4d03-a4af-6b627f3c5b36", - last_modified: 42, - host: "localhost", - bucket: "main", - collection: "some-entry", - }, - ]) + serveChangesEntries(10000, [collectionDetails]) ); const c = RemoteSettings("some-entry"); c.maybeSync = () => { - throw new Error("boom"); + throw new RemoteSettingsClient.CorruptedDataError("main/some-entry"); }; - let notificationObserved = false; + let notificationsObserved = []; const observer = { observe(aSubject, aTopic, aData) { - Services.obs.removeObserver(this, "remote-settings:changes-poll-end"); - notificationObserved = true; + Services.obs.removeObserver(this, aTopic); + notificationsObserved.push([aTopic, aSubject.wrappedJSObject]); }, }; Services.obs.addObserver(observer, "remote-settings:changes-poll-end"); + Services.obs.addObserver(observer, "remote-settings:sync-error"); Services.prefs.setIntPref(PREF_LAST_ETAG, 42); - // pollChanges() fails with adequate error and no notification. + // pollChanges() fails with adequate error and a sync-error notification. let error; try { await RemoteSettings.pollChanges(); @@ -758,11 +758,25 @@ add_task(async function test_client_error() { error = e; } - Assert.ok( - !notificationObserved, - "a notification should not have been observed" + Assert.equal( + notificationsObserved.length, + 1, + "only the error notification should not have been observed" ); - Assert.ok(/boom/.test(error.message), "original client error is thrown"); + console.log(notificationsObserved); + let [topicObserved, subjectObserved] = notificationsObserved[0]; + Assert.equal(topicObserved, "remote-settings:sync-error"); + Assert.ok( + subjectObserved.error instanceof RemoteSettingsClient.CorruptedDataError, + `original error is provided (got ${subjectObserved.error})` + ); + Assert.deepEqual( + subjectObserved.error.details, + collectionDetails, + "information about collection is provided" + ); + + Assert.ok(/Corrupted/.test(error.message), "original client error is thrown"); // When an error occurs, last etag was not overwritten. Assert.equal(Services.prefs.getIntPref(PREF_LAST_ETAG), 42); // ensure that we've accumulated the correct telemetry