Skip to content

Commit

Permalink
Merge branch 'cecilia/BITS-2951/add-tests-for-playlog-3' into cecilia…
Browse files Browse the repository at this point in the history
…/BITS-2951/add-tests-for-playlog-4
  • Loading branch information
ceciliasaraiva authored Jul 31, 2024
2 parents 432861a + 42bef82 commit af27a74
Show file tree
Hide file tree
Showing 17 changed files with 231 additions and 69 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ on:
workflow_call:

pull_request:
branches: '*'
branches:
- '**'

merge_group:
types: [checks_requested]
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ on:
workflow_call:
workflow_dispatch:
pull_request:
branches: '*'
branches:
- '**'

merge_group:
types: [checks_requested]
Expand Down
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -100,6 +101,7 @@ let package = Package(
dependencies: [
.common,
.KeychainAccess,
.Logging
],
plugins: [
.plugin(name: "SwiftLint", package: "SwiftLintPlugin"),
Expand Down Expand Up @@ -155,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")
}
2 changes: 1 addition & 1 deletion Sources/Auth/Login/DeviceLoginPollHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct DeviceLoginPollHelper {
private class PollErrorPolicy: AuthErrorPolicy {
func handleError<T>(errorResponse: ErrorResponse?, throwable: Error?) -> AuthResult<T> {
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 })
Expand Down
27 changes: 21 additions & 6 deletions Sources/Auth/Login/LoginRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@ final class LoginRepository {
clientUniqueKey: authConfig.clientUniqueKey
)
}

if let successData = response.successData {

switch response {
case .success(let successData):
try saveTokens(response: successData)
case .failure(let error):
AuthLoggable.finalizeLoginNetworkError(error: error).log()
}

return response
}

Expand Down Expand Up @@ -99,14 +102,20 @@ final class LoginRepository {
}

func initializeDeviceLogin() async throws -> AuthResult<DeviceAuthorizationResponse> {
await retryWithPolicy(exponentialBackoffPolicy) {
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 {
AuthLoggable.initializeDeviceLoginNetworkError(error: error).log()
}

return result
}

func pollForDeviceLoginResponse(deviceCode: String) async throws -> AuthResult<LoginResponse> {
Expand All @@ -116,8 +125,14 @@ final class LoginRepository {
grantType: GRANT_TYPE_DEVICE_CODE,
retryPolicy: exponentialBackoffPolicy
)
if let data = response.successData {
try saveTokens(response: data)

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)

loggable.log()
}

return response
Expand Down
8 changes: 7 additions & 1 deletion Sources/Auth/Model/AuthConfig.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import Foundation
import protocol Logging.LogHandler

public typealias LogHandlerFactory = (String) -> LogHandler

public struct AuthConfig {
public let clientId: String
Expand All @@ -10,6 +13,7 @@ public struct AuthConfig {
public let tidalLoginServiceBaseUri: String
public let tidalAuthServiceBaseUri: String
public let enableCertificatePinning: Bool
public let logHandlerFactory: LogHandlerFactory?

public init(
clientId: String,
Expand All @@ -20,7 +24,8 @@ public struct AuthConfig {
scopes: Set<String> = [],
tidalLoginServiceBaseUri: String = "https://login.tidal.com",
tidalAuthServiceBaseUri: String = "https://auth.tidal.com",
enableCertificatePinning: Bool = true
enableCertificatePinning: Bool = true,
logHandlerFactory: LogHandlerFactory? = nil
) {
self.clientId = clientId
self.clientUniqueKey = clientUniqueKey
Expand All @@ -31,5 +36,6 @@ public struct AuthConfig {
self.tidalLoginServiceBaseUri = tidalLoginServiceBaseUri
self.tidalAuthServiceBaseUri = tidalAuthServiceBaseUri
self.enableCertificatePinning = enableCertificatePinning
self.logHandlerFactory = logHandlerFactory
}
}
94 changes: 94 additions & 0 deletions Sources/Auth/Model/AuthLoggable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import Logging
import Foundation

enum AuthLoggable {
// swiftlint:disable identifier_name
case initializeDeviceLoginNetworkError(error: Error)
case finalizeLoginNetworkError(error: Error)
case finalizeDeviceLoginNetworkError(error: Error)
case finalizeDevicePollingLimitReached
case getCredentialsUpgradeTokenNetworkError(error: Error)
case getCredentialsScopeIsNotGranted
case getCredentialsClientUniqueKeyIsDifferent
case getCredentialsUpgradeTokenNoTokenInResponse
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"

var loggingMessage: Logger.Message {
return switch self {
case .initializeDeviceLoginNetworkError:
"InitializeDeviceLoginNetworkError"
case .finalizeLoginNetworkError:
"FinalizeLoginNetworkError"
case .finalizeDeviceLoginNetworkError:
"FinalizeDeviceLoginNetworkError"
case .finalizeDevicePollingLimitReached:
"FinalizeDevicePollingLimitReached"
case .getCredentialsUpgradeTokenNetworkError:
"GetCredentialsUpgradeTokenNetworkError"
case .getCredentialsScopeIsNotGranted:
"GetCredentialsScopeIsNotGranted"
case .getCredentialsClientUniqueKeyIsDifferent:
"GetCredentialsClientUniqueKeyIsDifferent"
case .getCredentialsUpgradeTokenNoTokenInResponse:
"GetCredentialsUpgradeTokenNoTokenIn"
case .getCredentialsRefreshTokenNetworkError:
"GetCredentialsRefreshTokenNetworkError"
case .getCredentialsRefreshTokenWithClientCredentialsNetworkError:
"GetCredentialsRefreshTokenWithClientCredentialsNetworkError"
case .getCredentialsRefreshTokenIsNotAvailable:
"getCredentialsRefreshTokenIsNotAvailable"
case .authLogout:
"AuthLogout"
}
}

var loggingMetadata: Logger.Metadata {
var metadata = [String: 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
}

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: logLevel, loggingMessage, metadata: loggingMetadata)
}
}
2 changes: 1 addition & 1 deletion Sources/Auth/Model/Credentials.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public struct Credentials: Codable, Hashable {
init(
authConfig: AuthConfig,
response: RefreshResponse
) throws {
) {
self.init(
authConfig: authConfig,
grantedScopes: response.scopesString.toScopes(),
Expand Down
3 changes: 1 addition & 2 deletions Sources/Auth/Network/HTTPService.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Common
import Foundation

// MARK: - HTTPService
Expand Down Expand Up @@ -31,7 +30,7 @@ extension HTTPService {

return request
}

func executeRequest<T: Decodable>(_ request: URLRequest) async throws -> T {
// Send the request asynchronously
let (data, response) = try await URLSession.shared.data(for: request)
Expand Down
8 changes: 7 additions & 1 deletion Sources/Auth/TidalAuth.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Foundation
import Logging
import Common

// MARK: - TidalAuth

Expand All @@ -16,8 +18,12 @@ 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 {
LoginRepository(
authConfig: config,
Expand Down
Loading

0 comments on commit af27a74

Please sign in to comment.