Skip to content

Commit a27cb4e

Browse files
committed
Make the „response provider“ generic for the returned data type
1 parent 892dbb0 commit a27cb4e

File tree

4 files changed

+66
-56
lines changed

4 files changed

+66
-56
lines changed

Sources/OAuthenticator/Authenticator.swift

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public enum AuthenticatorError: Error {
1818
}
1919

2020
/// Manage state required to executed authenticated URLRequests.
21-
public actor Authenticator {
21+
public actor Authenticator<UserDataType: Sendable> {
2222
public typealias UserAuthenticator = @Sendable (URL, String) async throws -> URL
2323
public typealias AuthenticationStatusHandler = (Result<Login, AuthenticatorError>) -> Void
2424

@@ -85,32 +85,32 @@ public actor Authenticator {
8585

8686
let config: Configuration
8787

88-
let urlLoader: URLResponseProvider
88+
let responseLoader: URLResponseProvider
89+
let userDataLoader: URLUserDataProvider<UserDataType>
8990
private var activeTokenTask: Task<Login, Error>?
9091
private var localLogin: Login?
9192

92-
public init(config: Configuration, urlLoader loader: URLResponseProvider? = nil) {
93+
public init(config: Configuration, responseLoader: URLResponseProvider? = nil, userDataLoader: @escaping URLUserDataProvider<UserDataType>) {
9394
self.config = config
9495

95-
self.urlLoader = loader ?? URLSession.defaultProvider
96+
self.responseLoader = responseLoader ?? URLSession.defaultProvider
97+
self.userDataLoader = userDataLoader
9698
}
9799

98-
/// A default `URLSession`-backed `URLResponseProvider`.
99-
@available(*, deprecated, message: "Please move to URLSession.defaultProvider")
100-
@MainActor
101-
public static let defaultResponseProvider: URLResponseProvider = {
102-
let session = URLSession(configuration: .default)
100+
public init(config: Configuration, urlLoader: URLResponseProvider? = nil) where UserDataType == Data {
101+
self.config = config
103102

104-
return session.responseProvider
105-
}()
103+
self.responseLoader = urlLoader ?? URLSession.defaultProvider
104+
self.userDataLoader = urlLoader ?? URLSession.defaultProvider
105+
}
106106

107107
/// Add authentication for `request`, execute it, and return its result.
108-
public func response(for request: URLRequest) async throws -> (Data, URLResponse) {
108+
public func response(for request: URLRequest) async throws -> (UserDataType, URLResponse) {
109109
let userAuthenticator = config.userAuthenticator
110110

111111
let login = try await loginTaskResult(manual: false, userAuthenticator: userAuthenticator)
112112

113-
let result = try await authedResponse(for: request, login: login)
113+
let result: (UserDataType, URLResponse) = try await authedResponse(for: request, login: login)
114114

115115
let action = try config.tokenHandling.responseStatusProvider(result)
116116

@@ -146,13 +146,13 @@ public actor Authenticator {
146146
}
147147
}
148148

149-
private func authedResponse(for request: URLRequest, login: Login) async throws -> (Data, URLResponse) {
149+
private func authedResponse(for request: URLRequest, login: Login) async throws -> (UserDataType, URLResponse) {
150150
var authedRequest = request
151151
let token = login.accessToken.value
152152

153153
authedRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
154154

155-
return try await urlLoader(authedRequest)
155+
return try await userDataLoader(authedRequest)
156156
}
157157

158158
/// Manually perform user authentication, if required.
@@ -161,6 +161,15 @@ public actor Authenticator {
161161
}
162162
}
163163

164+
/// A default `URLSession`-backed `URLResponseProvider`.
165+
@available(*, deprecated, message: "Please move to URLSession.defaultProvider")
166+
@MainActor
167+
public let defaultAuthenticatorResponseProvider: URLResponseProvider = {
168+
let session = URLSession(configuration: .default)
169+
170+
return session.responseProvider
171+
}()
172+
164173
extension Authenticator {
165174
private func retrieveLogin() async throws -> Login? {
166175
guard let storage = config.loginStorage else {
@@ -256,7 +265,7 @@ extension Authenticator {
256265
let scheme = try config.appCredentials.callbackURLScheme
257266

258267
let url = try await userAuthenticator(codeURL, scheme)
259-
let login = try await config.tokenHandling.loginProvider(url, config.appCredentials, codeURL, urlLoader)
268+
let login = try await config.tokenHandling.loginProvider(url, config.appCredentials, codeURL, responseLoader)
260269

261270
try await storeLogin(login)
262271

@@ -276,7 +285,7 @@ extension Authenticator {
276285
return nil
277286
}
278287

279-
let login = try await refreshProvider(login, config.appCredentials, urlLoader)
288+
let login = try await refreshProvider(login, config.appCredentials, responseLoader)
280289

281290
try await storeLogin(login)
282291

@@ -285,7 +294,7 @@ extension Authenticator {
285294
}
286295

287296
extension Authenticator {
288-
public nonisolated var responseProvider: URLResponseProvider {
297+
public nonisolated var responseProvider: URLUserDataProvider<UserDataType> {
289298
{ try await self.response(for: $0) }
290299
}
291300
}

Sources/OAuthenticator/Models.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Foundation
55
/// This is used to abstract the actual networking system from the underlying authentication
66
/// mechanism.
77
public typealias URLResponseProvider = @Sendable (URLRequest) async throws -> (Data, URLResponse)
8+
public typealias URLUserDataProvider<UserDataType: Sendable> = @Sendable (URLRequest) async throws -> (UserDataType, URLResponse)
89

910
public struct Token: Codable, Hashable, Sendable {
1011
public let value: String
@@ -97,7 +98,7 @@ public struct TokenHandling {
9798
public typealias AuthorizationURLProvider = @Sendable (AppCredentials) throws -> URL
9899
public typealias LoginProvider = @Sendable (URL, AppCredentials, URL, URLResponseProvider) async throws -> Login
99100
public typealias RefreshProvider = @Sendable (Login, AppCredentials, URLResponseProvider) async throws -> Login
100-
public typealias ResponseStatusProvider = @Sendable ((Data, URLResponse)) throws -> ResponseStatus
101+
public typealias ResponseStatusProvider = @Sendable ((any Sendable, URLResponse)) throws -> ResponseStatus
101102

102103
public let authorizationURLProvider: AuthorizationURLProvider
103104
public let loginProvider: LoginProvider
@@ -115,12 +116,12 @@ public struct TokenHandling {
115116
}
116117

117118
@Sendable
118-
public static func allResponsesValid(result: (Data, URLResponse)) throws -> ResponseStatus {
119+
public static func allResponsesValid<UserDataType: Sendable>(result: (UserDataType, URLResponse)) throws -> ResponseStatus {
119120
return .valid
120121
}
121122

122123
@Sendable
123-
public static func refreshOrAuthorizeWhenUnauthorized(result: (Data, URLResponse)) throws -> ResponseStatus {
124+
public static func refreshOrAuthorizeWhenUnauthorized<UserDataType: Sendable>(result: (UserDataType, URLResponse)) throws -> ResponseStatus {
124125
guard let response = result.1 as? HTTPURLResponse else {
125126
throw AuthenticatorError.httpResponseExpected
126127
}

Tests/OAuthenticatorTests/AuthenticatorTests.swift

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ final class AuthenticatorTests: XCTestCase {
101101
storeTokenExp.fulfill()
102102
}
103103

104-
let config = Authenticator.Configuration(
104+
let config = Authenticator<Data>.Configuration(
105105
appCredentials: Self.mockCredentials,
106106
loginStorage: storage,
107107
// loginStorage: nil,
@@ -148,10 +148,10 @@ final class AuthenticatorTests: XCTestCase {
148148
XCTFail()
149149
}
150150

151-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
152-
loginStorage: storage,
153-
tokenHandling: tokenHandling,
154-
userAuthenticator: Self.disabledUserAuthenticator)
151+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
152+
loginStorage: storage,
153+
tokenHandling: tokenHandling,
154+
userAuthenticator: Self.disabledUserAuthenticator)
155155

156156
let auth = Authenticator(config: config, urlLoader: mockLoader)
157157

@@ -200,10 +200,10 @@ final class AuthenticatorTests: XCTestCase {
200200
XCTAssertEqual(login.accessToken.value, "REFRESHED")
201201
}
202202

203-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
204-
loginStorage: storage,
205-
tokenHandling: tokenHandling,
206-
userAuthenticator: Self.disabledUserAuthenticator)
203+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
204+
loginStorage: storage,
205+
tokenHandling: tokenHandling,
206+
userAuthenticator: Self.disabledUserAuthenticator)
207207

208208
let auth = Authenticator(config: config, urlLoader: mockLoader)
209209

@@ -235,10 +235,10 @@ final class AuthenticatorTests: XCTestCase {
235235
return URL(string: "my://login")!
236236
}
237237

238-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
239-
tokenHandling: tokenHandling,
240-
mode: .manualOnly,
241-
userAuthenticator: mockUserAuthenticator)
238+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
239+
tokenHandling: tokenHandling,
240+
mode: .manualOnly,
241+
userAuthenticator: mockUserAuthenticator)
242242

243243
let loadExp = expectation(description: "load url")
244244
let mockLoader: URLResponseProvider = { request in
@@ -301,11 +301,11 @@ final class AuthenticatorTests: XCTestCase {
301301
}
302302

303303
// Configure Authenticator with result callback
304-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
305-
tokenHandling: tokenHandling,
306-
mode: .manualOnly,
307-
userAuthenticator: mockUserAuthenticator,
308-
authenticationStatusHandler: authenticationCallback)
304+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
305+
tokenHandling: tokenHandling,
306+
mode: .manualOnly,
307+
userAuthenticator: mockUserAuthenticator,
308+
authenticationStatusHandler: authenticationCallback)
309309

310310
let loadExp = expectation(description: "load url")
311311
let mockLoader: URLResponseProvider = { request in
@@ -357,11 +357,11 @@ final class AuthenticatorTests: XCTestCase {
357357
}
358358

359359
// Configure Authenticator with result callback
360-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
361-
tokenHandling: tokenHandling,
362-
mode: .manualOnly,
363-
userAuthenticator: Authenticator.failingUserAuthenticator,
364-
authenticationStatusHandler: authenticationCallback)
360+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
361+
tokenHandling: tokenHandling,
362+
mode: .manualOnly,
363+
userAuthenticator: Authenticator<Data>.failingUserAuthenticator,
364+
authenticationStatusHandler: authenticationCallback)
365365

366366
let auth = Authenticator(config: config, urlLoader: nil)
367367
do {
@@ -408,10 +408,10 @@ final class AuthenticatorTests: XCTestCase {
408408
XCTAssertEqual(login.accessToken.value, "REFRESHED")
409409
}
410410

411-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
412-
loginStorage: storage,
413-
tokenHandling: tokenHandling,
414-
userAuthenticator: Self.disabledUserAuthenticator)
411+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
412+
loginStorage: storage,
413+
tokenHandling: tokenHandling,
414+
userAuthenticator: Self.disabledUserAuthenticator)
415415

416416
let auth = Authenticator(config: config, urlLoader: mockLoader.responseProvider)
417417

@@ -468,10 +468,10 @@ final class AuthenticatorTests: XCTestCase {
468468
savedLogins.append(login)
469469
}
470470

471-
let config = Authenticator.Configuration(appCredentials: Self.mockCredentials,
472-
loginStorage: storage,
473-
tokenHandling: tokenHandling,
474-
userAuthenticator: Self.disabledUserAuthenticator)
471+
let config = Authenticator<Data>.Configuration(appCredentials: Self.mockCredentials,
472+
loginStorage: storage,
473+
tokenHandling: tokenHandling,
474+
userAuthenticator: Self.disabledUserAuthenticator)
475475

476476
let auth = Authenticator(config: config, urlLoader: mockLoader)
477477

Tests/OAuthenticatorTests/GoogleTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ final class GoogleTests: XCTestCase {
4444

4545
let creds = AppCredentials(clientId: "client_id", clientPassword: "client_pwd", scopes: ["scope1", "scope2"], callbackURL: callback!)
4646
let tokenHandling = GoogleAPI.googleAPITokenHandling(with: googleParameters)
47-
let config = Authenticator.Configuration(
47+
let config = Authenticator<Data>.Configuration(
4848
appCredentials: creds,
4949
tokenHandling: tokenHandling,
50-
userAuthenticator: Authenticator.failingUserAuthenticator
50+
userAuthenticator: Authenticator<Data>.failingUserAuthenticator
5151
)
5252

5353
// Validate URL is properly constructed
@@ -76,10 +76,10 @@ final class GoogleTests: XCTestCase {
7676

7777
let creds = AppCredentials(clientId: "client_id", clientPassword: "client_pwd", scopes: ["scope1", "scope2"], callbackURL: callback!)
7878
let tokenHandling = GoogleAPI.googleAPITokenHandling(with: googleParameters)
79-
let config = Authenticator.Configuration(
79+
let config = Authenticator<Data>.Configuration(
8080
appCredentials: creds,
8181
tokenHandling: tokenHandling,
82-
userAuthenticator: Authenticator.failingUserAuthenticator
82+
userAuthenticator: Authenticator<Data>.failingUserAuthenticator
8383
)
8484

8585
// Validate URL is properly constructed

0 commit comments

Comments
 (0)