From e605a2c0cabf9e8950125bead0ce3a954ad2d4a1 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 3 Dec 2024 14:12:24 +0800 Subject: [PATCH 1/9] Upgrade NextcloudKit minimum version to 5.0.1 Signed-off-by: Claudio Cambra --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index a70f176..105b7ee 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,7 @@ let package = Package( url: "https://github.com/claucambra/NextcloudCapabilitiesKit.git", .upToNextMajor(from: "2.0.0") ), - .package(url: "https://github.com/nextcloud/NextcloudKit", .upToNextMajor(from: "2.9.9")), + .package(url: "https://github.com/nextcloud/NextcloudKit", from: "5.0.1"), .package(url: "https://github.com/realm/realm-swift.git", exact: "10.49.0"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0") ], From 18b2beddd2364ae71a48a1927e017a9d92c52a9b Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 3 Dec 2024 14:17:10 +0800 Subject: [PATCH 2/9] Adapt to upstream NextcloudKitDelegate changes Signed-off-by: Claudio Cambra f delegate Signed-off-by: Claudio Cambra --- .../Enumeration/RemoteChangeObserver.swift | 59 ++++++++++++++++++- .../NextcloudKit+RemoteInterface.swift | 2 +- .../Interface/RemoteInterface.swift | 2 +- Tests/Interface/MockRemoteInterface.swift | 4 +- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift b/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift index a64b425..151bd19 100644 --- a/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift +++ b/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift @@ -5,6 +5,7 @@ // Created by Claudio Cambra on 17/4/24. // +import Alamofire import FileProvider import Foundation import NextcloudCapabilitiesKit @@ -13,7 +14,7 @@ import OSLog public let NotifyPushAuthenticatedNotificationName = Notification.Name("NotifyPushAuthenticated") -public class RemoteChangeObserver: NSObject, NKCommonDelegate, URLSessionWebSocketDelegate { +public class RemoteChangeObserver: NSObject, NextcloudKitDelegate, URLSessionWebSocketDelegate { public let remoteInterface: RemoteInterface public let changeNotificationInterface: ChangeNotificationInterface public let domain: NSFileProviderDomain? @@ -370,7 +371,63 @@ public class RemoteChangeObserver: NSObject, NKCommonDelegate, URLSessionWebSock } } + // MARK: - NextcloudKitDelegate methods + public func networkReachabilityObserver(_ typeReachability: NKCommon.TypeReachability) { networkReachability = typeReachability } + + public func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) { } + + public func downloadProgress( + _ progress: Float, + totalBytes: Int64, + totalBytesExpected: Int64, + fileName: String, + serverUrl: String, + session: URLSession, + task: URLSessionTask + ) { } + + public func uploadProgress( + _ progress: Float, + totalBytes: Int64, + totalBytesExpected: Int64, + fileName: String, + serverUrl: String, + session: URLSession, + task: URLSessionTask + ) { } + + public func downloadingFinish( + _ session: URLSession, + downloadTask: URLSessionDownloadTask, + didFinishDownloadingTo location: URL + ) { } + + public func downloadComplete( + fileName: String, + serverUrl: String, + etag: String?, + date: Date?, + dateLastModified: Date?, + length: Int64, + task: URLSessionTask, + error: NKError + ) { } + + public func uploadComplete( + fileName: String, + serverUrl: String, + ocId: String?, + etag: String?, + date: Date?, + size: Int64, + task: URLSessionTask, + error: NKError + ) { } + + public func request( + _ request: Alamofire.DataRequest, didParseResponse response: Alamofire.AFDataResponse + ) { } } diff --git a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift index 9426fc0..780f70e 100644 --- a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift @@ -21,7 +21,7 @@ extension NextcloudKit: RemoteInterface { ) } - public func setDelegate(_ delegate: any NKCommonDelegate) { + public func setDelegate(_ delegate: any NextcloudKitDelegate) { setup(delegate: delegate) } diff --git a/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift index 461042e..b49f041 100644 --- a/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift @@ -24,7 +24,7 @@ public protocol RemoteInterface { var account: Account { get } - func setDelegate(_ delegate: NKCommonDelegate) + func setDelegate(_ delegate: NextcloudKitDelegate) func createFolder( remotePath: String, diff --git a/Tests/Interface/MockRemoteInterface.swift b/Tests/Interface/MockRemoteInterface.swift index 4aa6216..0845e13 100644 --- a/Tests/Interface/MockRemoteInterface.swift +++ b/Tests/Interface/MockRemoteInterface.swift @@ -16,7 +16,7 @@ public class MockRemoteInterface: RemoteInterface { public var account: Account public var capabilities = mockCapabilities public var rootItem: MockRemoteItem? - public var delegate: (any NKCommonDelegate)? + public var delegate: (any NextcloudKitDelegate)? private var accountString: String { account.ncKitAccount } @@ -94,7 +94,7 @@ public class MockRemoteInterface: RemoteInterface { return name } - public func setDelegate(_ delegate: any NKCommonDelegate) { + public func setDelegate(_ delegate: any NextcloudKitDelegate) { self.delegate = delegate } From d1eb501857f95b8b50bf10f9d6726c498ac99f32 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 3 Dec 2024 14:18:38 +0800 Subject: [PATCH 3/9] Add account argument now required by several upstream functions Signed-off-by: Claudio Cambra --- .../Interface/NextcloudKit+RemoteInterface.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift index 780f70e..36a1aba 100644 --- a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift @@ -33,6 +33,7 @@ extension NextcloudKit: RemoteInterface { return await withCheckedContinuation { continuation in createFolder( serverUrlFileName: remotePath, + account: account.ncKitAccount, options: options, taskHandler: taskHandler ) { account, ocId, date, error in @@ -66,6 +67,7 @@ extension NextcloudKit: RemoteInterface { fileNameLocalPath: localPath, dateCreationFile: creationDate, dateModificationFile: modificationDate, + account: account.ncKitAccount, options: options, requestHandler: requestHandler, taskHandler: taskHandler, @@ -97,6 +99,7 @@ extension NextcloudKit: RemoteInterface { serverUrlFileNameSource: remotePathSource, serverUrlFileNameDestination: remotePathDestination, overwrite: overwrite, + account: account.ncKitAccount, options: options, taskHandler: taskHandler ) { account, error in @@ -125,6 +128,7 @@ extension NextcloudKit: RemoteInterface { download( serverUrlFileName: remotePath, fileNameLocalPath: localPath, + account: account.ncKitAccount, options: options, requestHandler: requestHandler, taskHandler: taskHandler, @@ -161,6 +165,7 @@ extension NextcloudKit: RemoteInterface { showHiddenFiles: showHiddenFiles, includeHiddenFiles: includeHiddenFiles, requestBody: requestBody, + account: account.ncKitAccount, options: options, taskHandler: taskHandler ) { account, files, data, error in @@ -210,7 +215,7 @@ extension NextcloudKit: RemoteInterface { ) async -> (account: String, userProfile: NKUserProfile?, data: Data?, error: NKError) { return await withCheckedContinuation { continuation in getUserProfile( - options: options, taskHandler: taskHandler + account: account.ncKitAccount, options: options, taskHandler: taskHandler ) { account, userProfile, data, error in continuation.resume(returning: (account, userProfile, data, error)) } From 36ed6a3a846f03e7373ca07f7d863ab18054dca8 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 3 Dec 2024 14:19:29 +0800 Subject: [PATCH 4/9] Adapt to removal of getPreview, use downloadPreview instead Signed-off-by: Claudio Cambra --- .../Interface/NextcloudKit+RemoteInterface.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift index 36a1aba..59ec4f0 100644 --- a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift @@ -190,10 +190,10 @@ extension NextcloudKit: RemoteInterface { url: URL, options: NKRequestOptions, taskHandler: @escaping (URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) { await withCheckedContinuation { continuation in - getPreview( - url: url, options: options, taskHandler: taskHandler + downloadPreview( + url: url, account: account.ncKitAccount, options: options, taskHandler: taskHandler ) { account, data, error in - continuation.resume(returning: (account, data, error)) + continuation.resume(returning: (account, data?.data, error)) } } } From 63ac37d7dc2c62d302980d39ef85987e46faced5 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 3 Dec 2024 14:22:33 +0800 Subject: [PATCH 5/9] Adapt to new return signatures upstream Signed-off-by: Claudio Cambra --- .../NextcloudKit+RemoteInterface.swift | 42 ++++++++++--------- .../Interface/RemoteInterface.swift | 10 ++--- .../Item/Item+Delete.swift | 2 +- .../Item/Item+Modify.swift | 4 +- Tests/Interface/MockRemoteInterface.swift | 17 ++++---- Tests/Interface/MockRemoteItem.swift | 2 +- .../MockRemoteInterfaceTests.swift | 2 +- 7 files changed, 41 insertions(+), 38 deletions(-) diff --git a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift index 59ec4f0..488fa35 100644 --- a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift @@ -36,8 +36,8 @@ extension NextcloudKit: RemoteInterface { account: account.ncKitAccount, options: options, taskHandler: taskHandler - ) { account, ocId, date, error in - continuation.resume(returning: (account, ocId, date, error)) + ) { account, ocId, date, _, error in + continuation.resume(returning: (account, ocId, date as NSDate?, error)) } } } @@ -57,7 +57,7 @@ extension NextcloudKit: RemoteInterface { etag: String?, date: NSDate?, size: Int64, - allHeaderFields: [AnyHashable : Any]?, + response: HTTPURLResponse?, afError: AFError?, remoteError: NKError ) { @@ -72,14 +72,14 @@ extension NextcloudKit: RemoteInterface { requestHandler: requestHandler, taskHandler: taskHandler, progressHandler: progressHandler - ) { account, ocId, etag, date, size, allHeaderFields, afError, nkError in + ) { account, ocId, etag, date, size, response, afError, nkError in continuation.resume(returning: ( account, ocId, etag, - date, + date as NSDate?, size, - allHeaderFields, + response?.response, afError, nkError )) @@ -93,7 +93,7 @@ extension NextcloudKit: RemoteInterface { overwrite: Bool, options: NKRequestOptions, taskHandler: @escaping (URLSessionTask) -> Void - ) async -> (account: String, error: NKError) { + ) async -> (account: String, data: Data?, error: NKError) { return await withCheckedContinuation { continuation in moveFileOrFolder( serverUrlFileNameSource: remotePathSource, @@ -102,8 +102,8 @@ extension NextcloudKit: RemoteInterface { account: account.ncKitAccount, options: options, taskHandler: taskHandler - ) { account, error in - continuation.resume(returning: (account, error)) + ) { account, data, error in + continuation.resume(returning: (account, data?.data, error)) } } } @@ -120,7 +120,7 @@ extension NextcloudKit: RemoteInterface { etag: String?, date: NSDate?, length: Int64, - allHeaderFields: [AnyHashable : Any]?, + response: HTTPURLResponse?, afError: AFError?, remoteError: NKError ) { @@ -133,13 +133,13 @@ extension NextcloudKit: RemoteInterface { requestHandler: requestHandler, taskHandler: taskHandler, progressHandler: progressHandler - ) { account, etag, date, length, allHeaderFields, afError, remoteError in + ) { account, etag, date, length, data, afError, remoteError in continuation.resume(returning: ( account, etag, - date, + date as NSDate?, length, - allHeaderFields, + data?.response, afError, remoteError )) @@ -169,7 +169,7 @@ extension NextcloudKit: RemoteInterface { options: options, taskHandler: taskHandler ) { account, files, data, error in - continuation.resume(returning: (account, files, data, error)) + continuation.resume(returning: (account, files ?? [], data?.data, error)) } } } @@ -178,10 +178,12 @@ extension NextcloudKit: RemoteInterface { remotePath: String, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } - ) async -> (account: String, error: NKError) { + ) async -> (account: String, response: HTTPURLResponse?, error: NKError) { return await withCheckedContinuation { continuation in - deleteFileOrFolder(serverUrlFileName: remotePath) { account, error in - continuation.resume(returning: (account, error)) + deleteFileOrFolder( + serverUrlFileName: remotePath, account: account.ncKitAccount + ) { account, response, error in + continuation.resume(returning: (account, response?.response, error)) } } } @@ -203,8 +205,8 @@ extension NextcloudKit: RemoteInterface { taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } ) async -> (account: String, data: Data?, error: NKError) { return await withCheckedContinuation { continuation in - getCapabilities(options: options, taskHandler: taskHandler) { account, data, error in - continuation.resume(returning: (account, data, error)) + getCapabilities(account: account.ncKitAccount, options: options, taskHandler: taskHandler) { account, data, error in + continuation.resume(returning: (account, data?.data, error)) } } } @@ -217,7 +219,7 @@ extension NextcloudKit: RemoteInterface { getUserProfile( account: account.ncKitAccount, options: options, taskHandler: taskHandler ) { account, userProfile, data, error in - continuation.resume(returning: (account, userProfile, data, error)) + continuation.resume(returning: (account, userProfile, data?.data, error)) } } } diff --git a/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift index b49f041..aa6c93b 100644 --- a/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift @@ -47,8 +47,8 @@ public protocol RemoteInterface { etag: String?, date: NSDate?, size: Int64, - allHeaderFields: [AnyHashable: Any]?, - afError: AFError?, + response: HTTPURLResponse?, + afError: AFError?, remoteError: NKError ) @@ -58,7 +58,7 @@ public protocol RemoteInterface { overwrite: Bool, options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void - ) async -> (account: String, error: NKError) + ) async -> (account: String, data: Data?, error: NKError) func download( remotePath: String, @@ -72,7 +72,7 @@ public protocol RemoteInterface { etag: String?, date: NSDate?, length: Int64, - allHeaderFields: [AnyHashable: Any]?, + response: HTTPURLResponse?, afError: AFError?, remoteError: NKError ) @@ -91,7 +91,7 @@ public protocol RemoteInterface { remotePath: String, options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void - ) async -> (account: String, error: NKError) + ) async -> (account: String, response: HTTPURLResponse?, error: NKError) func downloadThumbnail( url: URL, diff --git a/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift b/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift index 6ce6a66..7ab8c81 100644 --- a/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift +++ b/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift @@ -22,7 +22,7 @@ public extension Item { } let ocId = itemIdentifier.rawValue - let (_, error) = await remoteInterface.delete( + let (_, _, error) = await remoteInterface.delete( remotePath: serverFileNameUrl, options: .init(), taskHandler: { task in if let domain { NSFileProviderManager(for: domain)?.register( diff --git a/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift b/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift index b2bed93..dc6fd55 100644 --- a/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift +++ b/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift @@ -23,7 +23,7 @@ public extension Item { let ocId = itemIdentifier.rawValue let isFolder = contentType.conforms(to: .directory) let oldRemotePath = metadata.serverUrl + "/" + metadata.fileName - let (_, moveError) = await remoteInterface.move( + let (_, _, moveError) = await remoteInterface.move( remotePathSource: oldRemotePath, remotePathDestination: newRemotePath, overwrite: false, @@ -421,7 +421,7 @@ public extension Item { let staleItemMetadata = staleItem.value guard dbManager.itemMetadataFromOcId(staleItemMetadata.ocId) != nil else { continue } - let (_, deleteError) = await remoteInterface.delete( + let (_, _, deleteError) = await remoteInterface.delete( remotePath: staleItem.key, options: .init(), taskHandler: { task in if let domain { NSFileProviderManager(for: domain)?.register( diff --git a/Tests/Interface/MockRemoteInterface.swift b/Tests/Interface/MockRemoteInterface.swift index 0845e13..2c63d2f 100644 --- a/Tests/Interface/MockRemoteInterface.swift +++ b/Tests/Interface/MockRemoteInterface.swift @@ -144,7 +144,7 @@ public class MockRemoteInterface: RemoteInterface { etag: String?, date: NSDate?, size: Int64, - allHeaderFields: [AnyHashable : Any]?, + response: HTTPURLResponse?, afError: AFError?, remoteError: NKError ) { @@ -213,12 +213,12 @@ public class MockRemoteInterface: RemoteInterface { overwrite: Bool = false, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } - ) async -> (account: String, error: NKError) { + ) async -> (account: String, data: Data?, error: NKError) { guard let itemNewName = try? name(from: remotePathDestination), let sourceItem = item(remotePath: remotePathSource), let destinationParent = parentItem(path: remotePathDestination), (overwrite || !destinationParent.children.contains(where: { $0.name == itemNewName })) - else { return (accountString, .urlError) } + else { return (accountString, nil, .urlError) } sourceItem.name = itemNewName sourceItem.parent?.children.removeAll(where: { $0.identifier == sourceItem.identifier }) @@ -244,7 +244,7 @@ public class MockRemoteInterface: RemoteInterface { children = nextChildren } - return (accountString, .success) + return (accountString, nil, .success) } public func download( @@ -259,7 +259,7 @@ public class MockRemoteInterface: RemoteInterface { etag: String?, date: NSDate?, length: Int64, - allHeaderFields: [AnyHashable : Any]?, + response: HTTPURLResponse?, afError: AFError?, remoteError: NKError ) { @@ -330,15 +330,16 @@ public class MockRemoteInterface: RemoteInterface { remotePath: String, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } - ) async -> (account: String, error: NKError) { + ) async -> (account: String, response: HTTPURLResponse?, error: NKError) { guard let item = item(remotePath: remotePath) else { - return (accountString, .urlError) + return (accountString, nil, .urlError) } item.children = [] item.parent?.children.removeAll(where: { $0.identifier == item.identifier }) item.parent = nil - return (accountString, .success) + + return (accountString, nil, .success) } public func downloadThumbnail( diff --git a/Tests/Interface/MockRemoteItem.swift b/Tests/Interface/MockRemoteItem.swift index 013774c..496b88f 100644 --- a/Tests/Interface/MockRemoteItem.swift +++ b/Tests/Interface/MockRemoteItem.swift @@ -32,7 +32,7 @@ public class MockRemoteItem: Equatable { let file = NKFile() file.fileName = name file.size = size - file.date = creationDate as NSDate + file.date = creationDate file.directory = directory file.etag = versionIdentifier file.ocId = identifier diff --git a/Tests/InterfaceTests/MockRemoteInterfaceTests.swift b/Tests/InterfaceTests/MockRemoteInterfaceTests.swift index 6c68966..f678eab 100644 --- a/Tests/InterfaceTests/MockRemoteInterfaceTests.swift +++ b/Tests/InterfaceTests/MockRemoteInterfaceTests.swift @@ -341,7 +341,7 @@ final class MockRemoteInterfaceTests: XCTestCase { let expectedRoot = remoteInterface.rootItem XCTAssertEqual(targetRootFile?.ocId, expectedRoot?.identifier) XCTAssertEqual(targetRootFile?.fileName, expectedRoot?.name) - XCTAssertEqual(targetRootFile?.date, expectedRoot?.creationDate as? NSDate) + XCTAssertEqual(targetRootFile?.date, expectedRoot?.creationDate) XCTAssertEqual(targetRootFile?.etag, expectedRoot?.versionIdentifier) let resultChildren = await remoteInterface.enumerate( From 35bba20b81d5d03b0be83175c7f74bb606d3df58 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 3 Dec 2024 14:24:26 +0800 Subject: [PATCH 6/9] Dirtily adapt to new nk sessions implementation upstream Signed-off-by: Claudio Cambra --- .../Interface/NextcloudKit+RemoteInterface.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift index 488fa35..f2a11c1 100644 --- a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift @@ -13,11 +13,15 @@ import NextcloudKit extension NextcloudKit: RemoteInterface { public var account: Account { - Account( - user: nkCommonInstance.user, - id: nkCommonInstance.userId, - serverUrl: nkCommonInstance.urlBase, - password: nkCommonInstance.password + guard let session = nkCommonInstance.nksessions.first else { + return Account(user: "", id: "", serverUrl: "", password: "") + } + + return Account( + user: session.user, + id: session.userId, + serverUrl: session.urlBase, + password: session.password ) } From 3548e346099525796268e7007aac7ef08f040880 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Tue, 3 Dec 2024 14:38:58 +0800 Subject: [PATCH 7/9] Use develop branch rather than 5.0.1 as this version was broken on macOS Signed-off-by: Claudio Cambra --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 105b7ee..7926455 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,7 @@ let package = Package( url: "https://github.com/claucambra/NextcloudCapabilitiesKit.git", .upToNextMajor(from: "2.0.0") ), - .package(url: "https://github.com/nextcloud/NextcloudKit", from: "5.0.1"), + .package(url: "https://github.com/nextcloud/NextcloudKit", branch: "develop"), .package(url: "https://github.com/realm/realm-swift.git", exact: "10.49.0"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0") ], From c8eb3d776e05fa0da68041aad7d2b04e23643153 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 4 Dec 2024 15:35:08 +0800 Subject: [PATCH 8/9] Add convenience method to construct ncKitAccountString in Account Signed-off-by: Claudio Cambra --- Sources/NextcloudFileProviderKit/Utilities/Account.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/NextcloudFileProviderKit/Utilities/Account.swift b/Sources/NextcloudFileProviderKit/Utilities/Account.swift index 3660372..78f2ef2 100644 --- a/Sources/NextcloudFileProviderKit/Utilities/Account.swift +++ b/Sources/NextcloudFileProviderKit/Utilities/Account.swift @@ -25,11 +25,15 @@ public struct Account: Equatable { public static let webDavFilesUrlSuffix: String = "/remote.php/dav/files/" public let username, id, password, ncKitAccount, serverUrl, davFilesUrl: String + public static func ncKitAccountString(from username: String, serverUrl: String) -> String { + username + " " + serverUrl + } + public init(user: String, id: String, serverUrl: String, password: String) { username = user self.id = id self.password = password - ncKitAccount = user + " " + serverUrl + ncKitAccount = Self.ncKitAccountString(from: user, serverUrl: serverUrl) self.serverUrl = serverUrl davFilesUrl = serverUrl + Self.webDavFilesUrlSuffix + id } From 0a447173297f6c0657ffad16ead8eaa622daa557 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 4 Dec 2024 16:03:25 +0800 Subject: [PATCH 9/9] No longer use account defined in remote interface, take in accounts as parameter In NextcloudKit, the main NextcloudKit object now supports multiple sessions and the methods of this object takes in the account identifier string (for us, ncKitAccount in Account) to allow for multi-user use. Additionally the constructor for NextcloudKit is now internal so we cannot use multiple instances of NextcloudKit Signed-off-by: Claudio Cambra --- .../Enumeration/Enumerator+SyncEngine.swift | 44 ++++--- .../Enumeration/Enumerator.swift | 59 ++++++---- .../Enumeration/RemoteChangeObserver.swift | 16 ++- .../NextcloudKit+RemoteInterface.swift | 29 +++-- .../Interface/RemoteInterface.swift | 21 +++- .../Item/Item+Create.swift | 40 +++++-- .../Item/Item+Delete.swift | 5 +- .../Item/Item+Fetch.swift | 10 +- .../Item/Item+Modify.swift | 28 ++++- .../NextcloudFileProviderKit/Item/Item.swift | 16 ++- .../Metadata/ItemMetadata+NKFile.swift | 2 +- .../Utilities/ThumbnailFetching.swift | 5 +- Tests/Interface/MockRemoteInterface.swift | 86 +++++++------- .../MockRemoteInterfaceTests.swift | 109 ++++++++++++------ .../EnumeratorTests.swift | 46 ++++++-- .../ItemCreateTests.swift | 18 ++- .../ItemDeleteTests.swift | 6 +- .../ItemFetchTests.swift | 6 +- .../ItemModifyTests.swift | 8 +- .../ItemPropertyTests.swift | 27 +++-- .../RemoteChangeObserverTests.swift | 29 +++-- 21 files changed, 399 insertions(+), 211 deletions(-) diff --git a/Sources/NextcloudFileProviderKit/Enumeration/Enumerator+SyncEngine.swift b/Sources/NextcloudFileProviderKit/Enumeration/Enumerator+SyncEngine.swift index 60898fc..9b5b019 100644 --- a/Sources/NextcloudFileProviderKit/Enumeration/Enumerator+SyncEngine.swift +++ b/Sources/NextcloudFileProviderKit/Enumeration/Enumerator+SyncEngine.swift @@ -18,6 +18,7 @@ import OSLog extension Enumerator { func fullRecursiveScan( + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager, scanChangesOnly: Bool @@ -34,6 +35,7 @@ extension Enumerator { let results = await self.scanRecursively( rootContainerDirectoryMetadata, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, scanChangesOnly: scanChangesOnly @@ -65,6 +67,7 @@ extension Enumerator { private func scanRecursively( _ directoryMetadata: ItemMetadata, + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager, scanChangesOnly: Bool @@ -89,7 +92,7 @@ extension Enumerator { let itemServerUrl = directoryMetadata.ocId == NSFileProviderItemIdentifier.rootContainer.rawValue - ? remoteInterface.account.davFilesUrl + ? account.davFilesUrl : directoryMetadata.serverUrl + "/" + directoryMetadata.fileName Self.logger.debug("About to read: \(itemServerUrl, privacy: .public)") @@ -98,6 +101,7 @@ extension Enumerator { metadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError ) = await Self.readServerUrl( itemServerUrl, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, domain: domain, @@ -156,7 +160,7 @@ extension Enumerator { Self.logger.info( """ Finished reading serverUrl: \(itemServerUrl, privacy: .public) - for user: \(remoteInterface.account.ncKitAccount, privacy: .public) + for user: \(account.ncKitAccount, privacy: .public) """ ) @@ -166,7 +170,7 @@ extension Enumerator { Self.logger.warning( """ Nil metadatas received in change read at \(itemServerUrl, privacy: .public) - for user: \(remoteInterface.account.ncKitAccount, privacy: .public) + for user: \(account.ncKitAccount, privacy: .public) """ ) } @@ -177,7 +181,7 @@ extension Enumerator { Self.logger.warning( """ Nil new metadatas received in change read at \(itemServerUrl, privacy: .public) - for user: \(remoteInterface.account.ncKitAccount, privacy: .public) + for user: \(account.ncKitAccount, privacy: .public) """ ) } @@ -188,7 +192,7 @@ extension Enumerator { Self.logger.warning( """ Nil updated metadatas received in change read at \(itemServerUrl, privacy: .public) - for user: \(remoteInterface.account.ncKitAccount, privacy: .public) + for user: \(account.ncKitAccount, privacy: .public) """ ) } @@ -199,7 +203,7 @@ extension Enumerator { Self.logger.warning( """ Nil deleted metadatas received in change read at \(itemServerUrl, privacy: .public) - for user: \(remoteInterface.account.ncKitAccount, privacy: .public) + for user: \(account.ncKitAccount, privacy: .public) """ ) } @@ -242,6 +246,7 @@ extension Enumerator { ) let childScanResult = await scanRecursively( childDirectory, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, scanChangesOnly: scanChangesOnly @@ -262,7 +267,7 @@ extension Enumerator { static func handleDepth1ReadFileOrFolder( serverUrl: String, - ncAccount: Account, + account: Account, dbManager: FilesDatabaseManager, files: [NKFile] ) async -> ( @@ -275,14 +280,14 @@ extension Enumerator { Self.logger.debug( """ Starting async conversion of NKFiles for serverUrl: \(serverUrl, privacy: .public) - for user: \(ncAccount.ncKitAccount, privacy: .public) + for user: \(account.ncKitAccount, privacy: .public) """ ) let (directoryMetadata, metadatas) = await withCheckedContinuation { continuation in ItemMetadata.metadatasFromDirectoryReadNKFiles( - files, account: ncAccount.ncKitAccount + files, account: account ) { directoryMetadata, _, metadatas in continuation.resume(returning: (directoryMetadata, metadatas)) } @@ -291,7 +296,7 @@ extension Enumerator { // STORE DATA FOR CURRENTLY SCANNED DIRECTORY // We have now scanned this directory's contents, so update with etag in order to not check // again if not needed unless it's the root container - if serverUrl != ncAccount.davFilesUrl { + if serverUrl != account.davFilesUrl { dbManager.addItemMetadata(directoryMetadata) } @@ -301,7 +306,7 @@ extension Enumerator { // They will get updated when they are the subject of a readServerUrl call. // (See above) let changedMetadatas = dbManager.updateItemMetadatas( - account: ncAccount.ncKitAccount, + account: account.ncKitAccount, serverUrl: serverUrl, updatedMetadatas: metadatas, updateDirectoryEtags: false @@ -318,6 +323,7 @@ extension Enumerator { static func readServerUrl( _ serverUrl: String, + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager, domain: NSFileProviderDomain? = nil, @@ -331,17 +337,16 @@ extension Enumerator { deletedMetadatas: [ItemMetadata]?, readError: NKError? ) { - let ncAccount = remoteInterface.account - let ncKitAccount = ncAccount.ncKitAccount + let ncKitAccount = account.ncKitAccount Self.logger.debug( """ Starting to read serverUrl: \(serverUrl, privacy: .public) for user: \(ncKitAccount, privacy: .public) at depth \(depth.rawValue, privacy: .public). - username: \(ncAccount.username, privacy: .public), - password is empty: \(ncAccount.password == "" ? "EMPTY" : "NOT EMPTY"), - serverUrl: \(ncAccount.serverUrl, privacy: .public) + username: \(account.username, privacy: .public), + password is empty: \(account.password == "" ? "EMPTY" : "NOT EMPTY"), + serverUrl: \(account.serverUrl, privacy: .public) """ ) @@ -351,6 +356,7 @@ extension Enumerator { showHiddenFiles: true, includeHiddenFiles: [], requestBody: nil, + account: account, options: .init(), taskHandler: { task in if let domain, let enumeratedItemIdentifier { @@ -387,7 +393,7 @@ extension Enumerator { Self.logger.debug( """ Read item is a file. Converting NKfile for serverUrl: \(serverUrl, privacy: .public) - for user: \(ncAccount.ncKitAccount, privacy: .public) + for user: \(account.ncKitAccount, privacy: .public) """ ) let itemMetadata = receivedFile.toItemMetadata() @@ -418,7 +424,7 @@ extension Enumerator { } if depth == .target { - if serverUrl == ncAccount.davFilesUrl { + if serverUrl == account.davFilesUrl { return (nil, nil, nil, nil, nil) } else { let metadata = receivedFile.toItemMetadata() @@ -435,7 +441,7 @@ extension Enumerator { allMetadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError ) = await handleDepth1ReadFileOrFolder( serverUrl: serverUrl, - ncAccount: ncAccount, + account: account, dbManager: dbManager, files: files ) diff --git a/Sources/NextcloudFileProviderKit/Enumeration/Enumerator.swift b/Sources/NextcloudFileProviderKit/Enumeration/Enumerator.swift index fb74516..a337898 100644 --- a/Sources/NextcloudFileProviderKit/Enumeration/Enumerator.swift +++ b/Sources/NextcloudFileProviderKit/Enumeration/Enumerator.swift @@ -29,8 +29,8 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { private let anchor = NSFileProviderSyncAnchor(Date().description.data(using: .utf8)!) private static let maxItemsPerFileProviderPage = 100 static let logger = Logger(subsystem: Logger.subsystem, category: "enumerator") + let account: Account let remoteInterface: RemoteInterface - let ncKitAccount: String let fastEnumeration: Bool var serverUrl: String = "" var isInvalidated = false @@ -42,6 +42,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { public init( enumeratedItemIdentifier: NSFileProviderItemIdentifier, + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager = .shared, domain: NSFileProviderDomain? = nil, @@ -50,7 +51,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { ) { self.enumeratedItemIdentifier = enumeratedItemIdentifier self.remoteInterface = remoteInterface - self.ncKitAccount = remoteInterface.account.ncKitAccount + self.account = account self.dbManager = dbManager self.domain = domain self.fastEnumeration = fastEnumeration @@ -63,7 +64,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { \(enumeratedItemIdentifier.rawValue, privacy: .public) """ ) - serverUrl = remoteInterface.account.davFilesUrl + serverUrl = account.davFilesUrl } else { Self.logger.debug( """ @@ -89,7 +90,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.info( """ - Set up enumerator for user: \(self.ncKitAccount, privacy: .public) + Set up enumerator for user: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) """ ) @@ -117,7 +118,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.debug( """ Received enumerate items request for enumerator with user: - \(self.ncKitAccount, privacy: .public) + \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) """ ) @@ -137,7 +138,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { if enumeratedItemIdentifier == .trashContainer { Self.logger.debug( """ - Enumerating trash set for user: \(self.ncKitAccount, privacy: .public) + Enumerating trash set for user: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) """ ) @@ -175,7 +176,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { { Self.logger.debug( """ - Enumerating initial page for user: \(self.ncKitAccount, privacy: .public) + Enumerating initial page for user: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) """ ) @@ -183,6 +184,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Task { let (metadatas, _, _, _, readError) = await Self.readServerUrl( serverUrl, + account: account, remoteInterface: remoteInterface, dbManager: dbManager ) @@ -190,7 +192,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { guard readError == nil else { Self.logger.error( """ - "Finishing enumeration for user: \(self.ncKitAccount, privacy: .public) + "Finishing enumeration for: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with error \(readError!.errorDescription, privacy: .public) """ @@ -207,7 +209,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { guard let metadatas else { Self.logger.error( """ - Finishing enumeration for user: \(self.ncKitAccount, privacy: .public) + Finishing enumeration for: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with invalid metadatas. """ @@ -222,13 +224,14 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.info( """ Finished reading serverUrl: \(self.serverUrl, privacy: .public) - for user: \(self.ncKitAccount, privacy: .public). + for user: \(self.account.ncKitAccount, privacy: .public). Processed \(metadatas.count) metadatas """ ) Self.completeEnumerationObserver( observer, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, numPage: 1, @@ -244,7 +247,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.debug( """ Enumerating page \(numPage, privacy: .public) - for user: \(self.ncKitAccount, privacy: .public) + for user: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) """ ) @@ -263,7 +266,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.debug( """ Received enumerate changes request for enumerator for user: - \(self.ncKitAccount, privacy: .public) + \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) """ ) @@ -279,7 +282,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { if enumeratedItemIdentifier == .workingSet { Self.logger.debug( - "Enumerating changes in working set for: \(self.ncKitAccount, privacy: .public)" + "Enumerating changes in working set for: \(self.account.ncKitAccount, privacy: .public)" ) // Unlike when enumerating items we can't progressively enumerate items as we need to @@ -288,6 +291,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { let ( _, newMetadatas, updatedMetadatas, deletedMetadatas, error ) = await fullRecursiveScan( + account: account, remoteInterface: remoteInterface, dbManager: dbManager, scanChangesOnly: true @@ -297,7 +301,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.info( """ Enumerator invalidated during working set change scan. - For user: \(self.ncKitAccount, privacy: .public) + For user: \(self.account.ncKitAccount, privacy: .public) """ ) listener?.enumerationActionFailed( @@ -311,7 +315,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.info( """ Finished recursive change enumeration of working set for user: - \(self.ncKitAccount, privacy: .public) + \(self.account.ncKitAccount, privacy: .public) with error: \(error!.errorDescription, privacy: .public) """ ) @@ -326,13 +330,14 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.info( """ Finished recursive change enumeration of working set for user: - \(self.ncKitAccount, privacy: .public). Enumerating items. + \(self.account.ncKitAccount, privacy: .public). Enumerating items. """ ) Self.completeChangesObserver( observer, anchor: anchor, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, newMetadatas: newMetadatas, @@ -344,7 +349,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { return } else if enumeratedItemIdentifier == .trashContainer { Self.logger.debug( - "Enumerating changes in trash set for user: \(self.ncKitAccount, privacy: .public)" + "Enumerating changes in trash set for: \(self.account.ncKitAccount, privacy: .public)" ) // TODO! @@ -355,7 +360,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.info( """ - Enumerating changes for user: \(self.ncKitAccount, privacy: .public) + Enumerating changes for user: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) """ ) @@ -368,6 +373,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { _, newMetadatas, updatedMetadatas, deletedMetadatas, readError ) = await Self.readServerUrl( serverUrl, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, stopAtMatchingEtags: true @@ -382,7 +388,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { guard readError == nil else { Self.logger.error( """ - Finishing enumeration of changes for: \(self.ncKitAccount, privacy: .public) + Finishing enumeration of changes for: \(self.account.ncKitAccount, privacy: .public) with serverUrl: \(self.serverUrl, privacy: .public) with error: \(readError!.errorDescription, privacy: .public) """ @@ -432,6 +438,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.completeChangesObserver( observer, anchor: anchor, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, newMetadatas: nil, @@ -460,13 +467,14 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { Self.logger.info( """ Finished reading serverUrl: \(self.serverUrl, privacy: .public) - for user: \(self.ncKitAccount, privacy: .public) + for user: \(self.account.ncKitAccount, privacy: .public) """ ) Self.completeChangesObserver( observer, anchor: anchor, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, newMetadatas: newMetadatas, @@ -486,6 +494,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { // TODO: Use async group private static func metadatasToFileProviderItems( _ itemMetadatas: [ItemMetadata], + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager, completionHandler: @escaping (_ items: [NSFileProviderItem]) -> Void @@ -518,6 +527,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { let item = Item( metadata: itemMetadata, parentItemIdentifier: parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) Self.logger.debug( @@ -553,13 +563,14 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { private static func completeEnumerationObserver( _ observer: NSFileProviderEnumerationObserver, + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager, numPage: Int, itemMetadatas: [ItemMetadata] ) { metadatasToFileProviderItems( - itemMetadatas, remoteInterface: remoteInterface, dbManager: dbManager + itemMetadatas, account: account, remoteInterface: remoteInterface, dbManager: dbManager ) { items in observer.didEnumerate(items) Self.logger.info("Did enumerate \(items.count) items") @@ -581,6 +592,7 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { private static func completeChangesObserver( _ observer: NSFileProviderChangeObserver, anchor: NSFileProviderSyncAnchor, + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager, newMetadatas: [ItemMetadata]?, @@ -621,7 +633,10 @@ public class Enumerator: NSObject, NSFileProviderEnumerator { } metadatasToFileProviderItems( - allUpdatedMetadatas, remoteInterface: remoteInterface, dbManager: dbManager + allUpdatedMetadatas, + account: account, + remoteInterface: remoteInterface, + dbManager: dbManager ) { updatedItems in if !updatedItems.isEmpty { diff --git a/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift b/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift index 151bd19..5030ca7 100644 --- a/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift +++ b/Sources/NextcloudFileProviderKit/Enumeration/RemoteChangeObserver.swift @@ -18,7 +18,8 @@ public class RemoteChangeObserver: NSObject, NextcloudKitDelegate, URLSessionWeb public let remoteInterface: RemoteInterface public let changeNotificationInterface: ChangeNotificationInterface public let domain: NSFileProviderDomain? - public var accountId: String { remoteInterface.account.ncKitAccount } + public var account: Account + public var accountId: String { account.ncKitAccount } public var webSocketPingIntervalNanoseconds: UInt64 = 3 * 1_000_000_000 public var webSocketReconfigureIntervalNanoseconds: UInt64 = 1 * 1_000_000_000 @@ -61,10 +62,12 @@ public class RemoteChangeObserver: NSObject, NextcloudKitDelegate, URLSessionWeb } public init( + account: Account, remoteInterface: RemoteInterface, changeNotificationInterface: ChangeNotificationInterface, domain: NSFileProviderDomain? ) { + self.account = account self.remoteInterface = remoteInterface self.changeNotificationInterface = changeNotificationInterface self.domain = domain @@ -131,7 +134,8 @@ public class RemoteChangeObserver: NSObject, NextcloudKitDelegate, URLSessionWeb private func configureNotifyPush() async { let (_, capabilitiesData, error) = await remoteInterface.fetchCapabilities( - options: .init(), + account: account, + options: .init(), taskHandler: { task in if let domain = self.domain { NSFileProviderManager(for: domain)?.register( @@ -200,8 +204,8 @@ public class RemoteChangeObserver: NSObject, NextcloudKitDelegate, URLSessionWeb logger.debug("Received auth challenge with method: \(authMethod, privacy: .public)") if authMethod == NSURLAuthenticationMethodHTTPBasic { let credential = URLCredential( - user: remoteInterface.account.username, - password: remoteInterface.account.password, + user: account.username, + password: account.password, persistence: .forSession ) completionHandler(.useCredential, credential) @@ -246,8 +250,8 @@ public class RemoteChangeObserver: NSObject, NextcloudKitDelegate, URLSessionWeb private func authenticateWebSocket() async { do { - try await webSocketTask?.send(.string(remoteInterface.account.username)) - try await webSocketTask?.send(.string(remoteInterface.account.password)) + try await webSocketTask?.send(.string(account.username)) + try await webSocketTask?.send(.string(account.password)) } catch let error { logger.error( """ diff --git a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift index f2a11c1..a270d66 100644 --- a/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/NextcloudKit+RemoteInterface.swift @@ -12,25 +12,13 @@ import NextcloudKit extension NextcloudKit: RemoteInterface { - public var account: Account { - guard let session = nkCommonInstance.nksessions.first else { - return Account(user: "", id: "", serverUrl: "", password: "") - } - - return Account( - user: session.user, - id: session.userId, - serverUrl: session.urlBase, - password: session.password - ) - } - public func setDelegate(_ delegate: any NextcloudKitDelegate) { setup(delegate: delegate) } public func createFolder( remotePath: String, + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> (account: String, ocId: String?, date: NSDate?, error: NKError) { @@ -51,6 +39,7 @@ extension NextcloudKit: RemoteInterface { localPath: String, creationDate: Date? = nil, modificationDate: Date? = nil, + account: Account, options: NKRequestOptions = .init(), requestHandler: @escaping (UploadRequest) -> Void = { _ in }, taskHandler: @escaping (URLSessionTask) -> Void = { _ in }, @@ -95,6 +84,7 @@ extension NextcloudKit: RemoteInterface { remotePathSource: String, remotePathDestination: String, overwrite: Bool, + account: Account, options: NKRequestOptions, taskHandler: @escaping (URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) { @@ -115,6 +105,7 @@ extension NextcloudKit: RemoteInterface { public func download( remotePath: String, localPath: String, + account: Account, options: NKRequestOptions = .init(), requestHandler: @escaping (DownloadRequest) -> Void = { _ in }, taskHandler: @escaping (URLSessionTask) -> Void = { _ in }, @@ -157,6 +148,7 @@ extension NextcloudKit: RemoteInterface { showHiddenFiles: Bool = false, includeHiddenFiles: [String] = [], requestBody: Data? = nil, + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> ( @@ -180,6 +172,7 @@ extension NextcloudKit: RemoteInterface { public func delete( remotePath: String, + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> (account: String, response: HTTPURLResponse?, error: NKError) { @@ -193,7 +186,10 @@ extension NextcloudKit: RemoteInterface { } public func downloadThumbnail( - url: URL, options: NKRequestOptions, taskHandler: @escaping (URLSessionTask) -> Void + url: URL, + account: Account, + options: NKRequestOptions, + taskHandler: @escaping (URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) { await withCheckedContinuation { continuation in downloadPreview( @@ -205,6 +201,7 @@ extension NextcloudKit: RemoteInterface { } public func fetchCapabilities( + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } ) async -> (account: String, data: Data?, error: NKError) { @@ -216,6 +213,7 @@ extension NextcloudKit: RemoteInterface { } public func fetchUserProfile( + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } ) async -> (account: String, userProfile: NKUserProfile?, data: Data?, error: NKError) { @@ -229,12 +227,13 @@ extension NextcloudKit: RemoteInterface { } public func tryAuthenticationAttempt( + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in } ) async -> AuthenticationAttemptResultState { // Test by trying to fetch user profile let (_, _, _, error) = - await fetchUserProfile(options: options, taskHandler: taskHandler) + await fetchUserProfile(account: account, options: options, taskHandler: taskHandler) if error == .success { return .success diff --git a/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift b/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift index aa6c93b..50d2a95 100644 --- a/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift +++ b/Sources/NextcloudFileProviderKit/Interface/RemoteInterface.swift @@ -22,12 +22,11 @@ public enum AuthenticationAttemptResultState: Int { public protocol RemoteInterface { - var account: Account { get } - func setDelegate(_ delegate: NextcloudKitDelegate) func createFolder( remotePath: String, + account: Account, options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> (account: String, ocId: String?, date: NSDate?, error: NKError) @@ -37,6 +36,7 @@ public protocol RemoteInterface { localPath: String, creationDate: Date?, modificationDate: Date?, + account: Account, options: NKRequestOptions, requestHandler: @escaping (_ request: UploadRequest) -> Void, taskHandler: @escaping (_ task: URLSessionTask) -> Void, @@ -56,6 +56,7 @@ public protocol RemoteInterface { remotePathSource: String, remotePathDestination: String, overwrite: Bool, + account: Account, options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) @@ -63,6 +64,7 @@ public protocol RemoteInterface { func download( remotePath: String, localPath: String, + account: Account, options: NKRequestOptions, requestHandler: @escaping (_ request: DownloadRequest) -> Void, taskHandler: @escaping (_ task: URLSessionTask) -> Void, @@ -83,31 +85,40 @@ public protocol RemoteInterface { showHiddenFiles: Bool, includeHiddenFiles: [String], requestBody: Data?, + account: Account, options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> (account: String, files: [NKFile], data: Data?, error: NKError) func delete( remotePath: String, + account: Account, options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> (account: String, response: HTTPURLResponse?, error: NKError) func downloadThumbnail( url: URL, + account: Account, options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) func fetchCapabilities( - options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void + account: Account, + options: NKRequestOptions, + taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) func fetchUserProfile( - options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void + account: Account, + options: NKRequestOptions, + taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> (account: String, userProfile: NKUserProfile?, data: Data?, error: NKError) func tryAuthenticationAttempt( - options: NKRequestOptions, taskHandler: @escaping (_ task: URLSessionTask) -> Void + account: Account, + options: NKRequestOptions, + taskHandler: @escaping (_ task: URLSessionTask) -> Void ) async -> AuthenticationAttemptResultState } diff --git a/Sources/NextcloudFileProviderKit/Item/Item+Create.swift b/Sources/NextcloudFileProviderKit/Item/Item+Create.swift index d0f9828..5591db4 100644 --- a/Sources/NextcloudFileProviderKit/Item/Item+Create.swift +++ b/Sources/NextcloudFileProviderKit/Item/Item+Create.swift @@ -17,13 +17,14 @@ extension Item { remotePath: String, parentItemIdentifier: NSFileProviderItemIdentifier, domain: NSFileProviderDomain? = nil, + account: Account, remoteInterface: RemoteInterface, progress: Progress, dbManager: FilesDatabaseManager ) async -> (Item?, Error?) { - let (account, _, _, createError) = await remoteInterface.createFolder( - remotePath: remotePath, options: .init(), taskHandler: { task in + let (_, _, _, createError) = await remoteInterface.createFolder( + remotePath: remotePath, account: account, options: .init(), taskHandler: { task in if let domain, let itemTemplate { NSFileProviderManager(for: domain)?.register( task, @@ -56,6 +57,7 @@ extension Item { showHiddenFiles: true, includeHiddenFiles: [], requestBody: nil, + account: account, options: .init(), taskHandler: { task in if let domain, let itemTemplate { @@ -92,6 +94,7 @@ extension Item { let fpItem = Item( metadata: directoryMetadata, parentItemIdentifier: parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) @@ -104,15 +107,17 @@ extension Item { itemTemplate: NSFileProviderItem, parentItemRemotePath: String, domain: NSFileProviderDomain? = nil, + account: Account, remoteInterface: RemoteInterface, progress: Progress, dbManager: FilesDatabaseManager ) async -> (Item?, Error?) { - let (account, ocId, etag, date, size, _, _, error) = await remoteInterface.upload( + let (_, ocId, etag, date, size, _, _, error) = await remoteInterface.upload( remotePath: remotePath, localPath: localPath, creationDate: itemTemplate.creationDate as? Date, modificationDate: itemTemplate.contentModificationDate as? Date, + account: account, options: .init(), requestHandler: { progress.setHandlersFromAfRequest($0) }, taskHandler: { task in @@ -151,7 +156,7 @@ extension Item { etag: \(etag ?? "", privacy: .public) date: \(date ?? NSDate(), privacy: .public) size: \(size, privacy: .public), - account: \(account, privacy: .public) + account: \(account.ncKitAccount, privacy: .public) """ ) @@ -168,7 +173,7 @@ extension Item { let newMetadata = ItemMetadata() newMetadata.date = (date ?? NSDate()) as Date newMetadata.etag = etag ?? "" - newMetadata.account = account + newMetadata.account = account.ncKitAccount newMetadata.fileName = itemTemplate.filename newMetadata.fileNameView = itemTemplate.filename newMetadata.ocId = ocId @@ -188,6 +193,7 @@ extension Item { let fpItem = Item( metadata: newMetadata, parentItemIdentifier: itemTemplate.parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) @@ -199,6 +205,7 @@ extension Item { contents: URL, remotePath: String, domain: NSFileProviderDomain? = nil, + account: Account, remoteInterface: RemoteInterface, progress: Progress, dbManager: FilesDatabaseManager @@ -268,7 +275,9 @@ extension Item { """ ) let (_, _, _, createError) = await remoteInterface.createFolder( - remotePath: childRemoteUrl, options: .init(), taskHandler: { task in + remotePath: childRemoteUrl, + account: account, + options: .init(), taskHandler: { task in if let domain { NSFileProviderManager(for: domain)?.register( task, @@ -305,6 +314,7 @@ extension Item { localPath: childUrlPath, creationDate: childUrlAttributes.creationDate, modificationDate: childUrlAttributes.contentModificationDate, + account: account, options: .init(), requestHandler: { progress.setHandlersFromAfRequest($0) }, taskHandler: { task in @@ -337,7 +347,10 @@ extension Item { // After everything, check into what the final state is of each folder now Self.logger.debug("Reading bpi folder at: \(remoteDirectoryPath, privacy: .public)") let (_, _, _, _, readError) = await Enumerator.readServerUrl( - remoteDirectoryPath, remoteInterface: remoteInterface, dbManager: dbManager + remoteDirectoryPath, + account: account, + remoteInterface: remoteInterface, + dbManager: dbManager ) if let readError, readError != .success { @@ -352,14 +365,14 @@ extension Item { } guard let bundleRootMetadata = dbManager.itemMetadata( - account: remoteInterface.account.ncKitAccount, locatedAtRemoteUrl: remotePath + account: account.ncKitAccount, locatedAtRemoteUrl: remotePath ) else { Self.logger.error( """ Could not find directory metadata for bundle or package at: \(remotePath, privacy: .public) of account: - \(remoteInterface.account.ncKitAccount, privacy: .public) + \(account.ncKitAccount, privacy: .public) with contents located at: \(contentsPath, privacy: .public) """ @@ -372,6 +385,7 @@ extension Item { return Item( metadata: bundleRootMetadata, parentItemIdentifier: rootItem.parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) } @@ -383,6 +397,7 @@ extension Item { options: NSFileProviderCreateItemOptions = [], request: NSFileProviderRequest = NSFileProviderRequest(), domain: NSFileProviderDomain? = nil, + account: Account, remoteInterface: RemoteInterface, progress: Progress, dbManager: FilesDatabaseManager = .shared @@ -412,7 +427,7 @@ extension Item { // TODO: Deduplicate if parentItemIdentifier == .rootContainer { - parentItemRemotePath = remoteInterface.account.davFilesUrl + parentItemRemotePath = account.davFilesUrl } else { guard let parentItemMetadata = dbManager.directoryMetadata( ocId: parentItemIdentifier.rawValue @@ -454,6 +469,7 @@ extension Item { remotePath: newServerUrlFileName, parentItemIdentifier: parentItemIdentifier, domain: domain, + account: account, remoteInterface: remoteInterface, progress: isBundleOrPackage ? Progress() : progress, dbManager: dbManager @@ -489,6 +505,7 @@ extension Item { ) let (metadatas, _, _, _, readError) = await Enumerator.readServerUrl( newServerUrlFileName, + account: account, remoteInterface: remoteInterface, dbManager: dbManager, domain: domain, @@ -519,6 +536,7 @@ extension Item { item = Item( metadata: itemMetadata, parentItemIdentifier: parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) } @@ -557,6 +575,7 @@ extension Item { contents: url, remotePath: newServerUrlFileName, domain: domain, + account: account, remoteInterface: remoteInterface, progress: progress, dbManager: dbManager @@ -573,6 +592,7 @@ extension Item { itemTemplate: itemTemplate, parentItemRemotePath: parentItemRemotePath, domain: domain, + account: account, remoteInterface: remoteInterface, progress: progress, dbManager: dbManager diff --git a/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift b/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift index 7ab8c81..879fccb 100644 --- a/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift +++ b/Sources/NextcloudFileProviderKit/Item/Item+Delete.swift @@ -23,7 +23,10 @@ public extension Item { let ocId = itemIdentifier.rawValue let (_, _, error) = await remoteInterface.delete( - remotePath: serverFileNameUrl, options: .init(), taskHandler: { task in + remotePath: serverFileNameUrl, + account: account, + options: .init(), + taskHandler: { task in if let domain { NSFileProviderManager(for: domain)?.register( task, diff --git a/Sources/NextcloudFileProviderKit/Item/Item+Fetch.swift b/Sources/NextcloudFileProviderKit/Item/Item+Fetch.swift index 030d765..9b0fb0b 100644 --- a/Sources/NextcloudFileProviderKit/Item/Item+Fetch.swift +++ b/Sources/NextcloudFileProviderKit/Item/Item+Fetch.swift @@ -28,7 +28,10 @@ public extension Item { while !remoteDirectoryPaths.isEmpty { let remoteDirectoryPath = remoteDirectoryPaths.removeFirst() let (metadatas, _, _, _, readError) = await Enumerator.readServerUrl( - remoteDirectoryPath, remoteInterface: remoteInterface, dbManager: dbManager + remoteDirectoryPath, + account: account, + remoteInterface: remoteInterface, + dbManager: dbManager ) if let readError, readError != .success { @@ -74,6 +77,7 @@ public extension Item { let (_, _, _, _, _, _, error) = await remoteInterface.download( remotePath: remotePath, localPath: childLocalPath, + account: account, options: .init(), requestHandler: { progress.setHandlersFromAfRequest($0) }, taskHandler: { task in @@ -208,6 +212,7 @@ public extension Item { let (_, _, _, _, _, _, error) = await remoteInterface.download( remotePath: serverUrlFileName, localPath: localPath.path, + account: account, options: .init(), requestHandler: { _ in }, taskHandler: { _ in }, @@ -259,6 +264,7 @@ public extension Item { let fpItem = Item( metadata: updatedMetadata, parentItemIdentifier: parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) @@ -286,7 +292,7 @@ public extension Item { ) let (_, data, error) = await remoteInterface.downloadThumbnail( - url: thumbnailUrl, options: .init(), taskHandler: { task in + url: thumbnailUrl, account: account, options: .init(), taskHandler: { task in if let domain { NSFileProviderManager(for: domain)?.register( task, diff --git a/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift b/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift index dc6fd55..b2a9b98 100644 --- a/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift +++ b/Sources/NextcloudFileProviderKit/Item/Item+Modify.swift @@ -27,6 +27,7 @@ public extension Item { remotePathSource: oldRemotePath, remotePathDestination: newRemotePath, overwrite: false, + account: account, options: .init(), taskHandler: { task in if let domain { @@ -82,6 +83,7 @@ public extension Item { let modifiedItem = Item( metadata: newMetadata, parentItemIdentifier: newParentItemIdentifier, + account: account, remoteInterface: remoteInterface ) return (modifiedItem, nil) @@ -135,6 +137,7 @@ public extension Item { localPath: localPath, creationDate: newCreationDate, modificationDate: newContentModificationDate, + account: account, options: .init(), requestHandler: { progress.setHandlersFromAfRequest($0) }, taskHandler: { task in @@ -205,6 +208,7 @@ public extension Item { let modifiedItem = Item( metadata: newMetadata, parentItemIdentifier: parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) return (modifiedItem, nil) @@ -254,7 +258,10 @@ public extension Item { while !directoriesToRead.isEmpty { let remoteDirectoryPath = directoriesToRead.removeFirst() let (metadatas, _, _, _, readError) = await Enumerator.readServerUrl( - remoteDirectoryPath, remoteInterface: remoteInterface, dbManager: dbManager + remoteDirectoryPath, + account: account, + remoteInterface: remoteInterface, + dbManager: dbManager ) // Important note -- the enumerator will import found items' metadata into the database. // This is important for when we want to start deleting stale items and want to avoid trying @@ -355,7 +362,10 @@ public extension Item { """ ) let (_, _, _, createError) = await remoteInterface.createFolder( - remotePath: childRemoteUrl, options: .init(), taskHandler: { task in + remotePath: childRemoteUrl, + account: account, + options: .init(), + taskHandler: { task in if let domain { NSFileProviderManager(for: domain)?.register( task, @@ -388,6 +398,7 @@ public extension Item { localPath: childUrlPath, creationDate: childUrlAttributes.creationDate, modificationDate: childUrlAttributes.contentModificationDate, + account: account, options: .init(), requestHandler: { progress.setHandlersFromAfRequest($0) }, taskHandler: { task in @@ -422,7 +433,10 @@ public extension Item { guard dbManager.itemMetadataFromOcId(staleItemMetadata.ocId) != nil else { continue } let (_, _, deleteError) = await remoteInterface.delete( - remotePath: staleItem.key, options: .init(), taskHandler: { task in + remotePath: staleItem.key, + account: account, + options: .init(), + taskHandler: { task in if let domain { NSFileProviderManager(for: domain)?.register( task, @@ -454,7 +468,10 @@ public extension Item { for remoteDirectoryPath in remoteDirectoriesPaths { // After everything, check into what the final state is of each folder now let (_, _, _, _, readError) = await Enumerator.readServerUrl( - remoteDirectoryPath, remoteInterface: remoteInterface, dbManager: dbManager + remoteDirectoryPath, + account: account, + remoteInterface: remoteInterface, + dbManager: dbManager ) if let readError, readError != .success { @@ -485,6 +502,7 @@ public extension Item { return Item( metadata: bundleRootMetadata, parentItemIdentifier: parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) } @@ -530,7 +548,7 @@ public extension Item { // remote changes and then, upon user interaction, will try to modify the item. // That is, if the parent item has changed at all (it might not have) if parentItemIdentifier == .rootContainer { - parentItemServerUrl = remoteInterface.account.davFilesUrl + parentItemServerUrl = account.davFilesUrl } else { guard let parentItemMetadata = dbManager.directoryMetadata( ocId: parentItemIdentifier.rawValue diff --git a/Sources/NextcloudFileProviderKit/Item/Item.swift b/Sources/NextcloudFileProviderKit/Item/Item.swift index a734fbe..76072c5 100644 --- a/Sources/NextcloudFileProviderKit/Item/Item.swift +++ b/Sources/NextcloudFileProviderKit/Item/Item.swift @@ -27,6 +27,7 @@ public class Item: NSObject, NSFileProviderItem { public let metadata: ItemMetadata public let parentItemIdentifier: NSFileProviderItemIdentifier + public let account: Account public let remoteInterface: RemoteInterface public var itemIdentifier: NSFileProviderItemIdentifier { @@ -152,7 +153,7 @@ public class Item: NSObject, NSFileProviderItem { public var fileSystemFlags: NSFileProviderFileSystemFlags { if metadata.lock, - (metadata.lockOwnerType != 0 || metadata.lockOwner != remoteInterface.account.username), + (metadata.lockOwnerType != 0 || metadata.lockOwner != account.username), metadata.lockTimeOut ?? Date() > Date() { return [.userReadable] @@ -179,18 +180,19 @@ public class Item: NSObject, NSFileProviderItem { #endif } - public static func rootContainer(remoteInterface: RemoteInterface) -> Item { + public static func rootContainer(account: Account, remoteInterface: RemoteInterface) -> Item { let metadata = ItemMetadata() - metadata.account = remoteInterface.account.ncKitAccount + metadata.account = account.ncKitAccount metadata.directory = true metadata.ocId = NSFileProviderItemIdentifier.rootContainer.rawValue metadata.fileName = "/" metadata.fileNameView = "/" - metadata.serverUrl = remoteInterface.account.davFilesUrl + metadata.serverUrl = account.davFilesUrl metadata.classFile = NKCommon.TypeClassFile.directory.rawValue return Item( metadata: metadata, parentItemIdentifier: .rootContainer, + account: account, remoteInterface: remoteInterface ) } @@ -200,22 +202,25 @@ public class Item: NSObject, NSFileProviderItem { public required init( metadata: ItemMetadata, parentItemIdentifier: NSFileProviderItemIdentifier, + account: Account, remoteInterface: RemoteInterface ) { self.metadata = ItemMetadata(value: metadata) // Safeguard against active items self.parentItemIdentifier = parentItemIdentifier + self.account = account self.remoteInterface = remoteInterface super.init() } public static func storedItem( identifier: NSFileProviderItemIdentifier, + account: Account, remoteInterface: RemoteInterface, dbManager: FilesDatabaseManager = .shared ) -> Item? { // resolve the given identifier to a record in the model guard identifier != .rootContainer else { - return Item.rootContainer(remoteInterface: remoteInterface) + return Item.rootContainer(account: account, remoteInterface: remoteInterface) } guard let metadata = dbManager.itemMetadataFromFileProviderItemIdentifier(identifier), @@ -225,6 +230,7 @@ public class Item: NSObject, NSFileProviderItem { return Item( metadata: metadata, parentItemIdentifier: parentItemIdentifier, + account: account, remoteInterface: remoteInterface ) } diff --git a/Sources/NextcloudFileProviderKit/Metadata/ItemMetadata+NKFile.swift b/Sources/NextcloudFileProviderKit/Metadata/ItemMetadata+NKFile.swift index bc61153..c34fa79 100644 --- a/Sources/NextcloudFileProviderKit/Metadata/ItemMetadata+NKFile.swift +++ b/Sources/NextcloudFileProviderKit/Metadata/ItemMetadata+NKFile.swift @@ -19,7 +19,7 @@ public extension ItemMetadata { // TODO: Convert to async/await static func metadatasFromDirectoryReadNKFiles( _ files: [NKFile], - account: String, + account: Account, completionHandler: @escaping ( _ directoryMetadata: ItemMetadata, _ childDirectoriesMetadatas: [ItemMetadata], diff --git a/Sources/NextcloudFileProviderKit/Utilities/ThumbnailFetching.swift b/Sources/NextcloudFileProviderKit/Utilities/ThumbnailFetching.swift index 0f6344a..5f7b713 100644 --- a/Sources/NextcloudFileProviderKit/Utilities/ThumbnailFetching.swift +++ b/Sources/NextcloudFileProviderKit/Utilities/ThumbnailFetching.swift @@ -15,6 +15,7 @@ fileprivate let logger = Logger(subsystem: Logger.subsystem, category: "thumbnai public func fetchThumbnails( for itemIdentifiers: [NSFileProviderItemIdentifier], requestedSize size: CGSize, + account: Account, usingRemoteInterface remoteInterface: RemoteInterface, perThumbnailCompletionHandler: @escaping ( NSFileProviderItemIdentifier, @@ -35,7 +36,9 @@ public func fetchThumbnails( for itemIdentifier in itemIdentifiers { guard let item = Item.storedItem( - identifier: itemIdentifier, remoteInterface: remoteInterface + identifier: itemIdentifier, + account: account, + remoteInterface: remoteInterface ) else { logger.error( """ diff --git a/Tests/Interface/MockRemoteInterface.swift b/Tests/Interface/MockRemoteInterface.swift index 2c63d2f..295d81e 100644 --- a/Tests/Interface/MockRemoteInterface.swift +++ b/Tests/Interface/MockRemoteInterface.swift @@ -13,19 +13,15 @@ import NextcloudKit fileprivate let mockCapabilities = ##"{"ocs":{"meta":{"status":"ok","statuscode":100,"message":"OK","totalitems":"","itemsperpage":""},"data":{"version":{"major":28,"minor":0,"micro":4,"string":"28.0.4","edition":"","extendedSupport":false},"capabilities":{"core":{"pollinterval":60,"webdav-root":"remote.php\/webdav","reference-api":true,"reference-regex":"(\\s|\\n|^)(https?:\\\/\\\/)((?:[-A-Z0-9+_]+\\.)+[-A-Z]+(?:\\\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\\s|\\n|$)"},"bruteforce":{"delay":0,"allow-listed":false},"files":{"bigfilechunking":true,"blacklisted_files":[".htaccess"],"directEditing":{"url":"https:\/\/mock.nc.com\/ocs\/v2.php\/apps\/files\/api\/v1\/directEditing","etag":"c748e8fc588b54fc5af38c4481a19d20","supportsFileId":true},"comments":true,"undelete":true,"versioning":true,"version_labeling":true,"version_deletion":true},"activity":{"apiv2":["filters","filters-api","previews","rich-strings"]},"circles":{"version":"28.0.0","status":{"globalScale":false},"settings":{"frontendEnabled":true,"allowedCircles":262143,"allowedUserTypes":31,"membersLimit":-1},"circle":{"constants":{"flags":{"1":"Single","2":"Personal","4":"System","8":"Visible","16":"Open","32":"Invite","64":"Join Request","128":"Friends","256":"Password Protected","512":"No Owner","1024":"Hidden","2048":"Backend","4096":"Local","8192":"Root","16384":"Circle Invite","32768":"Federated","65536":"Mount point"},"source":{"core":{"1":"Nextcloud Account","2":"Nextcloud Group","4":"Email Address","8":"Contact","16":"Circle","10000":"Nextcloud App"},"extra":{"10001":"Circles App","10002":"Admin Command Line"}}},"config":{"coreFlags":[1,2,4],"systemFlags":[512,1024,2048]}},"member":{"constants":{"level":{"1":"Member","4":"Moderator","8":"Admin","9":"Owner"}},"type":{"0":"single","1":"user","2":"group","4":"mail","8":"contact","16":"circle","10000":"app"}}},"ocm":{"enabled":true,"apiVersion":"1.0-proposal1","endPoint":"https:\/\/mock.nc.com\/ocm","resourceTypes":[{"name":"file","shareTypes":["user","group"],"protocols":{"webdav":"\/public.php\/webdav\/"}}]},"dav":{"chunking":"1.0","bulkupload":"1.0"},"deck":{"version":"1.12.2","canCreateBoards":true,"apiVersions":["1.0","1.1"]},"files_sharing":{"api_enabled":true,"public":{"enabled":true,"password":{"enforced":false,"askForOptionalPassword":false},"expire_date":{"enabled":true,"days":7,"enforced":true},"multiple_links":true,"expire_date_internal":{"enabled":false},"expire_date_remote":{"enabled":false},"send_mail":false,"upload":true,"upload_files_drop":true},"resharing":true,"user":{"send_mail":false,"expire_date":{"enabled":true}},"group_sharing":true,"group":{"enabled":true,"expire_date":{"enabled":true}},"default_permissions":31,"federation":{"outgoing":true,"incoming":true,"expire_date":{"enabled":true},"expire_date_supported":{"enabled":true}},"sharee":{"query_lookup_default":false,"always_show_unique":true},"sharebymail":{"enabled":true,"send_password_by_mail":true,"upload_files_drop":{"enabled":true},"password":{"enabled":true,"enforced":false},"expire_date":{"enabled":true,"enforced":true}}},"fulltextsearch":{"remote":true,"providers":[{"id":"deck","name":"Deck"},{"id":"files","name":"Files"}]},"notes":{"api_version":["0.2","1.3"],"version":"4.9.4"},"notifications":{"ocs-endpoints":["list","get","delete","delete-all","icons","rich-strings","action-web","user-status","exists"],"push":["devices","object-data","delete"],"admin-notifications":["ocs","cli"]},"notify_push":{"type":["files","activities","notifications"],"endpoints":{"websocket":"wss:\/\/mock.nc.com\/push\/ws","pre_auth":"https:\/\/mock.nc.com\/apps\/notify_push\/pre_auth"}},"password_policy":{"minLength":10,"enforceNonCommonPassword":true,"enforceNumericCharacters":false,"enforceSpecialCharacters":false,"enforceUpperLowerCase":false,"api":{"generate":"https:\/\/mock.nc.com\/ocs\/v2.php\/apps\/password_policy\/api\/v1\/generate","validate":"https:\/\/mock.nc.com\/ocs\/v2.php\/apps\/password_policy\/api\/v1\/validate"}},"provisioning_api":{"version":"1.18.0","AccountPropertyScopesVersion":2,"AccountPropertyScopesFederatedEnabled":true,"AccountPropertyScopesPublishedEnabled":true},"richdocuments":{"version":"8.3.4","mimetypes":["application\/vnd.oasis.opendocument.text","application\/vnd.oasis.opendocument.spreadsheet","application\/vnd.oasis.opendocument.graphics","application\/vnd.oasis.opendocument.presentation","application\/vnd.oasis.opendocument.text-flat-xml","application\/vnd.oasis.opendocument.spreadsheet-flat-xml","application\/vnd.oasis.opendocument.graphics-flat-xml","application\/vnd.oasis.opendocument.presentation-flat-xml","application\/vnd.lotus-wordpro","application\/vnd.visio","application\/vnd.ms-visio.drawing","application\/vnd.wordperfect","application\/rtf","text\/rtf","application\/msonenote","application\/msword","application\/vnd.openxmlformats-officedocument.wordprocessingml.document","application\/vnd.openxmlformats-officedocument.wordprocessingml.template","application\/vnd.ms-word.document.macroEnabled.12","application\/vnd.ms-word.template.macroEnabled.12","application\/vnd.ms-excel","application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet","application\/vnd.openxmlformats-officedocument.spreadsheetml.template","application\/vnd.ms-excel.sheet.macroEnabled.12","application\/vnd.ms-excel.template.macroEnabled.12","application\/vnd.ms-excel.addin.macroEnabled.12","application\/vnd.ms-excel.sheet.binary.macroEnabled.12","application\/vnd.ms-powerpoint","application\/vnd.openxmlformats-officedocument.presentationml.presentation","application\/vnd.openxmlformats-officedocument.presentationml.template","application\/vnd.openxmlformats-officedocument.presentationml.slideshow","application\/vnd.ms-powerpoint.addin.macroEnabled.12","application\/vnd.ms-powerpoint.presentation.macroEnabled.12","application\/vnd.ms-powerpoint.template.macroEnabled.12","application\/vnd.ms-powerpoint.slideshow.macroEnabled.12","text\/csv"],"mimetypesNoDefaultOpen":["image\/svg+xml","application\/pdf","text\/plain","text\/spreadsheet"],"mimetypesSecureView":[],"collabora":{"convert-to":{"available":true,"endpoint":"\/cool\/convert-to"},"hasMobileSupport":true,"hasProxyPrefix":false,"hasTemplateSaveAs":false,"hasTemplateSource":true,"hasWASMSupport":false,"hasZoteroSupport":true,"productName":"Collabora Online Development Edition","productVersion":"23.05.10.1","productVersionHash":"baa6eef","serverId":"8bee4df3"},"direct_editing":true,"templates":true,"productName":"Nextcloud Office","editonline_endpoint":"https:\/\/mock.nc.com\/apps\/richdocuments\/editonline","config":{"wopi_url":"https:\/\/mock.nc.com\/","public_wopi_url":"https:\/\/mock.nc.com","wopi_callback_url":"","disable_certificate_verification":null,"edit_groups":null,"use_groups":null,"doc_format":null,"timeout":15}},"spreed":{"features":["audio","video","chat-v2","conversation-v4","guest-signaling","empty-group-room","guest-display-names","multi-room-users","favorites","last-room-activity","no-ping","system-messages","delete-messages","mention-flag","in-call-flags","conversation-call-flags","notification-levels","invite-groups-and-mails","locked-one-to-one-rooms","read-only-rooms","listable-rooms","chat-read-marker","chat-unread","webinary-lobby","start-call-flag","chat-replies","circles-support","force-mute","sip-support","sip-support-nopin","chat-read-status","phonebook-search","raise-hand","room-description","rich-object-sharing","temp-user-avatar-api","geo-location-sharing","voice-message-sharing","signaling-v3","publishing-permissions","clear-history","direct-mention-flag","notification-calls","conversation-permissions","rich-object-list-media","rich-object-delete","unified-search","chat-permission","silent-send","silent-call","send-call-notification","talk-polls","breakout-rooms-v1","recording-v1","avatar","chat-get-context","single-conversation-status","chat-keep-notifications","typing-privacy","remind-me-later","bots-v1","markdown-messages","media-caption","session-state","note-to-self","recording-consent","sip-support-dialout","message-expiration","reactions","chat-reference-id"],"config":{"attachments":{"allowed":true,"folder":"\/Talk"},"call":{"enabled":true,"breakout-rooms":true,"recording":false,"recording-consent":0,"supported-reactions":["\u2764\ufe0f","\ud83c\udf89","\ud83d\udc4f","\ud83d\udc4d","\ud83d\udc4e","\ud83d\ude02","\ud83e\udd29","\ud83e\udd14","\ud83d\ude32","\ud83d\ude25"],"sip-enabled":false,"sip-dialout-enabled":false,"predefined-backgrounds":["1_office.jpg","2_home.jpg","3_abstract.jpg","4_beach.jpg","5_park.jpg","6_theater.jpg","7_library.jpg","8_space_station.jpg"],"can-upload-background":true,"can-enable-sip":true},"chat":{"max-length":32000,"read-privacy":0,"has-translation-providers":false,"typing-privacy":0},"conversations":{"can-create":true},"previews":{"max-gif-size":3145728},"signaling":{"session-ping-limit":200,"hello-v2-token-key":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECOu2NBMo4juGx6hHNIGa550gGaxN\nzqe\/TPxsX3QRjCrkyvdQaltjuRt\/9PddhpbMxcJSzwVLqZRVHylfllD8pg==\n-----END PUBLIC KEY-----\n"}},"version":"18.0.7"},"systemtags":{"enabled":true},"theming":{"name":"Nextcloud","url":"https:\/\/nextcloud.com","slogan":"a safe home for all your data","color":"#6ea68f","color-text":"#000000","color-element":"#6ea68f","color-element-bright":"#6ea68f","color-element-dark":"#6ea68f","logo":"https:\/\/mock.nc.com\/core\/img\/logo\/logo.svg?v=1","background":"#6ea68f","background-plain":true,"background-default":true,"logoheader":"https:\/\/mock.nc.com\/core\/img\/logo\/logo.svg?v=1","favicon":"https:\/\/mock.nc.com\/core\/img\/logo\/logo.svg?v=1"},"user_status":{"enabled":true,"restore":true,"supports_emoji":true},"weather_status":{"enabled":true}}}}}"## public class MockRemoteInterface: RemoteInterface { - public var account: Account public var capabilities = mockCapabilities public var rootItem: MockRemoteItem? public var delegate: (any NextcloudKitDelegate)? - private var accountString: String { account.ncKitAccount } - - public init(account: Account, rootItem: MockRemoteItem? = nil) { - self.account = account + public init(rootItem: MockRemoteItem? = nil) { self.rootItem = rootItem } - func sanitisedPath(_ path: String) -> String { + func sanitisedPath(_ path: String, account: Account) -> String { var sanitisedPath = path let filesPath = account.davFilesUrl if sanitisedPath.hasPrefix(filesPath) { @@ -42,10 +38,10 @@ public class MockRemoteInterface: RemoteInterface { return sanitisedPath } - func item(remotePath: String) -> MockRemoteItem? { + func item(remotePath: String, account: Account) -> MockRemoteItem? { guard let rootItem, !remotePath.isEmpty else { return nil } - let sanitisedPath = sanitisedPath(remotePath) + let sanitisedPath = sanitisedPath(remotePath, account: account) guard sanitisedPath != "/" else { return rootItem } var pathComponents = sanitisedPath.components(separatedBy: "/") @@ -65,8 +61,8 @@ public class MockRemoteInterface: RemoteInterface { return nil } - func parentPath(path: String) -> String { - let sanitisedPath = sanitisedPath(path) + func parentPath(path: String, account: Account) -> String { + let sanitisedPath = sanitisedPath(path, account: account) var pathComponents = sanitisedPath.components(separatedBy: "/") if pathComponents.first?.isEmpty == true { pathComponents.removeFirst() } guard !pathComponents.isEmpty else { return "/" } @@ -74,9 +70,9 @@ public class MockRemoteInterface: RemoteInterface { return account.davFilesUrl + "/" + pathComponents.joined(separator: "/") } - func parentItem(path: String) -> MockRemoteItem? { - let parentRemotePath = parentPath(path: path) - return item(remotePath: parentRemotePath) + func parentItem(path: String, account: Account) -> MockRemoteItem? { + let parentRemotePath = parentPath(path: path, account: account) + return item(remotePath: parentRemotePath, account: account) } func randomIdentifier() -> String { @@ -100,6 +96,7 @@ public class MockRemoteInterface: RemoteInterface { public func createFolder( remotePath: String, + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> (account: String, ocId: String?, date: NSDate?, error: NKError) { @@ -107,7 +104,7 @@ public class MockRemoteInterface: RemoteInterface { do { itemName = try name(from: remotePath) } catch { - return (accountString, nil, nil, .urlError) + return (account.ncKitAccount, nil, nil, .urlError) } let item = MockRemoteItem( @@ -120,13 +117,13 @@ public class MockRemoteInterface: RemoteInterface { userId: account.id, serverUrl: account.serverUrl ) - guard let parent = parentItem(path: remotePath) else { - return (accountString, nil, nil, .urlError) + guard let parent = parentItem(path: remotePath, account: account) else { + return (account.ncKitAccount, nil, nil, .urlError) } parent.children.append(item) item.parent = parent - return (accountString, item.identifier, item.creationDate as NSDate, .success) + return (account.ncKitAccount, item.identifier, item.creationDate as NSDate, .success) } public func upload( @@ -134,6 +131,7 @@ public class MockRemoteInterface: RemoteInterface { localPath: String, creationDate: Date? = .init(), modificationDate: Date? = .init(), + account: Account, options: NKRequestOptions = .init(), requestHandler: @escaping (Alamofire.UploadRequest) -> Void = { _ in }, taskHandler: @escaping (URLSessionTask) -> Void = { _ in }, @@ -153,7 +151,7 @@ public class MockRemoteInterface: RemoteInterface { itemName = try name(from: localPath) debugPrint("Handling item upload:", itemName) } catch { - return (accountString, nil, nil, nil, 0, nil, nil, .urlError) + return (account.ncKitAccount, nil, nil, nil, 0, nil, nil, .urlError) } let itemLocalUrl = URL(fileURLWithPath: localPath) @@ -162,11 +160,11 @@ public class MockRemoteInterface: RemoteInterface { itemData = try Data(contentsOf: itemLocalUrl) debugPrint("Acquired data:", itemData) } catch { - return (accountString, nil, nil, nil, 0, nil, nil, .urlError) + return (account.ncKitAccount, nil, nil, nil, 0, nil, nil, .urlError) } - guard let parent = parentItem(path: remotePath) else { - return (accountString, nil, nil, nil, 0, nil, nil, .urlError) + guard let parent = parentItem(path: remotePath, account: account) else { + return (account.ncKitAccount, nil, nil, nil, 0, nil, nil, .urlError) } debugPrint("Parent is:", parent.remotePath) @@ -196,7 +194,7 @@ public class MockRemoteInterface: RemoteInterface { } return ( - accountString, + account.ncKitAccount, item.identifier, item.versionIdentifier, item.modificationDate as NSDate, @@ -211,14 +209,15 @@ public class MockRemoteInterface: RemoteInterface { remotePathSource: String, remotePathDestination: String, overwrite: Bool = false, + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> (account: String, data: Data?, error: NKError) { guard let itemNewName = try? name(from: remotePathDestination), - let sourceItem = item(remotePath: remotePathSource), - let destinationParent = parentItem(path: remotePathDestination), + let sourceItem = item(remotePath: remotePathSource, account: account), + let destinationParent = parentItem(path: remotePathDestination, account: account), (overwrite || !destinationParent.children.contains(where: { $0.name == itemNewName })) - else { return (accountString, nil, .urlError) } + else { return (account.ncKitAccount, nil, .urlError) } sourceItem.name = itemNewName sourceItem.parent?.children.removeAll(where: { $0.identifier == sourceItem.identifier }) @@ -244,12 +243,13 @@ public class MockRemoteInterface: RemoteInterface { children = nextChildren } - return (accountString, nil, .success) + return (account.ncKitAccount, nil, .success) } public func download( remotePath: String, localPath: String, + account: Account, options: NKRequestOptions = .init(), requestHandler: @escaping (DownloadRequest) -> Void = { _ in }, taskHandler: @escaping (URLSessionTask) -> Void = { _ in }, @@ -263,8 +263,8 @@ public class MockRemoteInterface: RemoteInterface { afError: AFError?, remoteError: NKError ) { - guard let item = item(remotePath: remotePath) else { - return (accountString, nil, nil, 0, nil, nil, .urlError) + guard let item = item(remotePath: remotePath, account: account) else { + return (account.ncKitAccount, nil, nil, 0, nil, nil, .urlError) } let localUrl = URL(fileURLWithPath: localPath) @@ -279,11 +279,11 @@ public class MockRemoteInterface: RemoteInterface { } } catch let error { print("Could not write item data: \(error)") - return (accountString, nil, nil, 0, nil, nil, .urlError) + return (account.ncKitAccount, nil, nil, 0, nil, nil, .urlError) } return ( - accountString, + account.ncKitAccount, item.versionIdentifier, item.creationDate as NSDate, item.size, @@ -299,18 +299,19 @@ public class MockRemoteInterface: RemoteInterface { showHiddenFiles: Bool = true, includeHiddenFiles: [String] = [], requestBody: Data? = nil, + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> (account: String, files: [NKFile], data: Data?, error: NKError) { - guard let item = item(remotePath: remotePath) else { - return (accountString, [], nil, .urlError) + guard let item = item(remotePath: remotePath, account: account) else { + return (account.ncKitAccount, [], nil, .urlError) } switch depth { case .target: - return (accountString, [item.nkfile], nil, .success) + return (account.ncKitAccount, [item.nkfile], nil, .success) case .targetAndDirectChildren: - return (accountString, [item.nkfile] + item.children.map { $0.nkfile }, nil, .success) + return (account.ncKitAccount, [item.nkfile] + item.children.map { $0.nkfile }, nil, .success) case .targetAndAllChildren: var files = [NKFile]() var queue = [item] @@ -322,43 +323,47 @@ public class MockRemoteInterface: RemoteInterface { } queue = nextQueue } - return (accountString, files, nil, .success) + return (account.ncKitAccount, files, nil, .success) } } public func delete( remotePath: String, + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> (account: String, response: HTTPURLResponse?, error: NKError) { - guard let item = item(remotePath: remotePath) else { - return (accountString, nil, .urlError) + guard let item = item(remotePath: remotePath, account: account) else { + return (account.ncKitAccount, nil, .urlError) } item.children = [] item.parent?.children.removeAll(where: { $0.identifier == item.identifier }) item.parent = nil - return (accountString, nil, .success) + return (account.ncKitAccount, nil, .success) } public func downloadThumbnail( url: URL, + account: Account, options: NKRequestOptions, taskHandler: @escaping (URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) { // TODO: Implement downloadThumbnail - return (accountString, nil, .success) + return (account.ncKitAccount, nil, .success) } public func fetchCapabilities( + account: Account, options: NKRequestOptions, taskHandler: @escaping (URLSessionTask) -> Void ) async -> (account: String, data: Data?, error: NKError) { - return (accountString, capabilities.data(using: .utf8), .success) + return (account.ncKitAccount, capabilities.data(using: .utf8), .success) } public func fetchUserProfile( + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> (account: String, userProfile: NKUserProfile?, data: Data?, error: NKError) { @@ -371,6 +376,7 @@ public class MockRemoteInterface: RemoteInterface { } public func tryAuthenticationAttempt( + account: Account, options: NKRequestOptions = .init(), taskHandler: @escaping (URLSessionTask) -> Void = { _ in } ) async -> AuthenticationAttemptResultState { diff --git a/Tests/InterfaceTests/MockRemoteInterfaceTests.swift b/Tests/InterfaceTests/MockRemoteInterfaceTests.swift index f678eab..9876278 100644 --- a/Tests/InterfaceTests/MockRemoteInterfaceTests.swift +++ b/Tests/InterfaceTests/MockRemoteInterfaceTests.swift @@ -30,7 +30,7 @@ final class MockRemoteInterfaceTests: XCTestCase { } func testItemForRemotePath() { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let itemA = MockRemoteItem( identifier: "a", versionIdentifier: "a", @@ -83,34 +83,42 @@ final class MockRemoteInterfaceTests: XCTestCase { itemA_B.children = [targetItem] targetItem.parent = itemA_B - XCTAssertEqual(remoteInterface.item( - remotePath: Self.account.davFilesUrl + "/a/b/target"), targetItem + XCTAssertEqual( + remoteInterface.item( + remotePath: Self.account.davFilesUrl + "/a/b/target", account: Self.account + ), targetItem ) } func testItemForRootPath() { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) - XCTAssertEqual(remoteInterface.item(remotePath: Self.account.davFilesUrl), rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) + XCTAssertEqual( + remoteInterface.item(remotePath: Self.account.davFilesUrl, account: Self.account), rootItem + ) } func testPathParentPath() { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let testPath = Self.account.davFilesUrl + "/a/B/c/d" let expectedPath = Self.account.davFilesUrl + "/a/B/c" - XCTAssertEqual(remoteInterface.parentPath(path: testPath), expectedPath) + XCTAssertEqual( + remoteInterface.parentPath(path: testPath, account: Self.account), expectedPath + ) } func testRootPathParentPath() { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let testPath = Self.account.davFilesUrl + "/" let expectedPath = Self.account.davFilesUrl + "/" - XCTAssertEqual(remoteInterface.parentPath(path: testPath), expectedPath) + XCTAssertEqual( + remoteInterface.parentPath(path: testPath, account: Self.account), expectedPath + ) } func testNameFromPath() throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let testPath = Self.account.davFilesUrl + "/a/b/c/d" let expectedName = "d" let name = try remoteInterface.name(from: testPath) @@ -118,40 +126,46 @@ final class MockRemoteInterfaceTests: XCTestCase { } func testCreateFolder() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let newFolderAPath = Self.account.davFilesUrl + "/A" let newFolderA_BPath = Self.account.davFilesUrl + "/A/B/" - let resultA = await remoteInterface.createFolder(remotePath: newFolderAPath) + let resultA = await remoteInterface.createFolder( + remotePath: newFolderAPath, account: Self.account + ) XCTAssertEqual(resultA.error, .success) - let resultA_B = await remoteInterface.createFolder(remotePath: newFolderA_BPath) + let resultA_B = await remoteInterface.createFolder( + remotePath: newFolderA_BPath, account: Self.account + ) XCTAssertEqual(resultA_B.error, .success) - let itemA = remoteInterface.item(remotePath: newFolderAPath) + let itemA = remoteInterface.item(remotePath: newFolderAPath, account: Self.account) XCTAssertNotNil(itemA) XCTAssertEqual(itemA?.name, "A") XCTAssertTrue(itemA?.directory ?? false) - let itemA_B = remoteInterface.item(remotePath: newFolderA_BPath) + let itemA_B = remoteInterface.item(remotePath: newFolderA_BPath, account: Self.account) XCTAssertNotNil(itemA_B) XCTAssertEqual(itemA_B?.name, "B") XCTAssertTrue(itemA_B?.directory ?? false) } func testUpload() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let fileUrl = URL.temporaryDirectory.appendingPathComponent("file.txt", conformingTo: .text) let fileData = Data("Hello, World!".utf8) let fileSize = Int64(fileData.count) try fileData.write(to: fileUrl) let result = await remoteInterface.upload( - remotePath: Self.account.davFilesUrl, localPath: fileUrl.path + remotePath: Self.account.davFilesUrl, localPath: fileUrl.path, account: Self.account ) XCTAssertEqual(result.remoteError, .success) - let remoteItem = remoteInterface.item(remotePath: Self.account.davFilesUrl + "/file.txt") + let remoteItem = remoteInterface.item( + remotePath: Self.account.davFilesUrl + "/file.txt", account: Self.account + ) XCTAssertNotNil(remoteItem) XCTAssertEqual(remoteItem?.name, "file.txt") @@ -163,7 +177,7 @@ final class MockRemoteInterfaceTests: XCTestCase { } func testMove() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let itemA = MockRemoteItem( identifier: "a", name: "a", @@ -215,7 +229,8 @@ final class MockRemoteInterfaceTests: XCTestCase { let result = await remoteInterface.move( remotePathSource: Self.account.davFilesUrl + "/a/c/target", - remotePathDestination: Self.account.davFilesUrl + "/b/targetRenamed" + remotePathDestination: Self.account.davFilesUrl + "/b/targetRenamed", + account: Self.account ) XCTAssertEqual(result.error, .success) XCTAssertEqual(itemB.children, [targetItem]) @@ -226,7 +241,7 @@ final class MockRemoteInterfaceTests: XCTestCase { } func testDownload() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let fileUrl = FileManager.default.temporaryDirectory.appendingPathComponent( "file.txt", conformingTo: .text ) @@ -235,10 +250,14 @@ final class MockRemoteInterfaceTests: XCTestCase { return } let fileData = Data("Hello, World!".utf8) - let _ = await remoteInterface.upload(remotePath: "/", localPath: fileUrl.path) + let _ = await remoteInterface.upload( + remotePath: "/", localPath: fileUrl.path, account: Self.account + ) let result = await remoteInterface.download( - remotePath: Self.account.davFilesUrl + "/file.txt", localPath: fileUrl.path + remotePath: Self.account.davFilesUrl + "/file.txt", + localPath: fileUrl.path, + account: Self.account ) XCTAssertEqual(result.remoteError, .success) @@ -247,7 +266,7 @@ final class MockRemoteInterfaceTests: XCTestCase { } func testEnumerate() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let itemA = MockRemoteItem( identifier: "a", name: "a", @@ -334,7 +353,9 @@ final class MockRemoteInterfaceTests: XCTestCase { itemC_A.children = [itemC_A_A] itemC_A_A.parent = itemC_A - let result = await remoteInterface.enumerate(remotePath: "/", depth: .target) + let result = await remoteInterface.enumerate( + remotePath: "/", depth: .target, account: Self.account + ) XCTAssertEqual(result.error, .success) XCTAssertEqual(result.files.count, 1) let targetRootFile = result.files.first @@ -345,7 +366,9 @@ final class MockRemoteInterfaceTests: XCTestCase { XCTAssertEqual(targetRootFile?.etag, expectedRoot?.versionIdentifier) let resultChildren = await remoteInterface.enumerate( - remotePath: Self.account.davFilesUrl, depth: .targetAndDirectChildren + remotePath: Self.account.davFilesUrl, + depth: .targetAndDirectChildren, + account: Self.account ) XCTAssertEqual(resultChildren.error, .success) XCTAssertEqual(resultChildren.files.count, 4) @@ -360,7 +383,9 @@ final class MockRemoteInterfaceTests: XCTestCase { ) let resultAChildren = await remoteInterface.enumerate( - remotePath: Self.account.davFilesUrl + "/a", depth: .targetAndDirectChildren + remotePath: Self.account.davFilesUrl + "/a", + depth: .targetAndDirectChildren, + account: Self.account ) XCTAssertEqual(resultAChildren.error, .success) XCTAssertEqual(resultAChildren.files.count, 3) @@ -370,7 +395,9 @@ final class MockRemoteInterfaceTests: XCTestCase { ) let resultCChildren = await remoteInterface.enumerate( - remotePath: Self.account.davFilesUrl + "/c", depth: .targetAndDirectChildren + remotePath: Self.account.davFilesUrl + "/c", + depth: .targetAndDirectChildren, + account: Self.account ) XCTAssertEqual(resultCChildren.error, .success) XCTAssertEqual(resultCChildren.files.count, 2) @@ -380,7 +407,9 @@ final class MockRemoteInterfaceTests: XCTestCase { ) let resultCRecursiveChildren = await remoteInterface.enumerate( - remotePath: Self.account.davFilesUrl + "/c", depth: .targetAndAllChildren + remotePath: Self.account.davFilesUrl + "/c", + depth: .targetAndAllChildren, + account: Self.account ) XCTAssertEqual(resultCRecursiveChildren.error, .success) XCTAssertEqual(resultCRecursiveChildren.files.count, 3) @@ -391,7 +420,7 @@ final class MockRemoteInterfaceTests: XCTestCase { } func testDelete() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let itemA = MockRemoteItem( identifier: "a", name: "a", @@ -441,26 +470,30 @@ final class MockRemoteInterfaceTests: XCTestCase { itemA_C.children = [itemA_C_D] itemA_C_D.parent = itemA_C - let result = await remoteInterface.delete(remotePath: Self.account.davFilesUrl + "/a/c") + let result = await remoteInterface.delete( + remotePath: Self.account.davFilesUrl + "/a/c", account: Self.account + ) XCTAssertEqual(result.error, .success) XCTAssertEqual(itemA.children, []) } func testFetchUserProfile() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) - let (account, profile, _, error) = await remoteInterface.fetchUserProfile() + let remoteInterface = MockRemoteInterface(rootItem: rootItem) + let (account, profile, _, error) = await remoteInterface.fetchUserProfile( + account: Self.account + ) XCTAssertEqual(error, .success) XCTAssertEqual(account, Self.account.ncKitAccount) XCTAssertNotNil(profile) } func testTryAuthenticationAttempt() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) - let state = await remoteInterface.tryAuthenticationAttempt() + let remoteInterface = MockRemoteInterface(rootItem: rootItem) + let state = await remoteInterface.tryAuthenticationAttempt(account: Self.account) XCTAssertEqual(state, .success) - let newMri = - MockRemoteInterface(account: Account(user: "", id: "", serverUrl: "", password: "")) - let failState = await newMri.tryAuthenticationAttempt() + let failState = await remoteInterface.tryAuthenticationAttempt( + account: Account(user: "", id: "", serverUrl: "", password: "") + ) XCTAssertEqual(failState, .authenticationError) } } diff --git a/Tests/NextcloudFileProviderKitTests/EnumeratorTests.swift b/Tests/NextcloudFileProviderKitTests/EnumeratorTests.swift index 89e5fa2..7cd6f10 100644 --- a/Tests/NextcloudFileProviderKitTests/EnumeratorTests.swift +++ b/Tests/NextcloudFileProviderKitTests/EnumeratorTests.swift @@ -94,10 +94,11 @@ final class EnumeratorTests: XCTestCase { func testRootEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) // Avoid build-time warning about unused variable, ensure compiler won't free - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let enumerator = Enumerator( enumeratedItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -129,6 +130,7 @@ final class EnumeratorTests: XCTestCase { let storedFolderItem = try XCTUnwrap( Item.storedItem( identifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -148,10 +150,11 @@ final class EnumeratorTests: XCTestCase { func testWorkingSetEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let enumerator = Enumerator( enumeratedItemIdentifier: .workingSet, + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -177,10 +180,11 @@ final class EnumeratorTests: XCTestCase { func testWorkingSetFastChangeEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let enumerator = Enumerator( enumeratedItemIdentifier: .workingSet, + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -220,10 +224,11 @@ final class EnumeratorTests: XCTestCase { func testWorkingSetSlowChangeEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let enumerator = Enumerator( enumeratedItemIdentifier: .workingSet, + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager, fastEnumeration: false @@ -250,7 +255,7 @@ final class EnumeratorTests: XCTestCase { func testFolderEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let oldEtag = "OLD" let folderMetadata = ItemMetadata() @@ -270,6 +275,7 @@ final class EnumeratorTests: XCTestCase { let enumerator = Enumerator( enumeratedItemIdentifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -284,6 +290,7 @@ final class EnumeratorTests: XCTestCase { let storedFolderItem = try XCTUnwrap( Item.storedItem( identifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -309,7 +316,7 @@ final class EnumeratorTests: XCTestCase { func testEnumerateFile() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let folderMetadata = ItemMetadata() folderMetadata.ocId = remoteFolder.identifier @@ -343,6 +350,7 @@ final class EnumeratorTests: XCTestCase { let enumerator = Enumerator( enumeratedItemIdentifier: .init(remoteItemA.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -364,7 +372,7 @@ final class EnumeratorTests: XCTestCase { func testFolderAndContentsChangeEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) remoteFolder.children.removeAll(where: { $0.identifier == remoteItemB.identifier }) remoteFolder.children.append(remoteItemC) @@ -417,6 +425,7 @@ final class EnumeratorTests: XCTestCase { let enumerator = Enumerator( enumeratedItemIdentifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -463,6 +472,7 @@ final class EnumeratorTests: XCTestCase { let storedFolderItem = try XCTUnwrap( Item.storedItem( identifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -497,7 +507,7 @@ final class EnumeratorTests: XCTestCase { func testFileMoveChangeEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) remoteFolder.children.removeAll(where: { $0.identifier == remoteItemA.identifier }) rootItem.children.append(remoteItemA) @@ -553,6 +563,7 @@ final class EnumeratorTests: XCTestCase { let enumerator = Enumerator( enumeratedItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -577,6 +588,7 @@ final class EnumeratorTests: XCTestCase { let storedItemA = try XCTUnwrap( Item.storedItem( identifier: .init(remoteItemA.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -591,7 +603,9 @@ final class EnumeratorTests: XCTestCase { Int(remoteItemA.modificationDate.timeIntervalSince1970) ) - let storedRootItem = Item.rootContainer(remoteInterface: remoteInterface) + let storedRootItem = Item.rootContainer( + account: Self.account, remoteInterface: remoteInterface + ) print(storedRootItem.metadata.serverUrl) storedRootItem.dbManager = Self.dbManager XCTAssertEqual(storedRootItem.childItemCount?.intValue, 3) // All items @@ -599,6 +613,7 @@ final class EnumeratorTests: XCTestCase { let storedFolder = try XCTUnwrap( Item.storedItem( identifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -610,7 +625,7 @@ final class EnumeratorTests: XCTestCase { func testFileLockStateEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) remoteFolder.children.append(remoteItemC) remoteItemC.parent = remoteFolder @@ -645,6 +660,7 @@ final class EnumeratorTests: XCTestCase { let enumerator = Enumerator( enumeratedItemIdentifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -677,6 +693,7 @@ final class EnumeratorTests: XCTestCase { let storedItemA = try XCTUnwrap( Item.storedItem( identifier: .init(remoteItemA.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -684,6 +701,7 @@ final class EnumeratorTests: XCTestCase { let storedItemB = try XCTUnwrap( Item.storedItem( identifier: .init(remoteItemB.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -691,6 +709,7 @@ final class EnumeratorTests: XCTestCase { let storedItemC = try XCTUnwrap( Item.storedItem( identifier: .init(remoteItemC.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -710,7 +729,7 @@ final class EnumeratorTests: XCTestCase { func testEnsureNoEmptyItemNameEnumeration() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) // Avoid build-time warning about unused variable, ensure compiler won't free - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) remoteItemA.name = "" remoteItemA.parent = remoteInterface.rootItem @@ -718,6 +737,7 @@ final class EnumeratorTests: XCTestCase { let enumerator = Enumerator( enumeratedItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -736,6 +756,7 @@ final class EnumeratorTests: XCTestCase { let storedItemA = try XCTUnwrap( Item.storedItem( identifier: .init(remoteItemA.identifier), + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager ) @@ -749,11 +770,12 @@ final class EnumeratorTests: XCTestCase { func testListenerInvocations() async throws { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let listener = MockEnumerationListener() let enumerator = Enumerator( enumeratedItemIdentifier: .workingSet, + account: Self.account, remoteInterface: remoteInterface, dbManager: Self.dbManager, listener: listener diff --git a/Tests/NextcloudFileProviderKitTests/ItemCreateTests.swift b/Tests/NextcloudFileProviderKitTests/ItemCreateTests.swift index 9395080..8b3b777 100644 --- a/Tests/NextcloudFileProviderKitTests/ItemCreateTests.swift +++ b/Tests/NextcloudFileProviderKitTests/ItemCreateTests.swift @@ -40,7 +40,7 @@ final class ItemCreateTests: XCTestCase { } func testCreateFolder() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let folderItemMetadata = ItemMetadata() folderItemMetadata.name = "folder" folderItemMetadata.fileName = "folder" @@ -52,11 +52,13 @@ final class ItemCreateTests: XCTestCase { let folderItemTemplate = Item( metadata: folderItemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) let (createdItemMaybe, error) = await Item.create( basedOn: folderItemTemplate, contents: nil, + account: Self.account, remoteInterface: remoteInterface, progress: Progress(), dbManager: Self.dbManager @@ -86,7 +88,7 @@ final class ItemCreateTests: XCTestCase { } func testCreateFile() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let fileItemMetadata = ItemMetadata() fileItemMetadata.fileName = "file" fileItemMetadata.fileNameView = "file" @@ -100,11 +102,13 @@ final class ItemCreateTests: XCTestCase { let fileItemTemplate = Item( metadata: fileItemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) let (createdItemMaybe, error) = await Item.create( basedOn: fileItemTemplate, contents: tempUrl, + account: Self.account, remoteInterface: remoteInterface, progress: Progress(), dbManager: Self.dbManager @@ -133,7 +137,7 @@ final class ItemCreateTests: XCTestCase { } func testCreateFileIntoFolder() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let folderItemMetadata = ItemMetadata() folderItemMetadata.name = "folder" @@ -146,12 +150,14 @@ final class ItemCreateTests: XCTestCase { let folderItemTemplate = Item( metadata: folderItemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) let (createdFolderItemMaybe, folderError) = await Item.create( basedOn: folderItemTemplate, contents: nil, + account: Self.account, remoteInterface: remoteInterface, progress: Progress(), dbManager: Self.dbManager @@ -172,6 +178,7 @@ final class ItemCreateTests: XCTestCase { let fileItemTemplate = Item( metadata: fileItemMetadata, parentItemIdentifier: createdFolderItem.itemIdentifier, + account: Self.account, remoteInterface: remoteInterface ) @@ -181,6 +188,7 @@ final class ItemCreateTests: XCTestCase { let (createdFileItemMaybe, fileError) = await Item.create( basedOn: fileItemTemplate, contents: tempUrl, + account: Self.account, remoteInterface: remoteInterface, progress: Progress(), dbManager: Self.dbManager @@ -218,7 +226,7 @@ final class ItemCreateTests: XCTestCase { let keynoteBundleFilename = "test.key" - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let bundleItemMetadata = ItemMetadata() bundleItemMetadata.name = keynoteBundleFilename bundleItemMetadata.fileName = keynoteBundleFilename @@ -289,6 +297,7 @@ final class ItemCreateTests: XCTestCase { let bundleItemTemplate = Item( metadata: bundleItemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) @@ -296,6 +305,7 @@ final class ItemCreateTests: XCTestCase { let (createdBundleItemMaybe, bundleError) = await Item.create( basedOn: bundleItemTemplate, contents: tempUrl, + account: Self.account, remoteInterface: remoteInterface, progress: Progress(), dbManager: Self.dbManager diff --git a/Tests/NextcloudFileProviderKitTests/ItemDeleteTests.swift b/Tests/NextcloudFileProviderKitTests/ItemDeleteTests.swift index 3eac433..7c0b7fa 100644 --- a/Tests/NextcloudFileProviderKitTests/ItemDeleteTests.swift +++ b/Tests/NextcloudFileProviderKitTests/ItemDeleteTests.swift @@ -38,7 +38,7 @@ final class ItemDeleteTests: XCTestCase { } func testDeleteFile() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let itemIdentifier = "file" let remoteItem = MockRemoteItem( identifier: itemIdentifier, @@ -63,6 +63,7 @@ final class ItemDeleteTests: XCTestCase { let item = Item( metadata: itemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) @@ -74,7 +75,7 @@ final class ItemDeleteTests: XCTestCase { } func testDeleteFolderAndContents() async { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let remoteFolder = MockRemoteItem( identifier: "folder", name: "folder", @@ -121,6 +122,7 @@ final class ItemDeleteTests: XCTestCase { let folder = Item( metadata: folderMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) diff --git a/Tests/NextcloudFileProviderKitTests/ItemFetchTests.swift b/Tests/NextcloudFileProviderKitTests/ItemFetchTests.swift index 0429f68..ed11dae 100644 --- a/Tests/NextcloudFileProviderKitTests/ItemFetchTests.swift +++ b/Tests/NextcloudFileProviderKitTests/ItemFetchTests.swift @@ -39,7 +39,7 @@ final class ItemFetchTests: XCTestCase { } func testFetchFileContents() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let remoteItem = MockRemoteItem( identifier: "item", versionIdentifier: "0", @@ -72,6 +72,7 @@ final class ItemFetchTests: XCTestCase { let item = Item( metadata: itemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) @@ -95,7 +96,7 @@ final class ItemFetchTests: XCTestCase { } func testFetchDirectoryContents() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let remoteDirectory = MockRemoteItem( identifier: "directory", versionIdentifier: "0", @@ -250,6 +251,7 @@ final class ItemFetchTests: XCTestCase { let item = Item( metadata: directoryMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) item.dbManager = Self.dbManager diff --git a/Tests/NextcloudFileProviderKitTests/ItemModifyTests.swift b/Tests/NextcloudFileProviderKitTests/ItemModifyTests.swift index 7f3353a..61850cf 100644 --- a/Tests/NextcloudFileProviderKitTests/ItemModifyTests.swift +++ b/Tests/NextcloudFileProviderKitTests/ItemModifyTests.swift @@ -40,7 +40,7 @@ final class ItemModifyTests: XCTestCase { } func testModifyFileContents() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let remoteItem = MockRemoteItem( identifier: "item", versionIdentifier: "0", @@ -108,12 +108,14 @@ final class ItemModifyTests: XCTestCase { let item = Item( metadata: itemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) let targetItem = Item( metadata: targetItemMetadata, parentItemIdentifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface ) targetItem.dbManager = Self.dbManager @@ -148,7 +150,7 @@ final class ItemModifyTests: XCTestCase { let db = Self.dbManager.ncDatabase() // Strong ref for in memory test db debugPrint(db) - let remoteInterface = MockRemoteInterface(account: Self.account, rootItem: rootItem) + let remoteInterface = MockRemoteInterface(rootItem: rootItem) let keynoteBundleFilename = "test.key" let keynoteIndexZipFilename = "Index.zip" @@ -509,6 +511,7 @@ final class ItemModifyTests: XCTestCase { let bundleItem = Item( metadata: bundleItemMetadata, parentItemIdentifier: .rootContainer, + account: Self.account, remoteInterface: remoteInterface ) bundleItem.dbManager = Self.dbManager @@ -591,6 +594,7 @@ final class ItemModifyTests: XCTestCase { let targetItem = Item( metadata: targetBundleMetadata, parentItemIdentifier: .init(remoteFolder.identifier), + account: Self.account, remoteInterface: remoteInterface ) targetItem.dbManager = Self.dbManager diff --git a/Tests/NextcloudFileProviderKitTests/ItemPropertyTests.swift b/Tests/NextcloudFileProviderKitTests/ItemPropertyTests.swift index 476d805..18f999f 100644 --- a/Tests/NextcloudFileProviderKitTests/ItemPropertyTests.swift +++ b/Tests/NextcloudFileProviderKitTests/ItemPropertyTests.swift @@ -36,7 +36,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertEqual(item.contentType, UTType.text) } @@ -59,7 +60,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertEqual(item.contentType, UTType.pdf) } @@ -82,7 +84,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertEqual(item.contentType, UTType.folder) } @@ -106,7 +109,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertEqual(item.contentType, UTType.package) } @@ -130,7 +134,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertEqual(item.contentType, UTType.bundle) } @@ -154,7 +159,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertEqual(item.contentType, UTType.folder) } @@ -178,7 +184,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertTrue(item.contentType.conforms(to: .bundle)) } @@ -206,7 +213,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertNotNil(item.userInfo?["locked"]) @@ -236,7 +244,8 @@ final class ItemPropertyTests: XCTestCase { let item = Item( metadata: metadata, parentItemIdentifier: .rootContainer, - remoteInterface: MockRemoteInterface(account: Self.account) + account: Self.account, + remoteInterface: MockRemoteInterface() ) XCTAssertNil(item.userInfo?["locked"]) diff --git a/Tests/NextcloudFileProviderKitTests/RemoteChangeObserverTests.swift b/Tests/NextcloudFileProviderKitTests/RemoteChangeObserverTests.swift index f497207..31bf509 100644 --- a/Tests/NextcloudFileProviderKitTests/RemoteChangeObserverTests.swift +++ b/Tests/NextcloudFileProviderKitTests/RemoteChangeObserverTests.swift @@ -44,7 +44,7 @@ final class RemoteChangeObserverTests: XCTestCase { } func testAuthentication() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities var authenticated = false @@ -56,6 +56,7 @@ final class RemoteChangeObserverTests: XCTestCase { } remoteChangeObserver = RemoteChangeObserver( + account: Self.account, remoteInterface: remoteInterface, changeNotificationInterface: MockChangeNotificationInterface(), domain: nil @@ -83,9 +84,10 @@ final class RemoteChangeObserverTests: XCTestCase { let incorrectAccount = Account(user: username, id: userId, serverUrl: serverUrl, password: "wrong!") - let remoteInterface = MockRemoteInterface(account: incorrectAccount) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities remoteChangeObserver = RemoteChangeObserver( + account: incorrectAccount, remoteInterface: remoteInterface, changeNotificationInterface: MockChangeNotificationInterface(), domain: nil @@ -99,7 +101,7 @@ final class RemoteChangeObserverTests: XCTestCase { } } XCTAssertTrue(remoteChangeObserver.webSocketAuthenticationFailCount > 0) - remoteInterface.account = Self.account + remoteChangeObserver.account = Self.account for _ in 0...Self.timeout { try await Task.sleep(nanoseconds: 1_000_000) @@ -114,9 +116,10 @@ final class RemoteChangeObserverTests: XCTestCase { func testStopRetryingConnection() async throws { let incorrectAccount = Account(user: username, id: userId, serverUrl: serverUrl, password: "wrong!") - let remoteInterface = MockRemoteInterface(account: incorrectAccount) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities let remoteChangeObserver = RemoteChangeObserver( + account: incorrectAccount, remoteInterface: remoteInterface, changeNotificationInterface: MockChangeNotificationInterface(), domain: nil @@ -138,7 +141,7 @@ final class RemoteChangeObserverTests: XCTestCase { } func testChangeRecognised() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities var authenticated = false @@ -153,6 +156,7 @@ final class RemoteChangeObserverTests: XCTestCase { let notificationInterface = MockChangeNotificationInterface() notificationInterface.changeHandler = { notified = true } remoteChangeObserver = RemoteChangeObserver( + account: Self.account, remoteInterface: remoteInterface, changeNotificationInterface: notificationInterface, domain: nil @@ -177,7 +181,7 @@ final class RemoteChangeObserverTests: XCTestCase { } func testIgnoreNonFileNotifications() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities var authenticated = false @@ -192,6 +196,7 @@ final class RemoteChangeObserverTests: XCTestCase { let notificationInterface = MockChangeNotificationInterface() notificationInterface.changeHandler = { notified = true } remoteChangeObserver = RemoteChangeObserver( + account: Self.account, remoteInterface: remoteInterface, changeNotificationInterface: notificationInterface, domain: nil @@ -219,11 +224,12 @@ final class RemoteChangeObserverTests: XCTestCase { func testPolling() async throws { var notified = false - let remoteInterface = MockRemoteInterface(account: Self.account) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = "" let notificationInterface = MockChangeNotificationInterface() notificationInterface.changeHandler = { notified = true } remoteChangeObserver = RemoteChangeObserver( + account: Self.account, remoteInterface: remoteInterface, changeNotificationInterface: notificationInterface, domain: nil @@ -257,7 +263,7 @@ final class RemoteChangeObserverTests: XCTestCase { } func testRetryOnRemoteClose() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities var authenticated = false @@ -269,6 +275,7 @@ final class RemoteChangeObserverTests: XCTestCase { } remoteChangeObserver = RemoteChangeObserver( + account: Self.account, remoteInterface: remoteInterface, changeNotificationInterface: MockChangeNotificationInterface(), domain: nil @@ -296,7 +303,7 @@ final class RemoteChangeObserverTests: XCTestCase { } func testPinging() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities var authenticated = false @@ -308,6 +315,7 @@ final class RemoteChangeObserverTests: XCTestCase { } remoteChangeObserver = RemoteChangeObserver( + account: Self.account, remoteInterface: remoteInterface, changeNotificationInterface: MockChangeNotificationInterface(), domain: nil @@ -338,7 +346,7 @@ final class RemoteChangeObserverTests: XCTestCase { } func testRetryOnConnectionLoss() async throws { - let remoteInterface = MockRemoteInterface(account: Self.account) + let remoteInterface = MockRemoteInterface() remoteInterface.capabilities = mockCapabilities var authenticated = false @@ -353,6 +361,7 @@ final class RemoteChangeObserverTests: XCTestCase { let notificationInterface = MockChangeNotificationInterface() notificationInterface.changeHandler = { notified = true } remoteChangeObserver = RemoteChangeObserver( + account: Self.account, remoteInterface: remoteInterface, changeNotificationInterface: notificationInterface, domain: nil