diff --git a/.idea/workspace.xml b/.idea/workspace.xml index c153100..43a4187 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,9 +2,19 @@ + + + - + + + + + + + + + - + - + - + @@ -49,19 +60,26 @@ + + + + - + + + + - @@ -76,7 +94,19 @@ + + + - + \ No newline at end of file diff --git a/Sources/ToolboxAPIClient/AppTools.swift b/Sources/ToolboxAPIClient/AppTools.swift new file mode 100644 index 0000000..bcafd78 --- /dev/null +++ b/Sources/ToolboxAPIClient/AppTools.swift @@ -0,0 +1,39 @@ +// +// AppTools.swift +// +// +// Created by Zé on 09/02/21. +// + +import Foundation +public struct AppTools { + public static var httpScheme: String? { + getConfig(config.httpScheme.rawValue) + } + + public static var httpPort: String? { + getConfig(config.httpPort.rawValue) + } + + public static var httpHost: String? { + getConfig(config.httpHost.rawValue) + } +} + +extension AppTools { + private enum config: String { + case httpScheme = "HTTP_SCHEME" + case httpPort = "HTTP_PORT" + case httpHost = "HTTP_HOST" + } + + private static func getConfig(_ paramName: String, _ file: String = "Info", _ fileExtension: String = "plist") -> String? { + var nsDict: NSDictionary? + if let path = Bundle.main.path(forResource: file, ofType: fileExtension) { + nsDict = NSDictionary(contentsOfFile: path) + return nsDict?[paramName] as? String + } + + return nil + } +} diff --git a/Sources/ToolboxAPIClient/Extensions/Endpoint+Extension.swift b/Sources/ToolboxAPIClient/Extensions/Endpoint+Extension.swift index 5f77d80..09c480f 100644 --- a/Sources/ToolboxAPIClient/Extensions/Endpoint+Extension.swift +++ b/Sources/ToolboxAPIClient/Extensions/Endpoint+Extension.swift @@ -5,10 +5,17 @@ import Foundation extension Endpoint { public var url: URL { +// var components = URLComponents() +// components.scheme = AppTools.httpScheme +// components.host = AppTools.httpHost +// components.port = Int(AppTools.httpPort ?? "") ?? 0 +// components.path = path +// components.queryItems = queryItems var components = URLComponents() - components.scheme = "https" - components.host = "run.mocky.io" - components.path = "/v3" + path + components.scheme = "http" + components.host = "localhost" + components.port = 8080 + components.path = path components.queryItems = queryItems guard let url = components.url else { preconditionFailure("Invalid URL components: \(components)") diff --git a/Sources/ToolboxAPIClient/Extensions/NetworkManagerProtocol+Extension.swift b/Sources/ToolboxAPIClient/Extensions/NetworkManagerProtocol+Extension.swift new file mode 100644 index 0000000..ebe936b --- /dev/null +++ b/Sources/ToolboxAPIClient/Extensions/NetworkManagerProtocol+Extension.swift @@ -0,0 +1,42 @@ +// +// Created by Zé on 12/02/21. +// +import Foundation +import Combine + +@available(OSX 10.15, *) +extension NetworkManagerProtocol { + internal func urlRequest(type: T.Type?, httpverb: String, httpBody: Data?, url: URL, headers: Headers) -> AnyPublisher { + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = httpverb + urlRequest.httpBody = httpBody + + headers.forEach { (key, value) in + if let value = value as? String { + urlRequest.setValue(value, forHTTPHeaderField: key) + } + } + + return URLSession.shared.dataTaskPublisher(for: urlRequest) + .map { $0.data } + .decode(type: T?.self, decoder: JSONDecoder()) + .eraseToAnyPublisher() + } + + internal func urlRequestNoMap(httpverb: String, httpBody: Data?, url: URL, headers: Headers) -> AnyPublisher { + var urlRequest = URLRequest(url: url) + urlRequest.httpMethod = httpverb + urlRequest.httpBody = httpBody + + headers.forEach { (key, value) in + if let value = value as? String { + urlRequest.setValue(value, forHTTPHeaderField: key) + } + } + + return URLSession.shared.dataTaskPublisher(for: urlRequest) + .compactMap { $0.response as? HTTPURLResponse } + .mapError { $0 as Error } + .eraseToAnyPublisher() + } +} diff --git a/Sources/ToolboxAPIClient/NetworkManager.swift b/Sources/ToolboxAPIClient/NetworkManager.swift index 840d9b3..2d913a7 100644 --- a/Sources/ToolboxAPIClient/NetworkManager.swift +++ b/Sources/ToolboxAPIClient/NetworkManager.swift @@ -20,32 +20,18 @@ public final class NetworkManager: NetworkManagerProtocol { return urlRequest(type: T.self, httpverb: endPoint.method.rawValue, httpBody: data, url: endPoint.url, headers: endPoint.headers) } - public func put(type: T.Type, url: URL, headers: Headers) -> AnyPublisher where T: Decodable { - Empty(completeImmediately: true).eraseToAnyPublisher() + public func put(type: T.Type, body: T, endPoint: Endpoint) -> AnyPublisher where T: Codable { + let jsonEncoder = JSONEncoder() + jsonEncoder.outputFormatting = .prettyPrinted + let data = try? jsonEncoder.encode(body) + return urlRequest(type: T.self, httpverb: endPoint.method.rawValue, httpBody: data, url: endPoint.url, headers: endPoint.headers) } - public func delete(type: T.Type, url: URL, headers: Headers) -> AnyPublisher where T: Decodable { - Empty(completeImmediately: true).eraseToAnyPublisher() + public func delete(endpoint: Endpoint) -> AnyPublisher { + urlRequestNoMap(httpverb: endpoint.method.rawValue, httpBody: nil, url: endpoint.url, headers: endpoint.headers) } public func patch(type: T.Type, url: URL, headers: Headers) -> AnyPublisher where T: Decodable { Empty(completeImmediately: true).eraseToAnyPublisher() } - - private func urlRequest(type: T.Type, httpverb: String, httpBody: Data?, url: URL, headers: Headers) -> AnyPublisher { - var urlRequest = URLRequest(url: url) - urlRequest.httpMethod = httpverb - urlRequest.httpBody = httpBody - - headers.forEach { (key, value) in - if let value = value as? String { - urlRequest.setValue(value, forHTTPHeaderField: key) - } - } - - return URLSession.shared.dataTaskPublisher(for: urlRequest) - .map { $0.data } - .decode(type: T?.self, decoder: JSONDecoder()) - .eraseToAnyPublisher() - } } diff --git a/Sources/ToolboxAPIClient/NetworkManagerProtocol.swift b/Sources/ToolboxAPIClient/Protocols/NetworkManagerProtocol.swift similarity index 70% rename from Sources/ToolboxAPIClient/NetworkManagerProtocol.swift rename to Sources/ToolboxAPIClient/Protocols/NetworkManagerProtocol.swift index 69def2d..0ec6a30 100644 --- a/Sources/ToolboxAPIClient/NetworkManagerProtocol.swift +++ b/Sources/ToolboxAPIClient/Protocols/NetworkManagerProtocol.swift @@ -10,7 +10,7 @@ public protocol NetworkManagerProtocol: class { func get(type: T.Type, endPoint: Endpoint) -> AnyPublisher where T: Decodable func post(type: T.Type, body: T, endPoint: Endpoint) -> AnyPublisher where T: Codable - func put(type: T.Type, url: URL, headers: Headers) -> AnyPublisher where T: Decodable - func delete(type: T.Type, url: URL, headers: Headers) -> AnyPublisher where T: Decodable + func put(type: T.Type, body: T, endPoint: Endpoint) -> AnyPublisher where T: Codable + func delete(endpoint: Endpoint) -> AnyPublisher func patch(type: T.Type, url: URL, headers: Headers) -> AnyPublisher where T: Decodable } diff --git a/Tests/ToolboxAPIClientTests/Network/Users/Extensions/UserEndpoint+Extension.swift b/Tests/ToolboxAPIClientTests/Network/Users/Extensions/UserEndpoint+Extension.swift new file mode 100644 index 0000000..1943c0e --- /dev/null +++ b/Tests/ToolboxAPIClientTests/Network/Users/Extensions/UserEndpoint+Extension.swift @@ -0,0 +1,32 @@ +// +// Created by Zé on 07/02/21. +// +import Foundation +import ToolboxAPIClient + +extension Endpoint { + + static var users: Self { + Endpoint(path: "/users") + } + + static func users(count: Int) -> Self { + Endpoint(path: "/users", queryItems: [URLQueryItem(name: "limit", value: "\(count)")]) + } + + static func user(id: String) -> Self { + Endpoint(path: "/users/\(id)") + } + + static func createUser(user: User) -> Self { + Endpoint(path: "/users", method: .post) + } + + static func deleteUser(id: String) -> Self { + Endpoint(path: "/users/\(id)", method: .delete) + } + + static func updateUser(user: User) -> Self { + Endpoint(path: "/users/\(user.id ?? "")", method: .put) + } +} diff --git a/Tests/ToolboxAPIClientTests/UserNetworkManager.swift b/Tests/ToolboxAPIClientTests/Network/Users/Manager/UserNetworkManager.swift similarity index 54% rename from Tests/ToolboxAPIClientTests/UserNetworkManager.swift rename to Tests/ToolboxAPIClientTests/Network/Users/Manager/UserNetworkManager.swift index a3fabd6..3347af9 100644 --- a/Tests/ToolboxAPIClientTests/UserNetworkManager.swift +++ b/Tests/ToolboxAPIClientTests/Network/Users/Manager/UserNetworkManager.swift @@ -6,20 +6,22 @@ import Combine import ToolboxAPIClient final class UserNetworkManager: UserNetworkManagerProtocol { + + let networkController: NetworkManagerProtocol init(networkController: NetworkManagerProtocol) { self.networkController = networkController } - func getUsers() -> AnyPublisher { + func getUsers() -> AnyPublisher<[User]?, Error> { let endpoint = Endpoint.users - return networkController.get(type: Users.self, endPoint: endpoint) + return networkController.get(type: [User].self, endPoint: endpoint) } - func getUsers(count: Int) -> AnyPublisher { + func getUsers(count: Int) -> AnyPublisher<[User]?, Error> { let endpoint = Endpoint.users(count: count) - return networkController.get(type: Users.self, endPoint: endpoint) + return networkController.get(type: [User].self, endPoint: endpoint) } func getUser(id: String) -> AnyPublisher { @@ -31,4 +33,14 @@ final class UserNetworkManager: UserNetworkManagerProtocol { let endpoint = Endpoint.createUser(user: user) return networkController.post(type: User.self, body: user, endPoint: endpoint) } + + func updateUser(user: User) -> AnyPublisher { + let endpoint = Endpoint.updateUser(user: user) + return networkController.put(type: User.self, body: user, endPoint: endpoint) + } + + func deleteUser(id: String) -> AnyPublisher { + let endpoint = Endpoint.deleteUser(id: id) + return networkController.delete(endpoint: endpoint) + } } diff --git a/Tests/ToolboxAPIClientTests/Network/Users/UserNetworkManagerProtocol.swift b/Tests/ToolboxAPIClientTests/Network/Users/Manager/UserNetworkManagerProtocol.swift similarity index 63% rename from Tests/ToolboxAPIClientTests/Network/Users/UserNetworkManagerProtocol.swift rename to Tests/ToolboxAPIClientTests/Network/Users/Manager/UserNetworkManagerProtocol.swift index 06c0ec1..2d9275f 100644 --- a/Tests/ToolboxAPIClientTests/Network/Users/UserNetworkManagerProtocol.swift +++ b/Tests/ToolboxAPIClientTests/Network/Users/Manager/UserNetworkManagerProtocol.swift @@ -9,8 +9,9 @@ import ToolboxAPIClient protocol UserNetworkManagerProtocol: class { var networkController: NetworkManagerProtocol { get } - func getUsers() -> AnyPublisher - func getUsers(count: Int) -> AnyPublisher + func getUsers() -> AnyPublisher<[User]?, Error> + func getUsers(count: Int) -> AnyPublisher<[User]?, Error> func getUser(id: String) -> AnyPublisher func createUser(user: User) -> AnyPublisher + func deleteUser(id: String) -> AnyPublisher } diff --git a/Tests/ToolboxAPIClientTests/Network/Users/Models/User.swift b/Tests/ToolboxAPIClientTests/Network/Users/Models/User.swift index e783622..ca2146c 100644 --- a/Tests/ToolboxAPIClientTests/Network/Users/Models/User.swift +++ b/Tests/ToolboxAPIClientTests/Network/Users/Models/User.swift @@ -4,8 +4,8 @@ import Foundation struct User: Codable { - let id: String + let id: String? let firstName: String let lastName: String let email: String -} \ No newline at end of file +} diff --git a/Tests/ToolboxAPIClientTests/Network/Users/Models/Users.swift b/Tests/ToolboxAPIClientTests/Network/Users/Models/Users.swift deleted file mode 100644 index 70a71c1..0000000 --- a/Tests/ToolboxAPIClientTests/Network/Users/Models/Users.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// Created by Zé on 07/02/21. -// -import Foundation - -struct Users: Codable { - let data: [User]? -} \ No newline at end of file diff --git a/Tests/ToolboxAPIClientTests/Network/Users/UserEndpoint+Extension.swift b/Tests/ToolboxAPIClientTests/Network/Users/UserEndpoint+Extension.swift deleted file mode 100644 index 526dd5f..0000000 --- a/Tests/ToolboxAPIClientTests/Network/Users/UserEndpoint+Extension.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by Zé on 07/02/21. -// -import Foundation -import ToolboxAPIClient - -extension Endpoint { - static var users: Self { - Endpoint(path: "/3925cd1d-b769-4673-a625-37eb3845c17b") - } - - static func users(count: Int) -> Self { - Endpoint(path: "/741cddcb-57de-4421-aba2-52720e751064", queryItems: [URLQueryItem(name: "limit", value: "\(count)")]) - } - - static func user(id: String) -> Self { - Endpoint(path: "/c706cbfe-6550-4d3a-9d9b-ae19d1425245/\(id)") - } - - static func createUser(user: User) -> Self { - Endpoint(path: "/c706cbfe-6550-4d3a-9d9b-ae19d1425245", method: .post) - } -} diff --git a/Tests/ToolboxAPIClientTests/Tests/app_zeneto_api_moduleTests.swift b/Tests/ToolboxAPIClientTests/Tests/app_zeneto_api_moduleTests.swift index 79e9f99..1ebe4a7 100644 --- a/Tests/ToolboxAPIClientTests/Tests/app_zeneto_api_moduleTests.swift +++ b/Tests/ToolboxAPIClientTests/Tests/app_zeneto_api_moduleTests.swift @@ -15,10 +15,11 @@ class app_zeneto_api_moduleTests: XCTestCase { let timeout: TimeInterval = 5.0 static var allTests = [ - ("getUsers", testGetUser), + ("createUser", testCreateUser), + // ("getUsersByID", testGetUserById), ("getUsersWithLimit", testGetUserWithLimit), - ("getUsersByID", testGetUserById), - ("createUser", testCreateUser) + ("getUsers", testGetUser), + // ("deleteUser", testDeleteUserById) ] override func setUpWithError() throws { @@ -46,8 +47,8 @@ class app_zeneto_api_moduleTests: XCTestCase { self?.expectation.fulfill() break } - }, receiveValue: { (value: Users?) in - guard let users = value?.data else { + }, receiveValue: { (value: [User]?) in + guard let users = value else { XCTFail() return } @@ -64,7 +65,7 @@ class app_zeneto_api_moduleTests: XCTestCase { let networkManager = NetworkManager() let userManager = UserNetworkManager(networkController: networkManager) var subscriptions = Set() - let count: Int = 1000 + let count: Int = 10 userManager.getUsers(count: count) .sink(receiveCompletion: { [weak self] completion in @@ -76,8 +77,8 @@ class app_zeneto_api_moduleTests: XCTestCase { self?.expectation.fulfill() break } - }, receiveValue: { (value: Users?) in - guard let users = value?.data else { + }, receiveValue: { (value: [User]?) in + guard let users = value else { XCTFail() return } @@ -87,13 +88,13 @@ class app_zeneto_api_moduleTests: XCTestCase { wait(for: [expectation], timeout: timeout) } - func testGetUserById() throws { + func testCreateUser() throws { let networkManager = NetworkManager() let userManager = UserNetworkManager(networkController: networkManager) var subscriptions = Set() - let id: String = "a94bff9b-d0fc-49ba-9a4f-4f9234d16561" - userManager.getUser(id: id) + let user = User(id: nil, firstName: "Mackenzie", lastName: "Sebire", email: "msebire0@diigo.com") + userManager.createUser(user: user) .sink(receiveCompletion: { [weak self] completion in switch completion { case let .failure(error): @@ -109,18 +110,23 @@ class app_zeneto_api_moduleTests: XCTestCase { wait(for: [expectation], timeout: timeout) } - - func testCreateUser() throws { + + func testUpdateUser() throws { let networkManager = NetworkManager() let userManager = UserNetworkManager(networkController: networkManager) var subscriptions = Set() - - let user = User(id: "a94bff9b-d0fc-49ba-9a4f-4f9234d16561", - firstName: "Mackenzie", - lastName: "Sebire", - email: "msebire0@diigo.com") - userManager.createUser(user: user) +// let user = User(id: "763A636A-E0D5-4077-AB97-95DB2226B26D", +// firstName: "Mackenzie", +// lastName: "Sebire", +// email: "msebire0@diigo.com") + + let user = User(id: "763A636A-E0D5-4077-AB97-95DB2226B26D", + firstName: "Zé", + lastName: "Pqno", + email: "pqno@ze.com") + + userManager.updateUser(user: user) .sink(receiveCompletion: { [weak self] completion in switch completion { case let .failure(error): @@ -136,4 +142,49 @@ class app_zeneto_api_moduleTests: XCTestCase { wait(for: [expectation], timeout: timeout) } + +// func test1GetUserById() throws { +// let networkManager = NetworkManager() +// let userManager = UserNetworkManager(networkController: networkManager) +// var subscriptions = Set() +// +// userManager.getUser(id: id) +// .sink(receiveCompletion: { [weak self] completion in +// switch completion { +// case let .failure(error): +// print("Error API: \(error)") +// XCTFail() +// case .finished: +// self?.expectation.fulfill() +// break +// } +// }, receiveValue: { (value: User?) in +// XCTAssertNotNil(value) +// }).store(in: &subscriptions) +// +// wait(for: [expectation], timeout: timeout) +// } + +// func test5DeleteUserById() throws { +// let networkManager = NetworkManager() +// let userManager = UserNetworkManager(networkController: networkManager) +// var subscriptions = Set() +// +// userManager.deleteUser(id: "") +// .sink(receiveCompletion: { [weak self] completion in +// switch completion { +// case let .failure(error): +// print("Error API: \(error)") +// XCTFail() +// case .finished: +// self?.expectation.fulfill() +// break +// } +// }, receiveValue: { (value: HTTPURLResponse) in +// let statusCode: Int = value.statusCode +// XCTAssertEqual(200, statusCode) +// }).store(in: &subscriptions) +// +// wait(for: [expectation], timeout: timeout) +// } }