From ed9d125a3c461c7846269fc89dcee8c06fe13762 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 22 Jul 2024 19:50:39 +0200 Subject: [PATCH 01/47] added Logging to Common --- Sources/Common/Logging/Logging.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Sources/Common/Logging/Logging.swift diff --git a/Sources/Common/Logging/Logging.swift b/Sources/Common/Logging/Logging.swift new file mode 100644 index 00000000..a280b387 --- /dev/null +++ b/Sources/Common/Logging/Logging.swift @@ -0,0 +1,18 @@ +import Foundation + +// Main interface for the logging. Clients can inject their own implementations +public protocol Logger { + func log(_ loggable: Loggable) +} + +// Loggable event. +public protocol Loggable { + var message: String { get } + var level: LogLevel { get } +} + +public enum LogLevel { + case info + case warning + case error +} From 296b64c2884aba5f56b2ca9bc6f383e75b8b391b Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 22 Jul 2024 19:54:59 +0200 Subject: [PATCH 02/47] split Logging to separate files --- Sources/Common/Logging/LogLevel.swift | 7 +++++++ Sources/Common/Logging/Loggable.swift | 6 ++++++ Sources/Common/Logging/Logger.swift | 6 ++++++ Sources/Common/Logging/Logging.swift | 18 ------------------ 4 files changed, 19 insertions(+), 18 deletions(-) create mode 100644 Sources/Common/Logging/LogLevel.swift create mode 100644 Sources/Common/Logging/Loggable.swift create mode 100644 Sources/Common/Logging/Logger.swift delete mode 100644 Sources/Common/Logging/Logging.swift diff --git a/Sources/Common/Logging/LogLevel.swift b/Sources/Common/Logging/LogLevel.swift new file mode 100644 index 00000000..fa94fcec --- /dev/null +++ b/Sources/Common/Logging/LogLevel.swift @@ -0,0 +1,7 @@ +import Foundation + +public enum LogLevel { + case info + case warning + case error +} diff --git a/Sources/Common/Logging/Loggable.swift b/Sources/Common/Logging/Loggable.swift new file mode 100644 index 00000000..0960ecf0 --- /dev/null +++ b/Sources/Common/Logging/Loggable.swift @@ -0,0 +1,6 @@ +import Foundation + +public protocol Loggable { + var message: String { get } + var level: LogLevel { get } +} diff --git a/Sources/Common/Logging/Logger.swift b/Sources/Common/Logging/Logger.swift new file mode 100644 index 00000000..59e09f94 --- /dev/null +++ b/Sources/Common/Logging/Logger.swift @@ -0,0 +1,6 @@ +import Foundation + +// Main interface for the logging. Clients can inject their own implementations +public protocol Logger { + func log(_ loggable: Loggable) +} diff --git a/Sources/Common/Logging/Logging.swift b/Sources/Common/Logging/Logging.swift deleted file mode 100644 index a280b387..00000000 --- a/Sources/Common/Logging/Logging.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation - -// Main interface for the logging. Clients can inject their own implementations -public protocol Logger { - func log(_ loggable: Loggable) -} - -// Loggable event. -public protocol Loggable { - var message: String { get } - var level: LogLevel { get } -} - -public enum LogLevel { - case info - case warning - case error -} From abaefbb3b88e8a95001b5519cf80094e91ee3ace Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 22 Jul 2024 19:55:33 +0200 Subject: [PATCH 03/47] added debug case to LogLevel --- Sources/Common/Logging/LogLevel.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Common/Logging/LogLevel.swift b/Sources/Common/Logging/LogLevel.swift index fa94fcec..8005a8da 100644 --- a/Sources/Common/Logging/LogLevel.swift +++ b/Sources/Common/Logging/LogLevel.swift @@ -1,7 +1,8 @@ import Foundation public enum LogLevel { + case debug case info - case warning case error + case warning } From 817bcd71800a984048d72a6beeabe73351693757 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 22 Jul 2024 19:57:37 +0200 Subject: [PATCH 04/47] added Logger to AuthConfig --- Sources/Auth/Model/AuthConfig.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/Auth/Model/AuthConfig.swift b/Sources/Auth/Model/AuthConfig.swift index 91c75bdb..54dc3136 100644 --- a/Sources/Auth/Model/AuthConfig.swift +++ b/Sources/Auth/Model/AuthConfig.swift @@ -1,3 +1,4 @@ +import Common import Foundation public struct AuthConfig { @@ -10,6 +11,7 @@ public struct AuthConfig { public let tidalLoginServiceBaseUri: String public let tidalAuthServiceBaseUri: String public let enableCertificatePinning: Bool + public let logger: Logger? public init( clientId: String, @@ -20,7 +22,8 @@ public struct AuthConfig { scopes: Set = [], tidalLoginServiceBaseUri: String = "https://login.tidal.com", tidalAuthServiceBaseUri: String = "https://auth.tidal.com", - enableCertificatePinning: Bool = true + enableCertificatePinning: Bool = true, + logger: Logger? = nil ) { self.clientId = clientId self.clientUniqueKey = clientUniqueKey @@ -31,5 +34,6 @@ public struct AuthConfig { self.tidalLoginServiceBaseUri = tidalLoginServiceBaseUri self.tidalAuthServiceBaseUri = tidalAuthServiceBaseUri self.enableCertificatePinning = enableCertificatePinning + self.logger = logger } } From c8cde10fb837e9cdd5caf50fef7a9bcec7dd82e5 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 23 Jul 2024 16:06:39 +0200 Subject: [PATCH 05/47] Loggable extension - for default values --- Sources/Common/Logging/Loggable.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/Common/Logging/Loggable.swift b/Sources/Common/Logging/Loggable.swift index 0960ecf0..841f019b 100644 --- a/Sources/Common/Logging/Loggable.swift +++ b/Sources/Common/Logging/Loggable.swift @@ -4,3 +4,13 @@ public protocol Loggable { var message: String { get } var level: LogLevel { get } } + +public extension Loggable { + var message: String { + return "Loggable message: \(self)" + } + + var level: LogLevel { + return .info + } +} From 444b750c3ea350278863a08b8ef84ae2b4cf22ff Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 23 Jul 2024 16:29:25 +0200 Subject: [PATCH 06/47] Loggable: renamed level to logLevel --- Sources/Common/Logging/Loggable.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Common/Logging/Loggable.swift b/Sources/Common/Logging/Loggable.swift index 841f019b..96a8e15f 100644 --- a/Sources/Common/Logging/Loggable.swift +++ b/Sources/Common/Logging/Loggable.swift @@ -2,7 +2,7 @@ import Foundation public protocol Loggable { var message: String { get } - var level: LogLevel { get } + var logLevel: LogLevel { get } } public extension Loggable { @@ -10,7 +10,7 @@ public extension Loggable { return "Loggable message: \(self)" } - var level: LogLevel { + var logLevel: LogLevel { return .info } } From 206df09dcfdf914f67acf8bae220acc1548ad813 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 23 Jul 2024 16:30:01 +0200 Subject: [PATCH 07/47] AuthLoggable - the implementation of Loggable in Auth module --- Sources/Auth/Model/AuthLoggable.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Sources/Auth/Model/AuthLoggable.swift diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift new file mode 100644 index 00000000..a349155a --- /dev/null +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -0,0 +1,17 @@ +import Common +import Foundation + +public enum AuthLoggable: Loggable { + case initializeLoginBackendError(error: Error) + case initializeDeviceLoginBackendError(error: Error) + case finalizeLoginBackendError(error: Error) + case finalizeDeviceLoginBackendError(error: Error) + case finalizeDevicePollingLimitReached + case getCredentialsUpgradeTokenBackendError(error: Error) + case getCredentialsScopeIsNotGranted + case getCredentialsClientUniqueKeyIsDifferent + case getCredentialsUpgradeTokenNoTokenInResponse + case getCredentialsRefreshTokenBackendError(error: Error) + case getCredentialsRefreshTokenWithClientCredentialsBackendError(error: Error) + case getCredentialsUserCredentialsDowngradedToClientCredentials +} From d86b53175861272d37360219ece83d32a0bb3238 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 12:26:10 +0200 Subject: [PATCH 08/47] adjusted cases of AuthLoggable --- Sources/Auth/Model/AuthLoggable.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index a349155a..2b178a0a 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -2,7 +2,6 @@ import Common import Foundation public enum AuthLoggable: Loggable { - case initializeLoginBackendError(error: Error) case initializeDeviceLoginBackendError(error: Error) case finalizeLoginBackendError(error: Error) case finalizeDeviceLoginBackendError(error: Error) @@ -13,5 +12,5 @@ public enum AuthLoggable: Loggable { case getCredentialsUpgradeTokenNoTokenInResponse case getCredentialsRefreshTokenBackendError(error: Error) case getCredentialsRefreshTokenWithClientCredentialsBackendError(error: Error) - case getCredentialsUserCredentialsDowngradedToClientCredentials + case getCredentialsUserCredentialsDowngradedToClientCredentials } From 36991643c184876a1a6c32ffdc08dc3f74e8fb3a Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 17:37:29 +0200 Subject: [PATCH 09/47] HTTPService - log server error --- Sources/Auth/Network/HTTPService.swift | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Sources/Auth/Network/HTTPService.swift b/Sources/Auth/Network/HTTPService.swift index 8fafb610..b784da16 100644 --- a/Sources/Auth/Network/HTTPService.swift +++ b/Sources/Auth/Network/HTTPService.swift @@ -4,7 +4,7 @@ import Foundation // MARK: - HTTPService protocol HTTPService { - func executeRequest(_ request: URLRequest) async throws -> T + func executeRequest(_ request: URLRequest, logServerError: ((Error) -> Void)?) async throws -> T func request(url: String, path: String, httpMethod: HTTPMethod, contentType: String, queryItems: [URLQueryItem]) -> URLRequest? } @@ -32,7 +32,7 @@ extension HTTPService { return request } - func executeRequest(_ request: URLRequest) async throws -> T { + func executeRequest(_ request: URLRequest, logServerError: ((Error) -> Void)?) async throws -> T { // Send the request asynchronously let (data, response) = try await URLSession.shared.data(for: request) @@ -40,9 +40,16 @@ extension HTTPService { guard let httpResponse = response as? HTTPURLResponse, (200 ... 299).contains(httpResponse.statusCode) else { let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: data) let statusCode = errorResponse?.status ?? (response as? HTTPURLResponse)?.statusCode ?? 1 - throw NetworkError(code: "\(statusCode)", subStatus: errorResponse?.subStatus) + let errorMessage = "Error response: \(String(describing: errorResponse)), status code: \(statusCode), request: \(request)" + let error = NetworkError(code: "\(statusCode)", subStatus: errorResponse?.subStatus, message: errorMessage) + logServerError?(error) + throw error } return try JSONDecoder().decode(T.self, from: data) } + + func executeRequest(_ request: URLRequest) async throws -> T { + try await executeRequest(request, logServerError: nil) + } } From 2ff6589256e245a024255b19dd56a3d090f7aa3d Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 17:38:12 +0200 Subject: [PATCH 10/47] logging initializeDeviceLoginBackendError --- Sources/Auth/Network/DefaultLoginService.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Network/DefaultLoginService.swift b/Sources/Auth/Network/DefaultLoginService.swift index 41e89cc9..b06f24f2 100644 --- a/Sources/Auth/Network/DefaultLoginService.swift +++ b/Sources/Auth/Network/DefaultLoginService.swift @@ -5,9 +5,11 @@ struct DefaultLoginService: LoginService, HTTPService { private let TOKEN_AUTH_PATH = "/v1/oauth2/token" private let DEVICE_AUTH_PATH = "/v1/oauth2/device_authorization" private let authBaseUrl: String + private let logger: Logger? - init(authBaseUrl: String) { + init(authBaseUrl: String, logger: Logger? = nil) { self.authBaseUrl = authBaseUrl + self.logger = logger } func getTokenWithCodeVerifier( @@ -49,7 +51,9 @@ struct DefaultLoginService: LoginService, HTTPService { throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) } - return try await executeRequest(request) + return try await executeRequest(request) { [logger] error in + logger?.log(AuthLoggable.initializeDeviceLoginBackendError(error: error)) + } } func getTokenFromDeviceCode( From 0f7010657eb25acec31120f7080d047fd924af76 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 18:16:08 +0200 Subject: [PATCH 11/47] error message for AuthLoggable --- Sources/Auth/Model/AuthLoggable.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 2b178a0a..ac95a539 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -2,6 +2,7 @@ import Common import Foundation public enum AuthLoggable: Loggable { + // swiftlint:disable identifier_name case initializeDeviceLoginBackendError(error: Error) case finalizeLoginBackendError(error: Error) case finalizeDeviceLoginBackendError(error: Error) @@ -13,4 +14,28 @@ public enum AuthLoggable: Loggable { case getCredentialsRefreshTokenBackendError(error: Error) case getCredentialsRefreshTokenWithClientCredentialsBackendError(error: Error) case getCredentialsUserCredentialsDowngradedToClientCredentials + // swiftlint:enable identifier_name + + public var message: String { + let errorDescription: String = associatedErrorDescription.map {", error: \($0)" } ?? "" + return "\(self)\(errorDescription)" + } + + private var associatedErrorDescription: String? { + switch self { + case .initializeDeviceLoginBackendError(let error), + .finalizeLoginBackendError(let error), + .finalizeDeviceLoginBackendError(let error), + .getCredentialsUpgradeTokenBackendError(let error), + .getCredentialsRefreshTokenBackendError(let error), + .getCredentialsRefreshTokenWithClientCredentialsBackendError(let error): + if let tidalError = error as? TidalError { + return "\(tidalError.localizedDescription), code: \(tidalError.code), substatus: \(tidalError.subStatus?.description ?? "nil"), message: \(tidalError.message ?? "nil")" + } else { + return error.localizedDescription + } + default: + return nil + } + } } From e6d853b556a5a683dac1f9c0ff453a9cba7a8a11 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 19:00:29 +0200 Subject: [PATCH 12/47] proper TidalError description --- Sources/Auth/Model/AuthLoggable.swift | 6 +----- Sources/Common/TidalError.swift | 6 +++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index ac95a539..182e10cb 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -29,11 +29,7 @@ public enum AuthLoggable: Loggable { .getCredentialsUpgradeTokenBackendError(let error), .getCredentialsRefreshTokenBackendError(let error), .getCredentialsRefreshTokenWithClientCredentialsBackendError(let error): - if let tidalError = error as? TidalError { - return "\(tidalError.localizedDescription), code: \(tidalError.code), substatus: \(tidalError.subStatus?.description ?? "nil"), message: \(tidalError.message ?? "nil")" - } else { - return error.localizedDescription - } + return error.localizedDescription default: return nil } diff --git a/Sources/Common/TidalError.swift b/Sources/Common/TidalError.swift index 2a95703e..3cf57f04 100644 --- a/Sources/Common/TidalError.swift +++ b/Sources/Common/TidalError.swift @@ -1,6 +1,6 @@ import Foundation -open class TidalError: Error { +open class TidalError: LocalizedError { public let code: String public let subStatus: Int? public let message: String? @@ -17,4 +17,8 @@ open class TidalError: Error { self.message = message self.throwable = throwable } + + public var errorDescription: String? { + "\(self), code: \(code), substatus: \(subStatus?.description ?? "nil"), message: \(message ?? "nil"), throwable: \(throwable?.localizedDescription ?? "nil")" + } } From 4f2daf97aa47d7969e5b6d088387d2b22d2100c2 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 19:20:55 +0200 Subject: [PATCH 13/47] removed creating errorMessage in HTTPService --- Sources/Auth/Network/HTTPService.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Sources/Auth/Network/HTTPService.swift b/Sources/Auth/Network/HTTPService.swift index b784da16..c6bbb7c7 100644 --- a/Sources/Auth/Network/HTTPService.swift +++ b/Sources/Auth/Network/HTTPService.swift @@ -1,4 +1,3 @@ -import Common import Foundation // MARK: - HTTPService @@ -31,7 +30,7 @@ extension HTTPService { return request } - + func executeRequest(_ request: URLRequest, logServerError: ((Error) -> Void)?) async throws -> T { // Send the request asynchronously let (data, response) = try await URLSession.shared.data(for: request) @@ -40,10 +39,7 @@ extension HTTPService { guard let httpResponse = response as? HTTPURLResponse, (200 ... 299).contains(httpResponse.statusCode) else { let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: data) let statusCode = errorResponse?.status ?? (response as? HTTPURLResponse)?.statusCode ?? 1 - let errorMessage = "Error response: \(String(describing: errorResponse)), status code: \(statusCode), request: \(request)" - let error = NetworkError(code: "\(statusCode)", subStatus: errorResponse?.subStatus, message: errorMessage) - logServerError?(error) - throw error + throw NetworkError(code: "\(statusCode)", subStatus: errorResponse?.subStatus) } return try JSONDecoder().decode(T.self, from: data) From b4e74c5202340f26cf7cc540268a53d8e90a7bbc Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 19:21:33 +0200 Subject: [PATCH 14/47] properly log AuthLoggable.initializeDeviceLoginBackendError --- Sources/Auth/Login/LoginRepository.swift | 4 +++- Sources/Auth/Network/DefaultLoginService.swift | 6 +----- Sources/Auth/Utils/Utils.swift | 6 ++++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index b8e9e161..ff986ec4 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -99,7 +99,9 @@ final class LoginRepository { } func initializeDeviceLogin() async throws -> AuthResult { - await retryWithPolicy(exponentialBackoffPolicy) { + await retryWithPolicy(exponentialBackoffPolicy, logError: { [logger = authConfig.logger] error in + logger?.log(AuthLoggable.initializeDeviceLoginBackendError(error: error)) + }) { let response = try await loginService.getDeviceAuthorization( clientId: authConfig.clientId, scope: authConfig.scopes.toScopesString() diff --git a/Sources/Auth/Network/DefaultLoginService.swift b/Sources/Auth/Network/DefaultLoginService.swift index b06f24f2..09da1649 100644 --- a/Sources/Auth/Network/DefaultLoginService.swift +++ b/Sources/Auth/Network/DefaultLoginService.swift @@ -5,11 +5,9 @@ struct DefaultLoginService: LoginService, HTTPService { private let TOKEN_AUTH_PATH = "/v1/oauth2/token" private let DEVICE_AUTH_PATH = "/v1/oauth2/device_authorization" private let authBaseUrl: String - private let logger: Logger? init(authBaseUrl: String, logger: Logger? = nil) { self.authBaseUrl = authBaseUrl - self.logger = logger } func getTokenWithCodeVerifier( @@ -51,9 +49,7 @@ struct DefaultLoginService: LoginService, HTTPService { throw NSError(domain: "Invalid URL", code: 0, userInfo: nil) } - return try await executeRequest(request) { [logger] error in - logger?.log(AuthLoggable.initializeDeviceLoginBackendError(error: error)) - } + return try await executeRequest(request) } func getTokenFromDeviceCode( diff --git a/Sources/Auth/Utils/Utils.swift b/Sources/Auth/Utils/Utils.swift index f93d5259..33f9100e 100644 --- a/Sources/Auth/Utils/Utils.swift +++ b/Sources/Auth/Utils/Utils.swift @@ -16,6 +16,7 @@ private let HTTP_STATUS_END = 600 func retryWithPolicy( _ retryPolicy: RetryPolicy, authErrorPolicy: AuthErrorPolicy? = nil, + logError: ((Error) -> Void)? = nil, block: () async throws -> T ) async -> AuthResult { var currentDelay = retryPolicy.delayMillis @@ -47,6 +48,11 @@ func retryWithPolicy( errorResponse: errorResponse, throwable: throwable ) + + if case .failure(let error) = result { + logError?(error) + } + return result } From ad6257347e0c604ab19e1b988c179fc6beaa3fa4 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Wed, 24 Jul 2024 19:22:41 +0200 Subject: [PATCH 15/47] got rid of Multiple Closures with Trailing Closure Violation --- Sources/Auth/Login/LoginRepository.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index ff986ec4..d7952d7b 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -101,14 +101,14 @@ final class LoginRepository { func initializeDeviceLogin() async throws -> AuthResult { await retryWithPolicy(exponentialBackoffPolicy, logError: { [logger = authConfig.logger] error in logger?.log(AuthLoggable.initializeDeviceLoginBackendError(error: error)) - }) { + }, block: { let response = try await loginService.getDeviceAuthorization( clientId: authConfig.clientId, scope: authConfig.scopes.toScopesString() ) deviceLoginPollHelper.prepareForPoll(interval: response.interval, maxDuration: response.expiresIn) return response - } + }) } func pollForDeviceLoginResponse(deviceCode: String) async throws -> AuthResult { From 36177bdae9884b5bce7732bc878ed2332051c15d Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 11:36:10 +0200 Subject: [PATCH 16/47] switched block and logError parameters in retryWithPolicy function --- Sources/Auth/Login/LoginRepository.swift | 6 +++--- Sources/Auth/Utils/Utils.swift | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index d7952d7b..fd3403c7 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -99,15 +99,15 @@ final class LoginRepository { } func initializeDeviceLogin() async throws -> AuthResult { - await retryWithPolicy(exponentialBackoffPolicy, logError: { [logger = authConfig.logger] error in - logger?.log(AuthLoggable.initializeDeviceLoginBackendError(error: error)) - }, block: { + await retryWithPolicy(exponentialBackoffPolicy, block: { let response = try await loginService.getDeviceAuthorization( clientId: authConfig.clientId, scope: authConfig.scopes.toScopesString() ) deviceLoginPollHelper.prepareForPoll(interval: response.interval, maxDuration: response.expiresIn) return response + }, logError: { [logger = authConfig.logger] error in + logger?.log(AuthLoggable.initializeDeviceLoginBackendError(error: error)) }) } diff --git a/Sources/Auth/Utils/Utils.swift b/Sources/Auth/Utils/Utils.swift index 33f9100e..b1d6b640 100644 --- a/Sources/Auth/Utils/Utils.swift +++ b/Sources/Auth/Utils/Utils.swift @@ -16,8 +16,8 @@ private let HTTP_STATUS_END = 600 func retryWithPolicy( _ retryPolicy: RetryPolicy, authErrorPolicy: AuthErrorPolicy? = nil, - logError: ((Error) -> Void)? = nil, - block: () async throws -> T + block: () async throws -> T, + logError: ((Error) -> Void)? = nil ) async -> AuthResult { var currentDelay = retryPolicy.delayMillis var attempts = 0 From b5fdbe44caa7ec6be27a99a0ceb69aec90160aaa Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 13:06:30 +0200 Subject: [PATCH 17/47] fixed losing throwables in error policies --- Sources/Auth/Login/DeviceLoginPollHelper.swift | 2 +- Sources/Auth/Utils/AuthErrorPolicy.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Login/DeviceLoginPollHelper.swift b/Sources/Auth/Login/DeviceLoginPollHelper.swift index ca9fce12..05d95443 100644 --- a/Sources/Auth/Login/DeviceLoginPollHelper.swift +++ b/Sources/Auth/Login/DeviceLoginPollHelper.swift @@ -64,7 +64,7 @@ struct DeviceLoginPollHelper { private class PollErrorPolicy: AuthErrorPolicy { func handleError(errorResponse: ErrorResponse?, throwable: Error?) -> AuthResult { guard let throwable = throwable as? NetworkError else { - return .failure(NetworkError(code: "0", throwable: nil)) + return .failure(NetworkError(code: "0", throwable: throwable)) } let subStatus = ApiErrorSubStatus.allCases.first(where: { $0.rawValue == errorResponse?.subStatus.description }) diff --git a/Sources/Auth/Utils/AuthErrorPolicy.swift b/Sources/Auth/Utils/AuthErrorPolicy.swift index b33d2a19..a6caaa8f 100644 --- a/Sources/Auth/Utils/AuthErrorPolicy.swift +++ b/Sources/Auth/Utils/AuthErrorPolicy.swift @@ -14,7 +14,7 @@ protocol AuthErrorPolicy { final class DefaultAuthErrorPolicy: AuthErrorPolicy { func handleError(errorResponse: ErrorResponse?, throwable: Error?) -> AuthResult { guard let throwable = throwable as? NetworkError else { - return .failure(NetworkError(code: "0", throwable: nil)) + return .failure(NetworkError(code: "0", throwable: throwable)) } let subStatus = throwable.getErrorResponse()?.subStatus From a58d4d01789ec0c0672a7eacbed2672c1cfaaa2d Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 13:47:34 +0200 Subject: [PATCH 18/47] AuthLoggable cases - renamed "Backend" to "Network" --- Sources/Auth/Login/LoginRepository.swift | 2 +- Sources/Auth/Model/AuthLoggable.swift | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index fd3403c7..4d7e6bee 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -107,7 +107,7 @@ final class LoginRepository { deviceLoginPollHelper.prepareForPoll(interval: response.interval, maxDuration: response.expiresIn) return response }, logError: { [logger = authConfig.logger] error in - logger?.log(AuthLoggable.initializeDeviceLoginBackendError(error: error)) + logger?.log(AuthLoggable.initializeDeviceLoginNetworkError(error: error)) }) } diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 182e10cb..57ce5319 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -3,16 +3,16 @@ import Foundation public enum AuthLoggable: Loggable { // swiftlint:disable identifier_name - case initializeDeviceLoginBackendError(error: Error) - case finalizeLoginBackendError(error: Error) - case finalizeDeviceLoginBackendError(error: Error) + case initializeDeviceLoginNetworkError(error: Error) + case finalizeLoginNetworkError(error: Error) + case finalizeDeviceLoginNetworkError(error: Error) case finalizeDevicePollingLimitReached - case getCredentialsUpgradeTokenBackendError(error: Error) + case getCredentialsUpgradeTokenNetworkError(error: Error) case getCredentialsScopeIsNotGranted case getCredentialsClientUniqueKeyIsDifferent case getCredentialsUpgradeTokenNoTokenInResponse - case getCredentialsRefreshTokenBackendError(error: Error) - case getCredentialsRefreshTokenWithClientCredentialsBackendError(error: Error) + case getCredentialsRefreshTokenNetworkError(error: Error) + case getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: Error) case getCredentialsUserCredentialsDowngradedToClientCredentials // swiftlint:enable identifier_name @@ -23,12 +23,12 @@ public enum AuthLoggable: Loggable { private var associatedErrorDescription: String? { switch self { - case .initializeDeviceLoginBackendError(let error), - .finalizeLoginBackendError(let error), - .finalizeDeviceLoginBackendError(let error), - .getCredentialsUpgradeTokenBackendError(let error), - .getCredentialsRefreshTokenBackendError(let error), - .getCredentialsRefreshTokenWithClientCredentialsBackendError(let error): + case .initializeDeviceLoginNetworkError(let error), + .finalizeLoginNetworkError(let error), + .finalizeDeviceLoginNetworkError(let error), + .getCredentialsUpgradeTokenNetworkError(let error), + .getCredentialsRefreshTokenNetworkError(let error), + .getCredentialsRefreshTokenWithClientCredentialsNetworkError(let error): return error.localizedDescription default: return nil From 081ad766eb944df7db3486633974d91fa7610837 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 14:14:08 +0200 Subject: [PATCH 19/47] log in LoginRepository instead of retry --- Sources/Auth/Login/LoginRepository.swift | 10 +++++++--- Sources/Auth/Utils/Utils.swift | 9 ++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index 4d7e6bee..8c5d5308 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -99,16 +99,20 @@ final class LoginRepository { } func initializeDeviceLogin() async throws -> AuthResult { - await retryWithPolicy(exponentialBackoffPolicy, block: { + let result = await retryWithPolicy(exponentialBackoffPolicy, block: { let response = try await loginService.getDeviceAuthorization( clientId: authConfig.clientId, scope: authConfig.scopes.toScopesString() ) deviceLoginPollHelper.prepareForPoll(interval: response.interval, maxDuration: response.expiresIn) return response - }, logError: { [logger = authConfig.logger] error in - logger?.log(AuthLoggable.initializeDeviceLoginNetworkError(error: error)) }) + + if case .failure(let error) = result { + authConfig.logger?.log(AuthLoggable.initializeDeviceLoginNetworkError(error: error)) + } + + return result } func pollForDeviceLoginResponse(deviceCode: String) async throws -> AuthResult { diff --git a/Sources/Auth/Utils/Utils.swift b/Sources/Auth/Utils/Utils.swift index b1d6b640..2581589b 100644 --- a/Sources/Auth/Utils/Utils.swift +++ b/Sources/Auth/Utils/Utils.swift @@ -16,8 +16,7 @@ private let HTTP_STATUS_END = 600 func retryWithPolicy( _ retryPolicy: RetryPolicy, authErrorPolicy: AuthErrorPolicy? = nil, - block: () async throws -> T, - logError: ((Error) -> Void)? = nil + block: () async throws -> T ) async -> AuthResult { var currentDelay = retryPolicy.delayMillis var attempts = 0 @@ -48,11 +47,7 @@ func retryWithPolicy( errorResponse: errorResponse, throwable: throwable ) - - if case .failure(let error) = result { - logError?(error) - } - + return result } From faec18b44704dcd45aa78fc9675bc414e7f93062 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 15:05:31 +0200 Subject: [PATCH 20/47] log "finalize login" and "finalize device login" errors --- Sources/Auth/Login/LoginRepository.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index 8c5d5308..c3a53be5 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -70,6 +70,10 @@ final class LoginRepository { if let successData = response.successData { try saveTokens(response: successData) } + + if case .failure(let error) = response { + authConfig.logger?.log(AuthLoggable.finalizeLoginNetworkError(error: error)) + } return response } @@ -125,6 +129,12 @@ final class LoginRepository { if let data = response.successData { try saveTokens(response: data) } + + if case .failure(let error) = response { + let loggable = error.subStatus?.description.isSubStatus(status: .expiredAccessToken) == true ? AuthLoggable.finalizeDevicePollingLimitReached : AuthLoggable.finalizeDeviceLoginNetworkError(error: error) + + authConfig.logger?.log(loggable) + } return response } From 6dda7c2e9bed86c2aa64fec9e230648dec0f51eb Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 17:03:30 +0200 Subject: [PATCH 21/47] log error in the upgradeToken flow --- Sources/Auth/TokenRepository.swift | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index 089da78b..4a5be0eb 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -76,7 +76,7 @@ struct TokenRepository { } private func upgradeToken(storedTokens: Tokens) async throws -> AuthResult { - let response = await retryWithPolicy(upgradeBackoffPolicy) { + let result = await retryWithPolicy(upgradeBackoffPolicy) { guard let refreshToken = storedTokens.refreshToken, let clientUniqueKey = authConfig.clientUniqueKey else { @@ -91,24 +91,25 @@ struct TokenRepository { scopes: authConfig.scopes.toScopesString(), grantType: GRANT_TYPE_UPGRADE ) - } - - if let successData = response.successData { + }.map { successData in let credentials = Credentials( authConfig: authConfig, userId: successData.userId.map { "\($0)" }, expiresIn: successData.expiresIn, token: successData.accessToken ) - - let result: AuthResult = .success(Tokens( + + return Tokens( credentials: credentials, refreshToken: successData.refreshToken - )) - return result + ) } - - return .failure(RetryableError(code: "1")) + + if case let .failure(error) = result { + authConfig.logger?.log(AuthLoggable.getCredentialsUpgradeTokenNetworkError(error: error)) + } + + return result } private func logout() -> AuthResult { From 4ad9ed19291a85b7a000c96ba5ef3a181c37f359 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 17:26:06 +0200 Subject: [PATCH 22/47] log if there is no token after upgrading token --- Sources/Auth/TokenRepository.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index 4a5be0eb..d092c419 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -105,7 +105,12 @@ struct TokenRepository { ) } - if case let .failure(error) = result { + switch result { + case .success(let tokens): + if tokens.credentials.token == nil { + authConfig.logger?.log(AuthLoggable.getCredentialsUpgradeTokenNoTokenInResponse) + } + case .failure(let error): authConfig.logger?.log(AuthLoggable.getCredentialsUpgradeTokenNetworkError(error: error)) } From 83b5d68724e1ff25a57241b208bcacc502c24eb1 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 17:56:38 +0200 Subject: [PATCH 23/47] log authLogout if no refresh token or client secret available --- Sources/Auth/Model/AuthLoggable.swift | 11 +++++++---- Sources/Auth/TokenRepository.swift | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 57ce5319..f47655c2 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -14,14 +14,15 @@ public enum AuthLoggable: Loggable { case getCredentialsRefreshTokenNetworkError(error: Error) case getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: Error) case getCredentialsUserCredentialsDowngradedToClientCredentials + case authLogout(reason: String) // swiftlint:enable identifier_name public var message: String { - let errorDescription: String = associatedErrorDescription.map {", error: \($0)" } ?? "" - return "\(self)\(errorDescription)" + let associatedValuesDescription = self.associatedValuesDescription ?? "" + return "\(self)\(associatedValuesDescription)" } - private var associatedErrorDescription: String? { + private var associatedValuesDescription: String? { switch self { case .initializeDeviceLoginNetworkError(let error), .finalizeLoginNetworkError(let error), @@ -29,7 +30,9 @@ public enum AuthLoggable: Loggable { .getCredentialsUpgradeTokenNetworkError(let error), .getCredentialsRefreshTokenNetworkError(let error), .getCredentialsRefreshTokenWithClientCredentialsNetworkError(let error): - return error.localizedDescription + return ", error: \(error.localizedDescription)" + case .authLogout(let reason): + return ", reason: \(reason)" default: return nil } diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index d092c419..23758cfc 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -38,7 +38,7 @@ struct TokenRepository { mutating func getCredentials(apiErrorSubStatus: String?) async throws -> AuthResult { var upgradedRefreshToken: String? let tokens = try loadTokensFromStore() - + let result: AuthResult if let tokens, needsCredentialsUpgrade { let upgradeCredentials = try await upgradeToken(storedTokens: tokens) @@ -72,6 +72,7 @@ struct TokenRepository { return await refreshCredentials { await getClientAccessToken(clientSecret: clientSecret) } } + authConfig.logger?.log(AuthLoggable.authLogout(reason: "no refresh token or client secret available")) return logout() } From 5bd06c91f7d4c00269f72acfc815882a9240652f Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Thu, 25 Jul 2024 18:35:54 +0200 Subject: [PATCH 24/47] removed unnecessary throw when initializing Credentials with refresh response --- Sources/Auth/Model/Credentials.swift | 2 +- Sources/Auth/TokenRepository.swift | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Sources/Auth/Model/Credentials.swift b/Sources/Auth/Model/Credentials.swift index 38edbdad..b63d97ca 100644 --- a/Sources/Auth/Model/Credentials.swift +++ b/Sources/Auth/Model/Credentials.swift @@ -50,7 +50,7 @@ public struct Credentials: Codable, Hashable { init( authConfig: AuthConfig, response: RefreshResponse - ) throws { + ) { self.init( authConfig: authConfig, grantedScopes: response.scopesString.toScopes(), diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index 23758cfc..5f6560fb 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -132,12 +132,8 @@ struct TokenRepository { let result = await apiCall() switch result { case let .success(data): - do { - let credentials = try Credentials(authConfig: authConfig, response: data) - return .success(credentials) - } catch { - return .failure(error.toTidalError) - } + let credentials = Credentials(authConfig: authConfig, response: data) + return .success(credentials) case let .failure(message): var refreshResult: AuthResult = .failure(message) if let errorCode = (message as? UnexpectedError)?.code, let code = Int(errorCode) { From a01fe21ca12c06c3858d2e3f03b9d4b8b333f47f Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 11:16:24 +0200 Subject: [PATCH 25/47] Made tests pass again - convert error to Retryable error for upgrading token --- Sources/Auth/TokenRepository.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index 5f6560fb..73688317 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -104,6 +104,8 @@ struct TokenRepository { credentials: credentials, refreshToken: successData.refreshToken ) + }.mapError { error in + return RetryableError(code: "1", throwable: error) as TidalError } switch result { From 5303ca39a91d86e85716a2607d7a2b02eda2b8b4 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 12:47:16 +0200 Subject: [PATCH 26/47] AuthLoggable - adjusted cases and message --- Sources/Auth/Model/AuthLoggable.swift | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index f47655c2..3576368b 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -13,28 +13,29 @@ public enum AuthLoggable: Loggable { case getCredentialsUpgradeTokenNoTokenInResponse case getCredentialsRefreshTokenNetworkError(error: Error) case getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: Error) - case getCredentialsUserCredentialsDowngradedToClientCredentials - case authLogout(reason: String) + case authLogout(reason: String, error: Error? = nil) // swiftlint:enable identifier_name public var message: String { - let associatedValuesDescription = self.associatedValuesDescription ?? "" - return "\(self)\(associatedValuesDescription)" - } - - private var associatedValuesDescription: String? { - switch self { + let associatedValuesPart = switch self { case .initializeDeviceLoginNetworkError(let error), .finalizeLoginNetworkError(let error), .finalizeDeviceLoginNetworkError(let error), .getCredentialsUpgradeTokenNetworkError(let error), .getCredentialsRefreshTokenNetworkError(let error), .getCredentialsRefreshTokenWithClientCredentialsNetworkError(let error): - return ", error: \(error.localizedDescription)" - case .authLogout(let reason): - return ", reason: \(reason)" + errorMessagePart(error: error) + case .authLogout(let reason, let error): + ", reason: \(reason)\(errorMessagePart(error: error))" default: - return nil + "" } + + return "\(self)\(associatedValuesPart)" + } + + private func errorMessagePart(error: Error?) -> String { + guard let error = error else { return "" } + return ", error: \(error.localizedDescription)" } } From 9158da89d682e1265aa18ac7e8c8a26f656309a8 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 12:50:04 +0200 Subject: [PATCH 27/47] log all scenarios in "update token" flow --- Sources/Auth/TokenRepository.swift | 61 ++++++++++++++++++------------ 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index 73688317..c196f532 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -62,17 +62,38 @@ struct TokenRepository { return .success(credentials) } + var refreshCredentialsBlock: (() async -> AuthResult)? + var networkErrorLoggableBlock: ((Error) -> AuthLoggable)? + // if a refreshToken is available, we'll use it if let refreshToken = storedTokens?.refreshToken { - return await refreshCredentials { await refreshUserAccessToken(refreshToken: refreshToken) } + refreshCredentialsBlock = { await refreshUserAccessToken(refreshToken: refreshToken) } + networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenNetworkError(error: $0) } + } else if let clientSecret = authConfig.clientSecret { + // if nothing is stored, we will try and refresh using a client secret + refreshCredentialsBlock = { await getClientAccessToken(clientSecret: clientSecret) } + networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: $0) } } - - // if nothing is stored, we will try and refresh using a client secret - if let clientSecret = authConfig.clientSecret { - return await refreshCredentials { await getClientAccessToken(clientSecret: clientSecret) } + + if let refreshCredentialsBlock { + let authResult = await refreshCredentialsBlock() + .map { Credentials(authConfig: authConfig, response: $0) } + + switch (authResult, networkErrorLoggableBlock) { + case (.failure(let error), _) where shouldLogoutWithLowerLevelTokenAfterUpdate(error: error): + authConfig.logger?.log(AuthLoggable.authLogout(reason: "User credentials were downgraded to client credentials after updating token", error: error)) + return .success(.init(authConfig: authConfig)) + + case (.failure(let error), .some(let networkErrorLoggableBlock)): + authConfig.logger?.log(networkErrorLoggableBlock(error)) + + default: break + } + + return authResult } - authConfig.logger?.log(AuthLoggable.authLogout(reason: "no refresh token or client secret available")) + authConfig.logger?.log(AuthLoggable.authLogout(reason: "No refresh token or client secret available")) return logout() } @@ -129,24 +150,16 @@ struct TokenRepository { token: nil )) } - - private func refreshCredentials(apiCall: () async -> AuthResult) async -> AuthResult { - let result = await apiCall() - switch result { - case let .success(data): - let credentials = Credentials(authConfig: authConfig, response: data) - return .success(credentials) - case let .failure(message): - var refreshResult: AuthResult = .failure(message) - if let errorCode = (message as? UnexpectedError)?.code, let code = Int(errorCode) { - if code <= HTTP_UNAUTHORIZED { - // if code 400, 401, the user is effectively logged out - // and we return a lower level token - refreshResult = .success(Credentials(authConfig: authConfig)) - } - } - - return refreshResult + + private func shouldLogoutWithLowerLevelTokenAfterUpdate(error: TidalError) -> Bool { + if let errorCode = (error as? UnexpectedError)?.code, + let code = Int(errorCode), + code <= HTTP_UNAUTHORIZED { + // if code 400, 401, the user is effectively logged out + // and we return a lower level token + return true + } else { + return false } } From 04151a70953cb2e7b720187cb053a491b4ed6a4c Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 13:35:51 +0200 Subject: [PATCH 28/47] added todo to log when token is not storec --- Sources/Auth/TokenRepository.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index c196f532..5a7ea9ae 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -70,6 +70,7 @@ struct TokenRepository { refreshCredentialsBlock = { await refreshUserAccessToken(refreshToken: refreshToken) } networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenNetworkError(error: $0) } } else if let clientSecret = authConfig.clientSecret { + //TODO: Log this as well // if nothing is stored, we will try and refresh using a client secret refreshCredentialsBlock = { await getClientAccessToken(clientSecret: clientSecret) } networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: $0) } From f434d5dee25f9b81d06787b2d9c1c03657af47f1 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 13:40:33 +0200 Subject: [PATCH 29/47] small refactoring in handling Result in LoginRepository --- Sources/Auth/Login/LoginRepository.swift | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index c3a53be5..5006bb84 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -66,15 +66,14 @@ final class LoginRepository { clientUniqueKey: authConfig.clientUniqueKey ) } - - if let successData = response.successData { - try saveTokens(response: successData) - } - if case .failure(let error) = response { + switch response { + case .success(let successData): + try saveTokens(response: successData) + case .failure(let error): authConfig.logger?.log(AuthLoggable.finalizeLoginNetworkError(error: error)) } - + return response } @@ -126,11 +125,11 @@ final class LoginRepository { grantType: GRANT_TYPE_DEVICE_CODE, retryPolicy: exponentialBackoffPolicy ) - if let data = response.successData { - try saveTokens(response: data) - } - if case .failure(let error) = response { + switch response { + case .success(let successData): + try saveTokens(response: successData) + case .failure(let error): let loggable = error.subStatus?.description.isSubStatus(status: .expiredAccessToken) == true ? AuthLoggable.finalizeDevicePollingLimitReached : AuthLoggable.finalizeDeviceLoginNetworkError(error: error) authConfig.logger?.log(loggable) From 619ea99fe4c5d7ce1840188b319ffd904e98503b Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 13:49:54 +0200 Subject: [PATCH 30/47] small formatting adjustments --- Sources/Auth/Login/LoginRepository.swift | 10 +++++----- Sources/Auth/TokenRepository.swift | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index 5006bb84..8f82ac8e 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -102,14 +102,14 @@ final class LoginRepository { } func initializeDeviceLogin() async throws -> AuthResult { - let result = await retryWithPolicy(exponentialBackoffPolicy, block: { + let result = await retryWithPolicy(exponentialBackoffPolicy) { let response = try await loginService.getDeviceAuthorization( clientId: authConfig.clientId, scope: authConfig.scopes.toScopesString() ) deviceLoginPollHelper.prepareForPoll(interval: response.interval, maxDuration: response.expiresIn) return response - }) + } if case .failure(let error) = result { authConfig.logger?.log(AuthLoggable.initializeDeviceLoginNetworkError(error: error)) @@ -127,9 +127,9 @@ final class LoginRepository { ) switch response { - case .success(let successData): - try saveTokens(response: successData) - case .failure(let error): + case .success(let successData): + try saveTokens(response: successData) + case .failure(let error): let loggable = error.subStatus?.description.isSubStatus(status: .expiredAccessToken) == true ? AuthLoggable.finalizeDevicePollingLimitReached : AuthLoggable.finalizeDeviceLoginNetworkError(error: error) authConfig.logger?.log(loggable) diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index 5a7ea9ae..fbea4aa2 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -70,7 +70,7 @@ struct TokenRepository { refreshCredentialsBlock = { await refreshUserAccessToken(refreshToken: refreshToken) } networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenNetworkError(error: $0) } } else if let clientSecret = authConfig.clientSecret { - //TODO: Log this as well + // TODO: Log this as well // if nothing is stored, we will try and refresh using a client secret refreshCredentialsBlock = { await getClientAccessToken(clientSecret: clientSecret) } networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: $0) } From f8a49eb8526f4193448f89c556c7fb326cada99c Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 14:15:29 +0200 Subject: [PATCH 31/47] removed logServerError parameter from HTTPService --- Sources/Auth/Network/HTTPService.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Sources/Auth/Network/HTTPService.swift b/Sources/Auth/Network/HTTPService.swift index c6bbb7c7..7a381ed8 100644 --- a/Sources/Auth/Network/HTTPService.swift +++ b/Sources/Auth/Network/HTTPService.swift @@ -3,7 +3,7 @@ import Foundation // MARK: - HTTPService protocol HTTPService { - func executeRequest(_ request: URLRequest, logServerError: ((Error) -> Void)?) async throws -> T + func executeRequest(_ request: URLRequest) async throws -> T func request(url: String, path: String, httpMethod: HTTPMethod, contentType: String, queryItems: [URLQueryItem]) -> URLRequest? } @@ -31,7 +31,7 @@ extension HTTPService { return request } - func executeRequest(_ request: URLRequest, logServerError: ((Error) -> Void)?) async throws -> T { + func executeRequest(_ request: URLRequest) async throws -> T { // Send the request asynchronously let (data, response) = try await URLSession.shared.data(for: request) @@ -44,8 +44,4 @@ extension HTTPService { return try JSONDecoder().decode(T.self, from: data) } - - func executeRequest(_ request: URLRequest) async throws -> T { - try await executeRequest(request, logServerError: nil) - } } From 30f644d3196260e47444e59d78683ee3783cb53c Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Fri, 26 Jul 2024 16:18:01 +0200 Subject: [PATCH 32/47] removed redundant Logger injection to DefaultLoginService --- Sources/Auth/Network/DefaultLoginService.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Auth/Network/DefaultLoginService.swift b/Sources/Auth/Network/DefaultLoginService.swift index 09da1649..41e89cc9 100644 --- a/Sources/Auth/Network/DefaultLoginService.swift +++ b/Sources/Auth/Network/DefaultLoginService.swift @@ -6,7 +6,7 @@ struct DefaultLoginService: LoginService, HTTPService { private let DEVICE_AUTH_PATH = "/v1/oauth2/device_authorization" private let authBaseUrl: String - init(authBaseUrl: String, logger: Logger? = nil) { + init(authBaseUrl: String) { self.authBaseUrl = authBaseUrl } From 9bccbd526d716b0872ea48e8c2f52806afa53be3 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 29 Jul 2024 15:00:12 +0200 Subject: [PATCH 33/47] added swift-log package --- Package.resolved | 9 +++++++++ Package.swift | 1 + 2 files changed, 10 insertions(+) diff --git a/Package.resolved b/Package.resolved index 2a14fe37..4c949b20 100644 --- a/Package.resolved +++ b/Package.resolved @@ -27,6 +27,15 @@ "version" : "4.2.2" } }, + { + "identity" : "swift-log", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-log.git", + "state" : { + "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537", + "version" : "1.6.1" + } + }, { "identity" : "swiftlintplugin", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index 676f05c4..a4fb5270 100644 --- a/Package.swift +++ b/Package.swift @@ -37,6 +37,7 @@ let package = Package( .package(url: "https://github.com/kishikawakatsumi/KeychainAccess.git", from: "4.2.2"), .package(url: "https://github.com/MobileNativeFoundation/Kronos.git", exact: "4.2.2"), .package(url: "https://github.com/lukepistrol/SwiftLintPlugin", from: "0.54.0"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0") ] + (shouldIncludeDocCPlugin ? [.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0")] : []), targets: [ .target( From 72077aff68fa3153ef7999d280238a3ddd6025ca Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 29 Jul 2024 15:06:04 +0200 Subject: [PATCH 34/47] added Logging to Auth package setup --- Package.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Package.swift b/Package.swift index a4fb5270..aedf9199 100644 --- a/Package.swift +++ b/Package.swift @@ -101,6 +101,7 @@ let package = Package( dependencies: [ .common, .KeychainAccess, + .Logging ], plugins: [ .plugin(name: "SwiftLint", package: "SwiftLintPlugin"), @@ -156,4 +157,5 @@ extension Target.Dependency { static let SWXMLHash = product(name: "SWXMLHash", package: "SWXMLHash") static let KeychainAccess = product(name: "KeychainAccess", package: "KeychainAccess") static let Kronos = product(name: "Kronos", package: "Kronos") + static let Logging = product(name: "Logging", package: "swift-log") } From f943aab2e3e1c206428657e40b76f6f9b34f3e9f Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 29 Jul 2024 17:59:21 +0200 Subject: [PATCH 35/47] added log handler factory to AuthConfig --- Sources/Auth/Model/AuthConfig.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Sources/Auth/Model/AuthConfig.swift b/Sources/Auth/Model/AuthConfig.swift index 54dc3136..5d526435 100644 --- a/Sources/Auth/Model/AuthConfig.swift +++ b/Sources/Auth/Model/AuthConfig.swift @@ -1,5 +1,8 @@ import Common import Foundation +import protocol Logging.LogHandler + +public typealias LogHandlerFactory = (String) -> LogHandler public struct AuthConfig { public let clientId: String @@ -12,6 +15,7 @@ public struct AuthConfig { public let tidalAuthServiceBaseUri: String public let enableCertificatePinning: Bool public let logger: Logger? + public let logHandlerFactory: LogHandlerFactory? public init( clientId: String, @@ -23,7 +27,8 @@ public struct AuthConfig { tidalLoginServiceBaseUri: String = "https://login.tidal.com", tidalAuthServiceBaseUri: String = "https://auth.tidal.com", enableCertificatePinning: Bool = true, - logger: Logger? = nil + logger: Logger? = nil, + logHandlerFactory: LogHandlerFactory? = nil ) { self.clientId = clientId self.clientUniqueKey = clientUniqueKey @@ -35,5 +40,6 @@ public struct AuthConfig { self.tidalAuthServiceBaseUri = tidalAuthServiceBaseUri self.enableCertificatePinning = enableCertificatePinning self.logger = logger + self.logHandlerFactory = logHandlerFactory } } From 338b5ffbd9b60c0f26f03557135665b80e791337 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 29 Jul 2024 18:00:48 +0200 Subject: [PATCH 36/47] bootstrap LoggingSystem --- Sources/Auth/TidalAuth.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/Auth/TidalAuth.swift b/Sources/Auth/TidalAuth.swift index e5e1f7c4..a43eeee9 100644 --- a/Sources/Auth/TidalAuth.swift +++ b/Sources/Auth/TidalAuth.swift @@ -1,4 +1,5 @@ import Foundation +import Logging // MARK: - TidalAuth @@ -16,6 +17,10 @@ public class TidalAuth: Auth & CredentialsProvider { let tokensStore = DefaultTokensStore(credentialsKey: config.credentialsKey, credentialsAccessGroup: config.credentialsAccessGroup) loginRepository = provideLoginRepository(config, tokensStore: tokensStore) tokenRepository = provideTokenRepository(config, tokensStore: tokensStore) + + // if logger is not provided, use logger that does nothing + let logHandlerFactory = config.logHandlerFactory ?? { _ in SwiftLogNoOpLogHandler() } + LoggingSystem.bootstrap(logHandlerFactory) } private func provideLoginRepository(_ config: AuthConfig, tokensStore: TokensStore) -> LoginRepository { From e5402320c2ec4f0764b75b39300a0416aa3623f7 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Mon, 29 Jul 2024 20:37:43 +0200 Subject: [PATCH 37/47] AuthLoggable - ability to log with swift-log --- Sources/Auth/Model/AuthLoggable.swift | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 3576368b..654de593 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -1,4 +1,5 @@ import Common +import Logging import Foundation public enum AuthLoggable: Loggable { @@ -39,3 +40,42 @@ public enum AuthLoggable: Loggable { return ", error: \(error.localizedDescription)" } } + +extension AuthLoggable { + private static let metadataErrorKey = "error" + private static let metadataReasonKey = "reason" + + var loggingMessage: Logging.Logger.Message { + "\(self)" + } + + var loggingMetadata: Logging.Logger.Metadata { + var metadata = [String: Logging.Logger.MetadataValue]() + + switch self { + case .initializeDeviceLoginNetworkError(let error), + .finalizeLoginNetworkError(let error), + .finalizeDeviceLoginNetworkError(let error), + .getCredentialsUpgradeTokenNetworkError(let error), + .getCredentialsRefreshTokenNetworkError(let error), + .getCredentialsRefreshTokenWithClientCredentialsNetworkError(let error): + metadata[Self.metadataErrorKey] = .string(error.localizedDescription) + case .authLogout(let reason, let error): + metadata[Self.metadataReasonKey] = .string(reason) + if let error = error { + metadata[Self.metadataErrorKey] = .string(error.localizedDescription) + } + return metadata + default: + return [:] + } + + return metadata + } + + func log() { + var logger = Logging.Logger(label: "auth_logger") + logger.logLevel = .trace + logger.log(level: .error, loggingMessage, metadata: loggingMetadata) + } +} From 53eda9a71391993a855794fa6be71ab6f94a2e92 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 10:28:27 +0200 Subject: [PATCH 38/47] Improved messages for AuthLoggable cases --- Sources/Auth/Model/AuthLoggable.swift | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 654de593..3b2c0b52 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -46,7 +46,30 @@ extension AuthLoggable { private static let metadataReasonKey = "reason" var loggingMessage: Logging.Logger.Message { - "\(self)" + return switch self { + case .initializeDeviceLoginNetworkError(let error): + "InitializeDeviceLoginNetworkError" + case .finalizeLoginNetworkError(let error): + "FinalizeLoginNetworkError" + case .finalizeDeviceLoginNetworkError(let error): + "FinalizeDeviceLoginNetworkError" + case .finalizeDevicePollingLimitReached: + "FinalizeDevicePollingLimitReached" + case .getCredentialsUpgradeTokenNetworkError(let error): + "GetCredentialsUpgradeTokenNetworkError" + case .getCredentialsScopeIsNotGranted: + "GetCredentialsScopeIsNotGranted" + case .getCredentialsClientUniqueKeyIsDifferent: + "GetCredentialsClientUniqueKeyIsDifferent" + case .getCredentialsUpgradeTokenNoTokenInResponse: + "GetCredentialsUpgradeTokenNoTokenIn" + case .getCredentialsRefreshTokenNetworkError(let error): + "GetCredentialsRefreshTokenNetworkError" + case .getCredentialsRefreshTokenWithClientCredentialsNetworkError(let error): + "GetCredentialsRefreshTokenWithClientCredentialsNetworkError" + case .authLogout(let reason, let error): + "AuthLogout" + } } var loggingMetadata: Logging.Logger.Metadata { From ff9d318c2d10ba42fba2863d76b75a4e3cf495d0 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 10:29:45 +0200 Subject: [PATCH 39/47] log test event --- Sources/Auth/TidalAuth.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/Auth/TidalAuth.swift b/Sources/Auth/TidalAuth.swift index a43eeee9..5a7ddbc9 100644 --- a/Sources/Auth/TidalAuth.swift +++ b/Sources/Auth/TidalAuth.swift @@ -1,5 +1,6 @@ import Foundation import Logging +import Common // MARK: - TidalAuth @@ -21,8 +22,17 @@ public class TidalAuth: Auth & CredentialsProvider { // if logger is not provided, use logger that does nothing let logHandlerFactory = config.logHandlerFactory ?? { _ in SwiftLogNoOpLogHandler() } LoggingSystem.bootstrap(logHandlerFactory) + + //TODO: remove + let throwable = NSError(domain: "test", code: 1, userInfo: ["abc": "def"]) + + let error = RetryableError(code: "123", subStatus: 1002, message: "Something went wrong", throwable: throwable) + + let authLoggable = AuthLoggable.finalizeLoginNetworkError(error: error) + + authLoggable.log() } - + private func provideLoginRepository(_ config: AuthConfig, tokensStore: TokensStore) -> LoginRepository { LoginRepository( authConfig: config, From 4fb5227cc115591aa122d4bcf8b88d391b69ea92 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 14:02:05 +0200 Subject: [PATCH 40/47] improved AuthLoggable error description --- Sources/Common/TidalError.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Common/TidalError.swift b/Sources/Common/TidalError.swift index 3cf57f04..209ff13a 100644 --- a/Sources/Common/TidalError.swift +++ b/Sources/Common/TidalError.swift @@ -19,6 +19,6 @@ open class TidalError: LocalizedError { } public var errorDescription: String? { - "\(self), code: \(code), substatus: \(subStatus?.description ?? "nil"), message: \(message ?? "nil"), throwable: \(throwable?.localizedDescription ?? "nil")" + "\(self), code: \(code), substatus: \(subStatus?.description ?? "nil"), message: \(message ?? "nil"), throwable: \(throwable.map { "\($0)" } ?? "nil"), throwable description: \(throwable?.localizedDescription ?? "nil")" } } From abb7d615d60de095370b6521e09c7346f99daec3 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 14:03:49 +0200 Subject: [PATCH 41/47] got rid of warnings in AuthLoggable --- Sources/Auth/Model/AuthLoggable.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 3b2c0b52..7c58802f 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -47,15 +47,15 @@ extension AuthLoggable { var loggingMessage: Logging.Logger.Message { return switch self { - case .initializeDeviceLoginNetworkError(let error): + case .initializeDeviceLoginNetworkError: "InitializeDeviceLoginNetworkError" - case .finalizeLoginNetworkError(let error): + case .finalizeLoginNetworkError: "FinalizeLoginNetworkError" - case .finalizeDeviceLoginNetworkError(let error): + case .finalizeDeviceLoginNetworkError: "FinalizeDeviceLoginNetworkError" case .finalizeDevicePollingLimitReached: "FinalizeDevicePollingLimitReached" - case .getCredentialsUpgradeTokenNetworkError(let error): + case .getCredentialsUpgradeTokenNetworkError: "GetCredentialsUpgradeTokenNetworkError" case .getCredentialsScopeIsNotGranted: "GetCredentialsScopeIsNotGranted" @@ -63,11 +63,11 @@ extension AuthLoggable { "GetCredentialsClientUniqueKeyIsDifferent" case .getCredentialsUpgradeTokenNoTokenInResponse: "GetCredentialsUpgradeTokenNoTokenIn" - case .getCredentialsRefreshTokenNetworkError(let error): + case .getCredentialsRefreshTokenNetworkError: "GetCredentialsRefreshTokenNetworkError" - case .getCredentialsRefreshTokenWithClientCredentialsNetworkError(let error): + case .getCredentialsRefreshTokenWithClientCredentialsNetworkError: "GetCredentialsRefreshTokenWithClientCredentialsNetworkError" - case .authLogout(let reason, let error): + case .authLogout: "AuthLogout" } } From be8022bed455cef21fbe5f9ba0560045a7e84224 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 14:04:33 +0200 Subject: [PATCH 42/47] use new logging in all implemented scenarios --- Sources/Auth/Login/LoginRepository.swift | 6 +++--- Sources/Auth/TokenRepository.swift | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Auth/Login/LoginRepository.swift b/Sources/Auth/Login/LoginRepository.swift index 8f82ac8e..5dc21986 100644 --- a/Sources/Auth/Login/LoginRepository.swift +++ b/Sources/Auth/Login/LoginRepository.swift @@ -71,7 +71,7 @@ final class LoginRepository { case .success(let successData): try saveTokens(response: successData) case .failure(let error): - authConfig.logger?.log(AuthLoggable.finalizeLoginNetworkError(error: error)) + AuthLoggable.finalizeLoginNetworkError(error: error).log() } return response @@ -112,7 +112,7 @@ final class LoginRepository { } if case .failure(let error) = result { - authConfig.logger?.log(AuthLoggable.initializeDeviceLoginNetworkError(error: error)) + AuthLoggable.initializeDeviceLoginNetworkError(error: error).log() } return result @@ -132,7 +132,7 @@ final class LoginRepository { case .failure(let error): let loggable = error.subStatus?.description.isSubStatus(status: .expiredAccessToken) == true ? AuthLoggable.finalizeDevicePollingLimitReached : AuthLoggable.finalizeDeviceLoginNetworkError(error: error) - authConfig.logger?.log(loggable) + loggable.log() } return response diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index fbea4aa2..6e9fec7e 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -82,11 +82,11 @@ struct TokenRepository { switch (authResult, networkErrorLoggableBlock) { case (.failure(let error), _) where shouldLogoutWithLowerLevelTokenAfterUpdate(error: error): - authConfig.logger?.log(AuthLoggable.authLogout(reason: "User credentials were downgraded to client credentials after updating token", error: error)) + AuthLoggable.authLogout(reason: "User credentials were downgraded to client credentials after updating token", error: error).log() return .success(.init(authConfig: authConfig)) case (.failure(let error), .some(let networkErrorLoggableBlock)): - authConfig.logger?.log(networkErrorLoggableBlock(error)) + networkErrorLoggableBlock(error).log() default: break } @@ -94,7 +94,7 @@ struct TokenRepository { return authResult } - authConfig.logger?.log(AuthLoggable.authLogout(reason: "No refresh token or client secret available")) + AuthLoggable.authLogout(reason: "No refresh token or client secret available").log() return logout() } @@ -133,10 +133,10 @@ struct TokenRepository { switch result { case .success(let tokens): if tokens.credentials.token == nil { - authConfig.logger?.log(AuthLoggable.getCredentialsUpgradeTokenNoTokenInResponse) + AuthLoggable.getCredentialsUpgradeTokenNoTokenInResponse.log() } case .failure(let error): - authConfig.logger?.log(AuthLoggable.getCredentialsUpgradeTokenNetworkError(error: error)) + AuthLoggable.getCredentialsUpgradeTokenNetworkError(error: error).log() } return result From 56c37ba3781c62b8c8f539f323e86aa95c94e6c1 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 14:20:38 +0200 Subject: [PATCH 43/47] removed custom logging facade --- Sources/Auth/Model/AuthConfig.swift | 6 +----- Sources/Auth/Model/AuthLoggable.swift | 3 +-- Sources/Common/Logging/LogLevel.swift | 8 -------- Sources/Common/Logging/Loggable.swift | 16 ---------------- Sources/Common/Logging/Logger.swift | 6 ------ 5 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 Sources/Common/Logging/LogLevel.swift delete mode 100644 Sources/Common/Logging/Loggable.swift delete mode 100644 Sources/Common/Logging/Logger.swift diff --git a/Sources/Auth/Model/AuthConfig.swift b/Sources/Auth/Model/AuthConfig.swift index 5d526435..6b615a05 100644 --- a/Sources/Auth/Model/AuthConfig.swift +++ b/Sources/Auth/Model/AuthConfig.swift @@ -1,4 +1,3 @@ -import Common import Foundation import protocol Logging.LogHandler @@ -14,7 +13,6 @@ public struct AuthConfig { public let tidalLoginServiceBaseUri: String public let tidalAuthServiceBaseUri: String public let enableCertificatePinning: Bool - public let logger: Logger? public let logHandlerFactory: LogHandlerFactory? public init( @@ -27,7 +25,6 @@ public struct AuthConfig { tidalLoginServiceBaseUri: String = "https://login.tidal.com", tidalAuthServiceBaseUri: String = "https://auth.tidal.com", enableCertificatePinning: Bool = true, - logger: Logger? = nil, logHandlerFactory: LogHandlerFactory? = nil ) { self.clientId = clientId @@ -39,7 +36,6 @@ public struct AuthConfig { self.tidalLoginServiceBaseUri = tidalLoginServiceBaseUri self.tidalAuthServiceBaseUri = tidalAuthServiceBaseUri self.enableCertificatePinning = enableCertificatePinning - self.logger = logger - self.logHandlerFactory = logHandlerFactory + self.logHandlerFactory = logHandlerFactory } } diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 7c58802f..6b3687b0 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -1,8 +1,7 @@ -import Common import Logging import Foundation -public enum AuthLoggable: Loggable { +public enum AuthLoggable { // swiftlint:disable identifier_name case initializeDeviceLoginNetworkError(error: Error) case finalizeLoginNetworkError(error: Error) diff --git a/Sources/Common/Logging/LogLevel.swift b/Sources/Common/Logging/LogLevel.swift deleted file mode 100644 index 8005a8da..00000000 --- a/Sources/Common/Logging/LogLevel.swift +++ /dev/null @@ -1,8 +0,0 @@ -import Foundation - -public enum LogLevel { - case debug - case info - case error - case warning -} diff --git a/Sources/Common/Logging/Loggable.swift b/Sources/Common/Logging/Loggable.swift deleted file mode 100644 index 96a8e15f..00000000 --- a/Sources/Common/Logging/Loggable.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Foundation - -public protocol Loggable { - var message: String { get } - var logLevel: LogLevel { get } -} - -public extension Loggable { - var message: String { - return "Loggable message: \(self)" - } - - var logLevel: LogLevel { - return .info - } -} diff --git a/Sources/Common/Logging/Logger.swift b/Sources/Common/Logging/Logger.swift deleted file mode 100644 index 59e09f94..00000000 --- a/Sources/Common/Logging/Logger.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -// Main interface for the logging. Clients can inject their own implementations -public protocol Logger { - func log(_ loggable: Loggable) -} From 3569020e68dacb39fd78c4b89691196981ed879f Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 14:23:26 +0200 Subject: [PATCH 44/47] removed logging test events --- Sources/Auth/TidalAuth.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Sources/Auth/TidalAuth.swift b/Sources/Auth/TidalAuth.swift index 5a7ddbc9..43f5c020 100644 --- a/Sources/Auth/TidalAuth.swift +++ b/Sources/Auth/TidalAuth.swift @@ -22,15 +22,6 @@ public class TidalAuth: Auth & CredentialsProvider { // if logger is not provided, use logger that does nothing let logHandlerFactory = config.logHandlerFactory ?? { _ in SwiftLogNoOpLogHandler() } LoggingSystem.bootstrap(logHandlerFactory) - - //TODO: remove - let throwable = NSError(domain: "test", code: 1, userInfo: ["abc": "def"]) - - let error = RetryableError(code: "123", subStatus: 1002, message: "Something went wrong", throwable: throwable) - - let authLoggable = AuthLoggable.finalizeLoginNetworkError(error: error) - - authLoggable.log() } private func provideLoginRepository(_ config: AuthConfig, tokensStore: TokensStore) -> LoginRepository { From 9ac760d7f0daa657f2985d3a9f789aada3711fd4 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 14:44:13 +0200 Subject: [PATCH 45/47] simplified Logging types usage --- Sources/Auth/Model/AuthLoggable.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 6b3687b0..69a5de35 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -44,7 +44,7 @@ extension AuthLoggable { private static let metadataErrorKey = "error" private static let metadataReasonKey = "reason" - var loggingMessage: Logging.Logger.Message { + var loggingMessage: Logger.Message { return switch self { case .initializeDeviceLoginNetworkError: "InitializeDeviceLoginNetworkError" @@ -71,8 +71,8 @@ extension AuthLoggable { } } - var loggingMetadata: Logging.Logger.Metadata { - var metadata = [String: Logging.Logger.MetadataValue]() + var loggingMetadata: Logger.Metadata { + var metadata = [String: Logger.MetadataValue]() switch self { case .initializeDeviceLoginNetworkError(let error), @@ -96,7 +96,7 @@ extension AuthLoggable { } func log() { - var logger = Logging.Logger(label: "auth_logger") + var logger = Logger(label: "auth_logger") logger.logLevel = .trace logger.log(level: .error, loggingMessage, metadata: loggingMetadata) } From 8a86bf835272af65df4294f9e96edb8d8a8f700d Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 15:04:44 +0200 Subject: [PATCH 46/47] AuthLoggable - cleanup --- Sources/Auth/Model/AuthLoggable.swift | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index 69a5de35..d3519376 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -1,7 +1,7 @@ import Logging import Foundation -public enum AuthLoggable { +enum AuthLoggable { // swiftlint:disable identifier_name case initializeDeviceLoginNetworkError(error: Error) case finalizeLoginNetworkError(error: Error) @@ -15,29 +15,6 @@ public enum AuthLoggable { case getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: Error) case authLogout(reason: String, error: Error? = nil) // swiftlint:enable identifier_name - - public var message: String { - let associatedValuesPart = switch self { - case .initializeDeviceLoginNetworkError(let error), - .finalizeLoginNetworkError(let error), - .finalizeDeviceLoginNetworkError(let error), - .getCredentialsUpgradeTokenNetworkError(let error), - .getCredentialsRefreshTokenNetworkError(let error), - .getCredentialsRefreshTokenWithClientCredentialsNetworkError(let error): - errorMessagePart(error: error) - case .authLogout(let reason, let error): - ", reason: \(reason)\(errorMessagePart(error: error))" - default: - "" - } - - return "\(self)\(associatedValuesPart)" - } - - private func errorMessagePart(error: Error?) -> String { - guard let error = error else { return "" } - return ", error: \(error.localizedDescription)" - } } extension AuthLoggable { From 232bdd4139fd4f7fa860c36febf0effdf566f5f6 Mon Sep 17 00:00:00 2001 From: Evgenii Kononenko Date: Tue, 30 Jul 2024 15:22:50 +0200 Subject: [PATCH 47/47] improved log level + log when refresh token is not available --- Sources/Auth/Model/AuthLoggable.swift | 16 +++++++++++++++- Sources/Auth/TokenRepository.swift | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Sources/Auth/Model/AuthLoggable.swift b/Sources/Auth/Model/AuthLoggable.swift index d3519376..c0e16bb5 100644 --- a/Sources/Auth/Model/AuthLoggable.swift +++ b/Sources/Auth/Model/AuthLoggable.swift @@ -14,9 +14,11 @@ enum AuthLoggable { case getCredentialsRefreshTokenNetworkError(error: Error) case getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: Error) case authLogout(reason: String, error: Error? = nil) + case getCredentialsRefreshTokenIsNotAvailable // swiftlint:enable identifier_name } +// MARK: - Logging extension AuthLoggable { private static let metadataErrorKey = "error" private static let metadataReasonKey = "reason" @@ -43,6 +45,8 @@ extension AuthLoggable { "GetCredentialsRefreshTokenNetworkError" case .getCredentialsRefreshTokenWithClientCredentialsNetworkError: "GetCredentialsRefreshTokenWithClientCredentialsNetworkError" + case .getCredentialsRefreshTokenIsNotAvailable: + "getCredentialsRefreshTokenIsNotAvailable" case .authLogout: "AuthLogout" } @@ -72,9 +76,19 @@ extension AuthLoggable { return metadata } + var logLevel: Logger.Level { + switch self { + case .getCredentialsRefreshTokenIsNotAvailable, .finalizeDevicePollingLimitReached: + return .notice + default: + return .error + } + } + func log() { var logger = Logger(label: "auth_logger") + // IIUC, this is a minimum level for the logger logger.logLevel = .trace - logger.log(level: .error, loggingMessage, metadata: loggingMetadata) + logger.log(level: logLevel, loggingMessage, metadata: loggingMetadata) } } diff --git a/Sources/Auth/TokenRepository.swift b/Sources/Auth/TokenRepository.swift index 6e9fec7e..793c88d1 100644 --- a/Sources/Auth/TokenRepository.swift +++ b/Sources/Auth/TokenRepository.swift @@ -70,8 +70,8 @@ struct TokenRepository { refreshCredentialsBlock = { await refreshUserAccessToken(refreshToken: refreshToken) } networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenNetworkError(error: $0) } } else if let clientSecret = authConfig.clientSecret { - // TODO: Log this as well // if nothing is stored, we will try and refresh using a client secret + AuthLoggable.getCredentialsRefreshTokenIsNotAvailable.log() refreshCredentialsBlock = { await getClientAccessToken(clientSecret: clientSecret) } networkErrorLoggableBlock = { AuthLoggable.getCredentialsRefreshTokenWithClientCredentialsNetworkError(error: $0) } }