Convert the last completion-based API in Account to async await.

This commit is contained in:
Brent Simmons 2024-04-05 18:16:52 -07:00
parent cf855466b3
commit ad151b5fa4
4 changed files with 36 additions and 44 deletions

View File

@ -400,8 +400,7 @@ public enum FetchType {
client: OAuthAuthorizationClient, client: OAuthAuthorizationClient,
accountType: AccountType, accountType: AccountType,
transport: Transport = URLSession.webserviceTransport(), transport: Transport = URLSession.webserviceTransport(),
secretsProvider: SecretsProvider, secretsProvider: SecretsProvider) async throws -> OAuthAuthorizationGrant {
completion: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ()) {
let grantingType: OAuthAuthorizationGranting.Type let grantingType: OAuthAuthorizationGranting.Type
switch accountType { switch accountType {
@ -411,7 +410,7 @@ public enum FetchType {
fatalError("\(accountType) does not support OAuth authorization code granting.") fatalError("\(accountType) does not support OAuth authorization code granting.")
} }
grantingType.requestOAuthAccessToken(with: response, transport: transport, secretsProvider: secretsProvider, completion: completion) return try await grantingType.requestOAuthAccessToken(with: response, transport: transport, secretsProvider: secretsProvider)
} }
public func receiveRemoteNotification(userInfo: [AnyHashable: Any]) async { public func receiveRemoteNotification(userInfo: [AnyHashable: Any]) async {

View File

@ -38,30 +38,34 @@ extension FeedlyAccountDelegate: OAuthAuthorizationGranting {
return FeedlyAPICaller.authorizationCodeUrlRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents) return FeedlyAPICaller.authorizationCodeUrlRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents)
} }
static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, secretsProvider: SecretsProvider, completion: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ()) { static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: any Web.Transport, secretsProvider: any Secrets.SecretsProvider) async throws -> OAuthAuthorizationGrant {
let client = environment.oauthAuthorizationClient(secretsProvider: secretsProvider) let client = environment.oauthAuthorizationClient(secretsProvider: secretsProvider)
let request = OAuthAccessTokenRequest(authorizationResponse: response, let request = OAuthAccessTokenRequest(authorizationResponse: response,
scope: oauthAuthorizationGrantScope, scope: oauthAuthorizationGrantScope,
client: client) client: client)
let caller = FeedlyAPICaller(transport: transport, api: environment, secretsProvider: secretsProvider) let caller = FeedlyAPICaller(transport: transport, api: environment, secretsProvider: secretsProvider)
caller.requestAccessToken(request) { result in
switch result { return try await withCheckedThrowingContinuation { continuation in
case .success(let response): caller.requestAccessToken(request) { result in
let accessToken = Credentials(type: .oauthAccessToken, username: response.id, secret: response.accessToken) switch result {
case .success(let response):
let refreshToken: Credentials? = { let accessToken = Credentials(type: .oauthAccessToken, username: response.id, secret: response.accessToken)
guard let token = response.refreshToken else {
return nil let refreshToken: Credentials? = {
} guard let token = response.refreshToken else {
return Credentials(type: .oauthRefreshToken, username: response.id, secret: token) return nil
}() }
return Credentials(type: .oauthRefreshToken, username: response.id, secret: token)
let grant = OAuthAuthorizationGrant(accessToken: accessToken, refreshToken: refreshToken) }()
completion(.success(grant)) let grant = OAuthAuthorizationGrant(accessToken: accessToken, refreshToken: refreshToken)
case .failure(let error): continuation.resume(returning: grant)
completion(.failure(error))
case .failure(let error):
continuation.resume(throwing: error)
}
} }
} }
} }

View File

@ -101,7 +101,8 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError {
} }
private func didEndAuthentication(url: URL?, error: Error?) { private func didEndAuthentication(url: URL?, error: Error?) {
MainActor.assumeIsolated {
Task { @MainActor in
guard !isCanceled else { guard !isCanceled else {
didFinish() didFinish()
return return
@ -109,15 +110,16 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError {
do { do {
guard let url = url else { guard let url = url else {
if let error = error { if let error {
throw error throw error
} }
throw URLError(.badURL) throw URLError(.badURL)
} }
let response = try OAuthAuthorizationResponse(url: url, client: oauthClient) let response = try OAuthAuthorizationResponse(url: url, client: self.oauthClient)
Account.requestOAuthAccessToken(with: response, client: oauthClient, accountType: accountType, secretsProvider: secretsProvider, completion: didEndRequestingAccessToken(_:)) let tokenResponse = try await Account.requestOAuthAccessToken(with: response, client: oauthClient, accountType: accountType, secretsProvider: secretsProvider)
saveAccount(for: tokenResponse)
} catch is ASWebAuthenticationSessionError { } catch is ASWebAuthenticationSessionError {
didFinish() // Primarily, cancellation. didFinish() // Primarily, cancellation.
@ -127,7 +129,8 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError {
} }
} }
} }
public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
guard let anchor = presentationAnchor else { guard let anchor = presentationAnchor else {
fatalError("\(self) has outlived presentation anchor.") fatalError("\(self) has outlived presentation anchor.")
@ -135,20 +138,6 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError {
return anchor return anchor
} }
@MainActor private func didEndRequestingAccessToken(_ result: Result<OAuthAuthorizationGrant, Error>) {
guard !isCanceled else {
didFinish()
return
}
switch result {
case .success(let tokenResponse):
saveAccount(for: tokenResponse)
case .failure(let error):
didFinish(error)
}
}
@MainActor private func saveAccount(for grant: OAuthAuthorizationGrant) { @MainActor private func saveAccount(for grant: OAuthAuthorizationGrant) {
guard !AccountManager.shared.duplicateServiceAccount(type: .feedly, username: grant.accessToken.username) else { guard !AccountManager.shared.duplicateServiceAccount(type: .feedly, username: grant.accessToken.username) else {
didFinish(OAuthAccountAuthorizationOperationError.duplicateAccount) didFinish(OAuthAccountAuthorizationOperationError.duplicateAccount)

View File

@ -167,7 +167,7 @@ public protocol OAuthAuthorizationCodeGrantRequesting {
protocol OAuthAuthorizationGranting: AccountDelegate { protocol OAuthAuthorizationGranting: AccountDelegate {
static func oauthAuthorizationCodeGrantRequest(secretsProvider: SecretsProvider) -> URLRequest @MainActor static func oauthAuthorizationCodeGrantRequest(secretsProvider: SecretsProvider) -> URLRequest
static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, secretsProvider: SecretsProvider, completion: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ()) @MainActor static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, secretsProvider: SecretsProvider) async throws -> OAuthAuthorizationGrant
} }