Convert validateCredentials to async await.

This commit is contained in:
Brent Simmons 2024-04-05 21:29:19 -07:00
parent 1ad010d4f8
commit 61af38fabf
2 changed files with 60 additions and 93 deletions

View File

@ -128,11 +128,11 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
self.refreshProgress.completeTask()
self.caller.retrieveItemIDs(type: .allForAccount) { result in
self.refreshProgress.completeTask()
switch result {
case .success(let articleIDs):
Task { @MainActor in
try? await account.markAsRead(Set(articleIDs))
self.refreshArticleStatus(for: account) { _ in
@ -145,40 +145,33 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
}
}
case .failure(let error):
completion(.failure(error))
}
}
}
case .failure(let error):
DispatchQueue.main.async {
Task { @MainActor in
self.refreshProgress.clear()
let wrappedError = AccountError.wrappedError(error: error, account: account)
if wrappedError.isCredentialsError, let basicCredentials = try? account.retrieveCredentials(type: .readerBasic), let endpoint = account.endpointURL {
self.caller.credentials = basicCredentials
self.caller.validateCredentials(endpoint: endpoint) { result in
switch result {
case .success(let apiCredentials):
if let apiCredentials = apiCredentials {
DispatchQueue.main.async {
try? account.storeCredentials(apiCredentials)
self.caller.credentials = apiCredentials
self.refreshAll(for: account, completion: completion)
}
} else {
DispatchQueue.main.async {
completion(.failure(wrappedError))
}
}
case .failure:
DispatchQueue.main.async {
completion(.failure(wrappedError))
}
do {
if let apiCredentials = try await self.caller.validateCredentials(endpoint: endpoint) {
try? account.storeCredentials(apiCredentials)
self.caller.credentials = apiCredentials
self.refreshAll(for: account, completion: completion)
}
else {
completion(.failure(wrappedError))
}
} catch {
completion(.failure(wrappedError))
}
} else {
@ -186,9 +179,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
}
}
}
}
func syncArticleStatus(for account: Account) async throws {
@ -838,35 +829,15 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, secretsProvider: SecretsProvider) async throws -> Credentials? {
try await withCheckedThrowingContinuation { continuation in
self.validateCredentials(transport: transport, credentials: credentials, endpoint: endpoint, secretsProvider: secretsProvider) { result in
switch result {
case .success(let credentials):
continuation.resume(returning: credentials)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
private static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, secretsProvider: SecretsProvider, completion: @escaping (Result<Credentials?, Error>) -> Void) {
guard let endpoint = endpoint else {
completion(.failure(TransportError.noURL))
return
guard let endpoint else {
throw TransportError.noURL
}
let caller = ReaderAPICaller(transport: transport, secretsProvider: secretsProvider)
caller.credentials = credentials
caller.validateCredentials(endpoint: endpoint) { result in
DispatchQueue.main.async {
completion(result)
}
}
return try await caller.validateCredentials(endpoint: endpoint)
}
// MARK: Suspend and Resume (for iOS)

View File

@ -15,7 +15,7 @@ enum CreateReaderAPISubscriptionResult {
case notFound
}
final class ReaderAPICaller: NSObject {
@MainActor final class ReaderAPICaller: NSObject {
enum ItemIDType {
case unread
@ -93,55 +93,51 @@ final class ReaderAPICaller: NSObject {
transport.cancelAll()
}
func validateCredentials(endpoint: URL, completion: @escaping (Result<Credentials?, Error>) -> Void) {
guard let credentials = credentials else {
completion(.failure(CredentialsError.incompleteCredentials))
return
func validateCredentials(endpoint: URL) async throws -> Credentials? {
guard let credentials else {
throw CredentialsError.incompleteCredentials
}
var request = URLRequest(url: endpoint.appendingPathComponent(ReaderAPIEndpoints.login.rawValue), credentials: credentials)
addVariantHeaders(&request)
transport.send(request: request) { result in
switch result {
case .success(let (_, data)):
guard let resultData = data else {
completion(.failure(TransportError.noData))
break
}
// Convert the return data to UTF8 and then parse out the Auth token
guard let rawData = String(data: resultData, encoding: .utf8) else {
completion(.failure(TransportError.noData))
break
}
var authData: [String: String] = [:]
rawData.split(separator: "\n").forEach({ (line: Substring) in
let items = line.split(separator: "=").map{String($0)}
if items.count == 2 {
authData[items[0]] = items[1]
}
})
guard let authString = authData["Auth"] else {
completion(.failure(CredentialsError.incompleteCredentials))
break
}
// Save Auth Token for later use
self.credentials = Credentials(type: .readerAPIKey, username: credentials.username, secret: authString)
completion(.success(self.credentials))
case .failure(let error):
if let transportError = error as? TransportError, case .httpError(let code) = transportError, code == 404 {
completion(.failure(ReaderAPIAccountDelegateError.urlNotFound))
} else {
completion(.failure(error))
do {
let (_, data) = try await transport.send(request: request)
guard let data else {
throw TransportError.noData
}
// Convert the return data to UTF8 and then parse out the Auth token
guard let rawData = String(data: data, encoding: .utf8) else {
throw TransportError.noData
}
var authData: [String: String] = [:]
rawData.split(separator: "\n").forEach({ (line: Substring) in
let items = line.split(separator: "=").map{String($0)}
if items.count == 2 {
authData[items[0]] = items[1]
}
})
guard let authString = authData["Auth"] else {
throw CredentialsError.incompleteCredentials
}
// Save Auth Token for later use
self.credentials = Credentials(type: .readerAPIKey, username: credentials.username, secret: authString)
return self.credentials
} catch {
if let transportError = error as? TransportError, case .httpError(let code) = transportError, code == 404 {
throw ReaderAPIAccountDelegateError.urlNotFound
} else {
throw error
}
}
}
func requestAuthorizationToken(endpoint: URL, completion: @escaping (Result<String, Error>) -> Void) {