diff --git a/BoxSDK.xcodeproj/project.pbxproj b/BoxSDK.xcodeproj/project.pbxproj index e1e55dfeb..50217ae9d 100644 --- a/BoxSDK.xcodeproj/project.pbxproj +++ b/BoxSDK.xcodeproj/project.pbxproj @@ -58,6 +58,13 @@ 05610A9B271099A7009F92CC /* SignRequestPrefillTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05610A9A271099A7009F92CC /* SignRequestPrefillTag.swift */; }; 05610A9D27109BFB009F92CC /* SignRequestSignerInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05610A9C27109BFB009F92CC /* SignRequestSignerInput.swift */; }; 056B628B2860C16F008E9418 /* SharedLinkDataSpecs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 056B628A2860C16F008E9418 /* SharedLinkDataSpecs.swift */; }; + 0571FC6328F7FA07004846E4 /* WebhooksModuleIntegrationSpecs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0571FC6228F7FA07004846E4 /* WebhooksModuleIntegrationSpecs.swift */; }; + 0571FC6728F9491D004846E4 /* BaseIntegrationSpecs+Webhooks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0571FC6628F9491D004846E4 /* BaseIntegrationSpecs+Webhooks.swift */; }; + 0571FC6928F94996004846E4 /* BaseIntegrationSpecs+Users.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0571FC6828F94996004846E4 /* BaseIntegrationSpecs+Users.swift */; }; + 0571FC6B28F949E6004846E4 /* BaseIntegrationSpecs+WebLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0571FC6A28F949E6004846E4 /* BaseIntegrationSpecs+WebLinks.swift */; }; + 0571FC6D28F94A47004846E4 /* BaseIntegrationSpecs+RetentionPolicies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0571FC6C28F94A47004846E4 /* BaseIntegrationSpecs+RetentionPolicies.swift */; }; + 0571FC6F28F94A78004846E4 /* BaseIntegrationSpecs+Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0571FC6E28F94A78004846E4 /* BaseIntegrationSpecs+Files.swift */; }; + 0571FC7128F94ABD004846E4 /* BaseIntegrationSpecs+Folders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0571FC7028F94ABD004846E4 /* BaseIntegrationSpecs+Folders.swift */; }; 0579F6F727577DAD00473A3C /* Nimble.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EFAB1F26F0F01100DF1830 /* Nimble.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0579F6F827577DAD00473A3C /* Quick.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EFAB2026F0F01100DF1830 /* Quick.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0579F70027577FD200473A3C /* FolderModuleIntegrationSpecs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0579F6FF27577FD200473A3C /* FolderModuleIntegrationSpecs.swift */; }; @@ -674,6 +681,13 @@ 056FE12726EF6F6800098F00 /* Quick.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Quick.xcframework; path = Carthage/Build/Quick.xcframework; sourceTree = ""; }; 056FE12826EF6F6800098F00 /* OHHTTPStubs.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = OHHTTPStubs.xcframework; path = Carthage/Build/OHHTTPStubs.xcframework; sourceTree = ""; }; 056FE12926EF6F6800098F00 /* Nimble.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Nimble.xcframework; path = Carthage/Build/Nimble.xcframework; sourceTree = ""; }; + 0571FC6228F7FA07004846E4 /* WebhooksModuleIntegrationSpecs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebhooksModuleIntegrationSpecs.swift; sourceTree = ""; }; + 0571FC6628F9491D004846E4 /* BaseIntegrationSpecs+Webhooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseIntegrationSpecs+Webhooks.swift"; sourceTree = ""; }; + 0571FC6828F94996004846E4 /* BaseIntegrationSpecs+Users.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseIntegrationSpecs+Users.swift"; sourceTree = ""; }; + 0571FC6A28F949E6004846E4 /* BaseIntegrationSpecs+WebLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseIntegrationSpecs+WebLinks.swift"; sourceTree = ""; }; + 0571FC6C28F94A47004846E4 /* BaseIntegrationSpecs+RetentionPolicies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseIntegrationSpecs+RetentionPolicies.swift"; sourceTree = ""; }; + 0571FC6E28F94A78004846E4 /* BaseIntegrationSpecs+Files.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseIntegrationSpecs+Files.swift"; sourceTree = ""; }; + 0571FC7028F94ABD004846E4 /* BaseIntegrationSpecs+Folders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BaseIntegrationSpecs+Folders.swift"; sourceTree = ""; }; 0579F6FF27577FD200473A3C /* FolderModuleIntegrationSpecs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderModuleIntegrationSpecs.swift; sourceTree = ""; }; 057C8CE3276799760092A6EC /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; 059CF240283D0666001809CA /* FolderModuleAsyncIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderModuleAsyncIntegrationTests.swift; sourceTree = ""; }; @@ -1259,6 +1273,20 @@ path = Async; sourceTree = ""; }; + 0571FC6528F948EF004846E4 /* Base */ = { + isa = PBXGroup; + children = ( + 05070D2C2757DBD7000AFE20 /* BaseIntegrationSpecs.swift */, + 0571FC6E28F94A78004846E4 /* BaseIntegrationSpecs+Files.swift */, + 0571FC7028F94ABD004846E4 /* BaseIntegrationSpecs+Folders.swift */, + 0571FC6C28F94A47004846E4 /* BaseIntegrationSpecs+RetentionPolicies.swift */, + 0571FC6828F94996004846E4 /* BaseIntegrationSpecs+Users.swift */, + 0571FC6628F9491D004846E4 /* BaseIntegrationSpecs+Webhooks.swift */, + 0571FC6A28F949E6004846E4 /* BaseIntegrationSpecs+WebLinks.swift */, + ); + path = Base; + sourceTree = ""; + }; 05EA725C2762649B001ECFF3 /* Resources */ = { isa = PBXGroup; children = ( @@ -1275,13 +1303,14 @@ 05EC89D527577AF900FEBAC6 /* IntegrationTests */ = { isa = PBXGroup; children = ( - 05070D2C2757DBD7000AFE20 /* BaseIntegrationSpecs.swift */, 0501990B282423970085003B /* BoxClientIntegrationSpecs.swift */, 05EA725A27621FA7001ECFF3 /* FileModuleIntegrationSpecs.swift */, 0579F6FF27577FD200473A3C /* FolderModuleIntegrationSpecs.swift */, 0BD51EDB28CF257E000DE69E /* RetentionPoliciesModuleIntegrationSpecs.swift */, 052A03F9287C1FC20063513C /* UsersModuleIntegrationSpecs.swift */, + 0571FC6228F7FA07004846E4 /* WebhooksModuleIntegrationSpecs.swift */, 054D536B283CFAB800CC2CBC /* Async */, + 0571FC6528F948EF004846E4 /* Base */, 053637BB276C952D00C18D26 /* Extensions */, 05EA725C2762649B001ECFF3 /* Resources */, 0547B737276C99AA002D9E7A /* Utils */, @@ -2829,24 +2858,31 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0571FC6328F7FA07004846E4 /* WebhooksModuleIntegrationSpecs.swift in Sources */, 0BD51EDC28CF257E000DE69E /* RetentionPoliciesModuleIntegrationSpecs.swift in Sources */, 0547B73E276C99AA002D9E7A /* CryptographyUtil.swift in Sources */, 053637BD276C96E400C18D26 /* IntegrationTestResources.swift in Sources */, 057C8CE4276799760092A6EC /* Date+Extensions.swift in Sources */, 05EA725B27621FA7001ECFF3 /* FileModuleIntegrationSpecs.swift in Sources */, + 0571FC7128F94ABD004846E4 /* BaseIntegrationSpecs+Folders.swift in Sources */, + 0571FC6728F9491D004846E4 /* BaseIntegrationSpecs+Webhooks.swift in Sources */, 05EC1082276A195100252112 /* Collaboration+Extensions.swift in Sources */, 05EC1084276A19A100252112 /* FolderItem+Extensions.swift in Sources */, 0547B73F276C99AA002D9E7A /* NameGenerator.swift in Sources */, 0547B73D276C99AA002D9E7A /* Constants.swift in Sources */, + 0571FC6B28F949E6004846E4 /* BaseIntegrationSpecs+WebLinks.swift in Sources */, 0543A2392757AA2C0005CA23 /* Configuration.swift in Sources */, + 0571FC6928F94996004846E4 /* BaseIntegrationSpecs+Users.swift in Sources */, 059CF241283D0666001809CA /* FolderModuleAsyncIntegrationTests.swift in Sources */, 053EE331283D1CB700B4BF5A /* BaseAsyncIntegrationTests.swift in Sources */, 0579F70027577FD200473A3C /* FolderModuleIntegrationSpecs.swift in Sources */, 052A03FA287C1FC20063513C /* UsersModuleIntegrationSpecs.swift in Sources */, 0547B743276C9A89002D9E7A /* FileUtil.swift in Sources */, 05EC1086276A1CFE00252112 /* Task+Extensions.swift in Sources */, + 0571FC6F28F94A78004846E4 /* BaseIntegrationSpecs+Files.swift in Sources */, 0501990C282423970085003B /* BoxClientIntegrationSpecs.swift in Sources */, 05070D2D2757DBD7000AFE20 /* BaseIntegrationSpecs.swift in Sources */, + 0571FC6D28F94A47004846E4 /* BaseIntegrationSpecs+RetentionPolicies.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/IntegrationTests/Base/BaseIntegrationSpecs+Files.swift b/IntegrationTests/Base/BaseIntegrationSpecs+Files.swift new file mode 100644 index 000000000..652662e65 --- /dev/null +++ b/IntegrationTests/Base/BaseIntegrationSpecs+Files.swift @@ -0,0 +1,67 @@ +// +// BaseIntegrationSpecs+Files.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 14/10/2022. +// Copyright © 2022 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +extension BaseIntegrationSpecs { + + func uploadFile(fileName: String, stringContent: String, toFolder folderId: String, callback: @escaping (File) -> Void) { + uploadFile(fileName: fileName, dataContent: stringContent.data(using: .utf8)!, toFolder: folderId, callback: callback) + } + + func uploadFile(fileName: String, toFolder folderId: String?, callback: @escaping (File) -> Void) { + guard let folderId = folderId else { + fail("folderId should not be nil") + return + } + + guard let dataContent = FileUtil.getFileContent(fileName: fileName) else { + fail("Can not get content of file \(fileName)") + return + } + + uploadFile(fileName: fileName, dataContent: dataContent, toFolder: folderId, callback: callback) + } + + func uploadFile(fileName: String, dataContent: Data, toFolder folderId: String, callback: @escaping (File) -> Void) { + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.files.upload( + data: dataContent, + name: fileName, + parentId: folderId + ) { result in + switch result { + case let .success(file): + callback(file) + case let .failure(error): + fail("Expected upload call to suceeded, but instead got \(error)") + } + + done() + } + } + } + + func deleteFile(_ file: File?) { + guard let file = file else { + return + } + + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.files.delete(fileId: file.id) { result in + if case let .failure(error) = result { + fail("Expected delete call to succeed, but instead got \(error)") + } + + done() + } + } + } +} diff --git a/IntegrationTests/Base/BaseIntegrationSpecs+Folders.swift b/IntegrationTests/Base/BaseIntegrationSpecs+Folders.swift new file mode 100644 index 000000000..53c4e727a --- /dev/null +++ b/IntegrationTests/Base/BaseIntegrationSpecs+Folders.swift @@ -0,0 +1,45 @@ +// +// BaseIntegrationSpecs+Folders.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 14/10/2022. +// Copyright © 2022 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +extension BaseIntegrationSpecs { + + func createFolder(name: String, parentId: String = "0", callback: @escaping (Folder) -> Void) { + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.folders.create(name: name, parentId: parentId) { result in + switch result { + case let .success(folder): + callback(folder) + case let .failure(error): + fail("Expected create call to suceeded, but instead got \(error)") + } + + done() + } + } + } + + func deleteFolder(_ folder: Folder?, recursive: Bool = false) { + guard let folder = folder else { + return + } + + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.folders.delete(folderId: folder.id, recursive: recursive) { result in + if case let .failure(error) = result { + fail("Expected delete call to succeed, but instead got \(error)") + } + + done() + } + } + } +} diff --git a/IntegrationTests/Base/BaseIntegrationSpecs+RetentionPolicies.swift b/IntegrationTests/Base/BaseIntegrationSpecs+RetentionPolicies.swift new file mode 100644 index 000000000..231574855 --- /dev/null +++ b/IntegrationTests/Base/BaseIntegrationSpecs+RetentionPolicies.swift @@ -0,0 +1,84 @@ +// +// BaseIntegrationSpecs+RetentionPolicies.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 14/10/2022. +// Copyright © 2022 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +extension BaseIntegrationSpecs { + + func createRetention( + name: String, + type: RetentionPolicyType = .finite, + length: Int? = 1, + dispositionAction: DispositionAction = .permanentlyDelete, + callback: @escaping (RetentionPolicy) -> Void + ) { + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.retentionPolicy.create( + name: name, + type: type, + length: length, + dispositionAction: dispositionAction + ) { result in + switch result { + case let .success(retention): + callback(retention) + case let .failure(error): + fail("Expected create retention call to suceeded, but instead got \(error)") + } + + done() + } + } + } + + func assignRetention( + _ retention: RetentionPolicy?, + assignedContentId: String?, + assignContentType: RetentionPolicyAssignmentItemType = .folder, + callback: @escaping (RetentionPolicyAssignment) -> Void + ) { + guard let retention = retention, let assignedContentId = assignedContentId else { + return + } + + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.retentionPolicy.assign( + policyId: retention.id, + assignedContentId: assignedContentId, + assignContentType: assignContentType + ) { result in + switch result { + case let .success(retentionAssignment): + callback(retentionAssignment) + case let .failure(error): + fail("Expected assign retention policy call to succeed, but instead got \(error)") + } + + done() + } + } + } + + func retireRetention(_ retention: RetentionPolicy?) { + guard let retention = retention else { + return + } + + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.retentionPolicy.update(policyId: retention.id, status: .retired) { result in + if case let .failure(error) = result { + fail("Expected update retention call to succeed, but instead got \(error)") + } + + done() + } + } + } +} diff --git a/IntegrationTests/Base/BaseIntegrationSpecs+Users.swift b/IntegrationTests/Base/BaseIntegrationSpecs+Users.swift new file mode 100644 index 000000000..cba626013 --- /dev/null +++ b/IntegrationTests/Base/BaseIntegrationSpecs+Users.swift @@ -0,0 +1,45 @@ +// +// BaseIntegrationSpecs+Users.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 14/10/2022. +// Copyright © 2022 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +extension BaseIntegrationSpecs { + + func createUser(name: String, callback: @escaping (User) -> Void) { + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.users.createAppUser(name: name) { result in + switch result { + case let .success(user): + callback(user) + case let .failure(error): + fail("Expected create call to suceeded, but instead got \(error)") + } + + done() + } + } + } + + func deleteUser(_ user: User?) { + guard let user = user else { + return + } + + waitUntil(timeout: .seconds(Constants.Timeout.large)) { done in + self.client.users.delete(userId: user.id, force: true) { result in + if case let .failure(error) = result { + fail("Expected delete call to succeed, but instead got \(error)") + } + + done() + } + } + } +} diff --git a/IntegrationTests/Base/BaseIntegrationSpecs+WebLinks.swift b/IntegrationTests/Base/BaseIntegrationSpecs+WebLinks.swift new file mode 100644 index 000000000..62a9a4763 --- /dev/null +++ b/IntegrationTests/Base/BaseIntegrationSpecs+WebLinks.swift @@ -0,0 +1,30 @@ +// +// BaseIntegrationSpecs+Weblinks.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 14/10/2022. +// Copyright © 2022 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +extension BaseIntegrationSpecs { + + func deleteWebLink(_ webLink: WebLink?) { + guard let webLink = webLink else { + return + } + + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.webLinks.delete(webLinkId: webLink.id) { result in + if case let .failure(error) = result { + fail("Expected delete call to succeed, but instead got \(error)") + } + + done() + } + } + } +} diff --git a/IntegrationTests/Base/BaseIntegrationSpecs+Webhooks.swift b/IntegrationTests/Base/BaseIntegrationSpecs+Webhooks.swift new file mode 100644 index 000000000..e882ceb71 --- /dev/null +++ b/IntegrationTests/Base/BaseIntegrationSpecs+Webhooks.swift @@ -0,0 +1,35 @@ +// +// BaseIntegrationSpecs+Webhooks.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 14/10/2022. +// Copyright © 2022 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +extension BaseIntegrationSpecs { + + func createWebhook( + targetType: String, + targetId: String, + triggers: [Webhook.EventTriggers], + address: String, + callback: @escaping (Webhook) -> Void + ) { + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.webhooks.create(targetType: targetType, targetId: targetId, triggers: triggers, address: address) { result in + switch result { + case let .success(webhook): + callback(webhook) + case let .failure(error): + fail("Expected create call to suceeded, but instead got \(error)") + } + + done() + } + } + } +} diff --git a/IntegrationTests/Base/BaseIntegrationSpecs.swift b/IntegrationTests/Base/BaseIntegrationSpecs.swift new file mode 100644 index 000000000..f2dc2ba18 --- /dev/null +++ b/IntegrationTests/Base/BaseIntegrationSpecs.swift @@ -0,0 +1,60 @@ +// +// BaseIntegrationSpecs.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 01/12/2021. +// Copyright © 2021 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +class BaseIntegrationSpecs: QuickSpec { + let sdk = BoxSDK(clientId: Configuration.shared.ccg.clientId, clientSecret: Configuration.shared.ccg.clientSecret) + var client: BoxClient! + + // MARK: BoxClient helper methods + + func initializeClient() { + if let enterpriseId = Configuration.shared.ccg.enterpriseId, !enterpriseId.isEmpty { + initializeCCGForAccountService(enterpriseId: enterpriseId) + } + else if let userId = Configuration.shared.ccg.userId, !userId.isEmpty { + initializeCCGForUser(userId: userId) + } + else { + fail("Can not create the CCG Client instance: either \"enterpriseId\" or \"userId\" is required") + } + } + + func initializeCCGForAccountService(enterpriseId: String) { + waitUntil(timeout: .seconds(Constants.Timeout.default)) { [weak self] done in + self?.sdk.getCCGClientForAccountService(enterpriseId: enterpriseId) { result in + switch result { + case let .success(resultClient): + self?.client = resultClient + case let .failure(error): + fail("Expected getCCGClientForAccountService call to suceeded, but instead got \(error)") + } + + done() + } + } + } + + func initializeCCGForUser(userId: String) { + waitUntil(timeout: .seconds(Constants.Timeout.default)) { [weak self] done in + self?.sdk.getCCGClientForUser(userId: userId) { result in + switch result { + case let .success(resultClient): + self?.client = resultClient + case let .failure(error): + fail("Expected getCCGClientForUser call to suceeded, but instead got \(error)") + } + + done() + } + } + } +} diff --git a/IntegrationTests/BaseIntegrationSpecs.swift b/IntegrationTests/BaseIntegrationSpecs.swift deleted file mode 100644 index 41de22d78..000000000 --- a/IntegrationTests/BaseIntegrationSpecs.swift +++ /dev/null @@ -1,275 +0,0 @@ -// -// BaseIntegrationSpecs.swift -// BoxSDKIntegrationTests-iOS -// -// Created by Artur Jankowski on 01/12/2021. -// Copyright © 2021 box. All rights reserved. -// - -@testable import BoxSDK -import Nimble -import Quick - -class BaseIntegrationSpecs: QuickSpec { - let sdk = BoxSDK(clientId: Configuration.shared.ccg.clientId, clientSecret: Configuration.shared.ccg.clientSecret) - var client: BoxClient! - - // MARK: BoxClient helper methods - - func initializeClient() { - if let enterpriseId = Configuration.shared.ccg.enterpriseId, !enterpriseId.isEmpty { - initializeCCGForAccountService(enterpriseId: enterpriseId) - } - else if let userId = Configuration.shared.ccg.userId, !userId.isEmpty { - initializeCCGForUser(userId: userId) - } - else { - fail("Can not create the CCG Client instance: either \"enterpriseId\" or \"userId\" is required") - } - } - - func initializeCCGForAccountService(enterpriseId: String) { - waitUntil(timeout: .seconds(Constants.Timeout.default)) { [weak self] done in - self?.sdk.getCCGClientForAccountService(enterpriseId: enterpriseId) { result in - switch result { - case let .success(resultClient): - self?.client = resultClient - case let .failure(error): - fail("Expected getCCGClientForAccountService call to suceeded, but instead got \(error)") - } - - done() - } - } - } - - func initializeCCGForUser(userId: String) { - waitUntil(timeout: .seconds(Constants.Timeout.default)) { [weak self] done in - self?.sdk.getCCGClientForUser(userId: userId) { result in - switch result { - case let .success(resultClient): - self?.client = resultClient - case let .failure(error): - fail("Expected getCCGClientForUser call to suceeded, but instead got \(error)") - } - - done() - } - } - } - - // MARK: Folder helper methods - - func createFolder(name: String, parentId: String = "0", callback: @escaping (Folder) -> Void) { - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.folders.create(name: name, parentId: parentId) { result in - switch result { - case let .success(folder): - callback(folder) - case let .failure(error): - fail("Expected create call to suceeded, but instead got \(error)") - } - - done() - } - } - } - - func deleteFolder(_ folder: Folder?, recursive: Bool = false) { - guard let folder = folder else { - return - } - - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.folders.delete(folderId: folder.id, recursive: recursive) { result in - if case let .failure(error) = result { - fail("Expected delete call to succeed, but instead got \(error)") - } - - done() - } - } - } - - // MARK: File helper methods - - func uploadFile(fileName: String, stringContent: String, toFolder folderId: String, callback: @escaping (File) -> Void) { - uploadFile(fileName: fileName, dataContent: stringContent.data(using: .utf8)!, toFolder: folderId, callback: callback) - } - - func uploadFile(fileName: String, toFolder folderId: String?, callback: @escaping (File) -> Void) { - guard let folderId = folderId else { - fail("folderId should not be nil") - return - } - - guard let dataContent = FileUtil.getFileContent(fileName: fileName) else { - fail("Can not get content of file \(fileName)") - return - } - - uploadFile(fileName: fileName, dataContent: dataContent, toFolder: folderId, callback: callback) - } - - func uploadFile(fileName: String, dataContent: Data, toFolder folderId: String, callback: @escaping (File) -> Void) { - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.files.upload( - data: dataContent, - name: fileName, - parentId: folderId - ) { result in - switch result { - case let .success(file): - callback(file) - case let .failure(error): - fail("Expected upload call to suceeded, but instead got \(error)") - } - - done() - } - } - } - - func deleteFile(_ file: File?) { - guard let file = file else { - return - } - - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.files.delete(fileId: file.id) { result in - if case let .failure(error) = result { - fail("Expected delete call to succeed, but instead got \(error)") - } - - done() - } - } - } - - // MARK: Retention Policy helper methods - - func createRetention( - name: String, - type: RetentionPolicyType = .finite, - length: Int? = 1, - dispositionAction: DispositionAction = .permanentlyDelete, - callback: @escaping (RetentionPolicy) -> Void - ) { - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.retentionPolicy.create( - name: name, - type: type, - length: length, - dispositionAction: dispositionAction - ) { result in - switch result { - case let .success(retention): - callback(retention) - case let .failure(error): - fail("Expected create retention call to suceeded, but instead got \(error)") - } - - done() - } - } - } - - func assignRetention( - _ retention: RetentionPolicy?, - assignedContentId: String?, - assignContentType: RetentionPolicyAssignmentItemType = .folder, - callback: @escaping (RetentionPolicyAssignment) -> Void - ) { - guard let retention = retention, let assignedContentId = assignedContentId else { - return - } - - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.retentionPolicy.assign( - policyId: retention.id, - assignedContentId: assignedContentId, - assignContentType: assignContentType - ) { result in - switch result { - case let .success(retentionAssignment): - callback(retentionAssignment) - case let .failure(error): - fail("Expected assign retention policy call to succeed, but instead got \(error)") - } - - done() - } - } - } - - func retireRetention(_ retention: RetentionPolicy?) { - guard let retention = retention else { - return - } - - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.retentionPolicy.update(policyId: retention.id, status: .retired) { result in - if case let .failure(error) = result { - fail("Expected update retention call to succeed, but instead got \(error)") - } - - done() - } - } - } - - // MARK: WebLink helper methods - - func deleteWebLink(_ webLink: WebLink?) { - guard let webLink = webLink else { - return - } - - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.webLinks.delete(webLinkId: webLink.id) { result in - if case let .failure(error) = result { - fail("Expected delete call to succeed, but instead got \(error)") - } - - done() - } - } - } - - // MARK: Users helper methods - - func createUser(name: String, callback: @escaping (User) -> Void) { - waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in - self.client.users.createAppUser(name: name) { result in - switch result { - case let .success(user): - callback(user) - case let .failure(error): - fail("Expected create call to suceeded, but instead got \(error)") - } - - done() - } - } - } - - func deleteUser(_ user: User?) { - guard let user = user else { - return - } - - waitUntil(timeout: .seconds(Constants.Timeout.large)) { done in - self.client.users.delete(userId: user.id, force: true) { result in - if case let .failure(error) = result { - fail("Expected delete call to succeed, but instead got \(error)") - } - - done() - } - } - } - - func randomizeName(name: String) -> String { - return "\(name)_\(UUID().uuidString)" - } -} diff --git a/IntegrationTests/FileModuleIntegrationSpecs.swift b/IntegrationTests/FileModuleIntegrationSpecs.swift index f25e93f06..240db4920 100644 --- a/IntegrationTests/FileModuleIntegrationSpecs.swift +++ b/IntegrationTests/FileModuleIntegrationSpecs.swift @@ -1125,7 +1125,7 @@ class FileModuleIntegrationSpecs: BaseIntegrationSpecs { expect(downloadedContent.count).notTo(equal(0)) expect(status.totalFileCount).to(equal(2)) - expect(status.state).to(equal("succeeded")) + expect(["succeeded", "in_progress"]).to(contain(status.state)) case let .failure(error): fail("Expected downloadZip call to suceeded, but it failed \(error)") diff --git a/IntegrationTests/RetentionPoliciesModuleIntegrationSpecs.swift b/IntegrationTests/RetentionPoliciesModuleIntegrationSpecs.swift index 1f58fc86d..441c33c5c 100644 --- a/IntegrationTests/RetentionPoliciesModuleIntegrationSpecs.swift +++ b/IntegrationTests/RetentionPoliciesModuleIntegrationSpecs.swift @@ -11,41 +11,52 @@ import Nimble import Quick class RetentionPoliciesModuleIntegrationSpecs: BaseIntegrationSpecs { - var folder: Folder! - var retentionPolicy: RetentionPolicy! + var rootFolder: Folder! override func spec() { beforeSuite { self.initializeClient() - self.createRetention(name: self.randomizeName(name: "iOS One Day")) { [weak self] retention in self?.retentionPolicy = retention } - self.createFolder(name: NameGenerator.getUniqueFolderName(for: "RetentionPoliciesModule")) { [weak self] createdFolder in self?.folder = createdFolder } + self.createFolder(name: NameGenerator.getUniqueFolderName(for: "RetentionPoliciesModule")) { [weak self] createdFolder in self?.rootFolder = createdFolder } } afterSuite { - self.retireRetention(self.retentionPolicy) - self.deleteFolder(self.folder, recursive: true) + self.deleteFolder(self.rootFolder, recursive: true) } describe("Retention Policy Assignment") { context("live cycle") { + var retentionPolicy: RetentionPolicy? + + beforeEach { + self.createRetention(name: NameGenerator.getUniqueName(for: "Retention")) { createdRetention in retentionPolicy = createdRetention } + } + + afterEach { + self.retireRetention(retentionPolicy) + } it("assign then get and unassign retention policy") { + guard let retentionPolicy = retentionPolicy else { + fail("An error occurred during setup initial data") + return + } + var retentionPolicyAssignment: RetentionPolicyAssignment? // Assign waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in self.client.retentionPolicy.assign( - policyId: self.retentionPolicy.id, - assignedContentId: self.folder.id, + policyId: retentionPolicy.id, + assignedContentId: self.rootFolder.id, assignContentType: RetentionPolicyAssignmentItemType.folder ) { result in switch result { case let .success(assignment): retentionPolicyAssignment = assignment - expect(retentionPolicyAssignment?.retentionPolicy?.id).to(equal(self.retentionPolicy.id)) + expect(retentionPolicyAssignment?.retentionPolicy?.id).to(equal(retentionPolicy.id)) expect(retentionPolicyAssignment?.assignedTo?.type).to(equal(RetentionPolicyAssignmentItemType.folder)) - expect(retentionPolicyAssignment?.assignedTo?.id).to(equal(self.folder.id)) + expect(retentionPolicyAssignment?.assignedTo?.id).to(equal(self.rootFolder.id)) case let .failure(error): fail("Expected assign call to succeed, but instead got \(error)") } @@ -61,9 +72,9 @@ class RetentionPoliciesModuleIntegrationSpecs: BaseIntegrationSpecs { self.client.retentionPolicy.getAssignment(assignmentId: retentionPolicyAssignment.id) { result in switch result { case let .success(assignment): - expect(assignment.retentionPolicy?.id).to(equal(self.retentionPolicy.id)) + expect(assignment.retentionPolicy?.id).to(equal(retentionPolicy.id)) expect(assignment.assignedTo?.type).to(equal(RetentionPolicyAssignmentItemType.folder)) - expect(assignment.assignedTo?.id).to(equal(self.folder.id)) + expect(assignment.assignedTo?.id).to(equal(self.rootFolder.id)) case let .failure(error): fail("Expected get call to succeed, but instead got \(error)") } diff --git a/IntegrationTests/WebhooksModuleIntegrationSpecs.swift b/IntegrationTests/WebhooksModuleIntegrationSpecs.swift new file mode 100644 index 000000000..b6c8ddd23 --- /dev/null +++ b/IntegrationTests/WebhooksModuleIntegrationSpecs.swift @@ -0,0 +1,235 @@ +// +// WebhooksModuleIntegrationSpecs.swift +// BoxSDKIntegrationTests-iOS +// +// Created by Artur Jankowski on 13/10/2022. +// Copyright © 2022 box. All rights reserved. +// + +@testable import BoxSDK +import Nimble +import Quick + +class WebhooksModuleIntegrationSpecs: BaseIntegrationSpecs { + var rootFolder: Folder! + + override func spec() { + + beforeSuite { + self.initializeClient() + self.createFolder(name: NameGenerator.getUniqueFolderName(for: "WebhooksModule")) { [weak self] createdFolder in self?.rootFolder = createdFolder } + } + + afterSuite { + self.deleteFolder(self.rootFolder, recursive: true) + } + + describe("Webhooks Module") { + + context("live cycle") { + var file: File? + + beforeEach { + self.uploadFile(fileName: IntegrationTestResources.smallImage.fileName, toFolder: self.rootFolder.id) { uploadedFile in file = uploadedFile } + } + + afterEach { + self.deleteFile(file) + } + + it("should correctly create update get and delete a webhook") { + guard let file = file else { + fail("An error occurred during setup initial data") + return + } + + var webhook: Webhook? + + // create + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.webhooks.create( + targetType: "file", + targetId: file.id, + triggers: [.fileDownloaded, .commentCreated, .sharedLinkCreated], + address: "https://example.com/webhooks" + ) { result in + switch result { + case let .success(webhookItem): + webhook = webhookItem + expect(webhookItem.address).to(equal(URL(string: "https://example.com/webhooks"))) + expect(webhookItem.triggers).to(contain([.fileDownloaded, .commentCreated, .sharedLinkCreated])) + expect(webhookItem.target?.rawData["id"] as? String).to(equal(file.id)) + + guard case let .file(targetFile) = webhookItem.target else { + fail("Expected file object as target") + return + } + expect(targetFile.id).to(equal(file.id)) + case let .failure(error): + fail("Expected create call to succeed, but instead got \(error)") + } + + done() + } + } + + guard let webhook = webhook else { return } + + // update + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.webhooks.update( + webhookId: webhook.id, + triggers: [.taskAssignmentUpdated, .metadataInstanceDeleted, .fileCopied], + address: "https://example.com/webhooks2" + + ) { result in + switch result { + case let .success(webhookItem): + expect(webhookItem.address).to(equal(URL(string: "https://example.com/webhooks2"))) + expect(webhookItem.triggers).to(contain([.taskAssignmentUpdated, .metadataInstanceDeleted, .fileCopied])) + expect(webhookItem.target?.rawData["id"] as? String).to(equal(file.id)) + case let .failure(error): + fail("Expected update call to suceeded, but instead got \(error)") + } + + done() + } + } + + // get + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.webhooks.get(webhookId: webhook.id) { result in + switch result { + case let .success(webhookItem): + expect(webhookItem.address).to(equal(URL(string: "https://example.com/webhooks2"))) + expect(webhookItem.triggers).to(contain([.taskAssignmentUpdated, .metadataInstanceDeleted, .fileCopied])) + expect(webhookItem.target?.rawData["id"] as? String).to(equal(file.id)) + + guard case let .file(targetFile) = webhookItem.target else { + fail("Expected file object as target") + return + } + expect(targetFile.id).to(equal(file.id)) + case let .failure(error): + fail("Expected get call to suceeded, but instead got \(error)") + } + + done() + } + } + + // delete + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.webhooks.delete(webhookId: webhook.id) { result in + if case let .failure(error) = result { + fail("Expected delete call to succeed, but instead got \(error)") + } + + done() + } + } + } + } + + context("list") { + var folder: Folder? + var file: File? + + beforeEach { + self.createFolder(name: self.rootFolder.id) { createdFolder in folder = createdFolder } + guard let folder = folder else { return } + + self.uploadFile(fileName: IntegrationTestResources.smallImage.fileName, toFolder: self.rootFolder.id) { uploadedFile in file = uploadedFile } + guard let file = file else { return } + + self.createWebhook( + targetType: "folder", + targetId: folder.id, + triggers: [.folderDownloaded, .signRequestExpired], + address: "https://example.com/webhooks" + ) { _ in } + + self.createWebhook( + targetType: "file", + targetId: file.id, + triggers: [.fileRenamed], + address: "https://example.com/webhooks" + ) { _ in } + } + + afterEach { + self.deleteFile(file) + self.deleteFolder(folder) + } + + it("should correctly list all webhook items") { + guard let folder = folder, let file = file else { + fail("An error occurred during setup initial data") + return + } + + let iterator = self.client.webhooks.list() + + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + iterator.next { result in + switch result { + case let .success(page): + expect(page.entries.count).to(equal(2)) + expect(page.entries.map { $0.target?.rawData["id"] as! String }).to(contain([folder.id, file.id])) + case let .failure(error): + fail("Expected list call to succeed, but instead got \(error)") + } + } + + done() + } + } + } + + context("EventTriggers") { + var file: File? + + beforeEach { + self.uploadFile(fileName: IntegrationTestResources.smallImage.fileName, toFolder: self.rootFolder.id) { uploadedFile in file = uploadedFile } + } + + afterEach { + self.deleteFile(file) + } + + it("should correctly create webhook with SIGN_REQUEST triggers") { + guard let file = file else { + fail("An error occurred during setup initial data") + return + } + + waitUntil(timeout: .seconds(Constants.Timeout.default)) { done in + self.client.webhooks.create( + targetType: "file", + targetId: file.id, + triggers: [.signRequestExpired, .signRequestDeclined, .signRequestCompleted], + address: "https://example.com/webhooks" + ) { result in + switch result { + case let .success(webhookItem): + expect(webhookItem.address).to(equal(URL(string: "https://example.com/webhooks"))) + expect(webhookItem.triggers).to(contain([.signRequestExpired, .signRequestDeclined, .signRequestCompleted])) + expect(webhookItem.target?.rawData["id"] as? String).to(equal(file.id)) + + guard case let .file(targetFile) = webhookItem.target else { + fail("Expected file object as target") + return + } + expect(targetFile.id).to(equal(file.id)) + case let .failure(error): + fail("Expected create call to succeed, but instead got \(error)") + } + + done() + } + } + } + } + } + } +} diff --git a/Sources/Responses/Webhook.swift b/Sources/Responses/Webhook.swift index de8efccad..e80fdbb1a 100644 --- a/Sources/Responses/Webhook.swift +++ b/Sources/Responses/Webhook.swift @@ -85,6 +85,12 @@ public class Webhook: BoxModel { case sharedLinkCreated /// A shared link was updated case sharedLinkUpdated + /// A sign request is completed + case signRequestCompleted + /// A sign request is declined + case signRequestDeclined + /// A sign request is expired + case signRequestExpired /// Custom value for enum values not yet implemented in the SDK case customValue(String) @@ -162,6 +168,12 @@ public class Webhook: BoxModel { self = .sharedLinkCreated case "SHARED_LINK.UPDATED": self = .sharedLinkUpdated + case "SIGN_REQUEST.COMPLETED": + self = .signRequestCompleted + case "SIGN_REQUEST.DECLINED": + self = .signRequestDeclined + case "SIGN_REQUEST.EXPIRED": + self = .signRequestExpired default: self = .customValue(value) } @@ -241,6 +253,12 @@ public class Webhook: BoxModel { return "SHARED_LINK.CREATED" case .sharedLinkUpdated: return "SHARED_LINK.UPDATED" + case .signRequestCompleted: + return "SIGN_REQUEST.COMPLETED" + case .signRequestDeclined: + return "SIGN_REQUEST.DECLINED" + case .signRequestExpired: + return "SIGN_REQUEST.EXPIRED" case let .customValue(value): return value } diff --git a/Tests/Responses/WebhookSpecs.swift b/Tests/Responses/WebhookSpecs.swift index 538ebd59a..addce14f5 100644 --- a/Tests/Responses/WebhookSpecs.swift +++ b/Tests/Responses/WebhookSpecs.swift @@ -90,6 +90,9 @@ class WebhookSpecs: QuickSpec { expect(Webhook.EventTriggers.sharedLinkDeleted).to(equal(Webhook.EventTriggers(Webhook.EventTriggers.sharedLinkDeleted.description))) expect(Webhook.EventTriggers.sharedLinkCreated).to(equal(Webhook.EventTriggers(Webhook.EventTriggers.sharedLinkCreated.description))) expect(Webhook.EventTriggers.sharedLinkUpdated).to(equal(Webhook.EventTriggers(Webhook.EventTriggers.sharedLinkUpdated.description))) + expect(Webhook.EventTriggers.signRequestCompleted).to(equal(Webhook.EventTriggers(Webhook.EventTriggers.signRequestCompleted.description))) + expect(Webhook.EventTriggers.signRequestDeclined).to(equal(Webhook.EventTriggers(Webhook.EventTriggers.signRequestDeclined.description))) + expect(Webhook.EventTriggers.signRequestExpired).to(equal(Webhook.EventTriggers(Webhook.EventTriggers.signRequestExpired.description))) expect(Webhook.EventTriggers.customValue("custom value")).to(equal(Webhook.EventTriggers("custom value"))) } }