Skip to content

Commit

Permalink
feat: Read UploadQueue for state
Browse files Browse the repository at this point in the history
  • Loading branch information
adrien-coye committed Mar 8, 2024
1 parent 51b65c3 commit 0e02c5d
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 57 deletions.
28 changes: 28 additions & 0 deletions kDriveCore/Data/Models/Upload/UploadFile.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public protocol UploadFilable {
}

public final class UploadFile: Object, UploadFilable {
public static let cache = NSCache<NSString, UploadFileProviderItem>()

/// As a number of chunks can fail in one UploadRequest, the retryCount is slightly higher now
public static let defaultMaxRetryCount = 5

Expand Down Expand Up @@ -330,6 +332,32 @@ public extension UploadFile {
}
}

public extension UploadFile {
/// Centralise error cleaning
func toUploadFileItemProvider() -> UploadFileProviderItem? {
guard let pathURL else {
return nil
}

// if let cached = Self.cache.object(forKey: id as NSString) {
// print("cache hit \(cached)")
// return cached
// }

let item = UploadFileProviderItem(uploadFileUUID: id,
parentDirectoryId: parentDirectoryId,
userId: userId,
driveId: driveId,
sourceUrl: pathURL,
conflictOption: conflictOption,
shouldRemoveAfterUpload: shouldRemoveAfterUpload)

// Self.cache.setObject(item, forKey: id as NSString)
// print("item:\(item)")
return item
}
}

public extension [UploadFile] {
func firstContaining(chunkUrl: String) -> UploadFile? {
// keep only the files with a valid uploading session
Expand Down
5 changes: 5 additions & 0 deletions kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import RealmSwift
public protocol UploadQueueable {
func getOperation(forUploadFileId uploadFileId: String) -> UploadOperationable?

/// Fetch an uploading item for a given fileProviderItemIdentifier if any
/// - Parameter fileProviderItemIdentifier: Identifier for lookup
/// - Returns:Matching UploadFile if any
func getUploadingFile(fileProviderItemIdentifier: String) -> UploadFile?

/// Read database to enqueue all non finished upload tasks.
func rebuildUploadQueueFromObjectsInRealm(_ caller: StaticString)

Expand Down
12 changes: 12 additions & 0 deletions kDriveCore/FileProvider/UploadFileProviderItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ import InfomaniakDI

/// DTO of the `UploadFile` Realm object
public final class UploadFileProviderItem: NSObject, NSFileProviderItem {
override public var description: String {
"""
\(super.description)
itemIdentifier:\(itemIdentifier.rawValue)
parentDirectoryId:\(parentDirectoryId)
userId:\(userId)
driveId:\(driveId)
sourceUrl:\(sourceUrl)
shouldRemoveAfterUpload:\(shouldRemoveAfterUpload)
"""
}

var parentDirectoryId: Int
var userId: Int
var driveId: Int
Expand Down
40 changes: 18 additions & 22 deletions kDriveFileProvider/FileProviderExtension+Actions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ extension FileProviderExtension {
.deleteDefinitely(file: ProxyFile(driveId: self.driveFileManager.drive.id, id: fileId))
if response {
self.fileProviderState.removeWorkingDocument(forKey: itemIdentifier)
completionHandler(nil)

// Signal after completionHandler
try await self.manager.signalEnumerator(for: .workingSet)
try await self.manager.signalEnumerator(for: itemIdentifier)
completionHandler(nil)
} else {
completionHandler(self.nsError(code: .serverUnreachable))
}
Expand Down Expand Up @@ -146,10 +148,7 @@ extension FileProviderExtension {
sourceUrl: storageUrl,
conflictOption: .version,
shouldRemoveAfterUpload: false /* should be true actually ?*/ )

backgroundUpload(importItem)

manager.signalEnumerator(for: parentItemIdentifier) { _ in
backgroundUpload(importItem) {
completionHandler(importItem, nil)
}
}
Expand All @@ -161,14 +160,11 @@ extension FileProviderExtension {
) {
Log.fileProvider("renameItem")
Task {
// TODO: Rename while upload
// Doc says we should do network request after renaming local file but we could end up with model desync
// if let item = self.fileProviderState.getImportedDocument(forKey: itemIdentifier) {
// item.filename = itemName
// try await self.manager.signalEnumerator(for: item.parentItemIdentifier)
// completionHandler(item, nil)
// return
// }
guard self.uploadQueue.getUploadingFile(fileProviderItemIdentifier: itemIdentifier.rawValue) == nil else {
Log.fileProvider("renameItem not supported while uploading", level: .error)
completionHandler(nil, NSFileProviderError(.noSuchItem))
return
}

guard let fileId = itemIdentifier.toFileId(),
let file = self.driveFileManager.getCachedFile(id: fileId) else {
Expand Down Expand Up @@ -206,13 +202,11 @@ extension FileProviderExtension {
) {
Log.fileProvider("reparentItem")
Task {
// TODO: reparentItem while uploading
// if let item = self.fileProviderState.getImportedDocument(forKey: itemIdentifier) {
// item.parentItemIdentifier = parentItemIdentifier
// try await self.manager.signalEnumerator(for: item.parentItemIdentifier)
// completionHandler(item, nil)
// return
// }
guard self.uploadQueue.getUploadingFile(fileProviderItemIdentifier: itemIdentifier.rawValue) == nil else {
Log.fileProvider("reparentItem not supported while uploading", level: .error)
completionHandler(nil, NSFileProviderError(.noSuchItem))
return
}

guard let fileId = itemIdentifier.toFileId(),
let file = self.driveFileManager.getCachedFile(id: fileId),
Expand All @@ -239,7 +233,7 @@ extension FileProviderExtension {
completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void
) {
let fileId = itemIdentifier.toFileId()
Log.fileProvider("setFavoriteRank forItemIdentifier:\(fileId)")
Log.fileProvider("setFavoriteRank forItemIdentifier:\(String(describing: fileId))")
// How should we save favourite rank in database?
guard let fileId,
let file = driveFileManager.getCachedFile(id: fileId) else {
Expand Down Expand Up @@ -337,9 +331,11 @@ extension FileProviderExtension {
}
item.isTrashed = false
self.fileProviderState.removeWorkingDocument(forKey: itemIdentifier)
completionHandler(item, nil)

// Signal after completionHandler
try await self.manager.signalEnumerator(for: .workingSet)
try await self.manager.signalEnumerator(for: item.parentItemIdentifier)
completionHandler(item, nil)
} catch {
completionHandler(nil, self.nsError(code: .noSuchItem))
}
Expand Down
75 changes: 40 additions & 35 deletions kDriveFileProvider/FileProviderExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ final class FileProviderExtension: NSFileProviderExtension {
super.init()
}

// TODO: Rework to support UploadFileProviderItem
override func item(for identifier: NSFileProviderItemIdentifier) throws -> NSFileProviderItem {
Log.fileProvider("item for identifier")
try isFileProviderExtensionEnabled()
Expand All @@ -84,37 +83,48 @@ final class FileProviderExtension: NSFileProviderExtension {
return item
}

// TODO: Read from upload queue
// else if let item = fileProviderState.getImportedDocument(forKey: identifier) {
// Log.fileProvider("item for identifier - Imported Document")
// return item
// }
// Read from upload queue
// TODO: try with a cache to see if identity of object is used a well as identifier.
else if let uploadFile = uploadQueue.getUploadingFile(fileProviderItemIdentifier: identifier.rawValue) {
Log.fileProvider("item for identifier - Uploading file")
guard let uploadItem = uploadFile.toUploadFileItemProvider() else {
Log.fileProvider("item for identifier - Unable to generate an UploadFileItemProvider", level: .error)
throw nsError(code: .noSuchItem)
}
return uploadItem
}

// Read DB
else if let fileId = identifier.toFileId(),
let file = driveFileManager.getCachedFile(id: fileId) {
Log.fileProvider("item for identifier - File:\(fileId)")
return FileProviderItem(file: file, domain: domain)
} else {
Log.fileProvider("item for identifier - nsError(code: .noSuchItem)")
throw nsError(code: .noSuchItem)
}

// did not match anything
Log.fileProvider("item for identifier - nsError(code: .noSuchItem)", level: .error)
throw nsError(code: .noSuchItem)
}

override func urlForItem(withPersistentIdentifier identifier: NSFileProviderItemIdentifier) -> URL? {
Log.fileProvider("urlForItem(withPersistentIdentifier identifier:)")

// TODO: Read from upload queue
// if let item = fileProviderState.getImportedDocument(forKey: identifier) {
// return item.storageUrl
// } else
// Read from upload queue
if let item = uploadQueue.getUploadingFile(fileProviderItemIdentifier: identifier.rawValue) {
Log.fileProvider("urlForItem - Uploading file")
return item.pathURL
}

if let fileId = identifier.toFileId(),
let file = driveFileManager.getCachedFile(id: fileId) {
// Read form DB
else if let fileId = identifier.toFileId(),
let file = driveFileManager.getCachedFile(id: fileId) {
Log.fileProvider("urlForItem - in database")
return FileProviderItem(file: file, domain: domain).storageUrl
} else {
return nil
}

// Did not match
Log.fileProvider("urlForItem - no match", level: .error)
return nil
}

override func persistentIdentifierForItem(at url: URL) -> NSFileProviderItemIdentifier? {
Expand Down Expand Up @@ -268,16 +278,14 @@ final class FileProviderExtension: NSFileProviderExtension {

if error != nil {
item.isDownloaded = false
self.manager.signalEnumerator(for: item.parentItemIdentifier) { _ in
completion(NSFileProviderError(.serverUnreachable))
}
completion(NSFileProviderError(.serverUnreachable))
self.manager.signalEnumerator(for: item.parentItemIdentifier) { _ in }
} else {
do {
try FileManager.default.copyOrReplace(sourceUrl: file.localUrl, destinationUrl: item.storageUrl)
item.isDownloaded = true
self.manager.signalEnumerator(for: item.parentItemIdentifier) { _ in
completion(nil)
}
completion(nil)
self.manager.signalEnumerator(for: item.parentItemIdentifier) { _ in }
} catch {
completion(error)
}
Expand All @@ -295,8 +303,8 @@ final class FileProviderExtension: NSFileProviderExtension {
private func saveFreshLocalFile(_ file: File, for item: FileProviderItem, completion: @escaping (Error?) -> Void) {
do {
try FileManager.default.copyOrReplace(sourceUrl: file.localUrl, destinationUrl: item.storageUrl)
manager.signalEnumerator(for: item.parentItemIdentifier) { _ in }
completion(nil)
manager.signalEnumerator(for: item.parentItemIdentifier) { _ in }
} catch {
completion(error)
}
Expand All @@ -322,25 +330,22 @@ final class FileProviderExtension: NSFileProviderExtension {

let uploadFile = uploadFileProviderItem.toUploadFile

// TODO: Remove observation
// TODO: Can we remove observation ?
var observationToken: ObservationToken?
observationToken = uploadQueueObservable.observeFileUploaded(self, fileId: uploadFile.id) { [weak self] uploadedFile, _ in
observationToken = uploadQueueObservable.observeFileUploaded(self, fileId: uploadFile.id) { uploadedFile, _ in
observationToken?.cancel()
observationToken = nil

guard let self else {
return
}

// Signal change on upload finished
manager.signalEnumerator(for: uploadFileProviderItem.parentItemIdentifier) { _ in
Task {
completion?()
// Signal change on upload finished, after completion
try await self.manager.signalEnumerator(for: .workingSet)
try await self.manager.signalEnumerator(for: uploadFileProviderItem.parentItemIdentifier)
}
}

uploadQueue.resumeAllOperations()
_ = uploadQueue.saveToRealm(uploadFile, itemIdentifier: uploadFileProviderItem.itemIdentifier, addToQueue: true)

// Upload started
completion?()
}

// MARK: - Enumeration
Expand Down

0 comments on commit 0e02c5d

Please sign in to comment.