Skip to content

Commit e5c564a

Browse files
authored
refactor: new HTTPClient type (#361)
* refactor: add new HTTPClient type * test: implement HTTPClientMock * refactor(storage): use new HTTPClient on storage methods * refactor: separate http types into multiple files * improve HTTPClientMock * adopt new type in all packages * Remove old HTTP client * rename _HTTPClient * Remove ambiguous access to NSEC_PER_SEC * Move stringfy function * fix after rebase
1 parent c320d20 commit e5c564a

36 files changed

+1232
-469
lines changed

Package.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ let package = Package(
6767
name: "FunctionsTests",
6868
dependencies: [
6969
"Functions",
70+
"TestHelpers",
7071
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
72+
.product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
73+
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
7174
]
7275
),
7376
.testTarget(

Sources/Auth/AuthAdmin.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import _Helpers
99
import Foundation
1010

1111
public struct AuthAdmin: Sendable {
12+
var configuration: AuthClient.Configuration { Current.configuration }
1213
var api: APIClient { Current.api }
1314
var encoder: JSONEncoder { Current.encoder }
1415

@@ -21,8 +22,8 @@ public struct AuthAdmin: Sendable {
2122
/// - Warning: Never expose your `service_role` key on the client.
2223
public func deleteUser(id: String, shouldSoftDelete: Bool = false) async throws {
2324
_ = try await api.execute(
24-
Request(
25-
path: "/admin/users/\(id)",
25+
HTTPRequest(
26+
url: configuration.url.appendingPathComponent("admin/users/\(id)"),
2627
method: .delete,
2728
body: encoder.encode(
2829
DeleteUserRequest(shouldSoftDelete: shouldSoftDelete)

Sources/Auth/AuthClient.swift

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public final class AuthClient: Sendable {
4343
configuration: configuration,
4444
sessionRefresher: SessionRefresher { [weak self] in
4545
try await self?.refreshSession(refreshToken: $0) ?? .empty
46-
}
46+
},
47+
http: HTTPClient(configuration: configuration)
4748
)
4849
}
4950

@@ -105,7 +106,7 @@ public final class AuthClient: Sendable {
105106

106107
return try await _signUp(
107108
request: .init(
108-
path: "/signup",
109+
url: configuration.url.appendingPathComponent("signup"),
109110
method: .post,
110111
query: [
111112
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
@@ -141,7 +142,7 @@ public final class AuthClient: Sendable {
141142
) async throws -> AuthResponse {
142143
try await _signUp(
143144
request: .init(
144-
path: "/signup",
145+
url: configuration.url.appendingPathComponent("signup"),
145146
method: .post,
146147
body: configuration.encoder.encode(
147148
SignUpRequest(
@@ -155,7 +156,7 @@ public final class AuthClient: Sendable {
155156
)
156157
}
157158

158-
private func _signUp(request: Request) async throws -> AuthResponse {
159+
private func _signUp(request: HTTPRequest) async throws -> AuthResponse {
159160
await sessionManager.remove()
160161
let response = try await api.execute(request).decoded(
161162
as: AuthResponse.self,
@@ -179,7 +180,7 @@ public final class AuthClient: Sendable {
179180
) async throws -> Session {
180181
try await _signIn(
181182
request: .init(
182-
path: "/token",
183+
url: configuration.url.appendingPathComponent("token"),
183184
method: .post,
184185
query: [URLQueryItem(name: "grant_type", value: "password")],
185186
body: configuration.encoder.encode(
@@ -202,7 +203,7 @@ public final class AuthClient: Sendable {
202203
) async throws -> Session {
203204
try await _signIn(
204205
request: .init(
205-
path: "/token",
206+
url: configuration.url.appendingPathComponent("token"),
206207
method: .post,
207208
query: [URLQueryItem(name: "grant_type", value: "password")],
208209
body: configuration.encoder.encode(
@@ -222,7 +223,7 @@ public final class AuthClient: Sendable {
222223
public func signInWithIdToken(credentials: OpenIDConnectCredentials) async throws -> Session {
223224
try await _signIn(
224225
request: .init(
225-
path: "/token",
226+
url: configuration.url.appendingPathComponent("token"),
226227
method: .post,
227228
query: [URLQueryItem(name: "grant_type", value: "id_token")],
228229
body: configuration.encoder.encode(credentials)
@@ -242,8 +243,8 @@ public final class AuthClient: Sendable {
242243
captchaToken: String? = nil
243244
) async throws -> Session {
244245
try await _signIn(
245-
request: Request(
246-
path: "/signup",
246+
request: HTTPRequest(
247+
url: configuration.url.appendingPathComponent("signup"),
247248
method: .post,
248249
body: configuration.encoder.encode(
249250
SignUpRequest(
@@ -255,7 +256,7 @@ public final class AuthClient: Sendable {
255256
)
256257
}
257258

258-
private func _signIn(request: Request) async throws -> Session {
259+
private func _signIn(request: HTTPRequest) async throws -> Session {
259260
await sessionManager.remove()
260261

261262
let session = try await api.execute(request).decoded(
@@ -293,7 +294,7 @@ public final class AuthClient: Sendable {
293294

294295
_ = try await api.execute(
295296
.init(
296-
path: "/otp",
297+
url: configuration.url.appendingPathComponent("otp"),
297298
method: .post,
298299
query: [
299300
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
@@ -336,7 +337,7 @@ public final class AuthClient: Sendable {
336337
await sessionManager.remove()
337338
_ = try await api.execute(
338339
.init(
339-
path: "/otp",
340+
url: configuration.url.appendingPathComponent("otp"),
340341
method: .post,
341342
body: configuration.encoder.encode(
342343
OTPParams(
@@ -368,8 +369,8 @@ public final class AuthClient: Sendable {
368369
let (codeChallenge, codeChallengeMethod) = prepareForPKCE()
369370

370371
return try await api.execute(
371-
Request(
372-
path: "/sso",
372+
HTTPRequest(
373+
url: configuration.url.appendingPathComponent("sso"),
373374
method: .post,
374375
body: configuration.encoder.encode(
375376
SignInWithSSORequest(
@@ -403,8 +404,8 @@ public final class AuthClient: Sendable {
403404
let (codeChallenge, codeChallengeMethod) = prepareForPKCE()
404405

405406
return try await api.execute(
406-
Request(
407-
path: "/sso",
407+
HTTPRequest(
408+
url: configuration.url.appendingPathComponent("sso"),
408409
method: .post,
409410
body: configuration.encoder.encode(
410411
SignInWithSSORequest(
@@ -429,7 +430,7 @@ public final class AuthClient: Sendable {
429430

430431
let session: Session = try await api.execute(
431432
.init(
432-
path: "/token",
433+
url: configuration.url.appendingPathComponent("token"),
433434
method: .post,
434435
query: [URLQueryItem(name: "grant_type", value: "pkce")],
435436
body: configuration.encoder.encode(
@@ -622,7 +623,7 @@ public final class AuthClient: Sendable {
622623

623624
let user = try await api.execute(
624625
.init(
625-
path: "/user",
626+
url: configuration.url.appendingPathComponent("user"),
626627
method: .get,
627628
headers: ["Authorization": "\(tokenType) \(accessToken)"]
628629
)
@@ -703,7 +704,7 @@ public final class AuthClient: Sendable {
703704

704705
try await api.authorizedExecute(
705706
.init(
706-
path: "/logout",
707+
url: configuration.url.appendingPathComponent("logout"),
707708
method: .post,
708709
query: [URLQueryItem(name: "scope", value: scope.rawValue)]
709710
)
@@ -737,7 +738,7 @@ public final class AuthClient: Sendable {
737738
) async throws -> AuthResponse {
738739
try await _verifyOTP(
739740
request: .init(
740-
path: "/verify",
741+
url: configuration.url.appendingPathComponent("verify"),
741742
method: .post,
742743
query: [
743744
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
@@ -770,7 +771,7 @@ public final class AuthClient: Sendable {
770771
) async throws -> AuthResponse {
771772
try await _verifyOTP(
772773
request: .init(
773-
path: "/verify",
774+
url: configuration.url.appendingPathComponent("verify"),
774775
method: .post,
775776
body: configuration.encoder.encode(
776777
VerifyOTPParams.mobile(
@@ -788,7 +789,7 @@ public final class AuthClient: Sendable {
788789
}
789790

790791
private func _verifyOTP(
791-
request: Request,
792+
request: HTTPRequest,
792793
shouldRemoveSession: Bool
793794
) async throws -> AuthResponse {
794795
if shouldRemoveSession {
@@ -823,8 +824,8 @@ public final class AuthClient: Sendable {
823824
}
824825

825826
_ = try await api.execute(
826-
Request(
827-
path: "/resend",
827+
HTTPRequest(
828+
url: configuration.url.appendingPathComponent("resend"),
828829
method: .post,
829830
query: [
830831
(emailRedirectTo ?? configuration.redirectToURL).map { URLQueryItem(
@@ -860,8 +861,8 @@ public final class AuthClient: Sendable {
860861
}
861862

862863
return try await api.execute(
863-
Request(
864-
path: "/resend",
864+
HTTPRequest(
865+
url: configuration.url.appendingPathComponent("resend"),
865866
method: .post,
866867
body: configuration.encoder.encode(
867868
ResendMobileParams(
@@ -878,7 +879,10 @@ public final class AuthClient: Sendable {
878879
/// Sends a re-authentication OTP to the user's email or phone number.
879880
public func reauthenticate() async throws {
880881
try await api.authorizedExecute(
881-
Request(path: "/reauthenticate", method: .get)
882+
HTTPRequest(
883+
url: configuration.url.appendingPathComponent("reauthenticate"),
884+
method: .get
885+
)
882886
)
883887
}
884888

@@ -889,7 +893,7 @@ public final class AuthClient: Sendable {
889893
/// Should be used only when you require the most current user data. For faster results,
890894
/// session.user is recommended.
891895
public func user(jwt: String? = nil) async throws -> User {
892-
var request = Request(path: "/user", method: .get)
896+
var request = HTTPRequest(url: configuration.url.appendingPathComponent("user"), method: .get)
893897

894898
if let jwt {
895899
request.headers["Authorization"] = "Bearer \(jwt)"
@@ -912,7 +916,11 @@ public final class AuthClient: Sendable {
912916

913917
var session = try await sessionManager.session()
914918
let updatedUser = try await api.authorizedExecute(
915-
.init(path: "/user", method: .put, body: configuration.encoder.encode(user))
919+
.init(
920+
url: configuration.url.appendingPathComponent("user"),
921+
method: .put,
922+
body: configuration.encoder.encode(user)
923+
)
916924
).decoded(as: User.self, decoder: configuration.decoder)
917925
session.user = updatedUser
918926
try await sessionManager.update(session)
@@ -954,7 +962,7 @@ public final class AuthClient: Sendable {
954962
}
955963

956964
let response = try await api.authorizedExecute(
957-
Request(
965+
HTTPRequest(
958966
url: url,
959967
method: .get
960968
)
@@ -968,8 +976,8 @@ public final class AuthClient: Sendable {
968976
/// with that identity once it's unlinked.
969977
public func unlinkIdentity(_ identity: UserIdentity) async throws {
970978
try await api.authorizedExecute(
971-
Request(
972-
path: "/user/identities/\(identity.identityId)",
979+
HTTPRequest(
980+
url: configuration.url.appendingPathComponent("user/identities/\(identity.identityId)"),
973981
method: .delete
974982
)
975983
)
@@ -985,7 +993,7 @@ public final class AuthClient: Sendable {
985993

986994
_ = try await api.execute(
987995
.init(
988-
path: "/recover",
996+
url: configuration.url.appendingPathComponent("recover"),
989997
method: .post,
990998
query: [
991999
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
@@ -1019,7 +1027,7 @@ public final class AuthClient: Sendable {
10191027

10201028
let session = try await api.execute(
10211029
.init(
1022-
path: "/token",
1030+
url: configuration.url.appendingPathComponent("token"),
10231031
method: .post,
10241032
query: [URLQueryItem(name: "grant_type", value: "refresh_token")],
10251033
body: configuration.encoder.encode(credentials)

Sources/Auth/AuthMFA.swift

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Foundation
33

44
/// Contains the full multi-factor authentication API.
55
public struct AuthMFA: Sendable {
6+
var configuration: AuthClient.Configuration { Current.configuration }
67
var api: APIClient { Current.api }
78
var encoder: JSONEncoder { Current.encoder }
89
var decoder: JSONDecoder { Current.decoder }
@@ -22,8 +23,9 @@ public struct AuthMFA: Sendable {
2223
/// - Returns: An authentication response after enrolling the factor.
2324
public func enroll(params: MFAEnrollParams) async throws -> AuthMFAEnrollResponse {
2425
try await api.authorizedExecute(
25-
Request(
26-
path: "/factors", method: .post,
26+
HTTPRequest(
27+
url: configuration.url.appendingPathComponent("factors"),
28+
method: .post,
2729
body: encoder.encode(params)
2830
)
2931
)
@@ -36,7 +38,10 @@ public struct AuthMFA: Sendable {
3638
/// - Returns: An authentication response with the challenge information.
3739
public func challenge(params: MFAChallengeParams) async throws -> AuthMFAChallengeResponse {
3840
try await api.authorizedExecute(
39-
Request(path: "/factors/\(params.factorId)/challenge", method: .post)
41+
HTTPRequest(
42+
url: configuration.url.appendingPathComponent("factors/\(params.factorId)/challenge"),
43+
method: .post
44+
)
4045
)
4146
.decoded(decoder: decoder)
4247
}
@@ -48,8 +53,9 @@ public struct AuthMFA: Sendable {
4853
/// - Returns: An authentication response after verifying the factor.
4954
public func verify(params: MFAVerifyParams) async throws -> AuthMFAVerifyResponse {
5055
let response: AuthMFAVerifyResponse = try await api.authorizedExecute(
51-
Request(
52-
path: "/factors/\(params.factorId)/verify", method: .post,
56+
HTTPRequest(
57+
url: configuration.url.appendingPathComponent("factors/\(params.factorId)/verify"),
58+
method: .post,
5359
body: encoder.encode(params)
5460
)
5561
).decoded(decoder: decoder)
@@ -69,7 +75,10 @@ public struct AuthMFA: Sendable {
6975
@discardableResult
7076
public func unenroll(params: MFAUnenrollParams) async throws -> AuthMFAUnenrollResponse {
7177
try await api.authorizedExecute(
72-
Request(path: "/factors/\(params.factorId)", method: .delete)
78+
HTTPRequest(
79+
url: configuration.url.appendingPathComponent("factors/\(params.factorId)"),
80+
method: .delete
81+
)
7382
)
7483
.decoded(decoder: decoder)
7584
}

0 commit comments

Comments
 (0)