diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift new file mode 100644 index 00000000..533413f1 --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftyJSON + +public class NKTextProcessingTask { + public var id: Int? + public var type: String? + public var status: Int? + public var userId: String? + public var appId: String? + public var input: String? + public var output: String? + public var identifier: String? + public var completionExpectedAt: Double? + + public init(id: Int? = nil, type: String? = nil, status: Int? = nil, userId: String? = nil, appId: String? = nil, input: String? = nil, output: String? = nil, identifier: String? = nil, completionExpectedAt: Double? = nil) { + self.id = id + self.type = type + self.status = status + self.userId = userId + self.appId = appId + self.input = input + self.output = output + self.identifier = identifier + self.completionExpectedAt = completionExpectedAt + } + + public init?(json: JSON) { + self.id = json["id"].int + self.type = json["type"].string + self.status = json["status"].int + self.userId = json["userId"].string + self.appId = json["appId"].string + self.input = json["input"].string + self.output = json["output"].string + self.identifier = json["identifier"].string + self.completionExpectedAt = json["completionExpectedAt"].double + } + + static func deserialize(multipleObjects data: JSON) -> [NKTextProcessingTask]? { + guard let allResults = data.array else { return nil } + return allResults.compactMap(NKTextProcessingTask.init) + } + + static func deserialize(singleObject data: JSON) -> NKTextProcessingTask? { + NKTextProcessingTask(json: data) + } + + public static func toV2(tasks: [NKTextProcessingTask]) -> TaskList { + let tasks = tasks.map { task in + AssistantTask( + id: Int64(task.id ?? 0), + type: task.type, + status: String(task.status ?? 0), + userId: task.userId, + appId: task.appId, + input: TaskInput(input: task.input), + output: TaskOutput(output: task.output), + completionExpectedAt: Int(task.completionExpectedAt ?? 0), + progress: nil, + lastUpdated: nil, + scheduledAt: nil, + endedAt: nil + ) + } + + return TaskList(tasks: tasks) + } +} diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift new file mode 100644 index 00000000..5841b4bf --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftyJSON + +public class NKTextProcessingTaskType { + public var id: String? + public var name: String? + public var description: String? + + public init(id: String? = nil, name: String? = nil, description: String? = nil) { + self.id = id + self.name = name + self.description = description + } + + public init?(json: JSON) { + self.id = json["id"].string + self.name = json["name"].string + self.description = json["description"].string + } + + static func deserialize(multipleObjects data: JSON) -> [NKTextProcessingTaskType]? { + guard let allResults = data.array else { return nil } + return allResults.compactMap(NKTextProcessingTaskType.init) + } + + public static func toV2(type: [NKTextProcessingTaskType]) -> TaskTypes { + let types = type.map { type in + TaskTypeData(id: type.id, name: type.name, description: type.description, inputShape: nil, outputShape: nil) + } + + return TaskTypes(types: types) + } +} + + diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift new file mode 100644 index 00000000..b17978a8 --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftyJSON + +public struct TaskList: Codable { + public var tasks: [AssistantTask] + + static func deserialize(from data: JSON) -> TaskList? { + let tasks = data.arrayValue.map { taskJson in + AssistantTask( + id: taskJson["id"].int64Value, + type: taskJson["type"].string, + status: taskJson["status"].string, + userId: taskJson["userId"].string, + appId: taskJson["appId"].string, + input: TaskInput(input: taskJson["input"]["input"].string), + output: TaskOutput(output: taskJson["output"]["output"].string), + completionExpectedAt: taskJson["completionExpectedAt"].int, + progress: taskJson["progress"].int, + lastUpdated: taskJson["lastUpdated"].int, + scheduledAt: taskJson["scheduledAt"].int, + endedAt: taskJson["endedAt"].int + ) + } + + return TaskList(tasks: tasks) + } +} + +public struct AssistantTask: Codable { + public let id: Int64 + public let type: String? + public let status: String? + public let userId: String? + public let appId: String? + public let input: TaskInput? + public let output: TaskOutput? + public let completionExpectedAt: Int? + public var progress: Int? + public let lastUpdated: Int? + public let scheduledAt: Int? + public let endedAt: Int? + + public init(id: Int64, type: String?, status: String?, userId: String?, appId: String?, input: TaskInput?, output: TaskOutput?, completionExpectedAt: Int?, progress: Int? = nil, lastUpdated: Int?, scheduledAt: Int?, endedAt: Int?) { + self.id = id + self.type = type + self.status = status + self.userId = userId + self.appId = appId + self.input = input + self.output = output + self.completionExpectedAt = completionExpectedAt + self.progress = progress + self.lastUpdated = lastUpdated + self.scheduledAt = scheduledAt + self.endedAt = endedAt + } + + static func deserialize(from data: JSON) -> AssistantTask? { + let task = AssistantTask( + id: data["id"].int64Value, + type: data["type"].string, + status: data["status"].string, + userId: data["userId"].string, + appId: data["appId"].string, + input: TaskInput(input: data["input"]["input"].string), + output: TaskOutput(output: data["output"]["output"].string), + completionExpectedAt: data["completionExpectedAt"].int, + progress: data["progress"].int, + lastUpdated: data["lastUpdated"].int, + scheduledAt: data["scheduledAt"].int, + endedAt: data["endedAt"].int + ) + + return task + } +} + +public struct TaskInput: Codable { + public var input: String? + + public init(input: String? = nil) { + self.input = input + } +} + +public struct TaskOutput: Codable { + public var output: String? + + public init(output: String? = nil) { + self.output = output + } +} + + diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift new file mode 100644 index 00000000..70b502a7 --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftyJSON + +public struct TaskTypes: Codable { + public let types: [TaskTypeData] + + static func deserialize(from data: JSON) -> TaskTypes? { + var taskTypes: [TaskTypeData] = [] + + for (key, subJson) in data { + let taskTypeData = TaskTypeData( + id: key, + name: subJson["name"].string, + description: subJson["description"].string, + inputShape: subJson["inputShape"].dictionary != nil ? TaskInputShape( + input: subJson["inputShape"]["input"].dictionary != nil ? Shape( + name: subJson["inputShape"]["input"]["name"].stringValue, + description: subJson["inputShape"]["input"]["description"].stringValue, + type: subJson["inputShape"]["input"]["type"].stringValue + ) : nil + ) : nil, + outputShape: subJson["outputShape"].dictionary != nil ? TaskOutputShape( + output: subJson["outputShape"]["output"].dictionary != nil ? Shape( + name: subJson["outputShape"]["output"]["name"].stringValue, + description: subJson["outputShape"]["output"]["description"].stringValue, + type: subJson["outputShape"]["output"]["type"].stringValue + ) : nil + ) : nil + ) + + taskTypes.append(taskTypeData) + } + + return TaskTypes(types: taskTypes) + } +} + +public struct TaskTypeData: Codable { + public let id: String? + public let name: String? + public let description: String? + public let inputShape: TaskInputShape? + public let outputShape: TaskOutputShape? + + public init(id: String?, name: String?, description: String?, inputShape: TaskInputShape?, outputShape: TaskOutputShape?) { + self.id = id + self.name = name + self.description = description + self.inputShape = inputShape + self.outputShape = outputShape + } +} + +public struct TaskInputShape: Codable { + public let input: Shape? + + public init(input: Shape?) { + self.input = input + } +} + +public struct TaskOutputShape: Codable { + public let output: Shape? + + public init(output: Shape?) { + self.output = output + } +} + +public struct Shape: Codable { + public let name: String + public let description: String + public let type: String + + public init(name: String, description: String, type: String) { + self.name = name + self.description = description + self.type = type + } +} + + + + + + diff --git a/Sources/NextcloudKit/NKSession.swift b/Sources/NextcloudKit/NKSession.swift index 1e66394a..2a9b169d 100644 --- a/Sources/NextcloudKit/NKSession.swift +++ b/Sources/NextcloudKit/NKSession.swift @@ -17,10 +17,10 @@ public struct NKSession: Sendable { public let httpMaximumConnectionsPerHost: Int public let httpMaximumConnectionsPerHostInDownload: Int public let httpMaximumConnectionsPerHostInUpload: Int - public let requestCachePolicy: URLRequest.CachePolicy public let dav: String = "remote.php/dav" public var internalTypeIdentifiers: [NKCommon.UTTypeConformsToServer] = [] public let sessionData: Alamofire.Session + public let sessionDataNoCache: Alamofire.Session public let sessionDownloadBackground: URLSession public let sessionUploadBackground: URLSession public let sessionUploadBackgroundWWan: URLSession @@ -37,8 +37,7 @@ public struct NKSession: Sendable { groupIdentifier: String, httpMaximumConnectionsPerHost: Int, httpMaximumConnectionsPerHostInDownload: Int, - httpMaximumConnectionsPerHostInUpload: Int, - requestCachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy) { + httpMaximumConnectionsPerHostInUpload: Int) { self.urlBase = urlBase self.user = user self.userId = userId @@ -50,29 +49,40 @@ public struct NKSession: Sendable { self.httpMaximumConnectionsPerHost = httpMaximumConnectionsPerHost self.httpMaximumConnectionsPerHostInDownload = httpMaximumConnectionsPerHostInDownload self.httpMaximumConnectionsPerHostInUpload = httpMaximumConnectionsPerHostInUpload - self.requestCachePolicy = requestCachePolicy let backgroundSessionDelegate = NKBackground(nkCommonInstance: nkCommonInstance) /// Strange but works ?!?! let sharedCookieStorage = user + "@" + urlBase - /// Session Alamofire - let configuration = URLSessionConfiguration.af.default - configuration.requestCachePolicy = requestCachePolicy - configuration.httpMaximumConnectionsPerHost = httpMaximumConnectionsPerHost + /// SessionData Alamofire + let configurationSessionData = URLSessionConfiguration.af.default + configurationSessionData.requestCachePolicy = .useProtocolCachePolicy + configurationSessionData.httpMaximumConnectionsPerHost = httpMaximumConnectionsPerHost #if os(iOS) || targetEnvironment(macCatalyst) - configuration.multipathServiceType = .handover + configurationSessionData.multipathServiceType = .handover #endif - configuration.httpCookieStorage = HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier: sharedCookieStorage) - sessionData = Alamofire.Session(configuration: configuration, + configurationSessionData.httpCookieStorage = HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier: sharedCookieStorage) + sessionData = Alamofire.Session(configuration: configurationSessionData, delegate: NextcloudKitSessionDelegate(nkCommonInstance: nkCommonInstance), rootQueue: nkCommonInstance.rootQueue, requestQueue: nkCommonInstance.requestQueue, serializationQueue: nkCommonInstance.serializationQueue, eventMonitors: [NKMonitor(nkCommonInstance: nkCommonInstance)]) + /// SessionDataNoCache Alamofire + let configurationSessionDataNoCache = URLSessionConfiguration.af.default + configurationSessionDataNoCache.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData + configurationSessionDataNoCache.httpMaximumConnectionsPerHost = httpMaximumConnectionsPerHost + + sessionDataNoCache = Alamofire.Session(configuration: configurationSessionDataNoCache, + delegate: NextcloudKitSessionDelegate(nkCommonInstance: nkCommonInstance), + rootQueue: nkCommonInstance.rootQueue, + requestQueue: nkCommonInstance.requestQueue, + serializationQueue: nkCommonInstance.serializationQueue, + eventMonitors: [NKMonitor(nkCommonInstance: nkCommonInstance)]) + /// Session Download Background let configurationDownloadBackground = URLSessionConfiguration.background(withIdentifier: NKCommon().getSessionConfigurationIdentifier(NKCommon().identifierSessionDownloadBackground, account: account)) configurationDownloadBackground.allowsCellularAccess = true @@ -83,7 +93,7 @@ public struct NKSession: Sendable { configurationDownloadBackground.isDiscretionary = false configurationDownloadBackground.httpMaximumConnectionsPerHost = self.httpMaximumConnectionsPerHostInDownload - configurationDownloadBackground.requestCachePolicy = requestCachePolicy + configurationDownloadBackground.requestCachePolicy = .useProtocolCachePolicy #if os(iOS) || targetEnvironment(macCatalyst) configurationDownloadBackground.multipathServiceType = .handover @@ -102,7 +112,7 @@ public struct NKSession: Sendable { configurationUploadBackground.isDiscretionary = false configurationUploadBackground.httpMaximumConnectionsPerHost = self.httpMaximumConnectionsPerHostInUpload - configurationUploadBackground.requestCachePolicy = requestCachePolicy + configurationUploadBackground.requestCachePolicy = .useProtocolCachePolicy #if os(iOS) || targetEnvironment(macCatalyst) configurationUploadBackground.multipathServiceType = .handover @@ -121,7 +131,7 @@ public struct NKSession: Sendable { configurationUploadBackgroundWWan.isDiscretionary = false configurationUploadBackgroundWWan.httpMaximumConnectionsPerHost = self.httpMaximumConnectionsPerHostInUpload - configurationUploadBackgroundWWan.requestCachePolicy = requestCachePolicy + configurationUploadBackgroundWWan.requestCachePolicy = .useProtocolCachePolicy configurationUploadBackgroundWWan.httpCookieStorage = HTTPCookieStorage.sharedCookieStorage(forGroupContainerIdentifier: sharedCookieStorage) sessionUploadBackgroundWWan = URLSession(configuration: configurationUploadBackgroundWWan, delegate: backgroundSessionDelegate, delegateQueue: OperationQueue.main) @@ -135,7 +145,7 @@ public struct NKSession: Sendable { configurationUploadBackgroundExt.isDiscretionary = false configurationUploadBackgroundExt.httpMaximumConnectionsPerHost = self.httpMaximumConnectionsPerHostInUpload - configurationUploadBackgroundExt.requestCachePolicy = requestCachePolicy + configurationUploadBackgroundExt.requestCachePolicy = .useProtocolCachePolicy configurationUploadBackgroundExt.sharedContainerIdentifier = groupIdentifier #if os(iOS) || targetEnvironment(macCatalyst) diff --git a/Sources/NextcloudKit/NextcloudKit+API.swift b/Sources/NextcloudKit/NextcloudKit+API.swift index c07077f3..3c25c0d6 100644 --- a/Sources/NextcloudKit/NextcloudKit+API.swift +++ b/Sources/NextcloudKit/NextcloudKit+API.swift @@ -463,7 +463,7 @@ public extension NextcloudKit { return options.queue.async { completion(account, nil, nil, .urlError) } } - nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + nkSession.sessionDataNoCache.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in task.taskDescription = options.taskDescription taskHandler(task) }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index 992e870d..2d795052 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: Nextcloud GmbH -// SPDX-FileCopyrightText: 2024 Marino Faggiana +// SPDX-FileCopyrightText: 2025 Milen Pivchev // SPDX-License-Identifier: GPL-3.0-or-later import Foundation @@ -34,7 +34,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["types"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let results = NKTextProcessingTaskType.factory(data: data) + let results = NKTextProcessingTaskType.deserialize(multipleObjects: data) options.queue.async { completion(account, results, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } @@ -75,7 +75,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["task"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let result = NKTextProcessingTask.factory(data: data) + let result = NKTextProcessingTask.deserialize(singleObject: data) options.queue.async { completion(account, result, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } @@ -112,7 +112,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["task"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let result = NKTextProcessingTask.factory(data: data) + let result = NKTextProcessingTask.deserialize(singleObject: data) options.queue.async { completion(account, result, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } @@ -149,7 +149,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["task"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let result = NKTextProcessingTask.factory(data: data) + let result = NKTextProcessingTask.deserialize(singleObject: data) options.queue.async { completion(account, result, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } @@ -186,7 +186,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["tasks"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let results = NKTextProcessingTask.factories(data: data) + let results = NKTextProcessingTask.deserialize(multipleObjects: data) options.queue.async { completion(account, results, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } @@ -196,70 +196,4 @@ public extension NextcloudKit { } } -public class NKTextProcessingTaskType { - public var id: String? - public var name: String? - public var description: String? - public init(id: String? = nil, name: String? = nil, description: String? = nil) { - self.id = id - self.name = name - self.description = description - } - - public init?(json: JSON) { - self.id = json["id"].string - self.name = json["name"].string - self.description = json["description"].string - } - - static func factory(data: JSON) -> [NKTextProcessingTaskType]? { - guard let allResults = data.array else { return nil } - return allResults.compactMap(NKTextProcessingTaskType.init) - } -} - -public class NKTextProcessingTask { - public var id: Int? - public var type: String? - public var status: Int? - public var userId: String? - public var appId: String? - public var input: String? - public var output: String? - public var identifier: String? - public var completionExpectedAt: Double? - - public init(id: Int? = nil, type: String? = nil, status: Int? = nil, userId: String? = nil, appId: String? = nil, input: String? = nil, output: String? = nil, identifier: String? = nil, completionExpectedAt: Double? = nil) { - self.id = id - self.type = type - self.status = status - self.userId = userId - self.appId = appId - self.input = input - self.output = output - self.identifier = identifier - self.completionExpectedAt = completionExpectedAt - } - - public init?(json: JSON) { - self.id = json["id"].int - self.type = json["type"].string - self.status = json["status"].int - self.userId = json["userId"].string - self.appId = json["appId"].string - self.input = json["input"].string - self.output = json["output"].string - self.identifier = json["identifier"].string - self.completionExpectedAt = json["completionExpectedAt"].double - } - - static func factories(data: JSON) -> [NKTextProcessingTask]? { - guard let allResults = data.array else { return nil } - return allResults.compactMap(NKTextProcessingTask.init) - } - - static func factory(data: JSON) -> NKTextProcessingTask? { - NKTextProcessingTask(json: data) - } -} diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift new file mode 100644 index 00000000..57e20c58 --- /dev/null +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -0,0 +1,165 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation +import Alamofire +import SwiftyJSON + +public extension NextcloudKit { + func textProcessingGetTypesV2(account: String, + supportedTaskType: String = "Text", + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ types: [TaskTypeData]?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + let endpoint = "ocs/v2.php/taskprocessing/tasktypes" + guard let nkSession = nkCommonInstance.getSession(account: account), + let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), + let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { + return options.queue.async { completion(account, nil, nil, .urlError) } + } + + nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + task.taskDescription = options.taskDescription + taskHandler(task) + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + if self.nkCommonInstance.levelLog > 0 { + debugPrint(response) + } + switch response.result { + case .failure(let error): + let error = NKError(error: error, afResponse: response, responseData: response.data) + options.queue.async { completion(account, nil, response, error) } + case .success(let jsonData): + let json = JSON(jsonData) + let data = json["ocs"]["data"]["types"] + let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError + if 200..<300 ~= statusCode { + let dict = TaskTypes.deserialize(from: data) + let result = dict?.types.map({$0}) + var filteredResult = result? + .filter({ $0.inputShape?.input?.type == supportedTaskType && $0.outputShape?.output?.type == supportedTaskType }) + .sorted(by: {$0.id! < $1.id!}) + options.queue.async { completion(account, filteredResult, response, .success) } + } else { + options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } + } + } + } + } + + func textProcessingScheduleV2(input: String, + taskType: TaskTypeData, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ task: AssistantTask?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + let endpoint = "/ocs/v2.php/taskprocessing/schedule" + guard let nkSession = nkCommonInstance.getSession(account: account), + let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), + let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { + return options.queue.async { completion(account, nil, nil, .urlError) } + } + + let inputField: [String: String] = ["input": input] + let parameters: [String: Any] = ["input": inputField, "type": taskType.id ?? "", "appId": "assistant", "customId": ""] + + nkSession.sessionData.request(url, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + task.taskDescription = options.taskDescription + taskHandler(task) + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + if self.nkCommonInstance.levelLog > 0 { + debugPrint(response) + } + switch response.result { + case .failure(let error): + let error = NKError(error: error, afResponse: response, responseData: response.data) + options.queue.async { completion(account, nil, response, error) } + case .success(let jsonData): + let json = JSON(jsonData) + let data = json["ocs"]["data"]["task"] + let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError + if 200..<300 ~= statusCode { + let result = AssistantTask.deserialize(from: data) + options.queue.async { completion(account, result, response, .success) } + } else { + options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } + } + } + } + } + + func textProcessingGetTasksV2(taskType: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ tasks: TaskList?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + let endpoint = "/ocs/v2.php/taskprocessing/tasks?taskType=\(taskType)" + guard let nkSession = nkCommonInstance.getSession(account: account), + let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), + let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { + return options.queue.async { completion(account, nil, nil, .urlError) } + } + + nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + task.taskDescription = options.taskDescription + taskHandler(task) + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + if self.nkCommonInstance.levelLog > 0 { + debugPrint(response) + } + switch response.result { + case .failure(let error): + let error = NKError(error: error, afResponse: response, responseData: response.data) + options.queue.async { completion(account, nil, response, error) } + case .success(let jsonData): + let json = JSON(jsonData) + let data = json["ocs"]["data"]["tasks"] + let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError + if 200..<300 ~= statusCode { + let result = TaskList.deserialize(from: data) + options.queue.async { completion(account, result, response, .success) } + } else { + options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } + } + } + } + } + + func textProcessingDeleteTaskV2(taskId: Int64, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + let endpoint = "/ocs/v2.php/taskprocessing/task/\(taskId)" + guard let nkSession = nkCommonInstance.getSession(account: account), + let url = nkCommonInstance.createStandardUrl(serverUrl: nkSession.urlBase, endpoint: endpoint, options: options), + let headers = nkCommonInstance.getStandardHeaders(account: account, options: options) else { + return options.queue.async { completion(account, nil, .urlError) } + } + + nkSession.sessionData.request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor(nkCommonInstance: nkCommonInstance)).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + task.taskDescription = options.taskDescription + taskHandler(task) + }.responseData(queue: self.nkCommonInstance.backgroundQueue) { response in + if self.nkCommonInstance.levelLog > 0 { + debugPrint(response) + } + switch response.result { + case .failure(let error): + let error = NKError(error: error, afResponse: response, responseData: response.data) + options.queue.async { completion(account, response, error) } + case .success(let jsonData): + let json = JSON(jsonData) + let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError + if 200..<300 ~= statusCode { + options.queue.async { completion(account, response, .success) } + } else { + options.queue.async { completion(account, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } + } + } + } + } +} + + diff --git a/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift index 1aaa19ab..0d6e077e 100644 --- a/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift +++ b/Sources/NextcloudKit/NextcloudKit+ShareDownloadLimit.swift @@ -12,6 +12,11 @@ public extension NextcloudKit { } func getDownloadLimit(account: String, token: String, completion: @escaping (NKDownloadLimit?, NKError) -> Void) { + guard token.isEmpty == false else { + self.nkCommonInstance.writeLog("[ERROR] Attempt to get share download limit with empty token!") + return + } + let endpoint = makeEndpoint(with: token) let options = NKRequestOptions() @@ -81,6 +86,11 @@ public extension NextcloudKit { } func removeShareDownloadLimit(account: String, token: String, completion: @escaping (_ error: NKError) -> Void) { + guard token.isEmpty == false else { + self.nkCommonInstance.writeLog("[ERROR] Attempt to remove share download limit with empty token!") + return + } + let endpoint = makeEndpoint(with: token) let options = NKRequestOptions() @@ -117,6 +127,11 @@ public extension NextcloudKit { } func setShareDownloadLimit(account: String, token: String, limit: Int, completion: @escaping (_ error: NKError) -> Void) { + guard token.isEmpty == false else { + self.nkCommonInstance.writeLog("[ERROR] Attempt to set share download limit with empty token!") + return + } + let endpoint = makeEndpoint(with: token) let options = NKRequestOptions() options.contentType = "application/json" diff --git a/Sources/NextcloudKit/NextcloudKit.swift b/Sources/NextcloudKit/NextcloudKit.swift index 8d843023..18289004 100644 --- a/Sources/NextcloudKit/NextcloudKit.swift +++ b/Sources/NextcloudKit/NextcloudKit.swift @@ -31,19 +31,11 @@ open class NextcloudKit { eventMonitors: [NKMonitor(nkCommonInstance: self.nkCommonInstance)]) }() -#if swift(<6.0) - init() { -#if !os(watchOS) - startNetworkReachabilityObserver() -#endif - } -#else public init() { #if !os(watchOS) startNetworkReachabilityObserver() #endif } -#endif deinit { #if !os(watchOS)