Backed out changeset c43b947f03b0 (bug 1634127) for causing newtab failures. CLOSED TREE

This commit is contained in:
Marian-Vasile Laza 2022-04-12 05:29:27 -07:00
parent ccc1737c63
commit 4d42714957
11 changed files with 157 additions and 169 deletions

View File

@ -303,7 +303,7 @@ const MessageLoaderUtils = {
"newtab"
);
// Await here in order to capture the exceptions for reporting.
await downloader.downloadToDisk(record.data, {
await downloader.download(record.data, {
retries: RS_DOWNLOAD_MAX_RETRIES,
});
RemoteL10n.reloadL10n();

View File

@ -410,12 +410,7 @@ class QuickSuggest extends EventEmitter {
await Promise.all(
event.data.deleted
.filter(d => d.attachment)
.map(entry =>
Promise.all([
this._rs.attachments.deleteDownloaded(entry), // type: data
this._rs.attachments.deleteFromDisk(entry), // type: icon
])
)
.map(entry => this._rs.attachments.delete(entry))
);
}
@ -425,7 +420,7 @@ class QuickSuggest extends EventEmitter {
this._rs
.get({ filters: { type: "icon" } })
.then(icons =>
Promise.all(icons.map(i => this._rs.attachments.downloadToDisk(i)))
Promise.all(icons.map(i => this._rs.attachments.download(i)))
),
]);
@ -435,7 +430,9 @@ class QuickSuggest extends EventEmitter {
this._resultsByKeyword.clear();
for (let record of data) {
let { buffer } = await this._rs.attachments.download(record);
let { buffer } = await this._rs.attachments.download(record, {
useCache: true,
});
let results = JSON.parse(new TextDecoder("utf-8").decode(buffer));
this._addResults(results);
}
@ -485,7 +482,7 @@ class QuickSuggest extends EventEmitter {
if (!record) {
return null;
}
return this._rs.attachments.downloadToDisk(record);
return this._rs.attachments.download(record);
}
}

View File

@ -33,9 +33,7 @@ const PublicSuffixList = {
.then(async records => {
if (records.length == 1) {
// Get the downloaded file URI (most likely to be a no-op here, since file will exist).
const fileURI = await this.CLIENT.attachments.downloadToDisk(
records[0]
);
const fileURI = await this.CLIENT.attachments.download(records[0]);
// Send a signal so that the C++ code loads the updated list on startup.
this.notifyUpdate(fileURI);
}
@ -82,7 +80,7 @@ const PublicSuffixList = {
async onUpdate({ data: { created, updated, deleted } }) {
// In theory, this will never happen, we will never delete the record.
if (deleted.length == 1) {
await this.CLIENT.attachments.deleteFromDisk(deleted[0]);
await this.CLIENT.attachments.delete(deleted[0]);
}
// Handle creation and update the same way
const changed = created.concat(updated.map(u => u.new));
@ -96,7 +94,7 @@ const PublicSuffixList = {
// Download the updated file.
let fileURI;
try {
fileURI = await this.CLIENT.attachments.downloadToDisk(changed[0]);
fileURI = await this.CLIENT.attachments.download(changed[0]);
} catch (err) {
Cu.reportError(err);
return;

View File

@ -62,26 +62,26 @@ registerCleanupFunction(() => {
});
/**
* downloadToDiskCalled is used by mockDownloadToDisk() and resetMockDownloadToDisk()
* downloadCalled is used by mockDownload() and resetMockDownload()
* to keep track weather CLIENT.attachments.download is called or not
* downloadToDiskBackup will help restore CLIENT.attachments.download to original definition
* downloadBackup will help restore CLIENT.attachments.download to original definition
* notifyUpdateBackup will help restore PublicSuffixList.notifyUpdate to original definition
*/
let downloadToDiskCalled = false;
const downloadToDiskBackup = CLIENT.attachments.downloadToDisk;
let downloadCalled = false;
const downloadBackup = CLIENT.attachments.download;
// returns a fake fileURI and sends a signal with filePath and no nsifile
function mockDownloadToDisk() {
downloadToDiskCalled = false;
CLIENT.attachments.downloadToDisk = async rec => {
downloadToDiskCalled = true;
const mockDownload = () => {
downloadCalled = false;
CLIENT.attachments.download = async rec => {
downloadCalled = true;
return `file://${mockedFilePath}`; // Create a fake file URI
};
}
};
// resetMockDownloadToDisk() must be run at the end of the test that uses mockDownloadToDisk()
const resetMockDownloadToDisk = () => {
CLIENT.attachments.downloadToDisk = downloadToDiskBackup;
// resetMockDownload() must be run at the end of the test that uses mockDownload()
const resetMockDownload = () => {
CLIENT.attachments.download = downloadBackup;
};
add_task(async () => {
@ -94,7 +94,7 @@ add_task(async () => {
attachment: {},
});
mockDownloadToDisk();
mockDownload();
const promiseSignal = TestUtils.topicObserved(SIGNAL);
await PublicSuffixList.init();
@ -106,13 +106,13 @@ add_task(async () => {
"File path sent when record is in DB."
);
await CLIENT.db.clear(); // Clean up the mockDownloaded record
resetMockDownloadToDisk();
resetMockDownload();
});
add_task(async () => {
info("File path sent when record updated.");
mockDownloadToDisk();
mockDownload();
const promiseSignal = TestUtils.topicObserved(SIGNAL);
await PublicSuffixList.init();
@ -124,53 +124,53 @@ add_task(async () => {
mockedFilePath,
"File path sent when record updated."
);
resetMockDownloadToDisk();
resetMockDownload();
});
add_task(async () => {
info("Attachment downloaded when record created.");
mockDownloadToDisk();
mockDownload();
await PublicSuffixList.init();
await CLIENT.emit("sync", { data: PAYLOAD_CREATED_RECORDS });
Assert.equal(
downloadToDiskCalled,
downloadCalled,
true,
"Attachment downloaded when record created."
);
resetMockDownloadToDisk();
resetMockDownload();
});
add_task(async () => {
info("Attachment downloaded when record updated.");
mockDownloadToDisk();
mockDownload();
await PublicSuffixList.init();
await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_RECORDS });
Assert.equal(
downloadToDiskCalled,
downloadCalled,
true,
"Attachment downloaded when record updated."
);
resetMockDownloadToDisk();
resetMockDownload();
});
add_task(async () => {
info("No download when more than one record is changed.");
mockDownloadToDisk();
mockDownload();
await PublicSuffixList.init();
await CLIENT.emit("sync", { data: PAYLOAD_UPDATED_AND_CREATED_RECORDS });
Assert.equal(
downloadToDiskCalled,
downloadCalled,
false,
"No download when more than one record is changed."
);
resetMockDownloadToDisk();
resetMockDownload();
});

View File

@ -610,7 +610,7 @@ class CRLiteFilters {
for (let filter of filtersToDownload) {
try {
// If we've already downloaded this, the backend should just grab it from its cache.
let localURI = await this.client.attachments.downloadToDisk(filter);
let localURI = await this.client.attachments.download(filter);
let buffer = await (await fetch(localURI)).arrayBuffer();
let bytes = new Uint8Array(buffer);
log.debug(`Downloaded ${filter.details.name}: ${bytes.length} bytes`);

View File

@ -116,37 +116,50 @@ class Downloader {
* If omitted, the attachmentId option must be set.
* @param {Object} options Some download options.
* @param {Number} options.retries Number of times download should be retried (default: `3`)
* @param {Boolean} options.checkHash Check content integrity (default: `true`)
* @param {Number} options.checkHash Check content integrity (default: `true`)
* @param {string} options.attachmentId The attachment identifier to use for
* caching and accessing the attachment.
* (default: `record.id`)
* (default: record.id)
* @param {Boolean} options.useCache Whether to use a cache to read and store
* the attachment. (default: false)
* @param {Boolean} options.fallbackToCache Return the cached attachment when the
* input record cannot be fetched.
* (default: `false`)
* (default: false)
* @param {Boolean} options.fallbackToDump Use the remote settings dump as a
* potential source of the attachment.
* (default: `false`)
* (default: false)
* @throws {Downloader.DownloadError} if the file could not be fetched.
* @throws {Downloader.BadContentError} if the downloaded content integrity is not valid.
* @returns {Object} An object with two properties:
* `buffer` `ArrayBuffer`: the file content.
* `record` `Object`: record associated with the attachment.
* `_source` `String`: identifies the source of the result. Used for testing.
* buffer: ArrayBuffer with the file content.
* record: Record associated with the bytes.
* _source: identifies the source of the result. Used for testing.
*/
async download(record, options) {
let {
retries,
checkHash,
attachmentId = record?.id,
useCache = false,
fallbackToCache = false,
fallbackToDump = false,
} = options || {};
if (!useCache) {
// For backwards compatibility.
// WARNING: Its return type is different from what's documented.
// See downloadToDisk's documentation.
return this.downloadToDisk(record, options);
}
if (!this.cacheImpl) {
throw new Error("useCache is true but there is no cacheImpl!");
}
if (!attachmentId) {
// Check for pre-condition. This should not happen, but it is explicitly
// checked to avoid mixing up attachments, which could be dangerous.
throw new Error(
"download() was called without attachmentId or `record.id`"
);
throw new Error("download() was called without attachmentId or recordID");
}
const dumpInfo = new LazyRecordAndBuffer(() =>
@ -171,7 +184,7 @@ class Downloader {
}
// Check if the requested attachment has already been cached.
if (record) {
if (useCache && record) {
if (await cacheInfo.isMatchingRequestedRecord(record)) {
try {
return { ...(await cacheInfo.getResult()), _source: "cache_match" };
@ -193,10 +206,12 @@ class Downloader {
checkHash,
});
const blob = new Blob([newBuffer]);
// Store in cache but don't wait for it before returning.
this.cacheImpl
.set(attachmentId, { record, blob })
.catch(e => Cu.reportError(e));
if (useCache) {
// Caching is optional, don't wait for the cache before returning.
this.cacheImpl
.set(attachmentId, { record, blob })
.catch(e => Cu.reportError(e));
}
return { buffer: newBuffer, record, _source: "remote_match" };
} catch (e) {
// No network, corrupted content, etc.
@ -248,30 +263,6 @@ class Downloader {
}
/**
* Delete the record attachment downloaded locally.
* No-op if the attachment does not exist.
*
* @param record A Remote Settings entry with attachment.
* @param {Object} options Some options.
* @param {string} options.attachmentId The attachment identifier to use for
* accessing and deleting the attachment.
* (default: `record.id`)
*/
async deleteDownloaded(record, options) {
let { attachmentId = record?.id } = options || {};
if (!attachmentId) {
// Check for pre-condition. This should not happen, but it is explicitly
// checked to avoid mixing up attachments, which could be dangerous.
throw new Error(
"deleteDownloaded() was called without attachmentId or `record.id`"
);
}
return this.cacheImpl.delete(attachmentId);
}
/**
* @deprecated See https://bugzilla.mozilla.org/show_bug.cgi?id=1634127
*
* Download the record attachment into the local profile directory
* and return a file:// URL that points to the local path.
*
@ -317,7 +308,7 @@ class Downloader {
checkHash: false, // Hash will be checked on file.
retries: 0, // Already in a retry loop.
});
await IOUtils.write(localFilePath, new Uint8Array(buffer), {
await OS.File.writeAtomic(localFilePath, buffer, {
tmpPath: `${localFilePath}.tmp`,
});
} catch (e) {
@ -335,7 +326,7 @@ class Downloader {
* @param {Object} record A Remote Settings entry with attachment.
* @param {Object} options Some download options.
* @param {Number} options.retries Number of times download should be retried (default: `3`)
* @param {Boolean} options.checkHash Check content integrity (default: `true`)
* @param {Number} options.checkHash Check content integrity (default: `true`)
* @throws {Downloader.DownloadError} if the file could not be fetched.
* @throws {Downloader.BadContentError} if the downloaded content integrity is not valid.
* @returns {ArrayBuffer} the file content.
@ -370,18 +361,12 @@ class Downloader {
}
/**
* @deprecated See https://bugzilla.mozilla.org/show_bug.cgi?id=1634127
*
* Delete the record attachment downloaded locally.
* This is the counterpart of `downloadToDisk()`.
* Use `deleteDownloaded()` if `download()` was used to retrieve
* the attachment.
*
* No-op if the related file does not exist.
*
* @param record A Remote Settings entry with attachment.
*/
async deleteFromDisk(record) {
async delete(record) {
const {
attachment: { filename },
} = record;
@ -390,10 +375,14 @@ class Downloader {
...this.folders,
filename
);
await IOUtils.remove(path);
await OS.File.remove(path, { ignoreAbsent: true });
await this._rmDirs();
}
async deleteCached(attachmentId) {
return this.cacheImpl.delete(attachmentId);
}
async _baseAttachmentsURL() {
if (!this._cdnURL) {
const server = Utils.SERVER_URL;
@ -468,7 +457,7 @@ class Downloader {
OS.Constants.Path.localProfileDir,
...this.folders
);
await IOUtils.makeDirectory(dirPath, { createAncestors: true });
await OS.File.makeDir(dirPath, { from: OS.Constants.Path.localProfileDir });
}
async _rmDirs() {
@ -478,7 +467,7 @@ class Downloader {
...this.folders.slice(0, i)
);
try {
await IOUtils.remove(dirPath);
await OS.File.removeEmptyDir(dirPath, { ignoreAbsent: true });
} catch (e) {
// This could fail if there's something in
// the folder we're not permitted to remove.

View File

@ -216,11 +216,7 @@ class AttachmentDownloader extends Downloader {
async deleteAll() {
let allRecords = await this._client.db.list();
return Promise.all(
allRecords
.filter(r => !!r.attachment)
.map(r =>
Promise.all([this.deleteDownloaded(r), this.deleteFromDisk(r)])
)
allRecords.filter(r => !!r.attachment).map(r => this.delete(r))
);
}
}

View File

@ -117,25 +117,27 @@ Remote files are not downloaded automatically. In order to keep attachments in s
.filter(d => d.attachment);
// Remove local files of deleted records
await Promise.all(
toDelete.map(record => client.attachments.deleteDownloaded(record))
await Promise.all(toDelete.map(entry => client.attachments.delete(entry)));
// Download attachments
const fileURLs = await Promise.all(
toDownload.map(entry => client.attachments.download(entry, { retries: 2 }))
);
// Download new attachments
// Open downloaded files...
const fileContents = await Promise.all(
toDownload.map(async record => {
const { buffer } = await client.attachments.download(record);
return buffer;
});
fileURLs.map(async url => {
const r = await fetch(url);
return r.blob();
})
);
});
The provided helper will:
- fetch the remote binary content
- write the file in the local IndexedDB
- write the file in the profile folder
- check the file size
- check the content SHA256 hash
- do nothing if the attachment was already present and sound locally.
- do nothing if the file is already present and sound locally.
.. important::
@ -147,22 +149,27 @@ The provided helper will:
.. note::
The ``download()`` method supports the following options:
- ``retries`` (default: ``3``): number of retries on network errors
- ``fallbackToCache`` (default: ``false``): allows callers to fall back to the cached file and record, if the requested record's attachment fails to download.
This enables callers to always have a valid pair of attachment and record,
provided that the attachment has been retrieved at least once.
- ``fallbackToDump`` (default: ``false``): activates a fallback to a dump that has been
packaged with the client, when other ways to load the attachment have failed.
See :ref:`services/packaging-attachments <services/packaging-attachments>` for more information.
The ``download()`` method does not return a file path but instead a ``file://`` URL which points to the locally-downloaded file.
This will allow us to package attachments as part of a Firefox release (see `Bug 1542177 <https://bugzilla.mozilla.org/show_bug.cgi?id=1542177>`_)
and return them to calling code as ``resource://`` from within a package archive.
.. note::
A ``downloadAsBytes()`` method returning an ``ArrayBuffer`` is also available, if writing the attachment locally is not necessary.
By default, the ``download()`` method is prone to leaving extraneous files in the profile directory
(see `Bug 1634127 <https://bugzilla.mozilla.org/show_bug.cgi?id=1634127>`_).
Pass the ``useCache`` option to use an IndexedDB-based cache, and unlock the following features:
Some ``downloadToDisk()`` and ``deleteFromDisk()`` methods are also available but generally discouraged, since they are prone to leaving extraneous files
in the profile directory (see `Bug 1634127 <https://bugzilla.mozilla.org/show_bug.cgi?id=1634127>`_).
The ``fallbackToCache`` option allows callers to fall back to the cached file and record, if the requested record's attachment fails to download.
This enables callers to always have a valid pair of attachment and record,
provided that the attachment has been retrieved at least once.
The ``fallbackToDump`` option activates a fallback to a dump that has been
packaged with the client, when other ways to load the attachment have failed.
See :ref:`services/packaging-attachments <services/packaging-attachments>` for more information.
.. note::
A ``downloadAsBytes()`` method returning an ``ArrayBuffer`` is also available, if writing the attachment into the user profile is not necessary.
.. _services/initial-data:

View File

@ -85,34 +85,21 @@ function run_test() {
async function clear_state() {
downloader = new Downloader("main", "some-collection");
const dummyCacheImpl = {
get: async attachmentId => {},
set: async (attachmentId, attachment) => {},
delete: async attachmentId => {},
};
// The download() method requires a cacheImpl, but the Downloader
// class does not have one. Define a dummy no-op one.
Object.defineProperty(downloader, "cacheImpl", {
value: dummyCacheImpl,
// Writable to allow specific tests to override cacheImpl.
writable: true,
});
await downloader.deleteDownloaded(RECORD);
await downloader.delete(RECORD);
}
add_task(clear_state);
add_task(async function test_download_writes_file_in_profile() {
const fileURL = await downloader.downloadToDisk(RECORD);
const fileURL = await downloader.download(RECORD);
const localFilePath = pathFromURL(fileURL);
Assert.equal(
fileURL,
PROFILE_URL + "/settings/main/some-collection/test_file.pem"
);
Assert.ok(await IOUtils.exists(localFilePath));
const stat = await IOUtils.stat(localFilePath);
Assert.ok(await OS.File.exists(localFilePath));
const stat = await OS.File.stat(localFilePath);
Assert.equal(stat.size, 1597);
});
add_task(clear_state);
@ -126,38 +113,39 @@ add_task(async function test_download_as_bytes() {
add_task(clear_state);
add_task(async function test_file_is_redownloaded_if_size_does_not_match() {
const fileURL = await downloader.downloadToDisk(RECORD);
const fileURL = await downloader.download(RECORD);
const localFilePath = pathFromURL(fileURL);
await IOUtils.writeUTF8(localFilePath, "bad-content");
let stat = await IOUtils.stat(localFilePath);
await OS.File.writeAtomic(localFilePath, "bad-content", {
encoding: "utf-8",
});
let stat = await OS.File.stat(localFilePath);
Assert.notEqual(stat.size, 1597);
await downloader.downloadToDisk(RECORD);
await downloader.download(RECORD);
stat = await IOUtils.stat(localFilePath);
stat = await OS.File.stat(localFilePath);
Assert.equal(stat.size, 1597);
});
add_task(clear_state);
add_task(async function test_file_is_redownloaded_if_corrupted() {
const fileURL = await downloader.downloadToDisk(RECORD);
const fileURL = await downloader.download(RECORD);
const localFilePath = pathFromURL(fileURL);
const byteArray = await IOUtils.read(localFilePath);
const byteArray = await OS.File.read(localFilePath);
byteArray[0] = 42;
await IOUtils.write(localFilePath, byteArray);
let content = await IOUtils.readUTF8(localFilePath);
await OS.File.writeAtomic(localFilePath, byteArray);
let content = await OS.File.read(localFilePath, { encoding: "utf-8" });
Assert.notEqual(content.slice(0, 5), "-----");
await downloader.downloadToDisk(RECORD);
await downloader.download(RECORD);
content = await IOUtils.readUTF8(localFilePath);
content = await OS.File.read(localFilePath, { encoding: "utf-8" });
Assert.equal(content.slice(0, 5), "-----");
});
add_task(clear_state);
add_task(async function test_download_is_retried_3_times_if_download_fails() {
const record = {
id: "abc",
attachment: {
...RECORD.attachment,
location: "404-error.pem",
@ -185,7 +173,6 @@ add_task(clear_state);
add_task(async function test_download_is_retried_3_times_if_content_fails() {
const record = {
id: "abc",
attachment: {
...RECORD.attachment,
hash: "always-wrong",
@ -210,41 +197,38 @@ add_task(async function test_download_is_retried_3_times_if_content_fails() {
add_task(clear_state);
add_task(async function test_delete_removes_local_file() {
const fileURL = await downloader.downloadToDisk(RECORD);
const fileURL = await downloader.download(RECORD);
const localFilePath = pathFromURL(fileURL);
Assert.ok(await IOUtils.exists(localFilePath));
Assert.ok(await OS.File.exists(localFilePath));
await downloader.deleteFromDisk(RECORD);
downloader.delete(RECORD);
Assert.ok(!(await IOUtils.exists(localFilePath)));
// And removes parent folders.
const parentFolder = OS.Path.join(
OS.Constants.Path.localProfileDir,
...downloader.folders
Assert.ok(!(await OS.File.exists(localFilePath)));
Assert.ok(
!(await OS.File.exists(
OS.Path.join(OS.Constants.Path.localProfileDir, ...downloader.folders)
))
);
Assert.ok(!(await IOUtils.exists(parentFolder)));
});
add_task(clear_state);
add_task(async function test_delete_all() {
const client = RemoteSettings("some-collection");
await client.db.create(RECORD);
await downloader.download(RECORD);
const fileURL = await downloader.downloadToDisk(RECORD);
const fileURL = await downloader.download(RECORD);
const localFilePath = pathFromURL(fileURL);
Assert.ok(await IOUtils.exists(localFilePath));
Assert.ok(await OS.File.exists(localFilePath));
await client.attachments.deleteAll();
Assert.ok(!(await IOUtils.exists(localFilePath)));
Assert.ok(!(await client.attachments.cacheImpl.get(RECORD.id)));
Assert.ok(!(await OS.File.exists(localFilePath)));
});
add_task(clear_state);
add_task(async function test_downloader_is_accessible_via_client() {
const client = RemoteSettings("some-collection");
const fileURL = await client.attachments.downloadToDisk(RECORD);
const fileURL = await client.attachments.download(RECORD);
Assert.equal(
fileURL,
@ -348,7 +332,9 @@ async function doTestDownloadCacheImpl({ simulateCorruption }) {
};
Object.defineProperty(downloader, "cacheImpl", { value: cacheImpl });
let downloadResult = await downloader.download(RECORD);
let downloadResult = await downloader.download(RECORD, {
useCache: true,
});
Assert.equal(downloadResult._source, "remote_match", "expected source");
Assert.equal(downloadResult.buffer.byteLength, 1597, "expected result");
Assert.equal(readCount, 1, "expected cache read attempts");
@ -444,7 +430,7 @@ add_task(async function test_download_cached() {
checkInfo(info5, "cache_fallback", "fallbackToCache ignores bad record");
// Bye bye cache.
await client.attachments.deleteDownloaded({ id: attachmentId });
await client.attachments.deleteCached(attachmentId);
await Assert.rejects(
downloadWithCache(null, { attachmentId, fallbackToCache: true }),
/DownloadError: Could not download dummy filename/,
@ -487,6 +473,7 @@ add_task(async function test_download_from_dump() {
// If record matches, should happen before network request.
const dump1 = await client.attachments.download(RECORD_OF_DUMP, {
// Note: attachmentId not set, so should fall back to record.id.
useCache: true,
fallbackToDump: true,
});
checkInfo(dump1, "dump_match");
@ -494,6 +481,7 @@ add_task(async function test_download_from_dump() {
// If no record given, should try network first, but then fall back to dump.
const dump2 = await client.attachments.download(null, {
attachmentId: RECORD_OF_DUMP.id,
useCache: true,
fallbackToDump: true,
});
checkInfo(dump2, "dump_fallback");
@ -505,6 +493,7 @@ add_task(async function test_download_from_dump() {
});
// The dump should take precedence over the cache.
const dump3 = await client.attachments.download(RECORD_OF_DUMP, {
useCache: true,
fallbackToCache: true,
fallbackToDump: true,
});
@ -514,6 +503,7 @@ add_task(async function test_download_from_dump() {
// as a fallback (when the cache and dump are identical).
const dump4 = await client.attachments.download(null, {
attachmentId: RECORD_OF_DUMP.id,
useCache: true,
fallbackToCache: true,
fallbackToDump: true,
});
@ -532,6 +522,7 @@ add_task(async function test_download_from_dump() {
// When the record is not given, use the cache if it has a more recent record.
const dump5 = await client.attachments.download(null, {
attachmentId: RECORD_OF_DUMP.id,
useCache: true,
fallbackToCache: true,
fallbackToDump: true,
});
@ -539,21 +530,24 @@ add_task(async function test_download_from_dump() {
// When a record is given, use whichever that has the matching last_modified.
const dump6 = await client.attachments.download(RECORD_OF_DUMP, {
useCache: true,
fallbackToCache: true,
fallbackToDump: true,
});
checkInfo(dump6, "dump_match");
const dump7 = await client.attachments.download(RECORD_NEWER_THAN_DUMP, {
useCache: true,
fallbackToCache: true,
fallbackToDump: true,
});
checkInfo(dump7, "cache_match", RECORD_NEWER_THAN_DUMP);
await client.attachments.deleteDownloaded(RECORD_OF_DUMP);
await client.attachments.deleteCached(RECORD_OF_DUMP.id);
await Assert.rejects(
client.attachments.download(null, {
attachmentId: "filename-without-meta.txt",
useCache: true,
fallbackToDump: true,
}),
/DownloadError: Could not download filename-without-meta.txt/,
@ -563,6 +557,7 @@ add_task(async function test_download_from_dump() {
await Assert.rejects(
client.attachments.download(null, {
attachmentId: "filename-without-content.txt",
useCache: true,
fallbackToDump: true,
}),
/Could not download resource:\/\/rs-downloader-test\/settings\/dump-bucket\/dump-collection\/filename-without-content\.txt(?!\.meta\.json)/,

View File

@ -446,7 +446,7 @@ class RegionDetector {
const toDelete = deleted.filter(d => d.attachment);
// Remove local files of deleted records
await Promise.all(
toDelete.map(entry => this._rsClient.attachments.deleteDownloaded(entry))
toDelete.map(entry => this._rsClient.attachments.delete(entry))
);
await this._ensureRegionFilesDownloaded();
}
@ -464,7 +464,10 @@ class RegionDetector {
log.info("_ensureRegionFilesDownloaded: Nothing to download");
return;
}
await Promise.all(records.map(r => this._rsClient.attachments.download(r)));
let opts = { useCache: true };
await Promise.all(
records.map(r => this._rsClient.attachments.download(r, opts))
);
log.info("_ensureRegionFilesDownloaded complete");
this._regionFilesReady = true;
}
@ -477,7 +480,9 @@ class RegionDetector {
*/
async _fetchAttachment(id) {
let record = (await this._rsClient.get({ filters: { id } })).pop();
let { buffer } = await this._rsClient.attachments.download(record);
let { buffer } = await this._rsClient.attachments.download(record, {
useCache: true,
});
let text = new TextDecoder("utf-8").decode(buffer);
return JSON.parse(text);
}

View File

@ -976,6 +976,7 @@ const ExtensionBlocklistMLBF = {
_source: rsAttachmentSource,
} = await this._client.attachments.download(record, {
attachmentId: this.RS_ATTACHMENT_ID,
useCache: true,
fallbackToCache: true,
fallbackToDump: true,
});