diff --git a/src/common/api/worker/facades/lazy/MailExportFacade.ts b/src/common/api/worker/facades/lazy/MailExportFacade.ts index 4ca5eef339bd..4119223c3fac 100644 --- a/src/common/api/worker/facades/lazy/MailExportFacade.ts +++ b/src/common/api/worker/facades/lazy/MailExportFacade.ts @@ -50,8 +50,8 @@ export class MailExportFacade { ) } - async loadMailDetails(mails: readonly Mail[]): Promise { - return this.mailExportTokenFacade.loadWithToken((token) => this.bulkMailLoader.loadMailDetails(mails, this.options(token))) + async loadMailDetails(mails: readonly Mail[], baseUrl: string): Promise { + return this.mailExportTokenFacade.loadWithToken((token) => this.bulkMailLoader.loadMailDetails(mails, { baseUrl, ...this.options(token) })) } async loadAttachments(mails: readonly Mail[], baseUrl: string): Promise { diff --git a/src/common/api/worker/rest/EntityRestClient.ts b/src/common/api/worker/rest/EntityRestClient.ts index a72ce5bb0f76..3670b155323e 100644 --- a/src/common/api/worker/rest/EntityRestClient.ts +++ b/src/common/api/worker/rest/EntityRestClient.ts @@ -1,4 +1,4 @@ -import { RestClient, SuspensionBehavior } from "./RestClient" +import type { RestClient, SuspensionBehavior } from "./RestClient" import type { CryptoFacade } from "../crypto/CryptoFacade" import { _verifyType, HttpMethod, MediaType, resolveTypeReference } from "../../common/EntityFunctions" import { SessionKeyNotFoundError } from "../../common/error/SessionKeyNotFoundError" @@ -287,7 +287,7 @@ export class EntityRestClient implements EntityRestInterface { } let json: string if (typeModel.type === Type.BlobElement) { - json = await this.loadMultipleBlobElements(listId, queryParams, headers, path, typeRef, opts.suspensionBehavior) + json = await this.loadMultipleBlobElements(listId, queryParams, headers, path, typeRef, opts) } else { json = await this.restClient.request(path, HttpMethod.GET, { queryParams, @@ -308,7 +308,7 @@ export class EntityRestClient implements EntityRestInterface { headers: Dict | undefined, path: string, typeRef: TypeRef, - suspensionBehavior?: SuspensionBehavior, + opts: EntityRestClientLoadOptions = {}, ): Promise { if (archiveId == null) { throw new Error("archiveId must be set to load BlobElementTypes") @@ -321,8 +321,20 @@ export class EntityRestClient implements EntityRestInterface { queryParams, ) const allParams = await this.blobAccessTokenFacade.createQueryParams(blobServerAccessInfo, additionalRequestParams, typeRef) + + let preferredServer: BlobServerUrl | null = null + if (opts.baseUrl != null) { + preferredServer = blobServerAccessInfo.servers.find((server) => server.url === opts.baseUrl) ?? null + } + + const serversToTry = + preferredServer != null + ? // preferredServer takes precedence over the reset + [preferredServer, ...blobServerAccessInfo.servers.filter((server) => server.url !== opts.baseUrl)] + : blobServerAccessInfo.servers + return tryServers( - blobServerAccessInfo.servers, + serversToTry, async (serverUrl) => this.restClient.request(path, HttpMethod.GET, { queryParams: allParams, @@ -330,7 +342,7 @@ export class EntityRestClient implements EntityRestInterface { responseType: MediaType.Json, baseUrl: serverUrl, noCORS: true, - suspensionBehavior, + suspensionBehavior: opts.suspensionBehavior, }), `can't load instances from server `, ) diff --git a/src/mail-app/native/main/MailExportController.ts b/src/mail-app/native/main/MailExportController.ts index 403e99298a5d..3ec996811f38 100644 --- a/src/mail-app/native/main/MailExportController.ts +++ b/src/mail-app/native/main/MailExportController.ts @@ -163,7 +163,7 @@ export class MailExportController { break } - const downloadedMailDetails = await this.mailExportFacade.loadMailDetails(downloadedMails) + const downloadedMailDetails = await this.mailExportFacade.loadMailDetails(downloadedMails, this.getServerUrl()) const attachmentInfo = await this.mailExportFacade.loadAttachments(downloadedMails, this.getServerUrl()) for (const { mail, mailDetails } of downloadedMailDetails) { if (this._state().type !== "exporting") { diff --git a/test/tests/api/worker/facades/MailExportFacadeTest.ts b/test/tests/api/worker/facades/MailExportFacadeTest.ts index 4f0aad53f97f..a08b88fed652 100644 --- a/test/tests/api/worker/facades/MailExportFacadeTest.ts +++ b/test/tests/api/worker/facades/MailExportFacadeTest.ts @@ -57,9 +57,15 @@ o.spec("MailExportFacade", () => { { mail: mail1, mailDetails: details1 }, { mail: mail2, mailDetails: details2 }, ] - when(bulkMailLoader.loadMailDetails([mail1, mail2], { extraHeaders: tokenHeaders, suspensionBehavior: SuspensionBehavior.Throw })).thenResolve(expected) + when( + bulkMailLoader.loadMailDetails([mail1, mail2], { + baseUrl: "baseUrl", + extraHeaders: tokenHeaders, + suspensionBehavior: SuspensionBehavior.Throw, + }), + ).thenResolve(expected) - const result = await facade.loadMailDetails([mail1, mail2]) + const result = await facade.loadMailDetails([mail1, mail2], "baseUrl") o(result).deepEquals(expected) }) diff --git a/test/tests/native/main/MailExportControllerTest.ts b/test/tests/native/main/MailExportControllerTest.ts index bd667cd73e44..9eab76880b3a 100644 --- a/test/tests/native/main/MailExportControllerTest.ts +++ b/test/tests/native/main/MailExportControllerTest.ts @@ -96,7 +96,7 @@ o.spec("MailExportController", function () { const attachmentData = new Uint8Array([1, 2, 3]) const dataFile = createDataFile("test", "application/octet-stream", attachmentData) when(mailExportFacade.loadFixedNumberOfMailsWithCache(mailBag.mails, startId, matchers.anything())).thenResolve([mail]) - when(mailExportFacade.loadMailDetails([mail])).thenResolve([{ mail, mailDetails }]) + when(mailExportFacade.loadMailDetails([mail], matchers.anything())).thenResolve([{ mail, mailDetails }]) when(mailExportFacade.loadAttachments([mail], matchers.anything())).thenResolve([attachmentInfo]) when(mailExportFacade.loadAttachmentData(mail, [attachmentInfo])).thenResolve([dataFile]) @@ -217,7 +217,13 @@ o.spec("MailExportController", function () { const archivedMailBag2 = mailboxDetail.mailbox.archivedMailBags[1] const { mail: mail3, mailBundle: mailBundle3 } = prepareMailData(archivedMailBag2, GENERATED_MAX_ID, 3) when(mailExportFacade.loadFixedNumberOfMailsWithCache(archivedMailBag2.mails, getElementId(mail3), "baseUrl")).thenResolve([]) - when(mailExportFacade.getExportServers(mailboxDetail.mailGroup)).thenResolve([{ _id: "id", url: "baseUrl", _type: BlobServerUrlTypeRef }]) + when(mailExportFacade.getExportServers(mailboxDetail.mailGroup)).thenResolve([ + { + _id: "id", + url: "baseUrl", + _type: BlobServerUrlTypeRef, + }, + ]) await controller.startExport(mailboxDetail) @@ -241,14 +247,21 @@ o.spec("MailExportController", function () { await controller.startExport(mailboxDetail) verify(mailExportFacade.loadFixedNumberOfMailsWithCache(currentMailBag.mails, GENERATED_MAX_ID, "baseUrl2")) - verify(mailExportFacade.loadAttachments([mail1], "baseUrl3")) + verify(mailExportFacade.loadMailDetails([mail1], "baseUrl3")) + verify(mailExportFacade.loadAttachments([mail1], "baseUrl1")) verify(mailExportFacade.loadAttachmentData(mail1, matchers.anything())) }) }) o.spec("handle errors", function () { o.test("SuspensionError", async () => { - when(mailExportFacade.getExportServers(mailboxDetail.mailGroup)).thenResolve([{ _id: "id", url: "baseUrl", _type: BlobServerUrlTypeRef }]) + when(mailExportFacade.getExportServers(mailboxDetail.mailGroup)).thenResolve([ + { + _id: "id", + url: "baseUrl", + _type: BlobServerUrlTypeRef, + }, + ]) let wasThrown = false when(mailExportFacade.loadFixedNumberOfMailsWithCache(matchers.anything(), matchers.anything(), matchers.anything())).thenDo(() => { if (wasThrown) {