From 721ec1509861a206028d0cce6c24a03d1799b15b Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 13 Feb 2025 18:40:58 +0100 Subject: [PATCH 01/21] WIP Signed-off-by: Milen Pivchev --- .../Assistant/v1/NKTextProcessingTask.swift | 50 +++++ .../v1/NKTextProcessingTaskType.swift | 30 +++ .../v2/NKTextProcessingTaskTypeV2.swift | 93 ++++++++ .../Assistant/v2/NKTextProcessingTaskV2.swift | 110 ++++++++++ .../NextcloudKit/NextcloudKit+Assistant.swift | 203 +++++++++++++----- .../AITests.swift | 79 +++++++ Tests/NextcloudKitIntegrationTests/Test.swift | 153 +++++++++++++ 7 files changed, 662 insertions(+), 56 deletions(-) create mode 100644 Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift create mode 100644 Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift create mode 100644 Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift create mode 100644 Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift create mode 100644 Tests/NextcloudKitIntegrationTests/AITests.swift create mode 100644 Tests/NextcloudKitIntegrationTests/Test.swift diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift new file mode 100644 index 00000000..072bee17 --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift @@ -0,0 +1,50 @@ +// 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 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/Models/Assistant/v1/NKTextProcessingTaskType.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift new file mode 100644 index 00000000..869f6016 --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift @@ -0,0 +1,30 @@ +// 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 factory(data: JSON) -> [NKTextProcessingTaskType]? { + guard let allResults = data.array else { return nil } + return allResults.compactMap(NKTextProcessingTaskType.init) + } +} + + diff --git a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift new file mode 100644 index 00000000..a8ae3e93 --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftyJSON + +//public class NKTextProcessingTaskTypeV2 { +// 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 struct NKTextProcessingTaskTypeV2: Codable { + public struct TaskTypes: Codable { + public let types: [String: TaskTypeData] + + init(types: [String : TaskTypeData]) { + self.types = types + } + } + + 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 struct TaskInputShape: Codable { + public let input: Shape? + } + + public struct TaskOutputShape: Codable { + public let output: Shape? + } + + public struct Shape: Codable { + public let name: String + public let description: String + public let type: String + } + + static func factory(data: JSON) -> NKTextProcessingTaskTypeV2.TaskTypes? { + var taskTypesDict: [String: NKTextProcessingTaskTypeV2.TaskTypeData] = [:] + + for (key, subJson) in data { + let taskTypeData = NKTextProcessingTaskTypeV2.TaskTypeData( + id: key, + name: subJson["name"].string, + description: subJson["description"].string, + inputShape: subJson["inputShape"].dictionary != nil ? NKTextProcessingTaskTypeV2.TaskInputShape( + input: subJson["inputShape"]["input"].dictionary != nil ? NKTextProcessingTaskTypeV2.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 ? NKTextProcessingTaskTypeV2.TaskOutputShape( + output: subJson["outputShape"]["output"].dictionary != nil ? NKTextProcessingTaskTypeV2.Shape( + name: subJson["outputShape"]["output"]["name"].stringValue, + description: subJson["outputShape"]["output"]["description"].stringValue, + type: subJson["outputShape"]["output"]["type"].stringValue + ) : nil + ) : nil + ) + + taskTypesDict[key] = taskTypeData + } + + let taskTypes = NKTextProcessingTaskTypeV2.TaskTypes(types: taskTypesDict) + return taskTypes + } +} + + diff --git a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift new file mode 100644 index 00000000..b848943d --- /dev/null +++ b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift @@ -0,0 +1,110 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftyJSON + +//public class NKTextProcessingTaskV2 { +// 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) +// } +//} + +public struct NKTextProcessingTaskV2 { + public struct TaskList: Codable { + public var tasks: [Task] + } + + public struct Task: 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 struct TaskInput: Codable { + public var input: String? + } + + public struct TaskOutput: Codable { + public var output: String? + } + + static func parseTaskList(from data: JSON) -> NKTextProcessingTaskV2.TaskList? { +// guard let data = jsonString.data(using: .utf8) else { return nil } + + do { +// let json = try JSON(data: data) +// let tasksArray = json["ocs"]["data"]["tasks"] + + let tasks = data.arrayValue.map { taskJson in + NKTextProcessingTaskV2.Task( + id: taskJson["id"].int64Value, + type: taskJson["type"].string, + status: taskJson["status"].string, + userId: taskJson["userId"].string, + appId: taskJson["appId"].string, + input: NKTextProcessingTaskV2.TaskInput(input: taskJson["input"]["input"].string), + output: NKTextProcessingTaskV2.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 NKTextProcessingTaskV2.TaskList(tasks: tasks) + } catch { + print("Failed to parse JSON: \(error)") + return nil + } + } +} diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index ef3bc5a3..fa9ded46 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 @@ -7,6 +7,8 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { + // MARK: v1 + func textProcessingGetTypes(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -194,72 +196,161 @@ public extension NextcloudKit { } } } -} -public class NKTextProcessingTaskType { - public var id: String? - public var name: String? - public var description: String? + // MARK: v2 - public init(id: String? = nil, name: String? = nil, description: String? = nil) { - self.id = id - self.name = name - self.description = description - } + func textProcessingGetTypesV2(account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ types: NKTextProcessingTaskTypeV2.TaskTypes?, _ 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) } + } - public init?(json: JSON) { - self.id = json["id"].string - self.name = json["name"].string - self.description = json["description"].string + nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor()).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) +// print(String(data: jsonData, encoding: .utf8)) + let data = json["ocs"]["data"]["types"] + let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError + if 200..<300 ~= statusCode { + let result = NKTextProcessingTaskTypeV2.factory(data: data) + options.queue.async { completion(account, result, response, .success) } + } else { + options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } + } + } + } } - static func factory(data: JSON) -> [NKTextProcessingTaskType]? { - guard let allResults = data.array else { return nil } - return allResults.compactMap(NKTextProcessingTaskType.init) - } -} + func textProcessingScheduleV2(input: String, + taskType: NKTextProcessingTaskTypeV2.TaskTypeData, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ task: NKTextProcessingTask?, _ 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) } + } -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? + let inputField: [String: String] = ["input": input] + let parameters: [String: Any] = ["input": inputField, "type": taskType.id ?? "", "appId": "assistant", "customId": ""] - 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 + nkSession.sessionData.request(url, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor()).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 = NKTextProcessingTask.factory(data: data) + options.queue.async { completion(account, result, response, .success) } + } else { + options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } + } + } + } } - 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 - } + func textProcessingGetTasksV2(taskType: String, + account: String, + options: NKRequestOptions = NKRequestOptions(), + taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + completion: @escaping (_ account: String, _ tasks: NKTextProcessingTaskV2.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) } + } - static func factories(data: JSON) -> [NKTextProcessingTask]? { - guard let allResults = data.array else { return nil } - return allResults.compactMap(NKTextProcessingTask.init) + nkSession.sessionData.request(url, method: .get, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor()).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) + print(String(data: jsonData, encoding: .utf8)) + let data = json["ocs"]["data"]["tasks"] + let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError + if 200..<300 ~= statusCode { +// let result = NKTextProcessingTask.factory(data: data) + let result = NKTextProcessingTaskV2.parseTaskList(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)) } + } + } + } } - static func factory(data: JSON) -> NKTextProcessingTask? { - NKTextProcessingTask(json: data) + 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()).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 data = json["ocs"]["data"]["task"] + 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/Tests/NextcloudKitIntegrationTests/AITests.swift b/Tests/NextcloudKitIntegrationTests/AITests.swift new file mode 100644 index 00000000..1e26665d --- /dev/null +++ b/Tests/NextcloudKitIntegrationTests/AITests.swift @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2024 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later + +import XCTest +import Alamofire +@testable import NextcloudKit + +final class AITests: BaseIntegrationXCTestCase { + func testTest() { + + let expectation = expectation(description: "Should finish last callback") + +// let folderName = "Share\(randomInt)" +// let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)" + let serverUrl = "https://daily.ltd2.nextcloud.com" +// let serverUrlFileName = "\(serverUrl)/\(folderName)" + let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" + + NextcloudKit.shared.textProcessingGetTypes(account: account) { account, types, responseData, error in + print("YES") + expectation.fulfill() + } +// NextcloudKit.shared.appendSession(account: TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username, password: TestConstants.password, userAgent: "", nextcloudVersion: 0, groupIdentifier: "") +// +// NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, account: TestConstants.account) { account, ocId, date, _, error in +// XCTAssertEqual(TestConstants.account, account) +// +// XCTAssertEqual(NKError.success.errorCode, error.errorCode) +// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) +// +// Thread.sleep(forTimeInterval: 0.2) +// +// let note = "Test note" +// +// NextcloudKit.shared.createShare(path: folderName, shareType: 0, shareWith: "nextcloud", note: note, account: "") { account, share, data, error in +// defer { expectation.fulfill() } +// +// XCTAssertEqual(TestConstants.account, account) +// XCTAssertEqual(NKError.success.errorCode, error.errorCode) +// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) +// XCTAssertEqual(note, share?.note) +// } +// } +// + waitForExpectations(timeout: 2) + } +// func test_createShare_withNote_shouldCreateShare() throws { +// let expectation = expectation(description: "Should finish last callback") +// +// let folderName = "Share\(randomInt)" +// let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)" +// let serverUrlFileName = "\(serverUrl)/\(folderName)" +// +// NextcloudKit.shared.appendSession(account: TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username, password: TestConstants.password, userAgent: "", nextcloudVersion: 0, groupIdentifier: "") +// +// NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, account: TestConstants.account) { account, ocId, date, _, error in +// XCTAssertEqual(TestConstants.account, account) +// +// XCTAssertEqual(NKError.success.errorCode, error.errorCode) +// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) +// +// Thread.sleep(forTimeInterval: 0.2) +// +// let note = "Test note" +// +// NextcloudKit.shared.createShare(path: folderName, shareType: 0, shareWith: "nextcloud", note: note, account: "") { account, share, data, error in +// defer { expectation.fulfill() } +// +// XCTAssertEqual(TestConstants.account, account) +// XCTAssertEqual(NKError.success.errorCode, error.errorCode) +// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) +// XCTAssertEqual(note, share?.note) +// } +// } +// +// waitForExpectations(timeout: 100) +// } +} diff --git a/Tests/NextcloudKitIntegrationTests/Test.swift b/Tests/NextcloudKitIntegrationTests/Test.swift new file mode 100644 index 00000000..fa5f77c5 --- /dev/null +++ b/Tests/NextcloudKitIntegrationTests/Test.swift @@ -0,0 +1,153 @@ +// +// Test.swift +// NextcloudKit +// +// Created by Milen Pivchev on 12.02.25. +// + +import Testing +import NextcloudKit + +struct Test { + + @Test func TestGetTypes() async throws { + let urlBase = "https://daily.ltd2.nextcloud.com" + let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" + + await withCheckedContinuation { continuation in + NextcloudKit.shared.appendSession(account: account, + urlBase: urlBase, + user: "milen.pivchev@nextcloud.com", + userId: "milen.pivchev@nextcloud.com", + password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", + userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", + nextcloudVersion: 31, + groupIdentifier: "group.it.twsweb.Crypto-Cloud") + + NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in + if error == .success, let userProfile { + NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) + + NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in + print(error) + print(types) + continuation.resume() + } + } + + } + } + } + + @Test func TestCreate() async throws { + let urlBase = "https://daily.ltd2.nextcloud.com" + let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" + + await withCheckedContinuation { continuation in + NextcloudKit.shared.appendSession(account: account, + urlBase: urlBase, + user: "milen.pivchev@nextcloud.com", + userId: "milen.pivchev@nextcloud.com", + password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", + userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", + nextcloudVersion: 31, + groupIdentifier: "group.it.twsweb.Crypto-Cloud") + + NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in + if error == .success, let userProfile { + NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) + + NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in + print(error) + print(types) + guard let type = types?.types["core:text2text"] else { return } + NextcloudKit.shared.textProcessingScheduleV2(input: "TestFROM2", taskType: type, account: account) { account, task, responseData, error in + print(error) + print(task) + print(responseData?.response) + continuation.resume() + } + } + + } + + } + } + } + + @Test func TestGetList() async throws { + let urlBase = "https://daily.ltd2.nextcloud.com" + let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" + + await withCheckedContinuation { continuation in + NextcloudKit.shared.appendSession(account: account, + urlBase: urlBase, + user: "milen.pivchev@nextcloud.com", + userId: "milen.pivchev@nextcloud.com", + password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", + userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", + nextcloudVersion: 31, + groupIdentifier: "group.it.twsweb.Crypto-Cloud") + + NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in + if error == .success, let userProfile { + NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) + + NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in + print(error) + print(types) + guard let type = types?.types["core:text2text"] else { return } + NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in + print(task) + + continuation.resume() + } + + } + } + + } + + } + } +} + +@Test func TestDeleteTask() async throws { + let urlBase = "https://daily.ltd2.nextcloud.com" + let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" + + await withCheckedContinuation { continuation in + NextcloudKit.shared.appendSession(account: account, + urlBase: urlBase, + user: "milen.pivchev@nextcloud.com", + userId: "milen.pivchev@nextcloud.com", + password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", + userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", + nextcloudVersion: 31, + groupIdentifier: "group.it.twsweb.Crypto-Cloud") + + NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in + if error == .success, let userProfile { + NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) + + NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in + print(error) + print(types) + guard let type = types?.types["core:text2text"] else { return } + NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in + print(task) + + NextcloudKit.shared.textProcessingDeleteTaskV2(taskId: task?.tasks.first?.id ?? 0, account: account) { account, responseData, error in + print(error) + continuation.resume() + } + + } + + } + } + + } + + } +} From 7c923b6f9d4d847dc667688613429e0ae9acea39 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 13 Feb 2025 18:59:17 +0100 Subject: [PATCH 02/21] Fix compile Signed-off-by: Milen Pivchev --- Sources/NextcloudKit/NextcloudKit+Assistant.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index ae9051be..2f3aa7d8 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -210,7 +210,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()).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + 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 @@ -252,7 +252,7 @@ public extension NextcloudKit { 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()).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + 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 @@ -289,7 +289,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()).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + 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 @@ -328,7 +328,7 @@ public extension NextcloudKit { return options.queue.async { completion(account, nil, .urlError) } } - nkSession.sessionData.request(url, method: .delete, encoding: URLEncoding.default, headers: headers, interceptor: NKInterceptor()).validate(statusCode: 200..<300).onURLSessionTaskCreation { task in + 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 From 6f41e72357eda57c1c7eabee1c6677455f5aad20 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 13 Feb 2025 19:08:15 +0100 Subject: [PATCH 03/21] Refactor Signed-off-by: Milen Pivchev --- .../v2/NKTextProcessingTaskTypeV2.swift | 27 +++---- .../Assistant/v2/NKTextProcessingTaskV2.swift | 18 ++--- .../NextcloudKit/NextcloudKit+Assistant.swift | 4 +- .../AITests.swift | 79 ------------------- 4 files changed, 23 insertions(+), 105 deletions(-) delete mode 100644 Tests/NextcloudKitIntegrationTests/AITests.swift diff --git a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift index a8ae3e93..54063a9b 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift @@ -28,20 +28,14 @@ import SwiftyJSON // } public struct NKTextProcessingTaskTypeV2: Codable { - public struct TaskTypes: Codable { - public let types: [String: TaskTypeData] - - init(types: [String : TaskTypeData]) { - self.types = types - } - } - + public let types: [String: TaskTypeData] + 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 let id: String? + public let name: String? + public let description: String? + public let inputShape: TaskInputShape? + public let outputShape: TaskOutputShape? } public struct TaskInputShape: Codable { @@ -58,7 +52,7 @@ public struct NKTextProcessingTaskTypeV2: Codable { public let type: String } - static func factory(data: JSON) -> NKTextProcessingTaskTypeV2.TaskTypes? { + static func factory(data: JSON) -> NKTextProcessingTaskTypeV2? { var taskTypesDict: [String: NKTextProcessingTaskTypeV2.TaskTypeData] = [:] for (key, subJson) in data { @@ -85,9 +79,12 @@ public struct NKTextProcessingTaskTypeV2: Codable { taskTypesDict[key] = taskTypeData } - let taskTypes = NKTextProcessingTaskTypeV2.TaskTypes(types: taskTypesDict) + let taskTypes = NKTextProcessingTaskTypeV2(types: taskTypesDict) return taskTypes } + } + + diff --git a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift index b848943d..54a95124 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift @@ -49,10 +49,8 @@ import SwiftyJSON // } //} -public struct NKTextProcessingTaskV2 { - public struct TaskList: Codable { - public var tasks: [Task] - } +public struct NKTextProcessingTaskV2: Codable { + public var tasks: [Task] public struct Task: Codable { public let id: Int64 @@ -77,12 +75,12 @@ public struct NKTextProcessingTaskV2 { public var output: String? } - static func parseTaskList(from data: JSON) -> NKTextProcessingTaskV2.TaskList? { -// guard let data = jsonString.data(using: .utf8) else { return nil } + static func parseTaskList(from data: JSON) -> NKTextProcessingTaskV2? { + // guard let data = jsonString.data(using: .utf8) else { return nil } do { -// let json = try JSON(data: data) -// let tasksArray = json["ocs"]["data"]["tasks"] + // let json = try JSON(data: data) + // let tasksArray = json["ocs"]["data"]["tasks"] let tasks = data.arrayValue.map { taskJson in NKTextProcessingTaskV2.Task( @@ -101,10 +99,12 @@ public struct NKTextProcessingTaskV2 { ) } - return NKTextProcessingTaskV2.TaskList(tasks: tasks) + return NKTextProcessingTaskV2(tasks: tasks) } catch { print("Failed to parse JSON: \(error)") return nil } } } + + diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index 2f3aa7d8..35a6e768 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -202,7 +202,7 @@ public extension NextcloudKit { func textProcessingGetTypesV2(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ types: NKTextProcessingTaskTypeV2.TaskTypes?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ types: NKTextProcessingTaskTypeV2?, _ 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), @@ -281,7 +281,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ tasks: NKTextProcessingTaskV2.TaskList?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ tasks: NKTextProcessingTaskV2?, _ 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), diff --git a/Tests/NextcloudKitIntegrationTests/AITests.swift b/Tests/NextcloudKitIntegrationTests/AITests.swift deleted file mode 100644 index 1e26665d..00000000 --- a/Tests/NextcloudKitIntegrationTests/AITests.swift +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-FileCopyrightText: Nextcloud GmbH -// SPDX-FileCopyrightText: 2024 Milen Pivchev -// SPDX-License-Identifier: GPL-3.0-or-later - -import XCTest -import Alamofire -@testable import NextcloudKit - -final class AITests: BaseIntegrationXCTestCase { - func testTest() { - - let expectation = expectation(description: "Should finish last callback") - -// let folderName = "Share\(randomInt)" -// let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)" - let serverUrl = "https://daily.ltd2.nextcloud.com" -// let serverUrlFileName = "\(serverUrl)/\(folderName)" - let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" - - NextcloudKit.shared.textProcessingGetTypes(account: account) { account, types, responseData, error in - print("YES") - expectation.fulfill() - } -// NextcloudKit.shared.appendSession(account: TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username, password: TestConstants.password, userAgent: "", nextcloudVersion: 0, groupIdentifier: "") -// -// NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, account: TestConstants.account) { account, ocId, date, _, error in -// XCTAssertEqual(TestConstants.account, account) -// -// XCTAssertEqual(NKError.success.errorCode, error.errorCode) -// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) -// -// Thread.sleep(forTimeInterval: 0.2) -// -// let note = "Test note" -// -// NextcloudKit.shared.createShare(path: folderName, shareType: 0, shareWith: "nextcloud", note: note, account: "") { account, share, data, error in -// defer { expectation.fulfill() } -// -// XCTAssertEqual(TestConstants.account, account) -// XCTAssertEqual(NKError.success.errorCode, error.errorCode) -// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) -// XCTAssertEqual(note, share?.note) -// } -// } -// - waitForExpectations(timeout: 2) - } -// func test_createShare_withNote_shouldCreateShare() throws { -// let expectation = expectation(description: "Should finish last callback") -// -// let folderName = "Share\(randomInt)" -// let serverUrl = "\(TestConstants.server)/remote.php/dav/files/\(TestConstants.username)" -// let serverUrlFileName = "\(serverUrl)/\(folderName)" -// -// NextcloudKit.shared.appendSession(account: TestConstants.account, urlBase: TestConstants.server, user: TestConstants.username, userId: TestConstants.username, password: TestConstants.password, userAgent: "", nextcloudVersion: 0, groupIdentifier: "") -// -// NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, account: TestConstants.account) { account, ocId, date, _, error in -// XCTAssertEqual(TestConstants.account, account) -// -// XCTAssertEqual(NKError.success.errorCode, error.errorCode) -// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) -// -// Thread.sleep(forTimeInterval: 0.2) -// -// let note = "Test note" -// -// NextcloudKit.shared.createShare(path: folderName, shareType: 0, shareWith: "nextcloud", note: note, account: "") { account, share, data, error in -// defer { expectation.fulfill() } -// -// XCTAssertEqual(TestConstants.account, account) -// XCTAssertEqual(NKError.success.errorCode, error.errorCode) -// XCTAssertEqual(NKError.success.errorDescription, error.errorDescription) -// XCTAssertEqual(note, share?.note) -// } -// } -// -// waitForExpectations(timeout: 100) -// } -} From 8f167d224911f10735996c84214b5de075484094 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 13 Feb 2025 19:47:37 +0100 Subject: [PATCH 04/21] WIP Signed-off-by: Milen Pivchev --- ...tProcessingTaskV2.swift => TaskList.swift} | 80 ++++++++++++------- ...essingTaskTypeV2.swift => TaskTypes.swift} | 63 ++++++++------- .../NextcloudKit/NextcloudKit+Assistant.swift | 16 ++-- 3 files changed, 91 insertions(+), 68 deletions(-) rename Sources/NextcloudKit/Models/Assistant/v2/{NKTextProcessingTaskV2.swift => TaskList.swift} (71%) rename Sources/NextcloudKit/Models/Assistant/v2/{NKTextProcessingTaskTypeV2.swift => TaskTypes.swift} (65%) diff --git a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift similarity index 71% rename from Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift rename to Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift index 54a95124..c5effe30 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskV2.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -49,33 +49,10 @@ import SwiftyJSON // } //} -public struct NKTextProcessingTaskV2: Codable { +public struct TaskList: Codable { public var tasks: [Task] - public struct Task: 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 struct TaskInput: Codable { - public var input: String? - } - - public struct TaskOutput: Codable { - public var output: String? - } - - static func parseTaskList(from data: JSON) -> NKTextProcessingTaskV2? { + static func factory(from data: JSON) -> TaskList? { // guard let data = jsonString.data(using: .utf8) else { return nil } do { @@ -83,14 +60,14 @@ public struct NKTextProcessingTaskV2: Codable { // let tasksArray = json["ocs"]["data"]["tasks"] let tasks = data.arrayValue.map { taskJson in - NKTextProcessingTaskV2.Task( + Task( id: taskJson["id"].int64Value, type: taskJson["type"].string, status: taskJson["status"].string, userId: taskJson["userId"].string, appId: taskJson["appId"].string, - input: NKTextProcessingTaskV2.TaskInput(input: taskJson["input"]["input"].string), - output: NKTextProcessingTaskV2.TaskOutput(output: taskJson["output"]["output"].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, @@ -99,12 +76,57 @@ public struct NKTextProcessingTaskV2: Codable { ) } - return NKTextProcessingTaskV2(tasks: tasks) + return TaskList(tasks: tasks) } catch { print("Failed to parse JSON: \(error)") return nil } } + + public struct Task: 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? + + static func factory(from data: JSON) -> TaskList.Task? { + // guard let data = jsonString.data(using: .utf8) else { return nil } + + + let task = Task( + 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 struct TaskOutput: Codable { + public var output: String? } diff --git a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift similarity index 65% rename from Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift rename to Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift index 54063a9b..464f671f 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/NKTextProcessingTaskTypeV2.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift @@ -27,48 +27,26 @@ import SwiftyJSON // } // } -public struct NKTextProcessingTaskTypeV2: Codable { +public struct TaskTypes: Codable { public let types: [String: TaskTypeData] - - 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 struct TaskInputShape: Codable { - public let input: Shape? - } - - public struct TaskOutputShape: Codable { - public let output: Shape? - } - - public struct Shape: Codable { - public let name: String - public let description: String - public let type: String - } - static func factory(data: JSON) -> NKTextProcessingTaskTypeV2? { - var taskTypesDict: [String: NKTextProcessingTaskTypeV2.TaskTypeData] = [:] + static func factory(data: JSON) -> TaskTypes? { + var taskTypesDict: [String: TaskTypeData] = [:] for (key, subJson) in data { - let taskTypeData = NKTextProcessingTaskTypeV2.TaskTypeData( + let taskTypeData = TaskTypeData( id: key, name: subJson["name"].string, description: subJson["description"].string, - inputShape: subJson["inputShape"].dictionary != nil ? NKTextProcessingTaskTypeV2.TaskInputShape( - input: subJson["inputShape"]["input"].dictionary != nil ? NKTextProcessingTaskTypeV2.Shape( + 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 ? NKTextProcessingTaskTypeV2.TaskOutputShape( - output: subJson["outputShape"]["output"].dictionary != nil ? NKTextProcessingTaskTypeV2.Shape( + 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 @@ -79,12 +57,35 @@ public struct NKTextProcessingTaskTypeV2: Codable { taskTypesDict[key] = taskTypeData } - let taskTypes = NKTextProcessingTaskTypeV2(types: taskTypesDict) + let taskTypes = TaskTypes(types: taskTypesDict) return 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 struct TaskInputShape: Codable { + public let input: Shape? +} + +public struct TaskOutputShape: Codable { + public let output: Shape? +} + +public struct Shape: Codable { + public let name: String + public let description: String + public let type: String +} + + + diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index 35a6e768..b629bd81 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -202,7 +202,7 @@ public extension NextcloudKit { func textProcessingGetTypesV2(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ types: NKTextProcessingTaskTypeV2?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ types: TaskTypes?, _ 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), @@ -227,7 +227,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["types"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let result = NKTextProcessingTaskTypeV2.factory(data: data) + let result = TaskTypes.factory(data: data) options.queue.async { completion(account, result, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } @@ -237,11 +237,11 @@ public extension NextcloudKit { } func textProcessingScheduleV2(input: String, - taskType: NKTextProcessingTaskTypeV2.TaskTypeData, + taskType: TaskTypeData, account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ task: NKTextProcessingTask?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ task: TaskList.Task?, _ 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), @@ -265,10 +265,11 @@ public extension NextcloudKit { options.queue.async { completion(account, nil, response, error) } case .success(let jsonData): let json = JSON(jsonData) + print(String(data: jsonData, encoding: .utf8)) 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 = TaskList.Task.factory(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)) } @@ -281,7 +282,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ tasks: NKTextProcessingTaskV2?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + 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), @@ -302,12 +303,11 @@ public extension NextcloudKit { options.queue.async { completion(account, nil, response, error) } case .success(let jsonData): let json = JSON(jsonData) - print(String(data: jsonData, encoding: .utf8)) let data = json["ocs"]["data"]["tasks"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { // let result = NKTextProcessingTask.factory(data: data) - let result = NKTextProcessingTaskV2.parseTaskList(from: data) + let result = TaskList.factory(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)) } From 5b29ab91eaeb91a27d69f69590496e2a620a1009 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 14 Feb 2025 14:09:59 +0100 Subject: [PATCH 05/21] Refactor Signed-off-by: Milen Pivchev --- .../NextcloudKit/Models/Assistant/v2/TaskList.swift | 10 +++++----- Sources/NextcloudKit/NextcloudKit+Assistant.swift | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift index c5effe30..260e3b0d 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -50,7 +50,7 @@ import SwiftyJSON //} public struct TaskList: Codable { - public var tasks: [Task] + public var tasks: [AssistantTask] static func factory(from data: JSON) -> TaskList? { // guard let data = jsonString.data(using: .utf8) else { return nil } @@ -60,7 +60,7 @@ public struct TaskList: Codable { // let tasksArray = json["ocs"]["data"]["tasks"] let tasks = data.arrayValue.map { taskJson in - Task( + AssistantTask( id: taskJson["id"].int64Value, type: taskJson["type"].string, status: taskJson["status"].string, @@ -83,7 +83,7 @@ public struct TaskList: Codable { } } - public struct Task: Codable { + public struct AssistantTask: Codable { public let id: Int64 public let type: String? public let status: String? @@ -97,11 +97,11 @@ public struct TaskList: Codable { public let scheduledAt: Int? public let endedAt: Int? - static func factory(from data: JSON) -> TaskList.Task? { + static func factory(from data: JSON) -> TaskList.AssistantTask? { // guard let data = jsonString.data(using: .utf8) else { return nil } - let task = Task( + let task = AssistantTask( id: data["id"].int64Value, type: data["type"].string, status: data["status"].string, diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index b629bd81..a9686354 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -241,7 +241,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ task: TaskList.Task?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + completion: @escaping (_ account: String, _ task: TaskList.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), @@ -269,7 +269,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 = TaskList.Task.factory(from: data) + let result = TaskList.AssistantTask.factory(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)) } From 1ccef6548d26916df525658f1bfbef9ffb33752c Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 14 Feb 2025 14:13:17 +0100 Subject: [PATCH 06/21] WIP Signed-off-by: Milen Pivchev --- .../Models/Assistant/v2/TaskList.swift | 70 +++++++++---------- Tests/NextcloudKitIntegrationTests/Test.swift | 1 - 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift index 260e3b0d..fdf5cb14 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -82,42 +82,42 @@ public struct TaskList: Codable { return nil } } +} - 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? - - static func factory(from data: JSON) -> TaskList.AssistantTask? { - // guard let data = jsonString.data(using: .utf8) else { return nil } - - - 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 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? + + static func factory(from data: JSON) -> AssistantTask? { + // guard let data = jsonString.data(using: .utf8) else { return nil } + + + 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 } } diff --git a/Tests/NextcloudKitIntegrationTests/Test.swift b/Tests/NextcloudKitIntegrationTests/Test.swift index fa5f77c5..23a3ac10 100644 --- a/Tests/NextcloudKitIntegrationTests/Test.swift +++ b/Tests/NextcloudKitIntegrationTests/Test.swift @@ -9,7 +9,6 @@ import Testing import NextcloudKit struct Test { - @Test func TestGetTypes() async throws { let urlBase = "https://daily.ltd2.nextcloud.com" let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" From 5a7207ed3f0690cbe67e368f0a1e72b3b8be9cf4 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 14 Feb 2025 14:16:07 +0100 Subject: [PATCH 07/21] WIP Signed-off-by: Milen Pivchev --- Sources/NextcloudKit/NextcloudKit+Assistant.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index a9686354..f99e598c 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -241,7 +241,7 @@ public extension NextcloudKit { account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ task: TaskList.AssistantTask?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + 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), @@ -269,7 +269,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 = TaskList.AssistantTask.factory(from: data) + let result = AssistantTask.factory(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)) } From 8ca321934cf07de475ef89d137e6f1b454647df4 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 14 Feb 2025 14:51:59 +0100 Subject: [PATCH 08/21] WIP Signed-off-by: Milen Pivchev --- Sources/NextcloudKit/NextcloudKit+Assistant.swift | 5 +++-- Tests/NextcloudKitIntegrationTests/Test.swift | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index f99e598c..1db14d15 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -202,7 +202,7 @@ public extension NextcloudKit { func textProcessingGetTypesV2(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ types: TaskTypes?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + 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), @@ -227,7 +227,8 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["types"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let result = TaskTypes.factory(data: data) + let dict = TaskTypes.factory(data: data) + let result = dict?.types.values.map({$0}) options.queue.async { completion(account, result, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } diff --git a/Tests/NextcloudKitIntegrationTests/Test.swift b/Tests/NextcloudKitIntegrationTests/Test.swift index 23a3ac10..9b9c7c89 100644 --- a/Tests/NextcloudKitIntegrationTests/Test.swift +++ b/Tests/NextcloudKitIntegrationTests/Test.swift @@ -59,7 +59,7 @@ struct Test { NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in print(error) print(types) - guard let type = types?.types["core:text2text"] else { return } + guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } NextcloudKit.shared.textProcessingScheduleV2(input: "TestFROM2", taskType: type, account: account) { account, task, responseData, error in print(error) print(task) @@ -95,7 +95,7 @@ struct Test { NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in print(error) print(types) - guard let type = types?.types["core:text2text"] else { return } + guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in print(task) @@ -132,7 +132,7 @@ struct Test { NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in print(error) print(types) - guard let type = types?.types["core:text2text"] else { return } + guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in print(task) From df068e4f584fb6b1c9a2e7c289e5e6fe087d9503 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 14 Feb 2025 18:35:00 +0100 Subject: [PATCH 09/21] WIP Signed-off-by: Milen Pivchev --- .../Assistant/v1/NKTextProcessingTask.swift | 21 ++++++++++++ .../v1/NKTextProcessingTaskType.swift | 8 +++++ .../Models/Assistant/v2/TaskList.swift | 23 +++++++++++++ .../Models/Assistant/v2/TaskTypes.swift | 32 ++++++++++++++++--- .../NextcloudKit/NextcloudKit+Assistant.swift | 2 +- 5 files changed, 80 insertions(+), 6 deletions(-) diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift index 072bee17..9c6e3d9c 100644 --- a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift @@ -47,4 +47,25 @@ public class NKTextProcessingTask { static func factory(data: JSON) -> NKTextProcessingTask? { NKTextProcessingTask(json: data) } + + 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 index 869f6016..01312f09 100644 --- a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift @@ -25,6 +25,14 @@ public class NKTextProcessingTaskType { guard let allResults = data.array else { return nil } return allResults.compactMap(NKTextProcessingTaskType.init) } + + 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 index fdf5cb14..fe410fb0 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -98,6 +98,21 @@ public struct AssistantTask: Codable { public let scheduledAt: Int? public let endedAt: Int? + 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 factory(from data: JSON) -> AssistantTask? { // guard let data = jsonString.data(using: .utf8) else { return nil } @@ -123,10 +138,18 @@ public struct AssistantTask: Codable { public struct TaskInput: Codable { public var input: String? + + init(input: String? = nil) { + self.input = input + } } public struct TaskOutput: Codable { public var output: String? + + 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 index 464f671f..92f470ab 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift @@ -28,10 +28,11 @@ import SwiftyJSON // } public struct TaskTypes: Codable { - public let types: [String: TaskTypeData] + public let types: [TaskTypeData] static func factory(data: JSON) -> TaskTypes? { - var taskTypesDict: [String: TaskTypeData] = [:] + var taskTypes: [TaskTypeData] = [] +// var taskTypesDict: [String: TaskTypeData] = [:] for (key, subJson) in data { let taskTypeData = TaskTypeData( @@ -54,11 +55,10 @@ public struct TaskTypes: Codable { ) : nil ) - taskTypesDict[key] = taskTypeData + taskTypes.append(taskTypeData) } - let taskTypes = TaskTypes(types: taskTypesDict) - return taskTypes + return TaskTypes(types: taskTypes) } } @@ -68,20 +68,42 @@ public struct TaskTypeData: Codable { public let description: String? public let inputShape: TaskInputShape? public let outputShape: TaskOutputShape? + + 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? + + init(input: Shape?) { + self.input = input + } } public struct TaskOutputShape: Codable { public let output: Shape? + + init(output: Shape?) { + self.output = output + } } public struct Shape: Codable { public let name: String public let description: String public let type: String + + init(name: String, description: String, type: String) { + self.name = name + self.description = description + self.type = type + } } diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index 1db14d15..826a55df 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -228,7 +228,7 @@ public extension NextcloudKit { let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { let dict = TaskTypes.factory(data: data) - let result = dict?.types.values.map({$0}) + let result = dict?.types.map({$0}) options.queue.async { completion(account, result, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } From c1983e07dc6f475ae5a39346266b65722fe9a303 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 14 Feb 2025 18:44:01 +0100 Subject: [PATCH 10/21] WIP Signed-off-by: Milen Pivchev --- .../NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift | 2 +- .../Models/Assistant/v1/NKTextProcessingTaskType.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift index 9c6e3d9c..ec868e0e 100644 --- a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift @@ -48,7 +48,7 @@ public class NKTextProcessingTask { NKTextProcessingTask(json: data) } - static func toV2(tasks: [NKTextProcessingTask]) -> TaskList { + public static func toV2(tasks: [NKTextProcessingTask]) -> TaskList { let tasks = tasks.map { task in AssistantTask( id: Int64(task.id ?? 0), diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift index 01312f09..43c761b0 100644 --- a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift @@ -26,7 +26,7 @@ public class NKTextProcessingTaskType { return allResults.compactMap(NKTextProcessingTaskType.init) } - static func toV2(type: [NKTextProcessingTaskType]) -> TaskTypes { + 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) } From 0b69ad57e32db2e429dd936596d649da523f7c10 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Mon, 17 Feb 2025 16:44:30 +0100 Subject: [PATCH 11/21] WIP Signed-off-by: Milen Pivchev --- .../NextcloudKit/NextcloudKit+Assistant.swift | 158 ----------------- .../NextcloudKit+AssistantV2.swift | 164 ++++++++++++++++++ 2 files changed, 164 insertions(+), 158 deletions(-) create mode 100644 Sources/NextcloudKit/NextcloudKit+AssistantV2.swift diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index 826a55df..bc12283a 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -7,8 +7,6 @@ import Alamofire import SwiftyJSON public extension NextcloudKit { - // MARK: v1 - func textProcessingGetTypes(account: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, @@ -196,162 +194,6 @@ public extension NextcloudKit { } } } - - // MARK: v2 - - func textProcessingGetTypesV2(account: String, - 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) -// print(String(data: jsonData, encoding: .utf8)) - let data = json["ocs"]["data"]["types"] - let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError - if 200..<300 ~= statusCode { - let dict = TaskTypes.factory(data: data) - let result = dict?.types.map({$0}) - options.queue.async { completion(account, result, 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) - print(String(data: jsonData, encoding: .utf8)) - let data = json["ocs"]["data"]["task"] - let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError - if 200..<300 ~= statusCode { - let result = AssistantTask.factory(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 = NKTextProcessingTask.factory(data: data) - let result = TaskList.factory(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 data = json["ocs"]["data"]["task"] - 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+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift new file mode 100644 index 00000000..18c98e27 --- /dev/null +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -0,0 +1,164 @@ +// 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.factory(data: data) + let result = dict?.types.map({$0}) + let filteredResult = result?.filter({ $0.inputShape?.input?.type == supportedTaskType && $0.outputShape?.output?.type == supportedTaskType }) + options.queue.async { completion(account, result, 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.factory(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.factory(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 data = json["ocs"]["data"]["task"] + 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)) } + } + } + } + } +} + + From aaf4de779af064665e470bc8cf3e591ab4af04d5 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Mon, 17 Feb 2025 17:00:47 +0100 Subject: [PATCH 12/21] WIP Signed-off-by: Milen Pivchev --- Sources/NextcloudKit/NextcloudKit+AssistantV2.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift index 18c98e27..4e45abea 100644 --- a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -38,7 +38,7 @@ public extension NextcloudKit { let dict = TaskTypes.factory(data: data) let result = dict?.types.map({$0}) let filteredResult = result?.filter({ $0.inputShape?.input?.type == supportedTaskType && $0.outputShape?.output?.type == supportedTaskType }) - options.queue.async { completion(account, result, response, .success) } + options.queue.async { completion(account, filteredResult, response, .success) } } else { options.queue.async { completion(account, nil, response, NKError(rootJson: json, fallbackStatusCode: response.response?.statusCode)) } } From 81dab95f304de72753ce6a3634b74ce8d86905ee Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Tue, 18 Feb 2025 12:22:34 +0100 Subject: [PATCH 13/21] WIP Signed-off-by: Milen Pivchev --- Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift | 1 - Sources/NextcloudKit/NextcloudKit+AssistantV2.swift | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift index 92f470ab..8f25f13b 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift @@ -32,7 +32,6 @@ public struct TaskTypes: Codable { static func factory(data: JSON) -> TaskTypes? { var taskTypes: [TaskTypeData] = [] -// var taskTypesDict: [String: TaskTypeData] = [:] for (key, subJson) in data { let taskTypeData = TaskTypeData( diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift index 4e45abea..bb07e082 100644 --- a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -37,7 +37,10 @@ public extension NextcloudKit { if 200..<300 ~= statusCode { let dict = TaskTypes.factory(data: data) let result = dict?.types.map({$0}) - let filteredResult = result?.filter({ $0.inputShape?.input?.type == supportedTaskType && $0.outputShape?.output?.type == supportedTaskType }) + 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)) } From 7a90d0666e43fa58b3ccf25fde90e03516031a36 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Tue, 18 Feb 2025 13:22:02 +0100 Subject: [PATCH 14/21] WIP Signed-off-by: Milen Pivchev --- .../Models/Assistant/v2/TaskList.swift | 53 +++++++------------ .../Models/Assistant/v2/TaskTypes.swift | 31 ++--------- 2 files changed, 24 insertions(+), 60 deletions(-) diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift index fe410fb0..55a4c920 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -53,34 +53,24 @@ public struct TaskList: Codable { public var tasks: [AssistantTask] static func factory(from data: JSON) -> TaskList? { - // guard let data = jsonString.data(using: .utf8) else { return nil } - - do { - // let json = try JSON(data: data) - // let tasksArray = json["ocs"]["data"]["tasks"] - - 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) - } catch { - print("Failed to parse JSON: \(error)") - return nil + 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) } } @@ -98,7 +88,7 @@ public struct AssistantTask: Codable { public let scheduledAt: Int? public let endedAt: Int? - 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?) { + 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 @@ -114,9 +104,6 @@ public struct AssistantTask: Codable { } static func factory(from data: JSON) -> AssistantTask? { - // guard let data = jsonString.data(using: .utf8) else { return nil } - - let task = AssistantTask( id: data["id"].int64Value, type: data["type"].string, @@ -139,7 +126,7 @@ public struct AssistantTask: Codable { public struct TaskInput: Codable { public var input: String? - init(input: String? = nil) { + public init(input: String? = nil) { self.input = input } } @@ -147,7 +134,7 @@ public struct TaskInput: Codable { public struct TaskOutput: Codable { public var output: String? - init(output: String? = nil) { + 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 index 8f25f13b..afb9a6b7 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift @@ -4,29 +4,6 @@ import SwiftyJSON -//public class NKTextProcessingTaskTypeV2 { -// 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 struct TaskTypes: Codable { public let types: [TaskTypeData] @@ -68,7 +45,7 @@ public struct TaskTypeData: Codable { public let inputShape: TaskInputShape? public let outputShape: TaskOutputShape? - init(id: String?, name: String?, description: String?, inputShape: TaskInputShape?, outputShape: TaskOutputShape?) { + public init(id: String?, name: String?, description: String?, inputShape: TaskInputShape?, outputShape: TaskOutputShape?) { self.id = id self.name = name self.description = description @@ -80,7 +57,7 @@ public struct TaskTypeData: Codable { public struct TaskInputShape: Codable { public let input: Shape? - init(input: Shape?) { + public init(input: Shape?) { self.input = input } } @@ -88,7 +65,7 @@ public struct TaskInputShape: Codable { public struct TaskOutputShape: Codable { public let output: Shape? - init(output: Shape?) { + public init(output: Shape?) { self.output = output } } @@ -98,7 +75,7 @@ public struct Shape: Codable { public let description: String public let type: String - init(name: String, description: String, type: String) { + public init(name: String, description: String, type: String) { self.name = name self.description = description self.type = type From c4ce8525b90dd4ee713211b3f7c9c3b269500904 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 20 Feb 2025 18:45:16 +0100 Subject: [PATCH 15/21] Refactor Signed-off-by: Milen Pivchev --- .../Models/Assistant/v2/TaskList.swift | 45 -------- ...wift => AssistantV2IntegrationTests.swift} | 105 +++++++++--------- 2 files changed, 50 insertions(+), 100 deletions(-) rename Tests/NextcloudKitIntegrationTests/{Test.swift => AssistantV2IntegrationTests.swift} (50%) diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift index 55a4c920..93336d41 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -4,51 +4,6 @@ import SwiftyJSON -//public class NKTextProcessingTaskV2 { -// 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) -// } -//} - public struct TaskList: Codable { public var tasks: [AssistantTask] diff --git a/Tests/NextcloudKitIntegrationTests/Test.swift b/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift similarity index 50% rename from Tests/NextcloudKitIntegrationTests/Test.swift rename to Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift index 9b9c7c89..1d04efb2 100644 --- a/Tests/NextcloudKitIntegrationTests/Test.swift +++ b/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift @@ -9,19 +9,23 @@ import Testing import NextcloudKit struct Test { - @Test func TestGetTypes() async throws { - let urlBase = "https://daily.ltd2.nextcloud.com" - let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" + let urlBase = "" + let account = "" + let user = "" + let password = "" + let groupIdentifier = "group.it.twsweb.Crypto-Cloud" + let userAgent = "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0" + @Test func TestGetTypes() async throws { await withCheckedContinuation { continuation in NextcloudKit.shared.appendSession(account: account, urlBase: urlBase, - user: "milen.pivchev@nextcloud.com", - userId: "milen.pivchev@nextcloud.com", - password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", - userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", + user: user, + userId: user, + password: password, + userAgent: userAgent, nextcloudVersion: 31, - groupIdentifier: "group.it.twsweb.Crypto-Cloud") + groupIdentifier: groupIdentifier) NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in if error == .success, let userProfile { @@ -39,18 +43,15 @@ struct Test { } @Test func TestCreate() async throws { - let urlBase = "https://daily.ltd2.nextcloud.com" - let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" - await withCheckedContinuation { continuation in NextcloudKit.shared.appendSession(account: account, urlBase: urlBase, - user: "milen.pivchev@nextcloud.com", - userId: "milen.pivchev@nextcloud.com", - password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", - userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", + user: user, + userId: user, + password: password, + userAgent: userAgent, nextcloudVersion: 31, - groupIdentifier: "group.it.twsweb.Crypto-Cloud") + groupIdentifier: groupIdentifier) NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in if error == .success, let userProfile { @@ -75,18 +76,15 @@ struct Test { } @Test func TestGetList() async throws { - let urlBase = "https://daily.ltd2.nextcloud.com" - let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" - await withCheckedContinuation { continuation in NextcloudKit.shared.appendSession(account: account, urlBase: urlBase, - user: "milen.pivchev@nextcloud.com", - userId: "milen.pivchev@nextcloud.com", - password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", - userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", + user: user, + userId: user, + password: password, + userAgent: userAgent, nextcloudVersion: 31, - groupIdentifier: "group.it.twsweb.Crypto-Cloud") + groupIdentifier: groupIdentifier) NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in if error == .success, let userProfile { @@ -109,44 +107,41 @@ struct Test { } } -} -@Test func TestDeleteTask() async throws { - let urlBase = "https://daily.ltd2.nextcloud.com" - let account = "milen.pivchev@nextcloud.com https://daily.ltd2.nextcloud.com" - - await withCheckedContinuation { continuation in - NextcloudKit.shared.appendSession(account: account, - urlBase: urlBase, - user: "milen.pivchev@nextcloud.com", - userId: "milen.pivchev@nextcloud.com", - password: "NDHLg-Yi2nL-iisCE-TtAYJ-xiBiB", - userAgent: "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0", - nextcloudVersion: 31, - groupIdentifier: "group.it.twsweb.Crypto-Cloud") - - NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in - if error == .success, let userProfile { - NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) - - NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in - print(error) - print(types) - guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } - NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in - print(task) - - NextcloudKit.shared.textProcessingDeleteTaskV2(taskId: task?.tasks.first?.id ?? 0, account: account) { account, responseData, error in - print(error) - continuation.resume() + @Test func TestDeleteTask() async throws { + await withCheckedContinuation { continuation in + NextcloudKit.shared.appendSession(account: account, + urlBase: urlBase, + user: user, + userId: user, + password: password, + userAgent: userAgent, + nextcloudVersion: 31, + groupIdentifier: groupIdentifier) + + NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in + if error == .success, let userProfile { + NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) + + NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in + print(error) + print(types) + guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } + NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in + print(task) + + NextcloudKit.shared.textProcessingDeleteTaskV2(taskId: task?.tasks.first?.id ?? 0, account: account) { account, responseData, error in + print(error) + continuation.resume() + } + } } - } + } } - } } From b9f25f476cbba79f626c26ae73f6b4851ed842aa Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Fri, 21 Feb 2025 17:38:26 +0100 Subject: [PATCH 16/21] Finish Signed-off-by: Milen Pivchev --- .../NextcloudKit+AssistantV2.swift | 1 - .../AssistantV2IntegrationTests.swift | 274 +++++++++--------- 2 files changed, 137 insertions(+), 138 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift index bb07e082..fde0867f 100644 --- a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -152,7 +152,6 @@ public extension NextcloudKit { options.queue.async { completion(account, 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 { options.queue.async { completion(account, response, .success) } diff --git a/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift b/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift index 1d04efb2..1ab071d6 100644 --- a/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift +++ b/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift @@ -8,140 +8,140 @@ import Testing import NextcloudKit -struct Test { - let urlBase = "" - let account = "" - let user = "" - let password = "" - let groupIdentifier = "group.it.twsweb.Crypto-Cloud" - let userAgent = "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0" - - @Test func TestGetTypes() async throws { - await withCheckedContinuation { continuation in - NextcloudKit.shared.appendSession(account: account, - urlBase: urlBase, - user: user, - userId: user, - password: password, - userAgent: userAgent, - nextcloudVersion: 31, - groupIdentifier: groupIdentifier) - - NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in - if error == .success, let userProfile { - NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) - - NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in - print(error) - print(types) - continuation.resume() - } - } - - } - } - } - - @Test func TestCreate() async throws { - await withCheckedContinuation { continuation in - NextcloudKit.shared.appendSession(account: account, - urlBase: urlBase, - user: user, - userId: user, - password: password, - userAgent: userAgent, - nextcloudVersion: 31, - groupIdentifier: groupIdentifier) - - NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in - if error == .success, let userProfile { - NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) - - NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in - print(error) - print(types) - guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } - NextcloudKit.shared.textProcessingScheduleV2(input: "TestFROM2", taskType: type, account: account) { account, task, responseData, error in - print(error) - print(task) - print(responseData?.response) - continuation.resume() - } - } - - } - - } - } - } - - @Test func TestGetList() async throws { - await withCheckedContinuation { continuation in - NextcloudKit.shared.appendSession(account: account, - urlBase: urlBase, - user: user, - userId: user, - password: password, - userAgent: userAgent, - nextcloudVersion: 31, - groupIdentifier: groupIdentifier) - - NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in - if error == .success, let userProfile { - NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) - - NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in - print(error) - print(types) - guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } - NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in - print(task) - - continuation.resume() - } - - } - } - - } - - } - } - - @Test func TestDeleteTask() async throws { - await withCheckedContinuation { continuation in - NextcloudKit.shared.appendSession(account: account, - urlBase: urlBase, - user: user, - userId: user, - password: password, - userAgent: userAgent, - nextcloudVersion: 31, - groupIdentifier: groupIdentifier) - - NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in - if error == .success, let userProfile { - NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) - - NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in - print(error) - print(types) - guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } - NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in - print(task) - - NextcloudKit.shared.textProcessingDeleteTaskV2(taskId: task?.tasks.first?.id ?? 0, account: account) { account, responseData, error in - print(error) - continuation.resume() - } - - } - - } - } - - } - - } - } -} +//struct AssistantV2IntegrationTests { +// let urlBase = "" +// let account = "" +// let user = "" +// let password = "" +// let groupIdentifier = "group.it.twsweb.Crypto-Cloud" +// let userAgent = "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0" +// +// @Test func TestGetTypes() async throws { +// await withCheckedContinuation { continuation in +// NextcloudKit.shared.appendSession(account: account, +// progress urlBase: urlBase, +// user: user, +// userId: user, +// password: password, +// userAgent: userAgent, +// nextcloudVersion: 31, +// groupIdentifier: groupIdentifier) +// +// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in +// if error == .success, let userProfile { +// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) +// +// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in +// print(error) +// print(types) +// continuation.resume() +// } +// } +// +// } +// } +// } +// +// @Test func TestCreate() async throws { +// await withCheckedContinuation { continuation in +// NextcloudKit.shared.appendSession(account: account, +// urlBase: urlBase, +// user: user, +// userId: user, +// password: password, +// userAgent: userAgent, +// nextcloudVersion: 31, +// groupIdentifier: groupIdentifier) +// +// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in +// if error == .success, let userProfile { +// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) +// +// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in +// print(error) +// print(types) +// guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } +// NextcloudKit.shared.textProcessingScheduleV2(input: "TestFROM2", taskType: type, account: account) { account, task, responseData, error in +// print(error) +// print(task) +// print(responseData?.response) +// continuation.resume() +// } +// } +// +// } +// +// } +// } +// } +// +// @Test func TestGetList() async throws { +// await withCheckedContinuation { continuation in +// NextcloudKit.shared.appendSession(account: account, +// urlBase: urlBase, +// user: user, +// userId: user, +// password: password, +// userAgent: userAgent, +// nextcloudVersion: 31, +// groupIdentifier: groupIdentifier) +// +// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in +// if error == .success, let userProfile { +// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) +// +// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in +// print(error) +// print(types) +// guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } +// NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in +// print(task) +// +// continuation.resume() +// } +// +// } +// } +// +// } +// +// } +// } +// +// @Test func TestDeleteTask() async throws { +// await withCheckedContinuation { continuation in +// NextcloudKit.shared.appendSession(account: account, +// urlBase: urlBase, +// user: user, +// userId: user, +// password: password, +// userAgent: userAgent, +// nextcloudVersion: 31, +// groupIdentifier: groupIdentifier) +// +// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in +// if error == .success, let userProfile { +// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) +// +// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in +// print(error) +// print(types) +// guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } +// NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in +// print(task) +// +// NextcloudKit.shared.textProcessingDeleteTaskV2(taskId: task?.tasks.first?.id ?? 0, account: account) { account, responseData, error in +// print(error) +// continuation.resume() +// } +// +// } +// +// } +// } +// +// } +// +// } +// } +//} From 69440101e4f4bb2a4602d248b2cb3b069038d0b6 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Mon, 24 Feb 2025 15:09:34 +0100 Subject: [PATCH 17/21] Compliance Signed-off-by: Milen Pivchev --- .../AssistantV2IntegrationTests.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift b/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift index 1ab071d6..6d1e7add 100644 --- a/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift +++ b/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift @@ -1,9 +1,6 @@ -// -// Test.swift -// NextcloudKit -// -// Created by Milen Pivchev on 12.02.25. -// +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Milen Pivchev +// SPDX-License-Identifier: GPL-3.0-or-later import Testing import NextcloudKit From 7a5afadfcefb332bed43f124dabff68bc8a647ee Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 6 Mar 2025 14:56:47 +0100 Subject: [PATCH 18/21] PR fixes Signed-off-by: Milen Pivchev --- .../Assistant/v1/NKTextProcessingTask.swift | 4 +- .../v1/NKTextProcessingTaskType.swift | 2 +- .../Models/Assistant/v2/TaskList.swift | 4 +- .../Models/Assistant/v2/TaskTypes.swift | 2 +- .../NextcloudKit/NextcloudKit+Assistant.swift | 10 +- .../NextcloudKit+AssistantV2.swift | 6 +- .../AssistantV2IntegrationTests.swift | 144 ------------------ 7 files changed, 14 insertions(+), 158 deletions(-) delete mode 100644 Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift index ec868e0e..533413f1 100644 --- a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTask.swift @@ -39,12 +39,12 @@ public class NKTextProcessingTask { self.completionExpectedAt = json["completionExpectedAt"].double } - static func factories(data: JSON) -> [NKTextProcessingTask]? { + static func deserialize(multipleObjects data: JSON) -> [NKTextProcessingTask]? { guard let allResults = data.array else { return nil } return allResults.compactMap(NKTextProcessingTask.init) } - static func factory(data: JSON) -> NKTextProcessingTask? { + static func deserialize(singleObject data: JSON) -> NKTextProcessingTask? { NKTextProcessingTask(json: data) } diff --git a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift index 43c761b0..5841b4bf 100644 --- a/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift +++ b/Sources/NextcloudKit/Models/Assistant/v1/NKTextProcessingTaskType.swift @@ -21,7 +21,7 @@ public class NKTextProcessingTaskType { self.description = json["description"].string } - static func factory(data: JSON) -> [NKTextProcessingTaskType]? { + static func deserialize(multipleObjects data: JSON) -> [NKTextProcessingTaskType]? { guard let allResults = data.array else { return nil } return allResults.compactMap(NKTextProcessingTaskType.init) } diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift index 93336d41..b17978a8 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskList.swift @@ -7,7 +7,7 @@ import SwiftyJSON public struct TaskList: Codable { public var tasks: [AssistantTask] - static func factory(from data: JSON) -> TaskList? { + static func deserialize(from data: JSON) -> TaskList? { let tasks = data.arrayValue.map { taskJson in AssistantTask( id: taskJson["id"].int64Value, @@ -58,7 +58,7 @@ public struct AssistantTask: Codable { self.endedAt = endedAt } - static func factory(from data: JSON) -> AssistantTask? { + static func deserialize(from data: JSON) -> AssistantTask? { let task = AssistantTask( id: data["id"].int64Value, type: data["type"].string, diff --git a/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift index afb9a6b7..70b502a7 100644 --- a/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift +++ b/Sources/NextcloudKit/Models/Assistant/v2/TaskTypes.swift @@ -7,7 +7,7 @@ import SwiftyJSON public struct TaskTypes: Codable { public let types: [TaskTypeData] - static func factory(data: JSON) -> TaskTypes? { + static func deserialize(from data: JSON) -> TaskTypes? { var taskTypes: [TaskTypeData] = [] for (key, subJson) in data { diff --git a/Sources/NextcloudKit/NextcloudKit+Assistant.swift b/Sources/NextcloudKit/NextcloudKit+Assistant.swift index bc12283a..2d795052 100644 --- a/Sources/NextcloudKit/NextcloudKit+Assistant.swift +++ b/Sources/NextcloudKit/NextcloudKit+Assistant.swift @@ -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)) } diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift index fde0867f..c2610e36 100644 --- a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -35,7 +35,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["types"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let dict = TaskTypes.factory(data: data) + 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 }) @@ -81,7 +81,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 = AssistantTask.factory(from: data) + 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)) } @@ -118,7 +118,7 @@ public extension NextcloudKit { let data = json["ocs"]["data"]["tasks"] let statusCode = json["ocs"]["meta"]["statuscode"].int ?? NKError.internalError if 200..<300 ~= statusCode { - let result = TaskList.factory(from: data) + 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)) } diff --git a/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift b/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift deleted file mode 100644 index 6d1e7add..00000000 --- a/Tests/NextcloudKitIntegrationTests/AssistantV2IntegrationTests.swift +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-FileCopyrightText: Nextcloud GmbH -// SPDX-FileCopyrightText: 2025 Milen Pivchev -// SPDX-License-Identifier: GPL-3.0-or-later - -import Testing -import NextcloudKit - -//struct AssistantV2IntegrationTests { -// let urlBase = "" -// let account = "" -// let user = "" -// let password = "" -// let groupIdentifier = "group.it.twsweb.Crypto-Cloud" -// let userAgent = "Mozilla/5.0 (iOS) Nextcloud-iOS/6.3.0" -// -// @Test func TestGetTypes() async throws { -// await withCheckedContinuation { continuation in -// NextcloudKit.shared.appendSession(account: account, -// progress urlBase: urlBase, -// user: user, -// userId: user, -// password: password, -// userAgent: userAgent, -// nextcloudVersion: 31, -// groupIdentifier: groupIdentifier) -// -// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in -// if error == .success, let userProfile { -// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) -// -// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in -// print(error) -// print(types) -// continuation.resume() -// } -// } -// -// } -// } -// } -// -// @Test func TestCreate() async throws { -// await withCheckedContinuation { continuation in -// NextcloudKit.shared.appendSession(account: account, -// urlBase: urlBase, -// user: user, -// userId: user, -// password: password, -// userAgent: userAgent, -// nextcloudVersion: 31, -// groupIdentifier: groupIdentifier) -// -// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in -// if error == .success, let userProfile { -// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) -// -// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in -// print(error) -// print(types) -// guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } -// NextcloudKit.shared.textProcessingScheduleV2(input: "TestFROM2", taskType: type, account: account) { account, task, responseData, error in -// print(error) -// print(task) -// print(responseData?.response) -// continuation.resume() -// } -// } -// -// } -// -// } -// } -// } -// -// @Test func TestGetList() async throws { -// await withCheckedContinuation { continuation in -// NextcloudKit.shared.appendSession(account: account, -// urlBase: urlBase, -// user: user, -// userId: user, -// password: password, -// userAgent: userAgent, -// nextcloudVersion: 31, -// groupIdentifier: groupIdentifier) -// -// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in -// if error == .success, let userProfile { -// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) -// -// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in -// print(error) -// print(types) -// guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } -// NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in -// print(task) -// -// continuation.resume() -// } -// -// } -// } -// -// } -// -// } -// } -// -// @Test func TestDeleteTask() async throws { -// await withCheckedContinuation { continuation in -// NextcloudKit.shared.appendSession(account: account, -// urlBase: urlBase, -// user: user, -// userId: user, -// password: password, -// userAgent: userAgent, -// nextcloudVersion: 31, -// groupIdentifier: groupIdentifier) -// -// NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in -// if error == .success, let userProfile { -// NextcloudKit.shared.updateSession(account: account, userId: userProfile.userId) -// -// NextcloudKit.shared.textProcessingGetTypesV2(account: account) { account, types, responseData, error in -// print(error) -// print(types) -// guard let type = types?.first(where: {$0.id == "core:text2text"}) else { return } -// NextcloudKit.shared.textProcessingGetTasksV2(taskType: type.id ?? "", account: account) { account, task, responseData, error in -// print(task) -// -// NextcloudKit.shared.textProcessingDeleteTaskV2(taskId: task?.tasks.first?.id ?? 0, account: account) { account, responseData, error in -// print(error) -// continuation.resume() -// } -// -// } -// -// } -// } -// -// } -// -// } -// } -//} From 652d84a088bec6e1f603287413ca49f1ecbb65ed Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Mon, 10 Mar 2025 17:50:12 +0100 Subject: [PATCH 19/21] Upload fix multisession (#129) * fix Signed-off-by: Marino Faggiana * fix Signed-off-by: Marino Faggiana --------- Signed-off-by: Marino Faggiana Co-authored-by: Marino Faggiana --- Sources/NextcloudKit/NKCommon.swift | 6 ++++++ Sources/NextcloudKit/NKSession.swift | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/NextcloudKit/NKCommon.swift b/Sources/NextcloudKit/NKCommon.swift index 8d491659..3ae16ab2 100644 --- a/Sources/NextcloudKit/NKCommon.swift +++ b/Sources/NextcloudKit/NKCommon.swift @@ -51,8 +51,10 @@ public struct NKCommon: Sendable { public var delegate: NextcloudKitDelegate? public var groupIdentifier: String? + // Foreground public let identifierSessionDownload: String = "com.nextcloud.nextcloudkit.session.download" public let identifierSessionUpload: String = "com.nextcloud.nextcloudkit.session.upload" + // Background public let identifierSessionDownloadBackground: String = "com.nextcloud.session.downloadbackground" public let identifierSessionUploadBackground: String = "com.nextcloud.session.uploadbackground" public let identifierSessionUploadBackgroundWWan: String = "com.nextcloud.session.uploadbackgroundWWan" @@ -474,6 +476,10 @@ public struct NKCommon: Sendable { // MARK: - Common + public func getSessionConfigurationIdentifier(_ identifier: String, account: String) -> String { + return "\(identifier).\(account)" + } + public func getSession(account: String) -> NKSession? { var session: NKSession? nksessions.forEach { result in diff --git a/Sources/NextcloudKit/NKSession.swift b/Sources/NextcloudKit/NKSession.swift index 018ecbd1..1e66394a 100644 --- a/Sources/NextcloudKit/NKSession.swift +++ b/Sources/NextcloudKit/NKSession.swift @@ -74,7 +74,7 @@ public struct NKSession: Sendable { eventMonitors: [NKMonitor(nkCommonInstance: nkCommonInstance)]) /// Session Download Background - let configurationDownloadBackground = URLSessionConfiguration.background(withIdentifier: NKCommon().identifierSessionDownloadBackground) + let configurationDownloadBackground = URLSessionConfiguration.background(withIdentifier: NKCommon().getSessionConfigurationIdentifier(NKCommon().identifierSessionDownloadBackground, account: account)) configurationDownloadBackground.allowsCellularAccess = true if #available(macOS 11, *) { @@ -93,7 +93,7 @@ public struct NKSession: Sendable { sessionDownloadBackground = URLSession(configuration: configurationDownloadBackground, delegate: backgroundSessionDelegate, delegateQueue: OperationQueue.main) /// Session Upload Background - let configurationUploadBackground = URLSessionConfiguration.background(withIdentifier: NKCommon().identifierSessionUploadBackground) + let configurationUploadBackground = URLSessionConfiguration.background(withIdentifier: NKCommon().getSessionConfigurationIdentifier(NKCommon().identifierSessionUploadBackground, account: account)) configurationUploadBackground.allowsCellularAccess = true if #available(macOS 11, *) { @@ -112,7 +112,7 @@ public struct NKSession: Sendable { sessionUploadBackground = URLSession(configuration: configurationUploadBackground, delegate: backgroundSessionDelegate, delegateQueue: OperationQueue.main) /// Session Upload Background WWan - let configurationUploadBackgroundWWan = URLSessionConfiguration.background(withIdentifier: NKCommon().identifierSessionUploadBackgroundWWan) + let configurationUploadBackgroundWWan = URLSessionConfiguration.background(withIdentifier: NKCommon().getSessionConfigurationIdentifier(NKCommon().identifierSessionUploadBackgroundWWan, account: account)) configurationUploadBackgroundWWan.allowsCellularAccess = false if #available(macOS 11, *) { From 3af9676cdb16d8e0bf03c4ce0098038e9b6d8416 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Thu, 6 Mar 2025 17:37:04 +0100 Subject: [PATCH 20/21] Fix: Login poll without cached responses. Signed-off-by: Milen Pivchev --- Sources/NextcloudKit/NextcloudKit.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/NextcloudKit/NextcloudKit.swift b/Sources/NextcloudKit/NextcloudKit.swift index 3c73daf6..8d843023 100644 --- a/Sources/NextcloudKit/NextcloudKit.swift +++ b/Sources/NextcloudKit/NextcloudKit.swift @@ -21,8 +21,12 @@ open class NextcloudKit { private let reachabilityManager = Alamofire.NetworkReachabilityManager() #endif public var nkCommonInstance = NKCommon() + internal lazy var unauthorizedSession: Alamofire.Session = { - return Alamofire.Session(configuration: URLSessionConfiguration.af.default, + let configuration = URLSessionConfiguration.af.default + configuration.requestCachePolicy = .reloadIgnoringLocalCacheData + + return Alamofire.Session(configuration: configuration, delegate: NextcloudKitSessionDelegate(nkCommonInstance: nkCommonInstance), eventMonitors: [NKMonitor(nkCommonInstance: self.nkCommonInstance)]) }() From 02b4f7d4412eb787c7b7daeee7d5996c8e6fa4d3 Mon Sep 17 00:00:00 2001 From: Milen Pivchev Date: Tue, 11 Mar 2025 16:07:46 +0100 Subject: [PATCH 21/21] Linter Signed-off-by: Milen Pivchev --- .../NextcloudKit+AssistantV2.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift index c2610e36..2856644d 100644 --- a/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift +++ b/Sources/NextcloudKit/NextcloudKit+AssistantV2.swift @@ -11,7 +11,7 @@ public extension NextcloudKit { supportedTaskType: String = "Text", options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ types: [TaskTypeData]?, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + 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), @@ -40,7 +40,7 @@ public extension NextcloudKit { 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)) } @@ -91,9 +91,9 @@ public extension NextcloudKit { } func textProcessingGetTasksV2(taskType: String, - account: String, - options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, + 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), @@ -128,10 +128,10 @@ public extension NextcloudKit { } func textProcessingDeleteTaskV2(taskId: Int64, - account: String, - options: NKRequestOptions = NKRequestOptions(), - taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (_ account: String, _ responseData: AFDataResponse?, _ error: NKError) -> Void) { + 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),