Skip to content

Commit

Permalink
chore: use HTTPClient
Browse files Browse the repository at this point in the history
  • Loading branch information
grdsdev committed Apr 19, 2024
1 parent b11f9d5 commit 4840b25
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 25 deletions.
75 changes: 50 additions & 25 deletions Sources/Functions/FunctionsClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,28 @@ public actor FunctionsClient {
let url: URL
/// Headers to be included in the requests.
var headers: [String: String]
/// The fetch handler used to make requests.
let fetch: FetchHandler

private let http: HTTPClient

/// Initializes a new instance of `FunctionsClient`.
///
/// - Parameters:
/// - url: The base URL for the functions.
/// - headers: Headers to be included in the requests. (Default: empty dictionary)
/// - logger: SupabaseLogger instance to use.
/// - fetch: The fetch handler used to make requests. (Default: URLSession.shared.data(for:))
public init(
url: URL,
headers: [String: String] = [:],
logger: (any SupabaseLogger)? = nil,
fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) }
) {
self.url = url
self.headers = headers
if headers["X-Client-Info"] == nil {
self.headers["X-Client-Info"] = "functions-swift/\(version)"
}
self.fetch = fetch
http = HTTPClient(logger: logger, fetchHandler: fetch)
}

/// Updates the authorization header.
Expand All @@ -64,10 +66,10 @@ public actor FunctionsClient {
options: FunctionInvokeOptions = .init(),
decode: (Data, HTTPURLResponse) throws -> Response
) async throws -> Response {
let (data, response) = try await rawInvoke(
let response = try await rawInvoke(
functionName: functionName, invokeOptions: options
)
return try decode(data, response)
return try decode(response.data, response.response)
}

/// Invokes a function and decodes the response as a specific type.
Expand Down Expand Up @@ -102,34 +104,40 @@ public actor FunctionsClient {
private func rawInvoke(
functionName: String,
invokeOptions: FunctionInvokeOptions
) async throws -> (Data, HTTPURLResponse) {
let url = url.appendingPathComponent(functionName)
var urlRequest = URLRequest(url: url)
urlRequest.allHTTPHeaderFields = invokeOptions.headers.merging(headers) { invoke, _ in invoke }
urlRequest.httpMethod = (invokeOptions.method ?? .post).rawValue
urlRequest.httpBody = invokeOptions.body

let (data, response) = try await fetch(urlRequest)

guard let httpResponse = response as? HTTPURLResponse else {
throw URLError(.badServerResponse)
}
) async throws -> Response {
let request = Request(
path: functionName,
method: .post,
headers: invokeOptions.headers.merging(headers) { invoke, _ in invoke },
body: invokeOptions.body
)
let response = try await http.fetch(request, baseURL: url)

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

let isRelayError = httpResponse.value(forHTTPHeaderField: "x-relay-error") == "true"
let isRelayError = response.response.value(forHTTPHeaderField: "x-relay-error") == "true"
if isRelayError {
throw FunctionsError.relayError
}

return (data, httpResponse)
return response
}

public func _invokeWithStream(

/// Invokes a function with streamed response.
///
/// Function MUST return a `text/event-stream` content type for this method to work.
///
/// - Parameters:
/// - functionName: The name of the function to invoke.
/// - invokeOptions: Options for invoking the function.
/// - Returns: A stream of Data.
///
/// - Warning: Experimental method.
public func _invoke(
_ functionName: String,
options invokeOptions: FunctionInvokeOptions
options invokeOptions: FunctionInvokeOptions = .init()
) -> AsyncThrowingStream<Data, any Error> {
let (stream, continuation) = AsyncThrowingStream<Data, any Error>.makeStream()
let delegate = StreamResponseDelegate(continuation: continuation)
Expand All @@ -142,7 +150,24 @@ public actor FunctionsClient {
urlRequest.httpMethod = (invokeOptions.method ?? .post).rawValue
urlRequest.httpBody = invokeOptions.body

let task = session.dataTask(with: urlRequest)
let task = session.dataTask(with: urlRequest) { data, response, _ in
guard let httpResponse = response as? HTTPURLResponse else {
continuation.finish(throwing: URLError(.badServerResponse))
return
}

guard 200 ..< 300 ~= httpResponse.statusCode else {
let error = FunctionsError.httpError(code: httpResponse.statusCode, data: data ?? Data())
continuation.finish(throwing: error)
return
}

let isRelayError = httpResponse.value(forHTTPHeaderField: "x-relay-error") == "true"
if isRelayError {
continuation.finish(throwing: FunctionsError.relayError)
}
}

task.resume()

continuation.onTermination = { _ in
Expand Down
1 change: 1 addition & 0 deletions Sources/Supabase/SupabaseClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public final class SupabaseClient: @unchecked Sendable {
public private(set) lazy var functions = FunctionsClient(
url: functionsURL,
headers: defaultHeaders,
logger: options.global.logger,
fetch: fetchWithAuth
)

Expand Down

0 comments on commit 4840b25

Please sign in to comment.