Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/front/macOS/kDrive/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationDidFinishLaunching(_ aNotification: Notification) {
DriveTargetAssembly.setupDI()
let testing = AppDelegate.isRunningTests
DriveTargetAssembly.setupDI(testing: testing)

guard !AppDelegate.isRunningTests else {
guard !testing else {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public protocol CoherentCacheProtocol: Sendable {
func getAccount(_ accountId: Int32, userDbId: Int32) async -> Account?
func addAccount(_ account: Account, userDbId: Int32) async
func removeAccount(_ accountId: Int32, userDbId: Int32) async
func updateAccount(_ account: Account) async

// MARK: - Drive

Expand Down Expand Up @@ -137,6 +138,10 @@ public actor CoherentCache: CoherentCacheProtocol, CoherentCacheObservation {
notifyAccountUpdate(userDbId: userDbId, indexedAccounts: user.accounts)
}

public func updateAccount(_ account: Account) {
// TODO: Implement
}

// MARK: - DRIVE

public func getDrive(_ driveId: Int32, accountId: Int32, userDbId: Int32) -> Drive? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,19 @@ import Foundation
public typealias IndexedAccounts = [Int32: Account]
public typealias UserAccounts = (userDbId: Int32, indexedAccounts: [Int32: Account])

// TODO: Update to track userDbId in Account to match server type
public struct Account: Identifiable, Hashable, Sendable {
public init(id: Int32, name: String, drives: [Int32: Drive]) {
self.id = id
public var id: Int32 {
dbId
}

public init(dbId: Int32, name: String, drives: [Int32: Drive]) {
self.dbId = dbId
self.name = name
self.drives = drives
}

public let id: Int32
public let dbId: Int32
public var name: String
public var drives: IndexedDrives
}
44 changes: 44 additions & 0 deletions src/front/macOS/kDriveCore/ServerBridge/Jobs/AccountJob.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Infomaniak kDrive - Desktop
Copyright (C) 2023-2025 Infomaniak Network SA

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

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

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

import Foundation
import InfomaniakConcurrency
import InfomaniakDI

public struct AccountJobs: Sendable {
@LazyInjectService private var coherentCache: CoherentCacheProtocol
@LazyInjectService private var queryFetcher: XPCQueryFetcherProtocol

public init() {}

public func accountInfoList() async throws -> [AccountInfoResponse] {
IKLogger.data.log("Query for accountInfoList")
let query = EmptyQuery()
let request = await RequestMessage<EmptyQuery>(num: RequestNum.ACCOUNT_INFOLIST, body: query)

let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<AccountListResponse>.self)

try decodedMessage.validate()

let accountList = decodedMessage.body.accountInfoList

await accountList.asyncForEach { await coherentCache.updateAccount($0.asAccount) }

return accountList
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Infomaniak kDrive - Desktop
Copyright (C) 2023-2025 Infomaniak Network SA

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

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

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

struct AccountListResponse: Codable, Sendable {
let accountInfoList: [AccountInfoResponse]
}

public struct AccountInfoResponse: Codable, Sendable {
let userDbId: Int32
let dbId: Int32
}

extension AccountInfoResponse {
var asAccount: Account {
Account(dbId: dbId, name: "", drives: [:])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ struct DriveListQuery: Codable, Sendable {
let userDbId: Int32
}

struct DriveUpdateQuery: Codable, Sendable {
let driveInfo: DriveResponse
}

struct DriveListResponse: Codable, Sendable {
let driveAvailableInfoList: [DriveResponse]
}
Expand Down
56 changes: 56 additions & 0 deletions src/front/macOS/kDriveCore/ServerBridge/Jobs/DriveJobs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
Infomaniak kDrive - Desktop
Copyright (C) 2023-2025 Infomaniak Network SA

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

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

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

import Foundation
import InfomaniakConcurrency
import InfomaniakDI

public struct DriveJobs: Sendable {
@LazyInjectService private var coherentCache: CoherentCacheProtocol
@LazyInjectService private var queryFetcher: XPCQueryFetcherProtocol

public init() {}

public func availableDrives(userDbId: Int32) async throws -> [DriveResponse] {
IKLogger.data.log("Query for availableDrives list")
let query = DriveListQuery(userDbId: userDbId)
let request = await RequestMessage<DriveListQuery>(num: RequestNum.USER_AVAILABLEDRIVES, body: query)

let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<DriveListResponse>.self)

try decodedMessage.validate()

let driveList = decodedMessage.body.driveAvailableInfoList

await driveList.asyncForEach { await coherentCache.updateDrive(drive: $0.asDrive) }

return driveList
}

public func driveUpdate(driveInfo: DriveResponse) async throws {
IKLogger.data.log("Query to update drive: \(driveInfo.driveId)")
let query = DriveUpdateQuery(driveInfo: driveInfo)
let request = await RequestMessage<DriveUpdateQuery>(num: RequestNum.DRIVE_UPDATE, body: query)

let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<EmptyResponse>.self)

try decodedMessage.validate()

await coherentCache.updateDrive(drive: driveInfo.asDrive)
}
}
36 changes: 3 additions & 33 deletions src/front/macOS/kDriveCore/ServerBridge/Jobs/LoginJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,48 +22,18 @@ import InfomaniakDI
public struct LoginJob: Sendable {
@LazyInjectService private var queryFetcher: XPCQueryFetcherProtocol

public enum LoginJobError: Error {
case userNotFound
case noReplyMessage
case serverError(code: KDC.ExitCode, cause: KDC.ExitCause)
}

public init() {}

/// Login job
/// - Parameters:
/// - code: auth code
/// - verifier: auth verifier
@discardableResult
public func login(code: String, verifier: String) async throws -> Int32 {
try await loginUserQuery(code: code, verifier: verifier)
}

/// Login _query_ only
/// - Parameters:
/// - code: auth code
/// - verifier: auth verifier
/// - Returns: userDbId
@discardableResult
private func loginUserQuery(code: String, verifier: String) async throws -> Int32 {
IKLogger.data.log("Query for login token")
let userQuery = LoginQuery(code: code, codeVerifier: verifier)
let request = await RequestMessage<LoginQuery>(num: RequestNum.LOGIN_REQUESTTOKEN, body: userQuery)

do {
let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<LoginResponse>.self)

guard let decodedMessage else {
throw LoginJobError.noReplyMessage
}
let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<LoginResponse>.self)

guard decodedMessage.code == .Ok, decodedMessage.cause == .Unknown else {
throw LoginJobError.serverError(code: decodedMessage.code, cause: decodedMessage.cause)
}
try decodedMessage.validate()

return decodedMessage.body.userDbId
} catch XPCQueryFetcher.QueryError.noReplyData {
throw LoginJobError.userNotFound
}
return decodedMessage.body.userDbId
}
}
45 changes: 13 additions & 32 deletions src/front/macOS/kDriveCore/ServerBridge/Jobs/UserJobs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,62 +24,43 @@ public struct UserJobs: Sendable {
@LazyInjectService private var coherentCache: CoherentCacheProtocol
@LazyInjectService private var queryFetcher: XPCQueryFetcherProtocol

public enum UserJobError: Error {
case responseListNotFound
case noReplyData
}

public init() {}

public func userDbIds() async throws -> [Int32] {
IKLogger.data.log("Query for userDbIds list")
let request = await RequestMessage<EmptyQuery>(num: RequestNum.USER_DBIDLIST, body: EmptyQuery())

do {
let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<UserDbIdsListResponse>.self)
let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<UserDbIdsListResponse>.self)

guard let userDbIds = decodedMessage?.body.userDbIdList else {
throw UserJobError.responseListNotFound
}
try decodedMessage.validate()

return userDbIds
} catch XPCQueryFetcher.QueryError.noReplyData {
throw UserJobError.noReplyData
}
return decodedMessage.body.userDbIdList
}

public func userInfoList() async throws -> [UserInfoResponse] {
IKLogger.data.log("Query for userInfo list")
let request = await RequestMessage<EmptyQuery>(num: RequestNum.USER_INFOLIST, body: EmptyQuery())

do {
let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<UserInfoListResponse>.self)
let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<UserInfoListResponse>.self)

guard let userList = decodedMessage?.body.userInfoList else {
throw UserJobError.responseListNotFound
}
try decodedMessage.validate()

await userList.asyncForEach { await coherentCache.updateUser($0.userCache) }
let userList = decodedMessage.body.userInfoList

return userList
} catch XPCQueryFetcher.QueryError.noReplyData {
throw UserJobError.noReplyData
}
await userList.asyncForEach { await coherentCache.updateUser($0.userCache) }

return userList
}

public func userDelete(dbId: Int32) async throws {
IKLogger.data.log("Query for userDelete")
let query = UserDeleteQuery(userDbId: dbId)
let request = await RequestMessage<UserDeleteQuery>(num: RequestNum.USER_DELETE, body: query)

do {
guard try await queryFetcher.query(request, responseType: CallbackMessage<EmptyResponse>.self) != nil else {
throw UserJobError.noReplyData
}
let decodedMessage = try await queryFetcher.query(request, responseType: CallbackMessage<EmptyResponse>.self)

try decodedMessage.validate()

await coherentCache.removeUser(dbId: dbId)
} catch XPCQueryFetcher.QueryError.noReplyData {
throw UserJobError.noReplyData
}
await coherentCache.removeUser(dbId: dbId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public struct CallbackMessage<Body: Codable>: Codable, CustomStringConvertible {
public let id: Int32
public let body: Body

enum CallbackError: Error {
case serverError(code: KDC.ExitCode, cause: KDC.ExitCause)
}

public init(code: KDC.ExitCode, cause: KDC.ExitCause, id: Int32, body: Body) {
self.code = code
self.cause = cause
Expand All @@ -42,3 +46,11 @@ public struct CallbackMessage<Body: Codable>: Codable, CustomStringConvertible {
"CallbackMessage(cause: \(cause.rawValue), code: \(code.rawValue), id: \(id), body: \(body))"
}
}

public extension CallbackMessage {
func validate() throws {
if code != .Ok || cause != .Unknown {
throw CallbackError.serverError(code: code, cause: cause)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ extension XPCGuiProtocol {
func sendQueryAsync(_ requestData: Data) async -> Data? {
await withCheckedContinuation { continuation in
self.processQuery(requestData) { data in
//IKLogger.xpc.log("[KD] recv raw callback string: \(String(data: data, encoding: .utf8))")
IKLogger.xpc.log("[KD] recv raw callback len: \(data.count)")
continuation.resume(returning: data)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Foundation
import InfomaniakDI

public protocol XPCQueryFetcherProtocol {
func query<Response: Decodable>(_ request: Encodable, responseType: Response.Type) async throws -> Response?
func query<Response: Decodable>(_ request: Encodable, responseType: Response.Type) async throws -> Response
}

struct XPCQueryFetcher: XPCQueryFetcherProtocol {
Expand All @@ -31,18 +31,24 @@ struct XPCQueryFetcher: XPCQueryFetcherProtocol {

enum QueryError: Error {
case noReplyData
case unableToDecodeReply(parsingError: Error)
}

public func query<Response: Decodable>(_ request: Encodable, responseType: Response.Type) async throws -> Response? {
public func query<Response: Decodable>(_ request: Encodable, responseType: Response.Type) async throws -> Response {
let requestData = try encoder.encode(request)

let guiConnection = try await xpcConnectionProvider.guiConnection
guard let replyData = await guiConnection.sendQueryAsync(requestData) else {
throw QueryError.noReplyData
}

let decodedMessage = try? decoder.decode(Response.self, from: replyData)
IKLogger.data.log("recv callback: \(String(describing: decodedMessage))")
return decodedMessage
do {
let decodedMessage = try decoder.decode(Response.self, from: replyData)

IKLogger.data.log("recv callback: \(String(describing: decodedMessage))")
return decodedMessage
} catch {
throw QueryError.unableToDecodeReply(parsingError: error)
}
}
}
Loading
Loading