From 0087c2be5bc14c9a11642e50df8bb8d9d40a082d Mon Sep 17 00:00:00 2001 From: dankinsoid <30962149+dankinsoid@users.noreply.github.com> Date: Mon, 11 Mar 2024 19:47:53 +0300 Subject: [PATCH] 0.42.0 --- Example/Sources/PetStore/CustomAuth.swift | 90 ------------------- Example/Sources/PetStore/ExampleOfCalls.swift | 1 + Example/Sources/PetStore/TokenRefresher.swift | 61 ------------- README.md | 2 +- .../TokenRefresher/TokenRefresher.swift | 3 + 5 files changed, 5 insertions(+), 152 deletions(-) delete mode 100644 Example/Sources/PetStore/CustomAuth.swift delete mode 100644 Example/Sources/PetStore/TokenRefresher.swift diff --git a/Example/Sources/PetStore/CustomAuth.swift b/Example/Sources/PetStore/CustomAuth.swift deleted file mode 100644 index f8d9fff..0000000 --- a/Example/Sources/PetStore/CustomAuth.swift +++ /dev/null @@ -1,90 +0,0 @@ -import Foundation -import SwiftNetworking - -protocol TokenCacheService { - - func saveToken(_ token: String) - func getToken() -> String? - func clearToken() -} - -extension APIClient { - - func bearerAuth(_ service: TokenCacheService) -> APIClient { - // It's not required to create a .tokenCacheService config in this case, but it allows to override the token cache service and use it in other services, for instance, in a token refresher. - configs(\.tokenCacheService, service) - .auth( - AuthModifier { request, configs in - guard let token = configs.tokenCacheService.getToken() else { - throw NoToken() - } - request.setHeader(.authorization(bearerToken: token)) - } - ) - } -} - -extension APIClient.Configs { - - var tokenCacheService: TokenCacheService { - get { - self[\.tokenCacheService] ?? valueFor( - live: UserDefaultsTokenCacheService() as TokenCacheService, - test: MockTokenCacheService() - ) - } - set { - self[\.tokenCacheService] = newValue - } - } -} - -struct UserDefaultsTokenCacheService: TokenCacheService { - - /// Key used to store the token in UserDefaults - private let tokenKey = "APIToken" - - /// UserDefaults instance for data storage - private let defaults: UserDefaults - - /// Initializer allowing injection of UserDefaults instance for flexibility and testability - init(defaults: UserDefaults = .standard) { - self.defaults = defaults - } - - /// Function to save the API token - func saveToken(_ token: String) { - defaults.set(token, forKey: tokenKey) - } - - /// Function to retrieve the API token - func getToken() -> String? { - defaults.string(forKey: tokenKey) - } - - /// Function to clear the API token - func clearToken() { - defaults.removeObject(forKey: tokenKey) - } -} - -final class MockTokenCacheService: TokenCacheService { - - private var token: String? - - static let shared = MockTokenCacheService() - - func saveToken(_ token: String) { - self.token = token - } - - func getToken() -> String? { - token - } - - func clearToken() { - token = nil - } -} - -private struct NoToken: Error {} diff --git a/Example/Sources/PetStore/ExampleOfCalls.swift b/Example/Sources/PetStore/ExampleOfCalls.swift index 523cd0f..c2df32a 100644 --- a/Example/Sources/PetStore/ExampleOfCalls.swift +++ b/Example/Sources/PetStore/ExampleOfCalls.swift @@ -11,6 +11,7 @@ func exampleOfAPICalls() async throws { _ = try await api().user("name").delete() } +/// In order to get actual #line and #fileID in loggs use the following function instead of variable. func api(fileID: String = #fileID, line: UInt = #line) -> PetStore { PetStore(baseURL: .production, fileID: fileID, line: line) } diff --git a/Example/Sources/PetStore/TokenRefresher.swift b/Example/Sources/PetStore/TokenRefresher.swift deleted file mode 100644 index 96a50db..0000000 --- a/Example/Sources/PetStore/TokenRefresher.swift +++ /dev/null @@ -1,61 +0,0 @@ -import Foundation -import SwiftNetworking - -protocol TokenRefresher { - - func refreshTokenIfNeeded( - operation: () async throws -> (Data, HTTPURLResponse) - ) async throws -> (Data, HTTPURLResponse) -} - -extension APIClient { - - func tokenRefresher(_ refresher: @escaping (APIClient.Configs) -> TokenRefresher) -> APIClient { - configs { configs in - let base = configs.httpClient - configs.httpClient = HTTPClient { request, configs in - try await refresher(configs).refreshTokenIfNeeded { - try await base.data(request, configs) - } - } - } - } -} - -struct APITokenRefresher: TokenRefresher { - - let tokenService: TokenCacheService - - init(_ configs: APIClient.Configs) { - tokenService = configs.tokenCacheService - } - - private let tokenRefreshStatusCode = 401 // Status code indicating the token needs to be refreshed - - /// Checks if the token needs to be refreshed based on the status code - func refreshTokenIfNeeded( - operation: () async throws -> (Data, HTTPURLResponse) - ) async throws -> (Data, HTTPURLResponse) { - let result = try await operation() - if result.1.statusCode == tokenRefreshStatusCode { - try await refreshToken() - return try await operation() - } - return result - } - - /// Function to refresh the API token - private func refreshToken() async throws { - // Implement token refresh logic here. - // tokenService.saveToken("") - } -} - -struct MockTokenRefresher: TokenRefresher { - - func refreshTokenIfNeeded( - operation: () async throws -> (Data, HTTPURLResponse) - ) async throws -> (Data, HTTPURLResponse) { - try await operation() - } -} diff --git a/README.md b/README.md index 62d4f32..1e1e6bb 100644 --- a/README.md +++ b/README.md @@ -239,7 +239,7 @@ import PackageDescription let package = Package( name: "SomeProject", dependencies: [ - .package(url: "https://github.com/dankinsoid/swift-api-client.git", from: "0.41.0") + .package(url: "https://github.com/dankinsoid/swift-api-client.git", from: "0.42.0") ], targets: [ .target( diff --git a/Sources/SwiftAPIClient/Modifiers/TokenRefresher/TokenRefresher.swift b/Sources/SwiftAPIClient/Modifiers/TokenRefresher/TokenRefresher.swift index 8e98863..5dd3cac 100644 --- a/Sources/SwiftAPIClient/Modifiers/TokenRefresher/TokenRefresher.swift +++ b/Sources/SwiftAPIClient/Modifiers/TokenRefresher/TokenRefresher.swift @@ -60,6 +60,9 @@ public struct TokenRefresherMiddleware: HTTPClientMiddleware { configs: APIClient.Configs, next: (URLRequest, APIClient.Configs) async throws -> (T, HTTPURLResponse) ) async throws -> (T, HTTPURLResponse) { + guard configs.isAuthEnabled else { + return try await next(request, configs) + } var token: String let currentToken = tokenCacheService.getToken() if let currentToken {