Skip to content

Commit

Permalink
refactor: new HTTPClient type (#361)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
grdsdev authored May 8, 2024
1 parent c320d20 commit e5c564a
Show file tree
Hide file tree
Showing 36 changed files with 1,232 additions and 469 deletions.
3 changes: 3 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ let package = Package(
name: "FunctionsTests",
dependencies: [
"Functions",
"TestHelpers",
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
.product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
]
),
.testTarget(
Expand Down
5 changes: 3 additions & 2 deletions Sources/Auth/AuthAdmin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import _Helpers
import Foundation

public struct AuthAdmin: Sendable {
var configuration: AuthClient.Configuration { Current.configuration }
var api: APIClient { Current.api }
var encoder: JSONEncoder { Current.encoder }

Expand All @@ -21,8 +22,8 @@ public struct AuthAdmin: Sendable {
/// - Warning: Never expose your `service_role` key on the client.
public func deleteUser(id: String, shouldSoftDelete: Bool = false) async throws {
_ = try await api.execute(
Request(
path: "/admin/users/\(id)",
HTTPRequest(
url: configuration.url.appendingPathComponent("admin/users/\(id)"),
method: .delete,
body: encoder.encode(
DeleteUserRequest(shouldSoftDelete: shouldSoftDelete)
Expand Down
76 changes: 42 additions & 34 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public final class AuthClient: Sendable {
configuration: configuration,
sessionRefresher: SessionRefresher { [weak self] in
try await self?.refreshSession(refreshToken: $0) ?? .empty
}
},
http: HTTPClient(configuration: configuration)
)
}

Expand Down Expand Up @@ -105,7 +106,7 @@ public final class AuthClient: Sendable {

return try await _signUp(
request: .init(
path: "/signup",
url: configuration.url.appendingPathComponent("signup"),
method: .post,
query: [
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
Expand Down Expand Up @@ -141,7 +142,7 @@ public final class AuthClient: Sendable {
) async throws -> AuthResponse {
try await _signUp(
request: .init(
path: "/signup",
url: configuration.url.appendingPathComponent("signup"),
method: .post,
body: configuration.encoder.encode(
SignUpRequest(
Expand All @@ -155,7 +156,7 @@ public final class AuthClient: Sendable {
)
}

private func _signUp(request: Request) async throws -> AuthResponse {
private func _signUp(request: HTTPRequest) async throws -> AuthResponse {
await sessionManager.remove()
let response = try await api.execute(request).decoded(
as: AuthResponse.self,
Expand All @@ -179,7 +180,7 @@ public final class AuthClient: Sendable {
) async throws -> Session {
try await _signIn(
request: .init(
path: "/token",
url: configuration.url.appendingPathComponent("token"),
method: .post,
query: [URLQueryItem(name: "grant_type", value: "password")],
body: configuration.encoder.encode(
Expand All @@ -202,7 +203,7 @@ public final class AuthClient: Sendable {
) async throws -> Session {
try await _signIn(
request: .init(
path: "/token",
url: configuration.url.appendingPathComponent("token"),
method: .post,
query: [URLQueryItem(name: "grant_type", value: "password")],
body: configuration.encoder.encode(
Expand All @@ -222,7 +223,7 @@ public final class AuthClient: Sendable {
public func signInWithIdToken(credentials: OpenIDConnectCredentials) async throws -> Session {
try await _signIn(
request: .init(
path: "/token",
url: configuration.url.appendingPathComponent("token"),
method: .post,
query: [URLQueryItem(name: "grant_type", value: "id_token")],
body: configuration.encoder.encode(credentials)
Expand All @@ -242,8 +243,8 @@ public final class AuthClient: Sendable {
captchaToken: String? = nil
) async throws -> Session {
try await _signIn(
request: Request(
path: "/signup",
request: HTTPRequest(
url: configuration.url.appendingPathComponent("signup"),
method: .post,
body: configuration.encoder.encode(
SignUpRequest(
Expand All @@ -255,7 +256,7 @@ public final class AuthClient: Sendable {
)
}

private func _signIn(request: Request) async throws -> Session {
private func _signIn(request: HTTPRequest) async throws -> Session {
await sessionManager.remove()

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

_ = try await api.execute(
.init(
path: "/otp",
url: configuration.url.appendingPathComponent("otp"),
method: .post,
query: [
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
Expand Down Expand Up @@ -336,7 +337,7 @@ public final class AuthClient: Sendable {
await sessionManager.remove()
_ = try await api.execute(
.init(
path: "/otp",
url: configuration.url.appendingPathComponent("otp"),
method: .post,
body: configuration.encoder.encode(
OTPParams(
Expand Down Expand Up @@ -368,8 +369,8 @@ public final class AuthClient: Sendable {
let (codeChallenge, codeChallengeMethod) = prepareForPKCE()

return try await api.execute(
Request(
path: "/sso",
HTTPRequest(
url: configuration.url.appendingPathComponent("sso"),
method: .post,
body: configuration.encoder.encode(
SignInWithSSORequest(
Expand Down Expand Up @@ -403,8 +404,8 @@ public final class AuthClient: Sendable {
let (codeChallenge, codeChallengeMethod) = prepareForPKCE()

return try await api.execute(
Request(
path: "/sso",
HTTPRequest(
url: configuration.url.appendingPathComponent("sso"),
method: .post,
body: configuration.encoder.encode(
SignInWithSSORequest(
Expand All @@ -429,7 +430,7 @@ public final class AuthClient: Sendable {

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

let user = try await api.execute(
.init(
path: "/user",
url: configuration.url.appendingPathComponent("user"),
method: .get,
headers: ["Authorization": "\(tokenType) \(accessToken)"]
)
Expand Down Expand Up @@ -703,7 +704,7 @@ public final class AuthClient: Sendable {

try await api.authorizedExecute(
.init(
path: "/logout",
url: configuration.url.appendingPathComponent("logout"),
method: .post,
query: [URLQueryItem(name: "scope", value: scope.rawValue)]
)
Expand Down Expand Up @@ -737,7 +738,7 @@ public final class AuthClient: Sendable {
) async throws -> AuthResponse {
try await _verifyOTP(
request: .init(
path: "/verify",
url: configuration.url.appendingPathComponent("verify"),
method: .post,
query: [
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
Expand Down Expand Up @@ -770,7 +771,7 @@ public final class AuthClient: Sendable {
) async throws -> AuthResponse {
try await _verifyOTP(
request: .init(
path: "/verify",
url: configuration.url.appendingPathComponent("verify"),
method: .post,
body: configuration.encoder.encode(
VerifyOTPParams.mobile(
Expand All @@ -788,7 +789,7 @@ public final class AuthClient: Sendable {
}

private func _verifyOTP(
request: Request,
request: HTTPRequest,
shouldRemoveSession: Bool
) async throws -> AuthResponse {
if shouldRemoveSession {
Expand Down Expand Up @@ -823,8 +824,8 @@ public final class AuthClient: Sendable {
}

_ = try await api.execute(
Request(
path: "/resend",
HTTPRequest(
url: configuration.url.appendingPathComponent("resend"),
method: .post,
query: [
(emailRedirectTo ?? configuration.redirectToURL).map { URLQueryItem(
Expand Down Expand Up @@ -860,8 +861,8 @@ public final class AuthClient: Sendable {
}

return try await api.execute(
Request(
path: "/resend",
HTTPRequest(
url: configuration.url.appendingPathComponent("resend"),
method: .post,
body: configuration.encoder.encode(
ResendMobileParams(
Expand All @@ -878,7 +879,10 @@ public final class AuthClient: Sendable {
/// Sends a re-authentication OTP to the user's email or phone number.
public func reauthenticate() async throws {
try await api.authorizedExecute(
Request(path: "/reauthenticate", method: .get)
HTTPRequest(
url: configuration.url.appendingPathComponent("reauthenticate"),
method: .get
)
)
}

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

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

var session = try await sessionManager.session()
let updatedUser = try await api.authorizedExecute(
.init(path: "/user", method: .put, body: configuration.encoder.encode(user))
.init(
url: configuration.url.appendingPathComponent("user"),
method: .put,
body: configuration.encoder.encode(user)
)
).decoded(as: User.self, decoder: configuration.decoder)
session.user = updatedUser
try await sessionManager.update(session)
Expand Down Expand Up @@ -954,7 +962,7 @@ public final class AuthClient: Sendable {
}

let response = try await api.authorizedExecute(
Request(
HTTPRequest(
url: url,
method: .get
)
Expand All @@ -968,8 +976,8 @@ public final class AuthClient: Sendable {
/// with that identity once it's unlinked.
public func unlinkIdentity(_ identity: UserIdentity) async throws {
try await api.authorizedExecute(
Request(
path: "/user/identities/\(identity.identityId)",
HTTPRequest(
url: configuration.url.appendingPathComponent("user/identities/\(identity.identityId)"),
method: .delete
)
)
Expand All @@ -985,7 +993,7 @@ public final class AuthClient: Sendable {

_ = try await api.execute(
.init(
path: "/recover",
url: configuration.url.appendingPathComponent("recover"),
method: .post,
query: [
(redirectTo ?? configuration.redirectToURL).map { URLQueryItem(
Expand Down Expand Up @@ -1019,7 +1027,7 @@ public final class AuthClient: Sendable {

let session = try await api.execute(
.init(
path: "/token",
url: configuration.url.appendingPathComponent("token"),
method: .post,
query: [URLQueryItem(name: "grant_type", value: "refresh_token")],
body: configuration.encoder.encode(credentials)
Expand Down
21 changes: 15 additions & 6 deletions Sources/Auth/AuthMFA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation

/// Contains the full multi-factor authentication API.
public struct AuthMFA: Sendable {
var configuration: AuthClient.Configuration { Current.configuration }
var api: APIClient { Current.api }
var encoder: JSONEncoder { Current.encoder }
var decoder: JSONDecoder { Current.decoder }
Expand All @@ -22,8 +23,9 @@ public struct AuthMFA: Sendable {
/// - Returns: An authentication response after enrolling the factor.
public func enroll(params: MFAEnrollParams) async throws -> AuthMFAEnrollResponse {
try await api.authorizedExecute(
Request(
path: "/factors", method: .post,
HTTPRequest(
url: configuration.url.appendingPathComponent("factors"),
method: .post,
body: encoder.encode(params)
)
)
Expand All @@ -36,7 +38,10 @@ public struct AuthMFA: Sendable {
/// - Returns: An authentication response with the challenge information.
public func challenge(params: MFAChallengeParams) async throws -> AuthMFAChallengeResponse {
try await api.authorizedExecute(
Request(path: "/factors/\(params.factorId)/challenge", method: .post)
HTTPRequest(
url: configuration.url.appendingPathComponent("factors/\(params.factorId)/challenge"),
method: .post
)
)
.decoded(decoder: decoder)
}
Expand All @@ -48,8 +53,9 @@ public struct AuthMFA: Sendable {
/// - Returns: An authentication response after verifying the factor.
public func verify(params: MFAVerifyParams) async throws -> AuthMFAVerifyResponse {
let response: AuthMFAVerifyResponse = try await api.authorizedExecute(
Request(
path: "/factors/\(params.factorId)/verify", method: .post,
HTTPRequest(
url: configuration.url.appendingPathComponent("factors/\(params.factorId)/verify"),
method: .post,
body: encoder.encode(params)
)
).decoded(decoder: decoder)
Expand All @@ -69,7 +75,10 @@ public struct AuthMFA: Sendable {
@discardableResult
public func unenroll(params: MFAUnenrollParams) async throws -> AuthMFAUnenrollResponse {
try await api.authorizedExecute(
Request(path: "/factors/\(params.factorId)", method: .delete)
HTTPRequest(
url: configuration.url.appendingPathComponent("factors/\(params.factorId)"),
method: .delete
)
)
.decoded(decoder: decoder)
}
Expand Down
Loading

0 comments on commit e5c564a

Please sign in to comment.