Merge pull request #4 from rock-n-code/feature/api-service
Feature: API service library
This commit is contained in:
commit
e56d3b76d8
@ -7,6 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
|
026D9825293B6374009FE888 /* Libraries in Frameworks */ = {isa = PBXBuildFile; productRef = 026D9824293B6374009FE888 /* Libraries */; };
|
||||||
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */; };
|
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */; };
|
||||||
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64F029363DBF005A4AF3 /* ContentView.swift */; };
|
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64F029363DBF005A4AF3 /* ContentView.swift */; };
|
||||||
02AE64F329363DC1005A4AF3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02AE64F229363DC1005A4AF3 /* Assets.xcassets */; };
|
02AE64F329363DC1005A4AF3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02AE64F229363DC1005A4AF3 /* Assets.xcassets */; };
|
||||||
@ -14,7 +15,6 @@
|
|||||||
02AE650029363DC1005A4AF3 /* BeRealTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64FF29363DC1005A4AF3 /* BeRealTests.swift */; };
|
02AE650029363DC1005A4AF3 /* BeRealTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64FF29363DC1005A4AF3 /* BeRealTests.swift */; };
|
||||||
02AE650A29363DC1005A4AF3 /* BeRealUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650929363DC1005A4AF3 /* BeRealUITests.swift */; };
|
02AE650A29363DC1005A4AF3 /* BeRealUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650929363DC1005A4AF3 /* BeRealUITests.swift */; };
|
||||||
02AE650C29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */; };
|
02AE650C29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */; };
|
||||||
02FFFD7B29395DD200306533 /* String+Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FFFD7A29395DD200306533 /* String+Constants.swift */; };
|
|
||||||
4694AAA0293A7C8800D54903 /* Modules in Frameworks */ = {isa = PBXBuildFile; productRef = 4694AA9F293A7C8800D54903 /* Modules */; };
|
4694AAA0293A7C8800D54903 /* Modules in Frameworks */ = {isa = PBXBuildFile; productRef = 4694AA9F293A7C8800D54903 /* Modules */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
@ -36,6 +36,7 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
026D9823293B6365009FE888 /* Libraries */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Libraries; sourceTree = "<group>"; };
|
||||||
02784F03293A8331005F839D /* Modules */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Modules; sourceTree = "<group>"; };
|
02784F03293A8331005F839D /* Modules */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Modules; sourceTree = "<group>"; };
|
||||||
02AE64EB29363DBF005A4AF3 /* BeReal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeReal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
02AE64EB29363DBF005A4AF3 /* BeReal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeReal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealApp.swift; sourceTree = "<group>"; };
|
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealApp.swift; sourceTree = "<group>"; };
|
||||||
@ -47,7 +48,6 @@
|
|||||||
02AE650529363DC1005A4AF3 /* BeRealUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BeRealUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
02AE650529363DC1005A4AF3 /* BeRealUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BeRealUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
02AE650929363DC1005A4AF3 /* BeRealUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITests.swift; sourceTree = "<group>"; };
|
02AE650929363DC1005A4AF3 /* BeRealUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITests.swift; sourceTree = "<group>"; };
|
||||||
02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITestsLaunchTests.swift; sourceTree = "<group>"; };
|
||||||
02FFFD7A29395DD200306533 /* String+Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Constants.swift"; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -55,6 +55,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
026D9825293B6374009FE888 /* Libraries in Frameworks */,
|
||||||
4694AAA0293A7C8800D54903 /* Modules in Frameworks */,
|
4694AAA0293A7C8800D54903 /* Modules in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -79,6 +80,7 @@
|
|||||||
02AE64E229363DBF005A4AF3 = {
|
02AE64E229363DBF005A4AF3 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
026D9823293B6365009FE888 /* Libraries */,
|
||||||
02784F03293A8331005F839D /* Modules */,
|
02784F03293A8331005F839D /* Modules */,
|
||||||
02AE64ED29363DBF005A4AF3 /* BeReal */,
|
02AE64ED29363DBF005A4AF3 /* BeReal */,
|
||||||
02AE64FE29363DC1005A4AF3 /* BeRealTests */,
|
02AE64FE29363DC1005A4AF3 /* BeRealTests */,
|
||||||
@ -101,8 +103,6 @@
|
|||||||
02AE64ED29363DBF005A4AF3 /* BeReal */ = {
|
02AE64ED29363DBF005A4AF3 /* BeReal */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
02CE555F293B44C900730DC9 /* Profile */,
|
|
||||||
02FFFD7929395DBF00306533 /* Extensions */,
|
|
||||||
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */,
|
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */,
|
||||||
02AE64F029363DBF005A4AF3 /* ContentView.swift */,
|
02AE64F029363DBF005A4AF3 /* ContentView.swift */,
|
||||||
02AE64F229363DC1005A4AF3 /* Assets.xcassets */,
|
02AE64F229363DC1005A4AF3 /* Assets.xcassets */,
|
||||||
@ -136,21 +136,6 @@
|
|||||||
path = BeRealUITests;
|
path = BeRealUITests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
02CE555F293B44C900730DC9 /* Profile */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
path = Profile;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
02FFFD7929395DBF00306533 /* Extensions */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
02FFFD7A29395DD200306533 /* String+Constants.swift */,
|
|
||||||
);
|
|
||||||
path = Extensions;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
4694AA9E293A7C8800D54903 /* Frameworks */ = {
|
4694AA9E293A7C8800D54903 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -176,6 +161,7 @@
|
|||||||
name = BeReal;
|
name = BeReal;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
4694AA9F293A7C8800D54903 /* Modules */,
|
4694AA9F293A7C8800D54903 /* Modules */,
|
||||||
|
026D9824293B6374009FE888 /* Libraries */,
|
||||||
);
|
);
|
||||||
productName = BeReal;
|
productName = BeReal;
|
||||||
productReference = 02AE64EB29363DBF005A4AF3 /* BeReal.app */;
|
productReference = 02AE64EB29363DBF005A4AF3 /* BeReal.app */;
|
||||||
@ -294,7 +280,6 @@
|
|||||||
files = (
|
files = (
|
||||||
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */,
|
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */,
|
||||||
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */,
|
02AE64EF29363DBF005A4AF3 /* BeRealApp.swift in Sources */,
|
||||||
02FFFD7B29395DD200306533 /* String+Constants.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -645,6 +630,10 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
026D9824293B6374009FE888 /* Libraries */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = Libraries;
|
||||||
|
};
|
||||||
4694AA9F293A7C8800D54903 /* Modules */ = {
|
4694AA9F293A7C8800D54903 /* Modules */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = Modules;
|
productName = Modules;
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
//
|
|
||||||
// String+Constants.swift
|
|
||||||
// BeReal
|
|
||||||
//
|
|
||||||
// Created by Javier Cicchelli on 01/12/2022.
|
|
||||||
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
static let empty = ""
|
|
||||||
}
|
|
9
Libraries/.gitignore
vendored
Normal file
9
Libraries/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.DS_Store
|
||||||
|
/.build
|
||||||
|
/Packages
|
||||||
|
/*.xcodeproj
|
||||||
|
xcuserdata/
|
||||||
|
DerivedData/
|
||||||
|
.swiftpm/config/registries.json
|
||||||
|
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
|
||||||
|
.netrc
|
21
Libraries/Package.swift
Normal file
21
Libraries/Package.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// swift-tools-version: 5.7
|
||||||
|
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "Libraries",
|
||||||
|
platforms: [.iOS(.v15)],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "Libraries",
|
||||||
|
targets: ["APIService"]),
|
||||||
|
],
|
||||||
|
dependencies: [],
|
||||||
|
targets: [
|
||||||
|
.target(name: "APIService"),
|
||||||
|
.testTarget(
|
||||||
|
name: "APIServiceTests",
|
||||||
|
dependencies: ["APIService"]
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
112
Libraries/Sources/APIService/Actors/APIService.swift
Normal file
112
Libraries/Sources/APIService/Actors/APIService.swift
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// APIService.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public actor APIService {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let client: Client
|
||||||
|
|
||||||
|
// MARK: Initialisers
|
||||||
|
|
||||||
|
public init(configuration: URLSessionConfiguration = .default) {
|
||||||
|
self.client = APIClient(configuration: configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Service
|
||||||
|
|
||||||
|
extension APIService: Service {
|
||||||
|
public func getUser(
|
||||||
|
credentials: Credentials
|
||||||
|
) async throws -> Me {
|
||||||
|
try await client.request(
|
||||||
|
endpoint: GetMeEndpoint(
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password
|
||||||
|
),
|
||||||
|
model: Me.self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getItems(
|
||||||
|
id: String,
|
||||||
|
credentials: Credentials
|
||||||
|
) async throws -> [Item] {
|
||||||
|
try await client.request(
|
||||||
|
endpoint: GetItemsEndpoint(
|
||||||
|
itemId: id,
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password
|
||||||
|
),
|
||||||
|
model: [Item].self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getData(
|
||||||
|
id: String,
|
||||||
|
credentials: Credentials
|
||||||
|
) async throws -> Data {
|
||||||
|
try await client.request(
|
||||||
|
endpoint: GetDataEndpoint(
|
||||||
|
itemId: id,
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func createFolder(
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
credentials: Credentials
|
||||||
|
) async throws -> Item {
|
||||||
|
try await client.request(
|
||||||
|
endpoint: CreateFolderEndpoint(
|
||||||
|
itemId: id,
|
||||||
|
folderName: name,
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password
|
||||||
|
),
|
||||||
|
model: Item.self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func uploadFile(
|
||||||
|
id: String,
|
||||||
|
file: File,
|
||||||
|
credentials: Credentials
|
||||||
|
) async throws -> Item {
|
||||||
|
try await client.request(
|
||||||
|
endpoint: UploadFileEndpoint(
|
||||||
|
itemId: id,
|
||||||
|
fileName: file.name,
|
||||||
|
fileData: file.data,
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password
|
||||||
|
),
|
||||||
|
model: Item.self
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func deleteItem(
|
||||||
|
id: String,
|
||||||
|
credentials: Credentials
|
||||||
|
) async throws {
|
||||||
|
try await client.request(
|
||||||
|
endpoint: DeleteItemEndpoint(
|
||||||
|
itemId: id,
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
65
Libraries/Sources/APIService/Classes/MockURLProtocol.swift
Normal file
65
Libraries/Sources/APIService/Classes/MockURLProtocol.swift
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// MockURLProtocol.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class MockURLProtocol: URLProtocol {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
static var mockData: [URL: MockURLResponse] = [:]
|
||||||
|
|
||||||
|
// MARK: Functions
|
||||||
|
|
||||||
|
override class func canInit(with task: URLSessionTask) -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func canInit(with request: URLRequest) -> Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
|
||||||
|
request
|
||||||
|
}
|
||||||
|
|
||||||
|
override func startLoading() {
|
||||||
|
guard
|
||||||
|
let url = request.url,
|
||||||
|
let response = Self.mockData[url]
|
||||||
|
else {
|
||||||
|
client?.urlProtocolDidFinishLoading(self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let data = response.data {
|
||||||
|
client?.urlProtocol(self, didLoad: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let httpResponse = HTTPURLResponse(
|
||||||
|
url: url,
|
||||||
|
statusCode: response.status.rawValue,
|
||||||
|
httpVersion: nil,
|
||||||
|
headerFields: response.headers
|
||||||
|
) {
|
||||||
|
client?.urlProtocol(self, didReceive: httpResponse, cacheStoragePolicy: .allowedInMemoryOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
client?.urlProtocolDidFinishLoading(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func stopLoading() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Structs
|
||||||
|
|
||||||
|
struct MockURLResponse {
|
||||||
|
let status: ResponseStatus
|
||||||
|
let headers: [String: String]
|
||||||
|
let data: Data?
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// CreateFolderEndpoint.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct CreateFolderEndpoint: Endpoint {
|
||||||
|
let path: String
|
||||||
|
let method: RequestMethod
|
||||||
|
let credentials: BasicCredentials
|
||||||
|
let headers: [String : String]
|
||||||
|
let body: Data?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension CreateFolderEndpoint {
|
||||||
|
init(
|
||||||
|
itemId: String,
|
||||||
|
folderName: String,
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) {
|
||||||
|
self.path = .init(format: .Formats.itemsWithId, itemId)
|
||||||
|
self.method = .post
|
||||||
|
self.credentials = .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
self.headers = [.Header.Keys.contentType: .Header.Values.contentTypeJSON]
|
||||||
|
self.body = {
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
let folder = Folder(name: folderName)
|
||||||
|
|
||||||
|
do {
|
||||||
|
return try encoder.encode(folder)
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Models
|
||||||
|
|
||||||
|
private extension CreateFolderEndpoint {
|
||||||
|
struct Folder: Encodable {
|
||||||
|
let name: String
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// DeleteItemEndpoint.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct DeleteItemEndpoint: Endpoint {
|
||||||
|
let path: String
|
||||||
|
let method: RequestMethod
|
||||||
|
let credentials: BasicCredentials
|
||||||
|
let headers: [String : String]
|
||||||
|
let body: Data?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension DeleteItemEndpoint {
|
||||||
|
init(
|
||||||
|
itemId: String,
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) {
|
||||||
|
self.path = .init(format: .Formats.itemsWithId, itemId)
|
||||||
|
self.method = .delete
|
||||||
|
self.credentials = .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
self.headers = [:]
|
||||||
|
self.body = nil
|
||||||
|
}
|
||||||
|
}
|
42
Libraries/Sources/APIService/Endpoints/GetDataEndpoint.swift
Normal file
42
Libraries/Sources/APIService/Endpoints/GetDataEndpoint.swift
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// GetDataEndpoint.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct GetDataEndpoint: Endpoint {
|
||||||
|
let path: String
|
||||||
|
let method: RequestMethod
|
||||||
|
let credentials: BasicCredentials
|
||||||
|
let headers: [String : String]
|
||||||
|
let body: Data?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension GetDataEndpoint {
|
||||||
|
init(
|
||||||
|
itemId: String,
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) {
|
||||||
|
self.path = .init(format: .Formats.itemsDataWithId, itemId)
|
||||||
|
self.method = .get
|
||||||
|
self.credentials = .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
self.headers = [:]
|
||||||
|
self.body = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String+Formats
|
||||||
|
|
||||||
|
private extension String.Formats {
|
||||||
|
static let itemsDataWithId = "/items/%@/data"
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// GetItemsEndpoint.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct GetItemsEndpoint: Endpoint {
|
||||||
|
let path: String
|
||||||
|
let method: RequestMethod
|
||||||
|
let credentials: BasicCredentials
|
||||||
|
let headers: [String : String]
|
||||||
|
let body: Data?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension GetItemsEndpoint {
|
||||||
|
init(
|
||||||
|
itemId: String,
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) {
|
||||||
|
self.path = .init(format: .Formats.itemsWithId, itemId)
|
||||||
|
self.method = .get
|
||||||
|
self.credentials = .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
self.headers = [.Header.Keys.contentType: .Header.Values.contentTypeJSON]
|
||||||
|
self.body = nil
|
||||||
|
}
|
||||||
|
}
|
35
Libraries/Sources/APIService/Endpoints/GetMeEndpoint.swift
Normal file
35
Libraries/Sources/APIService/Endpoints/GetMeEndpoint.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// GetMeEndpoint.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 03/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct GetMeEndpoint: Endpoint {
|
||||||
|
let path: String
|
||||||
|
let method: RequestMethod
|
||||||
|
let credentials: BasicCredentials
|
||||||
|
let headers: [String : String]
|
||||||
|
let body: Data?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension GetMeEndpoint {
|
||||||
|
init(
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) {
|
||||||
|
self.path = "/me"
|
||||||
|
self.method = .get
|
||||||
|
self.credentials = .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
self.headers = [.Header.Keys.contentType: .Header.Values.contentTypeJSON]
|
||||||
|
self.body = nil
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// UploadFileEndpoint.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct UploadFileEndpoint: Endpoint {
|
||||||
|
let path: String
|
||||||
|
let method: RequestMethod
|
||||||
|
let credentials: BasicCredentials
|
||||||
|
let headers: [String : String]
|
||||||
|
let body: Data?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension UploadFileEndpoint {
|
||||||
|
init(
|
||||||
|
itemId: String,
|
||||||
|
fileName: String,
|
||||||
|
fileData: Data,
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) {
|
||||||
|
self.path = .init(format: .Formats.itemsWithId, itemId)
|
||||||
|
self.method = .post
|
||||||
|
self.credentials = .init(
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
self.headers = [
|
||||||
|
.Header.Keys.contentType: .Header.Values.contentTypeOctet,
|
||||||
|
.Header.Keys.contentDisposition: .init(format: .Formats.attachment, fileName)
|
||||||
|
]
|
||||||
|
self.body = fileData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String+Formats
|
||||||
|
|
||||||
|
private extension String.Formats {
|
||||||
|
static let attachment = "attachment;filename*=utf-8''%@"
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// RequestMethod.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 03/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
enum RequestMethod: String {
|
||||||
|
case delete = "DELETE"
|
||||||
|
case get = "GET"
|
||||||
|
case patch = "PATCH"
|
||||||
|
case post = "POST"
|
||||||
|
case put = "PUT"
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// ResponseStatus.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
enum ResponseStatus: Int {
|
||||||
|
case ok = 200
|
||||||
|
case created = 201
|
||||||
|
case noContent = 204
|
||||||
|
case badRequest = 400
|
||||||
|
case unauthorized = 401
|
||||||
|
case forbidden = 403
|
||||||
|
case notFound = 404
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// DateFormatter+Formatter.swift
|
||||||
|
// APIServices
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension DateFormatter {
|
||||||
|
static let iso8601 = {
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
|
||||||
|
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
|
||||||
|
|
||||||
|
return dateFormatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
static let isoZulu = {
|
||||||
|
let dateFormatter = DateFormatter()
|
||||||
|
|
||||||
|
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
|
||||||
|
|
||||||
|
return dateFormatter
|
||||||
|
}()
|
||||||
|
}
|
13
Libraries/Sources/APIService/Extensions/Int+Ports.swift
Normal file
13
Libraries/Sources/APIService/Extensions/Int+Ports.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// Int+Ports.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension Int {
|
||||||
|
enum Ports {
|
||||||
|
static let `default` = 8080
|
||||||
|
}
|
||||||
|
}
|
13
Libraries/Sources/APIService/Extensions/String+Formats.swift
Normal file
13
Libraries/Sources/APIService/Extensions/String+Formats.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// String+Formats.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
enum Formats {
|
||||||
|
static let itemsWithId = "/items/%@"
|
||||||
|
}
|
||||||
|
}
|
22
Libraries/Sources/APIService/Extensions/String+Headers.swift
Normal file
22
Libraries/Sources/APIService/Extensions/String+Headers.swift
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// String+Headers.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 03/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
enum Header {
|
||||||
|
enum Keys {
|
||||||
|
static let authorization = "Authorization"
|
||||||
|
static let contentType = "Content-Type"
|
||||||
|
static let contentDisposition = "Content-Disposition"
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Values {
|
||||||
|
static let contentTypeJSON = "application/json"
|
||||||
|
static let contentTypeOctet = "application/octet-stream"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
Libraries/Sources/APIService/Extensions/String+Hosts.swift
Normal file
13
Libraries/Sources/APIService/Extensions/String+Hosts.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// String+Hosts.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
enum Hosts {
|
||||||
|
static let `default` = "163.172.147.216"
|
||||||
|
}
|
||||||
|
}
|
13
Libraries/Sources/APIService/Extensions/String+Schemes.swift
Normal file
13
Libraries/Sources/APIService/Extensions/String+Schemes.swift
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// String+Schemes.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
enum Schemes {
|
||||||
|
static let http = "http"
|
||||||
|
}
|
||||||
|
}
|
37
Libraries/Sources/APIService/Models/Item.swift
Normal file
37
Libraries/Sources/APIService/Models/Item.swift
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Item.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 03/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct Item {
|
||||||
|
let idParent: String?
|
||||||
|
let id: String
|
||||||
|
let name: String
|
||||||
|
let isDirectory: Bool
|
||||||
|
let lastModifiedAt: Date
|
||||||
|
let size: Int?
|
||||||
|
let contentType: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Decodable
|
||||||
|
|
||||||
|
extension Item: Decodable {
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case idParent = "parentId"
|
||||||
|
case name
|
||||||
|
case isDirectory = "isDir"
|
||||||
|
case lastModifiedAt = "modificationDate"
|
||||||
|
case size
|
||||||
|
case contentType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Equatable
|
||||||
|
|
||||||
|
extension Item: Equatable {}
|
21
Libraries/Sources/APIService/Models/Me.swift
Normal file
21
Libraries/Sources/APIService/Models/Me.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Me.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 03/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
public struct Me {
|
||||||
|
let firstName: String
|
||||||
|
let lastName: String
|
||||||
|
let rootItem: Item
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Decodable
|
||||||
|
|
||||||
|
extension Me: Decodable {}
|
||||||
|
|
||||||
|
// MARK: - Equatable
|
||||||
|
|
||||||
|
extension Me: Equatable {}
|
14
Libraries/Sources/APIService/Protocols/Client.swift
Normal file
14
Libraries/Sources/APIService/Protocols/Client.swift
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// Client.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol Client {
|
||||||
|
func request<Model: Decodable>(endpoint: some Endpoint, model: Model.Type) async throws -> Model
|
||||||
|
@discardableResult func request(endpoint: some Endpoint) async throws -> Data
|
||||||
|
}
|
47
Libraries/Sources/APIService/Protocols/Endpoint.swift
Normal file
47
Libraries/Sources/APIService/Protocols/Endpoint.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// Endpoint.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 03/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol Endpoint {
|
||||||
|
var scheme: String { get }
|
||||||
|
var host: String { get }
|
||||||
|
var port: Int { get }
|
||||||
|
var path: String { get }
|
||||||
|
var method: RequestMethod { get }
|
||||||
|
var credentials: BasicCredentials { get }
|
||||||
|
var headers: [String: String] { get }
|
||||||
|
var body: Data? { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Defaults
|
||||||
|
|
||||||
|
extension Endpoint {
|
||||||
|
var scheme: String { .Schemes.http }
|
||||||
|
var host: String { .Hosts.default }
|
||||||
|
var port: Int { .Ports.default }
|
||||||
|
var authorizationHeader: [String: String] {
|
||||||
|
let makeAuthHeader = MakeAuthorizationHeaderUseCase()
|
||||||
|
|
||||||
|
do {
|
||||||
|
return try makeAuthHeader(
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Structs
|
||||||
|
|
||||||
|
struct BasicCredentials {
|
||||||
|
let username: String
|
||||||
|
let password: String
|
||||||
|
}
|
30
Libraries/Sources/APIService/Protocols/Service.swift
Normal file
30
Libraries/Sources/APIService/Protocols/Service.swift
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//
|
||||||
|
// Service.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public protocol Service {
|
||||||
|
func getUser(credentials: Credentials) async throws -> Me
|
||||||
|
func getItems(id: String, credentials: Credentials) async throws -> [Item]
|
||||||
|
func getData(id: String, credentials: Credentials) async throws -> Data
|
||||||
|
func createFolder(id: String, name: String, credentials: Credentials) async throws -> Item
|
||||||
|
func uploadFile(id: String, file: File, credentials: Credentials) async throws -> Item
|
||||||
|
func deleteItem(id: String, credentials: Credentials) async throws
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Structs
|
||||||
|
|
||||||
|
public struct Credentials {
|
||||||
|
let username: String
|
||||||
|
let password: String
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct File {
|
||||||
|
let name: String
|
||||||
|
let data: Data
|
||||||
|
}
|
120
Libraries/Sources/APIService/Structs/APIClient.swift
Normal file
120
Libraries/Sources/APIService/Structs/APIClient.swift
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
//
|
||||||
|
// APIClient.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct APIClient {
|
||||||
|
private let session: URLSession
|
||||||
|
private let decoder: JSONDecoder
|
||||||
|
private let makeURLRequest: MakeURLRequestUseCase
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Initialisers
|
||||||
|
|
||||||
|
extension APIClient {
|
||||||
|
init(configuration: URLSessionConfiguration = .default) {
|
||||||
|
self.session = .init(configuration: configuration)
|
||||||
|
self.decoder = .init()
|
||||||
|
self.makeURLRequest = .init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: - Client
|
||||||
|
|
||||||
|
extension APIClient: Client {
|
||||||
|
func request<Model>(
|
||||||
|
endpoint: some Endpoint,
|
||||||
|
model: Model.Type
|
||||||
|
) async throws -> Model where Model : Decodable {
|
||||||
|
let urlRequest = try makeURLRequest(endpoint: endpoint)
|
||||||
|
let (data, response) = try await session.data(for: urlRequest)
|
||||||
|
|
||||||
|
guard
|
||||||
|
let httpResponse = response as? HTTPURLResponse,
|
||||||
|
let responseStatus = ResponseStatus(rawValue: httpResponse.statusCode)
|
||||||
|
else {
|
||||||
|
throw APIClientError.responseNotReturned
|
||||||
|
}
|
||||||
|
|
||||||
|
switch responseStatus {
|
||||||
|
case .ok,
|
||||||
|
.created:
|
||||||
|
return try await decode(data, as: model)
|
||||||
|
case .noContent:
|
||||||
|
throw APIClientError.notSupported
|
||||||
|
case .badRequest:
|
||||||
|
throw APIClientError.itemNameInvalidOrDefined
|
||||||
|
case .unauthorized,
|
||||||
|
.forbidden:
|
||||||
|
throw APIClientError.authenticationFailed
|
||||||
|
case .notFound:
|
||||||
|
throw APIClientError.itemDoesNotExist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func request(
|
||||||
|
endpoint: some Endpoint
|
||||||
|
) async throws -> Data {
|
||||||
|
let urlRequest = try makeURLRequest(endpoint: endpoint)
|
||||||
|
let (data, response) = try await session.data(for: urlRequest)
|
||||||
|
|
||||||
|
guard
|
||||||
|
let httpResponse = response as? HTTPURLResponse,
|
||||||
|
let responseStatus = ResponseStatus(rawValue: httpResponse.statusCode)
|
||||||
|
else {
|
||||||
|
throw APIClientError.responseNotReturned
|
||||||
|
}
|
||||||
|
|
||||||
|
switch responseStatus {
|
||||||
|
case .ok,
|
||||||
|
.noContent:
|
||||||
|
return data
|
||||||
|
case .created:
|
||||||
|
throw APIClientError.notSupported
|
||||||
|
case .badRequest:
|
||||||
|
throw APIClientError.itemIsNotFile
|
||||||
|
case .unauthorized,
|
||||||
|
.forbidden:
|
||||||
|
throw APIClientError.authenticationFailed
|
||||||
|
case .notFound:
|
||||||
|
throw APIClientError.itemDoesNotExist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private extension APIClient {
|
||||||
|
func decode<Model: Decodable>(
|
||||||
|
_ data: Data,
|
||||||
|
as model: Model.Type
|
||||||
|
) async throws -> Model {
|
||||||
|
do {
|
||||||
|
decoder.dateDecodingStrategy = .formatted(.isoZulu)
|
||||||
|
|
||||||
|
return try decoder.decode(model, from: data)
|
||||||
|
} catch {
|
||||||
|
decoder.dateDecodingStrategy = .iso8601
|
||||||
|
|
||||||
|
return try decoder.decode(model, from: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Errors
|
||||||
|
|
||||||
|
public enum APIClientError: Error {
|
||||||
|
case responseNotReturned
|
||||||
|
case authenticationFailed
|
||||||
|
case itemIsNotFile
|
||||||
|
case itemNameInvalidOrDefined
|
||||||
|
case itemDoesNotExist
|
||||||
|
case notSupported
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// MakeAuthorizationHeaderUseCase.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct MakeAuthorizationHeaderUseCase {
|
||||||
|
func callAsFunction(
|
||||||
|
username: String,
|
||||||
|
password: String
|
||||||
|
) throws -> [String: String] {
|
||||||
|
guard !username.isEmpty else { throw MakeAuthorizationHeaderError.usernameIsEmpty }
|
||||||
|
guard !password.isEmpty else { throw MakeAuthorizationHeaderError.passwordIsEmpty }
|
||||||
|
|
||||||
|
let loginString = String(format: .Formats.usernameAndPassword, username, password)
|
||||||
|
|
||||||
|
guard let loginData = loginString.data(using: .utf8) else {
|
||||||
|
throw MakeAuthorizationHeaderError.loginDataNotCreated
|
||||||
|
}
|
||||||
|
|
||||||
|
let loginBase64 = loginData.base64EncodedString()
|
||||||
|
|
||||||
|
return [.Header.Keys.authorization: String(format: .Formats.authorizationValue, loginBase64)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Errors
|
||||||
|
|
||||||
|
enum MakeAuthorizationHeaderError: Error {
|
||||||
|
case usernameIsEmpty
|
||||||
|
case passwordIsEmpty
|
||||||
|
case loginDataNotCreated
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - String+Formats
|
||||||
|
|
||||||
|
private extension String.Formats {
|
||||||
|
static let usernameAndPassword = "%@:%@"
|
||||||
|
static let authorizationValue = "Basic %@"
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// MakeURLRequestUseCase.swift
|
||||||
|
// APIService
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct MakeURLRequestUseCase {
|
||||||
|
func callAsFunction(endpoint: some Endpoint) throws -> URLRequest {
|
||||||
|
var urlComponents = URLComponents()
|
||||||
|
|
||||||
|
urlComponents.scheme = endpoint.scheme
|
||||||
|
urlComponents.host = endpoint.host
|
||||||
|
urlComponents.port = endpoint.port
|
||||||
|
urlComponents.path = endpoint.path
|
||||||
|
|
||||||
|
guard let url = urlComponents.url else { throw MakeURLRequestError.urlNotCreated }
|
||||||
|
|
||||||
|
var urlRequest = URLRequest(url: url)
|
||||||
|
|
||||||
|
urlRequest.httpMethod = endpoint.method.rawValue
|
||||||
|
urlRequest.httpBody = endpoint.body
|
||||||
|
urlRequest.allHTTPHeaderFields = endpoint.authorizationHeader.merging(endpoint.headers) { first, _ in first }
|
||||||
|
|
||||||
|
return urlRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Errors
|
||||||
|
|
||||||
|
enum MakeURLRequestError: Error {
|
||||||
|
case urlNotCreated
|
||||||
|
}
|
@ -0,0 +1,228 @@
|
|||||||
|
//
|
||||||
|
// APIService+CreateFolderTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class APIServiceCreateFolderTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let dateFormatter = DateFormatter.isoZulu
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
private let url = URL(string: "http://163.172.147.216:8080/items/")!
|
||||||
|
|
||||||
|
private var service: APIService!
|
||||||
|
private var data: Data!
|
||||||
|
private var result: Item!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
service = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
service = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someFolderName_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
data = "{\"id\":\"058c53609cac8d8388b96792cbc42bea31c73def\",\"parentId\":\"\(itemId)\",\"name\":\"Some new folder name\",\"isDir\":true,\"modificationDate\":\"2022-12-04T20:54:12.513214855Z\"}".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .created,
|
||||||
|
headers: [.Header.Keys.contentType: .Header.Values.contentTypeJSON],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try await service.createFolder(
|
||||||
|
id: itemId,
|
||||||
|
name: "Some new folder name",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, Item(
|
||||||
|
idParent: itemId,
|
||||||
|
id: "058c53609cac8d8388b96792cbc42bea31c73def",
|
||||||
|
name: "Some new folder name",
|
||||||
|
isDirectory: true,
|
||||||
|
lastModifiedAt: dateFormatter.date(from: "2022-12-04T20:54:12.513214855Z")!,
|
||||||
|
size: nil,
|
||||||
|
contentType: nil
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someFolderName_andWrongCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.createFolder(
|
||||||
|
id: itemId,
|
||||||
|
name: "Some new folder name",
|
||||||
|
credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withNonExistingParentFolderId_someFolderName_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.createFolder(
|
||||||
|
id: itemId,
|
||||||
|
name: "Some new folder name",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyParentFolderId_someFolderName_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent("")] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.createFolder(
|
||||||
|
id: "",
|
||||||
|
name: "Some new folder name",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someInvalidFolderName_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.createFolder(
|
||||||
|
id: itemId,
|
||||||
|
name: "./Some:new:folder:name\\",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someExistingFolderName_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.createFolder(
|
||||||
|
id: itemId,
|
||||||
|
name: "Some new folder name",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someEmptyFolderName_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.createFolder(
|
||||||
|
id: itemId,
|
||||||
|
name: "",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
//
|
||||||
|
// APIService+DeleteItemTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class APIServiceDeleteItemTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
private let url = URL(string: "http://163.172.147.216:8080/items/")!
|
||||||
|
|
||||||
|
private var service: APIService!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
service = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
service = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withExistingId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .noContent,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
try await service.deleteItem(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingId_andWrongCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await service.deleteItem(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withNonExistingId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await service.deleteItem(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent("")] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await service.deleteItem(
|
||||||
|
id: "",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
//
|
||||||
|
// APIService+GetDataTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class APIServiceGetDataTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
private let url = URL(string: "http://163.172.147.216:8080/items/")!
|
||||||
|
|
||||||
|
private var service: APIService!
|
||||||
|
private var data: Data!
|
||||||
|
private var result: Data!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
service = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
service = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withExistingFileId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
data = "This is just a simple text for testing purposes.".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId + "/data")] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [.Header.Keys.contentType: .Header.Values.contentTypeOctet],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try await service.getData(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingFileId_andWrongCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId + "/data")] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getData(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withNonExistingFileId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId + "/data")] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getData(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyFileId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent("/data")] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getData(
|
||||||
|
id: "",
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemIsNotFile {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeFolderId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId + "/data")] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getData(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemIsNotFile {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
//
|
||||||
|
// APIService+GetItemsTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class APIServiceGetItemsTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let dateFormatter = DateFormatter.isoZulu
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
private let url = URL(string: "http://163.172.147.216:8080/items/")!
|
||||||
|
|
||||||
|
private var service: APIService!
|
||||||
|
private var data: Data!
|
||||||
|
private var result: [Item]!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
service = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
service = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withExistingId_andRightCredentials_whenDataArrayPopulated() async throws {
|
||||||
|
// GIVEN
|
||||||
|
data = "[{\"id\":\"a077432ceb69b4f2dcbd4932d3ec63c3a4f14784\",\"parentId\":\"4b8e41fd4a6a89712f15bbf102421b9338cfab11\",\"name\":\"Tokyo\",\"isDir\":true,\"modificationDate\":\"2022-11-25T17:33:57.095027128Z\"},{\"id\":\"f5d351f7e532cae7c7be28488564b567ffeb425a\",\"parentId\":\"4b8e41fd4a6a89712f15bbf102421b9338cfab11\",\"name\":\"Meme.jpg\",\"isDir\":false,\"size\":43144,\"contentType\":\"image/jpeg\",\"modificationDate\":\"2022-12-01T15:24:12.29816675Z\"}]\n".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [.Header.Keys.contentType: .Header.Values.contentTypeJSON],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try await service.getItems(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, [
|
||||||
|
.init(
|
||||||
|
idParent: "4b8e41fd4a6a89712f15bbf102421b9338cfab11",
|
||||||
|
id: "a077432ceb69b4f2dcbd4932d3ec63c3a4f14784",
|
||||||
|
name: "Tokyo",
|
||||||
|
isDirectory: true,
|
||||||
|
lastModifiedAt: dateFormatter.date(from: "2022-11-25T17:33:57.095027128Z")!,
|
||||||
|
size: nil,
|
||||||
|
contentType: nil
|
||||||
|
),
|
||||||
|
.init(
|
||||||
|
idParent: "4b8e41fd4a6a89712f15bbf102421b9338cfab11",
|
||||||
|
id: "f5d351f7e532cae7c7be28488564b567ffeb425a",
|
||||||
|
name: "Meme.jpg",
|
||||||
|
isDirectory: false,
|
||||||
|
lastModifiedAt: dateFormatter.date(from: "2022-12-01T15:24:12.29816675Z")!,
|
||||||
|
size: 43144,
|
||||||
|
contentType: "image/jpeg"
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingId_andRightCredentials_whenDataArrayEmpty() async throws {
|
||||||
|
// GIVEN
|
||||||
|
data = "[]".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [.Header.Keys.contentType: .Header.Values.contentTypeJSON],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try await service.getItems(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingId_andWrongCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getItems(
|
||||||
|
id: itemId,
|
||||||
|
credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withNonExistingId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent("xxx")] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getItems(
|
||||||
|
id: "xxx",
|
||||||
|
credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyId_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent("")] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getItems(
|
||||||
|
id: "",
|
||||||
|
credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,120 @@
|
|||||||
|
//
|
||||||
|
// APIService+GetUserTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class APIServiceGetUserTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let dateFormatter = DateFormatter.iso8601
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let url = URL(string: "http://163.172.147.216:8080/me")!
|
||||||
|
|
||||||
|
private var service: APIService!
|
||||||
|
private var data: Data!
|
||||||
|
private var result: Me!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
service = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
service = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
data = "{\"firstName\":\"Noel\",\"lastName\":\"Flantier\",\"rootItem\":{\"id\":\"4b8e41fd4a6a89712f15bbf102421b9338cfab11\",\"parentId\":\"\",\"name\":\"dossierTest\",\"isDir\":true,\"modificationDate\":\"2021-11-29T10:57:13Z\"}}\n".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [.Header.Keys.contentType: .Header.Values.contentTypeJSON],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try await service.getUser(credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
))
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, Me(
|
||||||
|
firstName: "Noel",
|
||||||
|
lastName: "Flantier",
|
||||||
|
rootItem: .init(
|
||||||
|
idParent: "",
|
||||||
|
id: "4b8e41fd4a6a89712f15bbf102421b9338cfab11",
|
||||||
|
name: "dossierTest",
|
||||||
|
isDirectory: true,
|
||||||
|
lastModifiedAt: dateFormatter.date(from: "2021-11-29T10:57:13Z")!,
|
||||||
|
size: nil,
|
||||||
|
contentType: nil
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withWrongCredentials() async throws {
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getUser(credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
))
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withMalformedResponseData() async throws {
|
||||||
|
// GIVEN
|
||||||
|
data = "{\"firstName\":\"Noel\",\"rootItem\":{\"id\":\"4b8e41fd4a6a89712f15bbf102421b9338cfab11\"\"name\":\"dossierTest\",\"isDir\":true}}\n".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [.Header.Keys.contentType: .Header.Values.contentTypeJSON],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.getUser(credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
))
|
||||||
|
} catch is DecodingError {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,247 @@
|
|||||||
|
//
|
||||||
|
// APIService+UploadFileTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class APIServiceUploadFileTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let dateFormatter = DateFormatter.isoZulu
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
private let url = URL(string: "http://163.172.147.216:8080/items/")!
|
||||||
|
|
||||||
|
private var service: APIService!
|
||||||
|
private var data: Data!
|
||||||
|
private var result: Item!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
service = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
service = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someFileNameAndData_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
data = "{\"id\":\"eb34443b0f1cd1b0a53dc889aa7ccb5a63edb2f8\",\"parentId\":\"\(itemId)\",\"name\":\"some-text-file.txt\",\"isDir\":false,\"size\":43,\"contentType\":\"text/plain; charset=utf-8\",\"modificationDate\":\"2022-12-04T21:20:01.218032276Z\"}".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .created,
|
||||||
|
headers: [.Header.Keys.contentType: .Header.Values.contentTypeJSON],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try await service.uploadFile(
|
||||||
|
id: itemId,
|
||||||
|
file: .init(
|
||||||
|
name: "some-text-file.txt",
|
||||||
|
data: "This is just a dummy content in text format".data(using: .utf8)!
|
||||||
|
),
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, Item(
|
||||||
|
idParent: itemId,
|
||||||
|
id: "eb34443b0f1cd1b0a53dc889aa7ccb5a63edb2f8",
|
||||||
|
name: "some-text-file.txt",
|
||||||
|
isDirectory: false,
|
||||||
|
lastModifiedAt: dateFormatter.date(from: "2022-12-04T21:20:01.218032276Z")!,
|
||||||
|
size: 43,
|
||||||
|
contentType: "text/plain; charset=utf-8"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someFileNameAndData_andWrongCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.uploadFile(
|
||||||
|
id: itemId,
|
||||||
|
file: .init(
|
||||||
|
name: "some-text-file.txt",
|
||||||
|
data: .init()
|
||||||
|
),
|
||||||
|
credentials: .init(
|
||||||
|
username: "usrname",
|
||||||
|
password: "passwrd"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withNonExistingParentFolderId_someFileNameAndData_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.uploadFile(
|
||||||
|
id: itemId,
|
||||||
|
file: .init(
|
||||||
|
name: "some-text-file.txt",
|
||||||
|
data: .init()
|
||||||
|
),
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyParentFolderId_someFileNameAndData_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent("")] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.uploadFile(
|
||||||
|
id: "",
|
||||||
|
file: .init(
|
||||||
|
name: "some-text-file.txt",
|
||||||
|
data: .init()
|
||||||
|
),
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withExistingParentFolderId_someInvalidFileNameAndData_andRightCredentials() async throws {
|
||||||
|
// GIVEN
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.uploadFile(
|
||||||
|
id: itemId,
|
||||||
|
file: .init(
|
||||||
|
name: "some-text-file.txt",
|
||||||
|
data: .init()
|
||||||
|
),
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyParentFolderId_someExistingNameAndData_andRightCredentials() async throws {
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.uploadFile(
|
||||||
|
id: itemId,
|
||||||
|
file: .init(
|
||||||
|
name: "some-text-file.txt",
|
||||||
|
data: .init()
|
||||||
|
),
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyParentFolderId_someEmptyFileNameAndData_andRightCredentials() async throws {
|
||||||
|
MockURLProtocol.mockData[url.appendingPathComponent(itemId)] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await service.uploadFile(
|
||||||
|
id: itemId,
|
||||||
|
file: .init(
|
||||||
|
name: "",
|
||||||
|
data: .init()
|
||||||
|
),
|
||||||
|
credentials: .init(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// CreateFolderEndpoint+InitTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class CreateFolderEndpointInitTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
|
||||||
|
private var endpoint: CreateFolderEndpoint!
|
||||||
|
private var folderName: String!
|
||||||
|
private var username: String!
|
||||||
|
private var password: String!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withItemId_someFolderName_andProperUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
folderName = "some-folder"
|
||||||
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
folderName: folderName,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)")
|
||||||
|
XCTAssertEqual(endpoint.method, .post)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [.Header.Keys.contentType: .Header.Values.contentTypeJSON])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="])
|
||||||
|
XCTAssertEqual(endpoint.body, "{\"name\":\"some-folder\"}".data(using: .utf8))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withItemId_someFolderName_andEmptyUsernameOrPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
folderName = "some-folder"
|
||||||
|
username = ""
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
folderName: folderName,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)")
|
||||||
|
XCTAssertEqual(endpoint.method, .post)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [.Header.Keys.contentType: .Header.Values.contentTypeJSON])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [:])
|
||||||
|
XCTAssertEqual(endpoint.body, "{\"name\":\"some-folder\"}".data(using: .utf8))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// DeleteItemEndpoint+InitTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class DeleteItemEndpoint_InitTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
|
||||||
|
private var endpoint: DeleteItemEndpoint!
|
||||||
|
private var username: String!
|
||||||
|
private var password: String!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withItemId_andProperUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)")
|
||||||
|
XCTAssertEqual(endpoint.method, .delete)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [:])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withItemId_andEmptyUsernameOrPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = ""
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)")
|
||||||
|
XCTAssertEqual(endpoint.method, .delete)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [:])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [:])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// GetDataEndpoint+InitTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class GetDataEndpointInitTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
|
||||||
|
private var endpoint: GetDataEndpoint!
|
||||||
|
private var username: String!
|
||||||
|
private var password: String!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withItemId_andProperUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)/data")
|
||||||
|
XCTAssertEqual(endpoint.method, .get)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [:])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withItemId_andEmptyUsernameOrPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = ""
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)/data")
|
||||||
|
XCTAssertEqual(endpoint.method, .get)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [:])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [:])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// GetItemsEndpoint+InitTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class GetItemsEndpointInitTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
|
||||||
|
private var endpoint: GetItemsEndpoint!
|
||||||
|
private var username: String!
|
||||||
|
private var password: String!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withItemId_andProperUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/" + itemId)
|
||||||
|
XCTAssertEqual(endpoint.method, .get)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [.Header.Keys.contentType: .Header.Values.contentTypeJSON])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withItemId_andEmptyUsernameOrPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = ""
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/" + itemId)
|
||||||
|
XCTAssertEqual(endpoint.method, .get)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [.Header.Keys.contentType: .Header.Values.contentTypeJSON])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [:])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// GetMeEndpoint+InitTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class GetMeEndpointInitTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private var endpoint: GetMeEndpoint!
|
||||||
|
private var username: String!
|
||||||
|
private var password: String!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withProperUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(username: username, password: password)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/me")
|
||||||
|
XCTAssertEqual(endpoint.method, .get)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [.Header.Keys.contentType: .Header.Values.contentTypeJSON])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyUsernameOrPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = ""
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(username: username, password: password)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/me")
|
||||||
|
XCTAssertEqual(endpoint.method, .get)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [.Header.Keys.contentType: .Header.Values.contentTypeJSON])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [:])
|
||||||
|
XCTAssertNil(endpoint.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// UploadFileEndpoint+InitTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class UploadFileEndpoint_InitTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let itemId = UUID().uuidString
|
||||||
|
|
||||||
|
private var endpoint: UploadFileEndpoint!
|
||||||
|
private var fileName: String!
|
||||||
|
private var fileData: Data!
|
||||||
|
private var username: String!
|
||||||
|
private var password: String!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withItemId_someFileNameAndData_andProperUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
fileName = "some-filename.txt"
|
||||||
|
fileData = "This is some raw text data for testing...".data(using: .utf8)
|
||||||
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
fileName: fileName,
|
||||||
|
fileData: fileData,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)")
|
||||||
|
XCTAssertEqual(endpoint.method, .post)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [
|
||||||
|
.Header.Keys.contentType: .Header.Values.contentTypeOctet,
|
||||||
|
.Header.Keys.contentDisposition: "attachment;filename*=utf-8''" + fileName
|
||||||
|
])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="])
|
||||||
|
XCTAssertEqual(endpoint.body, fileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withItemId_someFileNameAndData_andEmptyUsernameOrPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
fileName = "some-filename.txt"
|
||||||
|
fileData = "This is some raw text data for testing...".data(using: .utf8)
|
||||||
|
username = "username"
|
||||||
|
password = ""
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
endpoint = .init(
|
||||||
|
itemId: itemId,
|
||||||
|
fileName: fileName,
|
||||||
|
fileData: fileData,
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(endpoint.scheme, .Schemes.http)
|
||||||
|
XCTAssertEqual(endpoint.host, .Hosts.default)
|
||||||
|
XCTAssertEqual(endpoint.port, .Ports.default)
|
||||||
|
XCTAssertEqual(endpoint.path, "/items/\(itemId)")
|
||||||
|
XCTAssertEqual(endpoint.method, .post)
|
||||||
|
XCTAssertEqual(endpoint.credentials.username, username)
|
||||||
|
XCTAssertEqual(endpoint.credentials.password, password)
|
||||||
|
XCTAssertEqual(endpoint.headers, [
|
||||||
|
.Header.Keys.contentType: .Header.Values.contentTypeOctet,
|
||||||
|
.Header.Keys.contentDisposition: "attachment;filename*=utf-8''" + fileName
|
||||||
|
])
|
||||||
|
XCTAssertEqual(endpoint.authorizationHeader, [:])
|
||||||
|
XCTAssertEqual(endpoint.body, fileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,443 @@
|
|||||||
|
//
|
||||||
|
// APIClient+RequestTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class APIClientRequestTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let makeURLRequest = MakeURLRequestUseCase()
|
||||||
|
private let dateFormatter = DateFormatter.iso8601
|
||||||
|
private let sessionConfiguration = {
|
||||||
|
let configuration = URLSessionConfiguration.default
|
||||||
|
|
||||||
|
configuration.protocolClasses = [MockURLProtocol.self]
|
||||||
|
|
||||||
|
return configuration
|
||||||
|
}()
|
||||||
|
|
||||||
|
private var client: APIClient!
|
||||||
|
private var url: URL!
|
||||||
|
private var data: Data!
|
||||||
|
|
||||||
|
// MARK: Setup
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
client = .init(configuration: sessionConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
client = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Request cases
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseStatusOk() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = "{\"firstName\":\"Noel\",\"lastName\":\"Flantier\",\"rootItem\":{\"id\":\"4b8e41fd4a6a89712f15bbf102421b9338cfab11\",\"parentId\":\"\",\"name\":\"dossierTest\",\"isDir\":true,\"modificationDate\":\"2021-11-29T10:57:13Z\"}}\n".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
let result = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, Me(
|
||||||
|
firstName: "Noel",
|
||||||
|
lastName: "Flantier",
|
||||||
|
rootItem: .init(
|
||||||
|
idParent: "",
|
||||||
|
id: "4b8e41fd4a6a89712f15bbf102421b9338cfab11",
|
||||||
|
name: "dossierTest",
|
||||||
|
isDirectory: true,
|
||||||
|
lastModifiedAt: dateFormatter.date(from: "2021-11-29T10:57:13Z")!,
|
||||||
|
size: nil,
|
||||||
|
contentType: nil
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseStatusCreated() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = "{\"firstName\":\"Noel\",\"lastName\":\"Flantier\",\"rootItem\":{\"id\":\"4b8e41fd4a6a89712f15bbf102421b9338cfab11\",\"parentId\":\"\",\"name\":\"dossierTest\",\"isDir\":true,\"modificationDate\":\"2021-11-29T10:57:13Z\"}}\n".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .created,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
let result = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, Me(
|
||||||
|
firstName: "Noel",
|
||||||
|
lastName: "Flantier",
|
||||||
|
rootItem: .init(
|
||||||
|
idParent: "",
|
||||||
|
id: "4b8e41fd4a6a89712f15bbf102421b9338cfab11",
|
||||||
|
name: "dossierTest",
|
||||||
|
isDirectory: true,
|
||||||
|
lastModifiedAt: dateFormatter.date(from: "2021-11-29T10:57:13Z")!,
|
||||||
|
size: nil,
|
||||||
|
contentType: nil
|
||||||
|
)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseStatusNoContent() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .noContent,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
} catch APIClientError.notSupported {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseStatusBadRequest() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
} catch APIClientError.itemNameInvalidOrDefined {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseStatusUnauthorized() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseStatusForbidden() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .forbidden,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseStatusNotFound() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_andSomeModel_whenResponseDataIncorrect() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = "{\"firstName\":\"Noel\",\"lastName\":\"Flantier\",\"rootItem\":{\"id\":\"4b8e41fd4a6a89712f15bbf102421b9338cfab11\"\"isDir\":true}}\n".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
_ = try await client.request(endpoint: endpoint, model: Me.self)
|
||||||
|
} catch is DecodingError {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_whenResponseStatusOk() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: UUID().uuidString,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = "This is just some dummy data for testing purposes".data(using: .utf8)
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .ok,
|
||||||
|
headers: [:],
|
||||||
|
data: data
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
let result = try await client.request(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_whenResponseStatusCreated() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: UUID().uuidString,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .created,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await client.request(endpoint: endpoint)
|
||||||
|
} catch APIClientError.notSupported {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_whenResponseStatusNoContent() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: UUID().uuidString,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
data = .init()
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .noContent,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
let result = try await client.request(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_whenResponseStatusBadRequest() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: UUID().uuidString,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .badRequest,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await client.request(endpoint: endpoint)
|
||||||
|
} catch APIClientError.itemIsNotFile {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_whenResponseStatusUnauthorized() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: UUID().uuidString,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .unauthorized,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await client.request(endpoint: endpoint)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_whenResponseStatusForbidden() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: UUID().uuidString,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .forbidden,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await client.request(endpoint: endpoint)
|
||||||
|
} catch APIClientError.authenticationFailed {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withSomeEndpoint_whenResponseStatusNotFound() async throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: UUID().uuidString,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
url = try makeURLRequest(endpoint: endpoint).url
|
||||||
|
|
||||||
|
MockURLProtocol.mockData[url] = MockURLResponse(
|
||||||
|
status: .notFound,
|
||||||
|
headers: [:],
|
||||||
|
data: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
do {
|
||||||
|
try await client.request(endpoint: endpoint)
|
||||||
|
} catch APIClientError.itemDoesNotExist {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} catch {
|
||||||
|
XCTAssertTrue(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// MakeAuthorizationHeaderUseCaseTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class MakeAuthorizationHeaderUseCaseTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let makeAuthHeader = MakeAuthorizationHeaderUseCase()
|
||||||
|
|
||||||
|
private var username: String!
|
||||||
|
private var password: String!
|
||||||
|
|
||||||
|
private var result: [String: String]!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withCorrectUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = "username"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
|
||||||
|
result = try makeAuthHeader(username: username, password: password)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertEqual(result.count, 1)
|
||||||
|
XCTAssertEqual(result[.Header.Keys.authorization], "Basic dXNlcm5hbWU6cGFzc3dvcmQ=")
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyUsername() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = ""
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
XCTAssertThrowsError(try makeAuthHeader(username: username, password: password)) { error in
|
||||||
|
XCTAssertEqual(error as? MakeAuthorizationHeaderError, .usernameIsEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = "username"
|
||||||
|
password = ""
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
XCTAssertThrowsError(try makeAuthHeader(username: username, password: password)) { error in
|
||||||
|
XCTAssertEqual(error as? MakeAuthorizationHeaderError, .passwordIsEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withEmptyUsernameAndPassword() throws {
|
||||||
|
// GIVEN
|
||||||
|
username = ""
|
||||||
|
password = ""
|
||||||
|
|
||||||
|
// WHEN & THEN
|
||||||
|
XCTAssertThrowsError(try makeAuthHeader(username: username, password: password)) { error in
|
||||||
|
XCTAssertEqual(error as? MakeAuthorizationHeaderError, .usernameIsEmpty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
//
|
||||||
|
// MakeURLRequestUseCaseTests.swift
|
||||||
|
// APIServiceTests
|
||||||
|
//
|
||||||
|
// Created by Javier Cicchelli on 04/12/2022.
|
||||||
|
// Copyright © 2022 Röck+Cöde. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@testable import APIService
|
||||||
|
|
||||||
|
final class MakeURLRequestUseCaseTests: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: Properties
|
||||||
|
|
||||||
|
private let makeURLRequest = MakeURLRequestUseCase()
|
||||||
|
|
||||||
|
private var result: URLRequest!
|
||||||
|
|
||||||
|
// MARK: Test cases
|
||||||
|
|
||||||
|
func test_withGetMeEndpoint() throws {
|
||||||
|
// GIVEN
|
||||||
|
let endpoint = GetMeEndpoint(
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try makeURLRequest(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(result.url?.absoluteString, "http://163.172.147.216:8080/me")
|
||||||
|
XCTAssertEqual(result.httpMethod, "GET")
|
||||||
|
XCTAssertEqual(result.allHTTPHeaderFields, [
|
||||||
|
.Header.Keys.contentType: .Header.Values.contentTypeJSON,
|
||||||
|
.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
||||||
|
])
|
||||||
|
XCTAssertNil(result.httpBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withGetItemsEndpoint() throws {
|
||||||
|
// GIVEN
|
||||||
|
let itemId = UUID().uuidString
|
||||||
|
let endpoint = GetItemsEndpoint(
|
||||||
|
itemId: itemId,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try makeURLRequest(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(result.url?.absoluteString, "http://163.172.147.216:8080/items/\(itemId)")
|
||||||
|
XCTAssertEqual(result.httpMethod, "GET")
|
||||||
|
XCTAssertEqual(result.allHTTPHeaderFields, [
|
||||||
|
.Header.Keys.contentType: .Header.Values.contentTypeJSON,
|
||||||
|
.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
||||||
|
])
|
||||||
|
XCTAssertNil(result.httpBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withUploadFileEndpoint() throws {
|
||||||
|
// GIVEN
|
||||||
|
let itemId = UUID().uuidString
|
||||||
|
let fileName = "some-file-name.txt"
|
||||||
|
let fileData = "This is just a line of text to make some test data".data(using: .utf8)!
|
||||||
|
let endpoint = UploadFileEndpoint(
|
||||||
|
itemId: itemId,
|
||||||
|
fileName: fileName,
|
||||||
|
fileData: fileData,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try makeURLRequest(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(result.url?.absoluteString, "http://163.172.147.216:8080/items/\(itemId)")
|
||||||
|
XCTAssertEqual(result.httpMethod, "POST")
|
||||||
|
XCTAssertEqual(result.allHTTPHeaderFields, [
|
||||||
|
.Header.Keys.contentType: .Header.Values.contentTypeOctet,
|
||||||
|
.Header.Keys.contentDisposition: "attachment;filename*=utf-8''\(fileName)",
|
||||||
|
.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
||||||
|
])
|
||||||
|
XCTAssertNotNil(result.httpBody)
|
||||||
|
XCTAssertEqual(result.httpBody, fileData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withCreateFolderEndpoint() throws {
|
||||||
|
// GIVEN
|
||||||
|
let itemId = UUID().uuidString
|
||||||
|
let folderName = "some-folder-name"
|
||||||
|
let endpoint = CreateFolderEndpoint(
|
||||||
|
itemId: itemId,
|
||||||
|
folderName: folderName,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
let body = "{\"name\":\"\(folderName)\"}".data(using: .utf8)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try makeURLRequest(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(result.url?.absoluteString, "http://163.172.147.216:8080/items/\(itemId)")
|
||||||
|
XCTAssertEqual(result.httpMethod, "POST")
|
||||||
|
XCTAssertEqual(result.allHTTPHeaderFields, [
|
||||||
|
.Header.Keys.contentType: .Header.Values.contentTypeJSON,
|
||||||
|
.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
||||||
|
])
|
||||||
|
XCTAssertNotNil(result.httpBody)
|
||||||
|
XCTAssertEqual(result.httpBody, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withDeleteItemEndpoint() throws {
|
||||||
|
// GIVEN
|
||||||
|
let itemId = UUID().uuidString
|
||||||
|
let endpoint = DeleteItemEndpoint(
|
||||||
|
itemId: itemId,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try makeURLRequest(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(result.url?.absoluteString, "http://163.172.147.216:8080/items/\(itemId)")
|
||||||
|
XCTAssertEqual(result.httpMethod, "DELETE")
|
||||||
|
XCTAssertEqual(result.allHTTPHeaderFields, [
|
||||||
|
.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
||||||
|
])
|
||||||
|
XCTAssertNil(result.httpBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_withGetDataEndpoint() throws {
|
||||||
|
// GIVEN
|
||||||
|
let itemId = UUID().uuidString
|
||||||
|
let endpoint = GetDataEndpoint(
|
||||||
|
itemId: itemId,
|
||||||
|
username: "username",
|
||||||
|
password: "password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
result = try makeURLRequest(endpoint: endpoint)
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
XCTAssertNotNil(result)
|
||||||
|
XCTAssertEqual(result.url?.absoluteString, "http://163.172.147.216:8080/items/\(itemId)/data")
|
||||||
|
XCTAssertEqual(result.httpMethod, "GET")
|
||||||
|
XCTAssertEqual(result.allHTTPHeaderFields, [
|
||||||
|
.Header.Keys.authorization: "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
|
||||||
|
])
|
||||||
|
XCTAssertNil(result.httpBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user