Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace to HTTPTypes Components from Helpers Components #564

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
"version" : "1.3.3"
}
},
{
"identity" : "swift-http-types",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-http-types",
"state" : {
"revision" : "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd",
"version" : "1.3.0"
}
},
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
Expand Down
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ let package = Package(
.library(name: "Supabase", targets: ["Supabase", "Functions", "PostgREST", "Auth", "Realtime", "Storage"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-http-types.git", from: "1.3.0"),
.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0" ..< "4.0.0"),
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.1.0"),
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"),
Expand All @@ -33,6 +34,7 @@ let package = Package(
name: "Helpers",
dependencies: [
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
.product(name: "HTTPTypes", package: "swift-http-types"),
]
),
.testTarget(
Expand Down
10 changes: 8 additions & 2 deletions Sources/Auth/AuthAdmin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import Foundation
import Helpers
import HTTPTypes

public struct AuthAdmin: Sendable {
let clientID: AuthClientID
Expand Down Expand Up @@ -62,10 +63,10 @@ public struct AuthAdmin: Sendable {
users: response.users,
aud: response.aud,
lastPage: 0,
total: httpResponse.headers["x-total-count"].flatMap(Int.init) ?? 0
total: httpResponse.headers[.xTotalCount].flatMap(Int.init) ?? 0
)

let links = httpResponse.headers["link"]?.components(separatedBy: ",") ?? []
let links = httpResponse.headers[.link]?.components(separatedBy: ",") ?? []
if !links.isEmpty {
for link in links {
let page = link.components(separatedBy: ";")[0].components(separatedBy: "=")[1].prefix(while: \.isNumber)
Expand All @@ -82,3 +83,8 @@ public struct AuthAdmin: Sendable {
return pagination
}
}

extension HTTPField.Name {
static let xTotalCount = Self("x-total-count")!
static let link = Self("link")!
}
6 changes: 3 additions & 3 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ public final class AuthClient: Sendable {
.init(
url: configuration.url.appendingPathComponent("user"),
method: .get,
headers: ["Authorization": "\(tokenType) \(accessToken)"]
headers: [.authorization: "\(tokenType) \(accessToken)"]
)
).decoded(as: User.self, decoder: configuration.decoder)

Expand Down Expand Up @@ -803,7 +803,7 @@ public final class AuthClient: Sendable {
url: configuration.url.appendingPathComponent("logout"),
method: .post,
query: [URLQueryItem(name: "scope", value: scope.rawValue)],
headers: [.init(name: "Authorization", value: "Bearer \(accessToken)")]
headers: [.authorization: "Bearer \(accessToken)"]
)
)
} catch let AuthError.api(_, _, _, response) where [404, 403, 401].contains(response.statusCode) {
Expand Down Expand Up @@ -982,7 +982,7 @@ public final class AuthClient: Sendable {
var request = HTTPRequest(url: configuration.url.appendingPathComponent("user"), method: .get)

if let jwt {
request.headers["Authorization"] = "Bearer \(jwt)"
request.headers[.authorization] = "Bearer \(jwt)"
return try await api.execute(request).decoded(decoder: configuration.decoder)
}

Expand Down
19 changes: 10 additions & 9 deletions Sources/Auth/Internal/APIClient.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import Helpers
import HTTPTypes

extension HTTPClient {
init(configuration: AuthClient.Configuration) {
Expand Down Expand Up @@ -31,12 +32,12 @@ struct APIClient: Sendable {
Dependencies[clientID].http
}

func execute(_ request: HTTPRequest) async throws -> HTTPResponse {
func execute(_ request: Helpers.HTTPRequest) async throws -> Helpers.HTTPResponse {
var request = request
request.headers = HTTPHeaders(configuration.headers).merged(with: request.headers)
request.headers = HTTPFields(configuration.headers).merging(with: request.headers)

if request.headers[API_VERSION_HEADER_NAME] == nil {
request.headers[API_VERSION_HEADER_NAME] = API_VERSIONS[._20240101]!.name.rawValue
if request.headers[.apiVersionHeaderName] == nil {
request.headers[.apiVersionHeaderName] = API_VERSIONS[._20240101]!.name.rawValue
}

let response = try await http.send(request)
Expand All @@ -49,20 +50,20 @@ struct APIClient: Sendable {
}

@discardableResult
func authorizedExecute(_ request: HTTPRequest) async throws -> HTTPResponse {
func authorizedExecute(_ request: Helpers.HTTPRequest) async throws -> Helpers.HTTPResponse {
var sessionManager: SessionManager {
Dependencies[clientID].sessionManager
}

let session = try await sessionManager.session()

var request = request
request.headers["Authorization"] = "Bearer \(session.accessToken)"
request.headers[.authorization] = "Bearer \(session.accessToken)"

return try await execute(request)
}

func handleError(response: HTTPResponse) -> AuthError {
func handleError(response: Helpers.HTTPResponse) -> AuthError {
guard let error = try? response.decoded(
as: _RawAPIErrorResponse.self,
decoder: configuration.decoder
Expand Down Expand Up @@ -105,8 +106,8 @@ struct APIClient: Sendable {
}
}

private func parseResponseAPIVersion(_ response: HTTPResponse) -> Date? {
guard let apiVersion = response.headers[API_VERSION_HEADER_NAME] else { return nil }
private func parseResponseAPIVersion(_ response: Helpers.HTTPResponse) -> Date? {
guard let apiVersion = response.headers[.apiVersionHeaderName] else { return nil }

let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
Expand Down
6 changes: 6 additions & 0 deletions Sources/Auth/Internal/Contants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
//

import Foundation
import HTTPTypes

let EXPIRY_MARGIN: TimeInterval = 30
let STORAGE_KEY = "supabase.auth.token"

let API_VERSION_HEADER_NAME = "X-Supabase-Api-Version"

extension HTTPField.Name {
static let apiVersionHeaderName = HTTPField.Name(API_VERSION_HEADER_NAME)!
}

let API_VERSIONS: [APIVersion.Name: APIVersion] = [
._20240101: ._20240101,
]
Expand Down
25 changes: 13 additions & 12 deletions Sources/Functions/FunctionsClient.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ConcurrencyExtras
import Foundation
import Helpers
import HTTPTypes

#if canImport(FoundationNetworking)
import FoundationNetworking
Expand All @@ -25,12 +26,12 @@ public final class FunctionsClient: Sendable {

struct MutableState {
/// Headers to be included in the requests.
var headers = HTTPHeaders()
var headers = HTTPFields()
}

private let mutableState = LockIsolated(MutableState())

var headers: HTTPHeaders {
var headers: HTTPFields {
mutableState.headers
}

Expand Down Expand Up @@ -71,9 +72,9 @@ public final class FunctionsClient: Sendable {
self.http = http

mutableState.withValue {
$0.headers = HTTPHeaders(headers)
if $0.headers["X-Client-Info"] == nil {
$0.headers["X-Client-Info"] = "functions-swift/\(version)"
$0.headers = HTTPFields(headers)
if $0.headers[.xClientInfo] == nil {
$0.headers[.xClientInfo] = "functions-swift/\(version)"
}
}
}
Expand Down Expand Up @@ -102,9 +103,9 @@ public final class FunctionsClient: Sendable {
public func setAuth(token: String?) {
mutableState.withValue {
if let token {
$0.headers["Authorization"] = "Bearer \(token)"
$0.headers[.authorization] = "Bearer \(token)"
} else {
$0.headers["Authorization"] = nil
$0.headers[.authorization] = nil
}
}
}
Expand Down Expand Up @@ -160,15 +161,15 @@ public final class FunctionsClient: Sendable {
private func rawInvoke(
functionName: String,
invokeOptions: FunctionInvokeOptions
) async throws -> HTTPResponse {
) async throws -> Helpers.HTTPResponse {
let request = buildRequest(functionName: functionName, options: invokeOptions)
let response = try await http.send(request)

guard 200 ..< 300 ~= response.statusCode else {
throw FunctionsError.httpError(code: response.statusCode, data: response.data)
}

let isRelayError = response.headers["x-relay-error"] == "true"
let isRelayError = response.headers[.xRelayError] == "true"
if isRelayError {
throw FunctionsError.relayError
}
Expand Down Expand Up @@ -211,17 +212,17 @@ public final class FunctionsClient: Sendable {
return stream
}

private func buildRequest(functionName: String, options: FunctionInvokeOptions) -> HTTPRequest {
private func buildRequest(functionName: String, options: FunctionInvokeOptions) -> Helpers.HTTPRequest {
var request = HTTPRequest(
url: url.appendingPathComponent(functionName),
method: options.httpMethod ?? .post,
query: options.query,
headers: mutableState.headers.merged(with: options.headers),
headers: mutableState.headers.merging(with: options.headers),
body: options.body
)

if let region = options.region ?? region {
request.headers["x-region"] = region
request.headers[.xRegion] = region
}

return request
Expand Down
17 changes: 9 additions & 8 deletions Sources/Functions/Types.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import Helpers
import HTTPTypes

/// An error type representing various errors that can occur while invoking functions.
public enum FunctionsError: Error, LocalizedError {
Expand All @@ -24,7 +25,7 @@ public struct FunctionInvokeOptions: Sendable {
/// Method to use in the function invocation.
let method: Method?
/// Headers to be included in the function invocation.
let headers: HTTPHeaders
let headers: HTTPFields
/// Body data to be sent with the function invocation.
let body: Data?
/// The Region to invoke the function in.
Expand All @@ -48,23 +49,23 @@ public struct FunctionInvokeOptions: Sendable {
region: String? = nil,
body: some Encodable
) {
var defaultHeaders = HTTPHeaders()
var defaultHeaders = HTTPFields()

switch body {
case let string as String:
defaultHeaders["Content-Type"] = "text/plain"
defaultHeaders[.contentType] = "text/plain"
self.body = string.data(using: .utf8)
case let data as Data:
defaultHeaders["Content-Type"] = "application/octet-stream"
defaultHeaders[.contentType] = "application/octet-stream"
self.body = data
default:
// default, assume this is JSON
defaultHeaders["Content-Type"] = "application/json"
defaultHeaders[.contentType] = "application/json"
self.body = try? JSONEncoder().encode(body)
}

self.method = method
self.headers = defaultHeaders.merged(with: HTTPHeaders(headers))
self.headers = defaultHeaders.merging(with: HTTPFields(headers))
self.region = region
self.query = query
}
Expand All @@ -84,7 +85,7 @@ public struct FunctionInvokeOptions: Sendable {
region: String? = nil
) {
self.method = method
self.headers = HTTPHeaders(headers)
self.headers = HTTPFields(headers)
self.region = region
self.query = query
body = nil
Expand All @@ -98,7 +99,7 @@ public struct FunctionInvokeOptions: Sendable {
case delete = "DELETE"
}

var httpMethod: HTTPMethod? {
var httpMethod: HTTPTypes.HTTPRequest.Method? {
switch method {
case .get:
.get
Expand Down
37 changes: 37 additions & 0 deletions Sources/Helpers/HTTP/HTTPFields.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import HTTPTypes

package extension HTTPFields {
init(_ dictionary: [String: String]) {
self.init(dictionary.map { .init(name: .init($0.key)!, value: $0.value) })
}

var dictionary: [String: String] {
let keyValues = self.map {
($0.name.rawName, $0.value)
}

return .init(keyValues, uniquingKeysWith: { $1 })
}

mutating func merge(with other: Self) {
for field in other {
self[field.name] = field.value
}
}

func merging(with other: Self) -> Self {
var copy = self

for field in other {
copy[field.name] = field.value
}

return copy
}
}

package extension HTTPField.Name {
static let xClientInfo = HTTPField.Name("X-Client-Info")!
static let xRegion = HTTPField.Name("x-region")!
static let xRelayError = HTTPField.Name("x-relay-error")!
}
Loading
Loading