Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor MailboxManager and MailApiFetcher #914

Merged
merged 8 commits into from
Aug 10, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ struct SettingsNotificationsView: View {
func updateTopicsForCurrentUserIfNeeded() {
Task {
guard let subscribedTopics else { return }

await notificationService.updateTopicsIfNeeded(subscribedTopics, userApiFetcher: mailboxManager.apiFetcher)
}
}
Expand Down
42 changes: 42 additions & 0 deletions MailCore/API/Extension/Realm+SafeWrite.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2022 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Foundation
import RealmSwift

public extension Realm {
func uncheckedSafeWrite(_ block: () throws -> Void) throws {
if isInWriteTransaction {
try block()
} else {
try write(block)
}
}

func safeWrite(_ block: () throws -> Void) throws {
#if DEBUG
dispatchPrecondition(condition: .notOnQueue(.main))
#endif

if isInWriteTransaction {
try block()
} else {
try write(block)
}
}
}
499 changes: 0 additions & 499 deletions MailCore/API/MailApiFetcher.swift

This file was deleted.

123 changes: 123 additions & 0 deletions MailCore/API/MailApiFetcher/MailApiFetchable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2022 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Foundation
import Alamofire

/// Public interface of `MailApiFetcher`
public typealias MailApiFetchable = MailApiCommonFetchable & MailApiExtendedFetchable

/// Main interface of the `MailApiFetcher`
public protocol MailApiCommonFetchable {
func mailboxes() async throws -> [Mailbox]

func addMailbox(mail: String, password: String) async throws -> MailboxLinkedResult

func updateMailboxPassword(mailbox: Mailbox, password: String) async throws -> Bool

func detachMailbox(mailbox: Mailbox) async throws -> Bool

func listBackups(mailbox: Mailbox) async throws -> BackupsList

func restoreBackup(mailbox: Mailbox, date: String) async throws -> Bool

func threads(mailbox: Mailbox,
folderId: String,
filter: Filter,
searchFilter: [URLQueryItem],
isDraftFolder: Bool) async throws -> ThreadResult

func threads(from resource: String, searchFilter: [URLQueryItem]) async throws -> ThreadResult

func download(message: Message) async throws -> URL

func quotas(mailbox: Mailbox) async throws -> Quotas

func undoAction(resource: String) async throws -> Bool

func star(mailbox: Mailbox, messages: [Message]) async throws -> MessageActionResult

func unstar(mailbox: Mailbox, messages: [Message]) async throws -> MessageActionResult

func downloadAttachments(message: Message) async throws -> URL

func blockSender(message: Message) async throws -> Bool

func reportPhishing(message: Message) async throws -> Bool

func create(mailbox: Mailbox, folder: NewFolder) async throws -> Folder

func createAttachment(mailbox: Mailbox,
attachmentData: Data,
attachment: Attachment,
progressObserver: @escaping (Double) -> Void) async throws -> Attachment

func attachmentsToForward(mailbox: Mailbox, message: Message) async throws -> AttachmentsToForwardResult
}

/// Extended capabilities of the `MailApiFetcher`
public protocol MailApiExtendedFetchable {

func permissions(mailbox: Mailbox) async throws -> MailboxPermissions

func contacts() async throws -> [Contact]

func addressBooks() async throws -> AddressBookResult

func addContact(_ recipient: Recipient, to addressBook: AddressBook) async throws -> Int

func signatures(mailbox: Mailbox) async throws -> SignatureResponse

func updateSignature(mailbox: Mailbox, signature: Signature) async throws -> Bool

func folders(mailbox: Mailbox) async throws -> [Folder]

func flushFolder(mailbox: Mailbox, folderId: String) async throws -> Bool

func messagesUids(mailboxUuid: String,
folderId: String,
paginationInfo: PaginationInfo?) async throws -> MessageUidsResult

func messagesByUids(mailboxUuid: String, folderId: String, messageUids: [String]) async throws -> MessageByUidsResult

func messagesDelta(mailboxUUid: String, folderId: String, signature: String) async throws -> MessageDeltaResult

func message(message: Message) async throws -> Message

func markAsSeen(mailbox: Mailbox, messages: [Message]) async throws -> MessageActionResult

func markAsUnseen(mailbox: Mailbox, messages: [Message]) async throws -> MessageActionResult

func move(mailbox: Mailbox, messages: [Message], destinationId: String) async throws -> UndoResponse

func delete(mailbox: Mailbox, messages: [Message]) async throws -> Empty

func attachment(attachment: Attachment) async throws -> Data

func draft(mailbox: Mailbox, draftUuid: String) async throws -> Draft

func draft(from message: Message) async throws -> Draft

func send(mailbox: Mailbox, draft: Draft) async throws -> SendResponse

func save(mailbox: Mailbox, draft: Draft) async throws -> DraftResponse

func deleteDraft(mailbox: Mailbox, draftId: String) async throws -> Empty?

func deleteDraft(draftResource: String) async throws -> Empty?
}
164 changes: 164 additions & 0 deletions MailCore/API/MailApiFetcher/MailApiFetcher+Common.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2022 Infomaniak Network SA

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import Alamofire
import Foundation
import InfomaniakCore
import InfomaniakLogin

/// implementing `MailApiCommonFetchable`
public extension MailApiFetcher {
// MARK: - API methods

func mailboxes() async throws -> [Mailbox] {
try await perform(request: authenticatedRequest(.mailboxes)).data
}

func addMailbox(mail: String, password: String) async throws -> MailboxLinkedResult {
try await perform(request: authenticatedRequest(
.addMailbox,
method: .post,
parameters: ["mail": mail, "password": password, "is_primary": false]
)).data
}

func updateMailboxPassword(mailbox: Mailbox, password: String) async throws -> Bool {
try await perform(request: authenticatedRequest(
.updateMailboxPassword(mailboxId: mailbox.mailboxId),
method: .put,
parameters: ["password": password]
)).data
}

func detachMailbox(mailbox: Mailbox) async throws -> Bool {
try await perform(request: authenticatedRequest(.detachMailbox(mailboxId: mailbox.mailboxId), method: .delete)).data
}

func listBackups(mailbox: Mailbox) async throws -> BackupsList {
try await perform(request: authenticatedRequest(.backups(hostingId: mailbox.hostingId, mailboxName: mailbox.mailbox)))
.data
}

@discardableResult
func restoreBackup(mailbox: Mailbox, date: String) async throws -> Bool {
try await perform(request: authenticatedRequest(.backups(hostingId: mailbox.hostingId, mailboxName: mailbox.mailbox),
method: .put,
parameters: ["date": date])).data
}

func threads(mailbox: Mailbox, folderId: String, filter: Filter = .all,
searchFilter: [URLQueryItem] = [], isDraftFolder: Bool = false) async throws -> ThreadResult {
try await perform(request: authenticatedRequest(.threads(
uuid: mailbox.uuid,
folderId: folderId,
filter: filter == .all ? nil : filter.rawValue,
searchFilters: searchFilter,
isDraftFolder: isDraftFolder
))).data
}

func threads(from resource: String, searchFilter: [URLQueryItem] = []) async throws -> ThreadResult {
try await perform(request: authenticatedRequest(.resource(resource, queryItems: searchFilter))).data
}

func download(message: Message) async throws -> URL {
let destination = DownloadRequest.suggestedDownloadDestination(options: [
.createIntermediateDirectories,
.removePreviousFile
])
let download = authenticatedSession.download(Endpoint.resource(message.downloadResource).url, to: destination)
return try await download.serializingDownloadedFileURL().value
}

func quotas(mailbox: Mailbox) async throws -> Quotas {
try await perform(request: authenticatedRequest(.quotas(mailbox: mailbox.mailbox, productId: mailbox.hostingId))).data
}

@discardableResult
func undoAction(resource: String) async throws -> Bool {
try await perform(request: authenticatedRequest(.resource(resource), method: .post)).data
}

func star(mailbox: Mailbox, messages: [Message]) async throws -> MessageActionResult {
try await perform(request: authenticatedRequest(.star(uuid: mailbox.uuid),
method: .post,
parameters: ["uids": messages.map(\.uid)])).data
}

func unstar(mailbox: Mailbox, messages: [Message]) async throws -> MessageActionResult {
try await perform(request: authenticatedRequest(.unstar(uuid: mailbox.uuid),
method: .post,
parameters: ["uids": messages.map(\.uid)])).data
}

func downloadAttachments(message: Message) async throws -> URL {
let destination = DownloadRequest.suggestedDownloadDestination(options: [
.createIntermediateDirectories,
.removePreviousFile
])
let download = authenticatedSession.download(
Endpoint.downloadAttachments(messageResource: message.resource).url,
to: destination
)
return try await download.serializingDownloadedFileURL().value
}

func blockSender(message: Message) async throws -> Bool {
try await perform(request: authenticatedRequest(.blockSender(messageResource: message.resource), method: .post)).data
}

func reportPhishing(message: Message) async throws -> Bool {
try await perform(request: authenticatedRequest(.report(messageResource: message.resource),
method: .post,
parameters: ["type": "phishing"])).data
}

func create(mailbox: Mailbox, folder: NewFolder) async throws -> Folder {
try await perform(request: authenticatedRequest(.folders(uuid: mailbox.uuid), method: .post, parameters: folder)).data
}

func createAttachment(
mailbox: Mailbox,
attachmentData: Data,
attachment: Attachment,
progressObserver: @escaping (Double) -> Void
) async throws -> Attachment {
let headers = HTTPHeaders([
"x-ws-attachment-filename": attachment.name,
"x-ws-attachment-mime-type": attachment.mimeType,
"x-ws-attachment-disposition": attachment.disposition.rawValue
])
var request = try URLRequest(url: Endpoint.createAttachment(uuid: mailbox.uuid).url, method: .post, headers: headers)
request.httpBody = attachmentData

let uploadRequest = authenticatedSession.request(request)
Task {
for await progress in uploadRequest.uploadProgress() {
progressObserver(progress.fractionCompleted)
}
}

return try await perform(request: uploadRequest).data
}

func attachmentsToForward(mailbox: Mailbox, message: Message) async throws -> AttachmentsToForwardResult {
let attachmentsToForward = AttachmentsToForward(toForwardUids: [message.uid], mode: AttachmentDisposition.inline.rawValue)
return try await perform(request: authenticatedRequest(.attachmentToForward(uuid: mailbox.uuid), method: .post,
parameters: attachmentsToForward)).data
}
}
Loading
Loading