session-ios/SessionMessagingKit/Jobs/Types/AttachmentUploadJob.swift
Morgan Pretty aed1b73185 Fixed a few additional issues uncovered
Added a explicit "timeout" error to make debugging a little easier
Added code to prevent the AttachmentUploadJob from continuing to try to upload if it's associated interaction has been deleted
Updated the getDefaultRoomsIfNeeded to make an unauthenticated sequence all to get both capabilities and rooms (so we will know if the server is blinded and retrieve the room images using blinded auth)
Fixed a bug where the notification badge wouldn't get cleared when removing data from a device
Fixed a bug where adding an open group could start with an invalid 'infoUpdates' value resulting in invalid data getting retrieved
Fixed a bug where under certain circumstances the PagedDatabaseObserver was filtering out updates (noticeable when restoring a device, would happen if the currentCount of content was smaller than the pageSize)
2022-07-26 11:36:32 +10:00

93 lines
3.6 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import PromiseKit
import SignalCoreKit
import SessionUtilitiesKit
public enum AttachmentUploadJob: JobExecutor {
public static var maxFailureCount: Int = 10
public static var requiresThreadId: Bool = true
public static let requiresInteractionId: Bool = true
public static func run(
_ job: Job,
queue: DispatchQueue,
success: @escaping (Job, Bool) -> (),
failure: @escaping (Job, Error?, Bool) -> (),
deferred: @escaping (Job) -> ()
) {
guard
let threadId: String = job.threadId,
let detailsData: Data = job.details,
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData),
let (attachment, openGroup): (Attachment, OpenGroup?) = Storage.shared.read({ db in
guard let attachment: Attachment = try Attachment.fetchOne(db, id: details.attachmentId) else {
return nil
}
return (attachment, try OpenGroup.fetchOne(db, id: threadId))
})
else {
failure(job, JobRunnerError.missingRequiredDetails, false)
return
}
// If the original interaction no longer exists then don't bother uploading the attachment (ie. the
// message was deleted before it even got sent)
if let interactionId: Int64 = job.interactionId {
guard Storage.shared.read({ db in try Interaction.exists(db, id: interactionId) }) == true else {
failure(job, StorageError.objectNotFound, true)
return
}
}
// Note: In the AttachmentUploadJob we intentionally don't provide our own db instance to prevent reentrancy
// issues when the success/failure closures get called before the upload as the JobRunner will attempt to
// update the state of the job immediately
attachment.upload(
queue: queue,
using: { db, data in
if let openGroup: OpenGroup = openGroup {
return OpenGroupAPI
.uploadFile(
db,
bytes: data.bytes,
to: openGroup.roomToken,
on: openGroup.server
)
.map { _, response -> String in response.id }
}
return FileServerAPI.upload(data)
.map { response -> String in response.id }
},
encrypt: (openGroup == nil),
success: { _ in success(job, false) },
failure: { error in failure(job, error, false) }
)
}
}
// MARK: - AttachmentUploadJob.Details
extension AttachmentUploadJob {
public struct Details: Codable {
/// This is the id for the messageSend job this attachmentUpload job is associated to, the value isn't used for any of
/// the logic but we want to mandate that the attachmentUpload job can only be used alongside a messageSend job
///
/// **Note:** If we do decide to remove this the `_003_YDBToGRDBMigration` will need to be updated as it
/// fails if this connection can't be made
public let messageSendJobId: Int64
/// The id of the `Attachment` to upload
public let attachmentId: String
public init(messageSendJobId: Int64, attachmentId: String) {
self.messageSendJobId = messageSendJobId
self.attachmentId = attachmentId
}
}
}