diff --git a/Sources/Functions/FunctionsClient.swift b/Sources/Functions/FunctionsClient.swift index 214c208c..063e8880 100644 --- a/Sources/Functions/FunctionsClient.swift +++ b/Sources/Functions/FunctionsClient.swift @@ -24,7 +24,7 @@ public final class FunctionsClient: Sendable { let url: URL /// The Region to invoke the functions in. - let region: String? + let region: FunctionRegion? struct MutableState { /// Headers to be included in the requests. @@ -51,7 +51,7 @@ public final class FunctionsClient: Sendable { public convenience init( url: URL, headers: [String: String] = [:], - region: String? = nil, + region: FunctionRegion? = nil, logger: (any SupabaseLogger)? = nil, fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) } ) { @@ -68,7 +68,7 @@ public final class FunctionsClient: Sendable { convenience init( url: URL, headers: [String: String] = [:], - region: String? = nil, + region: FunctionRegion? = nil, logger: (any SupabaseLogger)? = nil, fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) }, sessionConfiguration: URLSessionConfiguration @@ -92,7 +92,7 @@ public final class FunctionsClient: Sendable { init( url: URL, headers: [String: String], - region: String?, + region: FunctionRegion?, http: any HTTPClientType, sessionConfiguration: URLSessionConfiguration = .default ) { @@ -109,24 +109,6 @@ public final class FunctionsClient: Sendable { } } - /// 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) - /// - region: The Region to invoke the functions in. - /// - logger: SupabaseLogger instance to use. - /// - fetch: The fetch handler used to make requests. (Default: URLSession.shared.data(for:)) - public convenience init( - url: URL, - headers: [String: String] = [:], - region: FunctionRegion? = nil, - logger: (any SupabaseLogger)? = nil, - fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) } - ) { - self.init(url: url, headers: headers, region: region?.rawValue, logger: logger, fetch: fetch) - } - /// Updates the authorization header. /// /// - Parameter token: The new JWT token sent in the authorization header. @@ -256,7 +238,7 @@ public final class FunctionsClient: Sendable { ) if let region = options.region ?? region { - request.headers[.xRegion] = region + request.headers[.xRegion] = region.rawValue } return request diff --git a/Sources/Functions/Types.swift b/Sources/Functions/Types.swift index e53f06fd..8cf15c90 100644 --- a/Sources/Functions/Types.swift +++ b/Sources/Functions/Types.swift @@ -28,7 +28,7 @@ public struct FunctionInvokeOptions: Sendable { /// Body data to be sent with the function invocation. let body: Data? /// The Region to invoke the function in. - let region: String? + let region: FunctionRegion? /// The query to be included in the function invocation. let query: [URLQueryItem] @@ -40,12 +40,11 @@ public struct FunctionInvokeOptions: Sendable { /// - headers: Headers to be included in the function invocation. (Default: empty dictionary) /// - region: The Region to invoke the function in. /// - body: The body data to be sent with the function invocation. (Default: nil) - @_disfavoredOverload public init( method: Method? = nil, query: [URLQueryItem] = [], headers: [String: String] = [:], - region: String? = nil, + region: FunctionRegion? = nil, body: some Encodable ) { var defaultHeaders = HTTPFields() @@ -76,12 +75,11 @@ public struct FunctionInvokeOptions: Sendable { /// - query: The query to be included in the function invocation. /// - headers: Headers to be included in the function invocation. (Default: empty dictionary) /// - region: The Region to invoke the function in. - @_disfavoredOverload public init( method: Method? = nil, query: [URLQueryItem] = [], headers: [String: String] = [:], - region: String? = nil + region: FunctionRegion? = nil ) { self.method = method self.headers = HTTPFields(headers) @@ -116,56 +114,29 @@ public struct FunctionInvokeOptions: Sendable { } } -public enum FunctionRegion: String, Sendable { - case apNortheast1 = "ap-northeast-1" - case apNortheast2 = "ap-northeast-2" - case apSouth1 = "ap-south-1" - case apSoutheast1 = "ap-southeast-1" - case apSoutheast2 = "ap-southeast-2" - case caCentral1 = "ca-central-1" - case euCentral1 = "eu-central-1" - case euWest1 = "eu-west-1" - case euWest2 = "eu-west-2" - case euWest3 = "eu-west-3" - case saEast1 = "sa-east-1" - case usEast1 = "us-east-1" - case usWest1 = "us-west-1" - case usWest2 = "us-west-2" -} - -extension FunctionInvokeOptions { - /// Initializes the `FunctionInvokeOptions` structure. - /// - /// - Parameters: - /// - method: Method to use in the function invocation. - /// - headers: Headers to be included in the function invocation. (Default: empty dictionary) - /// - region: The Region to invoke the function in. - /// - body: The body data to be sent with the function invocation. (Default: nil) - public init( - method: Method? = nil, - headers: [String: String] = [:], - region: FunctionRegion? = nil, - body: some Encodable - ) { - self.init( - method: method, - headers: headers, - region: region?.rawValue, - body: body - ) +public struct FunctionRegion: RawRepresentable, Sendable { + public let rawValue: String + public init(rawValue: String) { + self.rawValue = rawValue } - /// Initializes the `FunctionInvokeOptions` structure. - /// - /// - Parameters: - /// - method: Method to use in the function invocation. - /// - headers: Headers to be included in the function invocation. (Default: empty dictionary) - /// - region: The Region to invoke the function in. - public init( - method: Method? = nil, - headers: [String: String] = [:], - region: FunctionRegion? = nil - ) { - self.init(method: method, headers: headers, region: region?.rawValue) + public static let apNortheast1 = FunctionRegion(rawValue: "ap-northeast-1") + public static let apNortheast2 = FunctionRegion(rawValue: "ap-northeast-2") + public static let apSouth1 = FunctionRegion(rawValue: "ap-south-1") + public static let apSoutheast1 = FunctionRegion(rawValue: "ap-southeast-1") + public static let apSoutheast2 = FunctionRegion(rawValue: "ap-southeast-2") + public static let caCentral1 = FunctionRegion(rawValue: "ca-central-1") + public static let euCentral1 = FunctionRegion(rawValue: "eu-central-1") + public static let euWest1 = FunctionRegion(rawValue: "eu-west-1") + public static let euWest2 = FunctionRegion(rawValue: "eu-west-2") + public static let euWest3 = FunctionRegion(rawValue: "eu-west-3") + public static let saEast1 = FunctionRegion(rawValue: "sa-east-1") + public static let usEast1 = FunctionRegion(rawValue: "us-east-1") + public static let usWest1 = FunctionRegion(rawValue: "us-west-1") + public static let usWest2 = FunctionRegion(rawValue: "us-west-2") +} +extension FunctionRegion: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self.init(rawValue: value) } } diff --git a/Sources/Supabase/Types.swift b/Sources/Supabase/Types.swift index 5d5bd9fb..c6ad14be 100644 --- a/Sources/Supabase/Types.swift +++ b/Sources/Supabase/Types.swift @@ -106,15 +106,10 @@ public struct SupabaseClientOptions: Sendable { public struct FunctionsOptions: Sendable { /// The Region to invoke the functions in. - public let region: String? - - @_disfavoredOverload - public init(region: String? = nil) { - self.region = region - } + public let region: FunctionRegion? public init(region: FunctionRegion? = nil) { - self.init(region: region?.rawValue) + self.region = region } } diff --git a/Tests/FunctionsTests/FunctionsClientTests.swift b/Tests/FunctionsTests/FunctionsClientTests.swift index 2d19c5d2..6680c4ca 100644 --- a/Tests/FunctionsTests/FunctionsClientTests.swift +++ b/Tests/FunctionsTests/FunctionsClientTests.swift @@ -31,7 +31,7 @@ final class FunctionsClientTests: XCTestCase { headers: [ "apikey": apiKey ], - region: region, + region: region.flatMap(FunctionRegion.init(rawValue:)), fetch: { request in try await self.session.data(for: request) }, @@ -49,7 +49,7 @@ final class FunctionsClientTests: XCTestCase { headers: ["apikey": apiKey], region: .saEast1 ) - XCTAssertEqual(client.region, "sa-east-1") + XCTAssertEqual(client.region?.rawValue, "sa-east-1") XCTAssertEqual(client.headers[.init("apikey")!], apiKey) XCTAssertNotNil(client.headers[.init("X-Client-Info")!]) @@ -203,6 +203,27 @@ final class FunctionsClientTests: XCTestCase { try await sut.invoke("hello-world", options: .init(region: .caCentral1)) } + func testInvokeWithRegion_usingExpressibleByLiteral() async throws { + Mock( + url: url.appendingPathComponent("hello-world"), + statusCode: 200, + data: [.post: Data()] + ) + .snapshotRequest { + #""" + curl \ + --request POST \ + --header "X-Client-Info: functions-swift/0.0.0" \ + --header "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" \ + --header "x-region: ca-central-1" \ + "http://localhost:5432/functions/v1/hello-world" + """# + } + .register() + + try await sut.invoke("hello-world", options: .init(region: "ca-central-1")) + } + func testInvokeWithoutRegion() async throws { region = nil diff --git a/Tests/SupabaseTests/SupabaseClientTests.swift b/Tests/SupabaseTests/SupabaseClientTests.swift index 437353cd..862d6b24 100644 --- a/Tests/SupabaseTests/SupabaseClientTests.swift +++ b/Tests/SupabaseTests/SupabaseClientTests.swift @@ -81,7 +81,7 @@ final class SupabaseClientTests: XCTestCase { expectNoDifference(client.headers, client.storage.configuration.headers) expectNoDifference(client.headers, client.rest.configuration.headers) - XCTAssertEqual(client.functions.region, "ap-northeast-1") + XCTAssertEqual(client.functions.region?.rawValue, "ap-northeast-1") let realtimeURL = client.realtimeV2.url XCTAssertEqual(realtimeURL.absoluteString, "https://project-ref.supabase.co/realtime/v1")