Skip to content

Commit

Permalink
feat: Add SupabaseLogger (#219)
Browse files Browse the repository at this point in the history
* Add logger type

* Add SupabaseLogger

* Fix tests

* Add SupabaseLoggingConfiguration

* Simplify logger

* Inline log methods

* Revert some breaking changes and deprecate some initializers
  • Loading branch information
grdsdev authored Jan 16, 2024
1 parent e398853 commit 42ca887
Show file tree
Hide file tree
Showing 23 changed files with 550 additions and 72 deletions.
23 changes: 19 additions & 4 deletions Examples/UserManagement/Supabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,28 @@
//

import Foundation
import OSLog
import Supabase

let supabase = SupabaseClient(
supabaseURL: URL(string: "https://PROJECT_ID.supabase.co")!,
supabaseKey: "YOUR_SUPABASE_ANON_KEY",
options: .init(auth: .init(storage: KeychainLocalStorage(
service: "supabase.gotrue.swift",
accessGroup: nil
)))
options: .init(
global: .init(logger: AppLogger())
)
)

struct AppLogger: SupabaseLogger {
let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "supabase")

func log(message: SupabaseLogMessage) {
switch message.level {
case .verbose:
logger.log(level: .info, "\(message.description)")
case .debug:
logger.log(level: .debug, "\(message.description)")
case .warning, .error:
logger.log(level: .error, "\(message.description)")
}
}
}
11 changes: 10 additions & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"pins" : [
{
"identity" : "keychainaccess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/kishikawakatsumi/KeychainAccess",
"state" : {
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
"version" : "4.2.2"
}
},
{
"identity" : "swift-concurrency-extras",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -47,4 +56,4 @@
}
],
"version" : 2
}
}
27 changes: 23 additions & 4 deletions Sources/Auth/AuthClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public actor AuthClient {
public var headers: [String: String]
public let flowType: AuthFlowType
public let localStorage: AuthLocalStorage
public let logger: SupabaseLogger?
public let encoder: JSONEncoder
public let decoder: JSONDecoder
public let fetch: FetchHandler
Expand All @@ -27,6 +28,7 @@ public actor AuthClient {
/// - headers: Custom headers to be included in requests.
/// - flowType: The authentication flow type.
/// - localStorage: The storage mechanism for local data.
/// - logger: The logger to use.
/// - encoder: The JSON encoder to use for encoding requests.
/// - decoder: The JSON decoder to use for decoding responses.
/// - fetch: The asynchronous fetch handler for network requests.
Expand All @@ -35,6 +37,7 @@ public actor AuthClient {
headers: [String: String] = [:],
flowType: AuthFlowType = Configuration.defaultFlowType,
localStorage: AuthLocalStorage,
logger: SupabaseLogger? = nil,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) }
Expand All @@ -45,6 +48,7 @@ public actor AuthClient {
self.headers = headers
self.flowType = flowType
self.localStorage = localStorage
self.logger = logger
self.encoder = encoder
self.decoder = decoder
self.fetch = fetch
Expand Down Expand Up @@ -75,6 +79,10 @@ public actor AuthClient {
Dependencies.current.value!.currentDate
}

private var logger: SupabaseLogger? {
Dependencies.current.value!.logger
}

/// Returns the session, refreshing it if necessary.
///
/// If no session can be found, a ``AuthError/sessionNotFound`` error is thrown.
Expand All @@ -94,6 +102,7 @@ public actor AuthClient {
/// - headers: Custom headers to be included in requests.
/// - flowType: The authentication flow type..
/// - localStorage: The storage mechanism for local data..
/// - logger: The logger to use.
/// - encoder: The JSON encoder to use for encoding requests.
/// - decoder: The JSON decoder to use for decoding responses.
/// - fetch: The asynchronous fetch handler for network requests.
Expand All @@ -102,6 +111,7 @@ public actor AuthClient {
headers: [String: String] = [:],
flowType: AuthFlowType = AuthClient.Configuration.defaultFlowType,
localStorage: AuthLocalStorage,
logger: SupabaseLogger? = nil,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) }
Expand All @@ -112,6 +122,7 @@ public actor AuthClient {
headers: headers,
flowType: flowType,
localStorage: localStorage,
logger: logger,
encoder: encoder,
decoder: decoder,
fetch: fetch
Expand All @@ -124,15 +135,19 @@ public actor AuthClient {
/// - Parameters:
/// - configuration: The client configuration.
public init(configuration: Configuration) {
let api = APIClient.live(http: HTTPClient(fetchHandler: configuration.fetch))
let api = APIClient.live(http: HTTPClient(
logger: configuration.logger,
fetchHandler: configuration.fetch
))

self.init(
configuration: configuration,
sessionManager: .live,
codeVerifierStorage: .live,
api: api,
eventEmitter: .live,
sessionStorage: .live
sessionStorage: .live,
logger: configuration.logger
)
}

Expand All @@ -143,7 +158,8 @@ public actor AuthClient {
codeVerifierStorage: CodeVerifierStorage,
api: APIClient,
eventEmitter: EventEmitter,
sessionStorage: SessionStorage
sessionStorage: SessionStorage,
logger: SupabaseLogger?
) {
mfa = AuthMFA()

Expand All @@ -159,7 +175,8 @@ public actor AuthClient {
try await self?.refreshSession(refreshToken: $0) ?? .empty
}
),
codeVerifierStorage: codeVerifierStorage
codeVerifierStorage: codeVerifierStorage,
logger: logger
)
)
}
Expand All @@ -172,9 +189,11 @@ public actor AuthClient {
session: Session?
)> {
let (id, stream) = eventEmitter.attachListener()
logger?.debug("auth state change listener with id '\(id.uuidString)' attached.")

Task { [id] in
await emitInitialSession(forStreamWithID: id)
logger?.debug("initial session for listener with id '\(id.uuidString)' emitted.")
}

return stream
Expand Down
81 changes: 81 additions & 0 deletions Sources/Auth/Deprecated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
// Created by Guilherme Souza on 14/12/23.
//

import _Helpers
import Foundation

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

@available(*, deprecated, renamed: "AuthClient")
public typealias GoTrueClient = AuthClient

Expand Down Expand Up @@ -45,3 +50,79 @@ extension JSONDecoder {
AuthClient.Configuration.jsonDecoder
}
}

extension AuthClient.Configuration {
/// Initializes a AuthClient Configuration with optional parameters.
///
/// - Parameters:
/// - url: The base URL of the Auth server.
/// - headers: Custom headers to be included in requests.
/// - flowType: The authentication flow type.
/// - localStorage: The storage mechanism for local data.
/// - encoder: The JSON encoder to use for encoding requests.
/// - decoder: The JSON decoder to use for decoding responses.
/// - fetch: The asynchronous fetch handler for network requests.
@available(
*,
deprecated,
message: "Replace usages of this initializer with new init(url:headers:flowType:localStorage:logger:encoder:decoder:fetch)"
)
public init(
url: URL,
headers: [String: String] = [:],
flowType: AuthFlowType = Self.defaultFlowType,
localStorage: AuthLocalStorage,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
fetch: @escaping AuthClient.FetchHandler = { try await URLSession.shared.data(for: $0) }
) {
self.init(
url: url,
headers: headers,
flowType: flowType,
localStorage: localStorage,
logger: nil,
encoder: encoder,
decoder: decoder,
fetch: fetch
)
}
}

extension AuthClient {
/// Initializes a AuthClient Configuration with optional parameters.
///
/// - Parameters:
/// - url: The base URL of the Auth server.
/// - headers: Custom headers to be included in requests.
/// - flowType: The authentication flow type.
/// - localStorage: The storage mechanism for local data.
/// - encoder: The JSON encoder to use for encoding requests.
/// - decoder: The JSON decoder to use for decoding responses.
/// - fetch: The asynchronous fetch handler for network requests.
@available(
*,
deprecated,
message: "Replace usages of this initializer with new init(url:headers:flowType:localStorage:logger:encoder:decoder:fetch)"
)
public init(
url: URL,
headers: [String: String] = [:],
flowType: AuthFlowType = Configuration.defaultFlowType,
localStorage: AuthLocalStorage,
encoder: JSONEncoder = AuthClient.Configuration.jsonEncoder,
decoder: JSONDecoder = AuthClient.Configuration.jsonDecoder,
fetch: @escaping AuthClient.FetchHandler = { try await URLSession.shared.data(for: $0) }
) {
self.init(
url: url,
headers: headers,
flowType: flowType,
localStorage: localStorage,
logger: nil,
encoder: encoder,
decoder: decoder,
fetch: fetch
)
}
}
2 changes: 2 additions & 0 deletions Sources/Auth/Internal/Dependencies.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@_spi(Internal) import _Helpers
import ConcurrencyExtras
import Foundation

Expand All @@ -12,4 +13,5 @@ struct Dependencies: Sendable {
var sessionRefresher: SessionRefresher
var codeVerifierStorage: CodeVerifierStorage
var currentDate: @Sendable () -> Date = { Date() }
var logger: SupabaseLogger?
}
12 changes: 12 additions & 0 deletions Sources/Auth/Storage/AuthLocalStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,15 @@ public protocol AuthLocalStorage: Sendable {
func retrieve(key: String) throws -> Data?
func remove(key: String) throws
}

extension AuthClient.Configuration {
#if os(iOS) || os(macOS) || os(watchOS) || os(tvOS)
public static let defaultLocalStorage: AuthLocalStorage = KeychainLocalStorage(
service: "supabase.gotrue.swift",
accessGroup: nil
)
#elseif os(Windows)
public static let defaultLocalStorage: AuthLocalStorage =
WinCredLocalStorage(service: "supabase.gotrue.swift")
#endif
}
80 changes: 80 additions & 0 deletions Sources/PostgREST/Deprecated.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// Deprecated.swift
//
//
// Created by Guilherme Souza on 16/01/24.
//

import Foundation

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

extension PostgrestClient.Configuration {
/// Initializes a new configuration for the PostgREST client.
/// - Parameters:
/// - url: The URL of the PostgREST server.
/// - schema: The schema to use.
/// - headers: The headers to include in requests.
/// - fetch: The fetch handler to use for requests.
/// - encoder: The JSONEncoder to use for encoding.
/// - decoder: The JSONDecoder to use for decoding.
@available(
*,
deprecated,
message: "Replace usages of this initializer with new init(url:schema:headers:logger:fetch:encoder:decoder:)"
)
public init(
url: URL,
schema: String? = nil,
headers: [String: String] = [:],
fetch: @escaping PostgrestClient.FetchHandler = { try await URLSession.shared.data(for: $0) },
encoder: JSONEncoder = PostgrestClient.Configuration.jsonEncoder,
decoder: JSONDecoder = PostgrestClient.Configuration.jsonDecoder
) {
self.init(
url: url,
schema: schema,
headers: headers,
logger: nil,
fetch: fetch,
encoder: encoder,
decoder: decoder
)
}
}

extension PostgrestClient {
/// Creates a PostgREST client with the specified parameters.
/// - Parameters:
/// - url: The URL of the PostgREST server.
/// - schema: The schema to use.
/// - headers: The headers to include in requests.
/// - session: The URLSession to use for requests.
/// - encoder: The JSONEncoder to use for encoding.
/// - decoder: The JSONDecoder to use for decoding.
@available(
*,
deprecated,
message: "Replace usages of this initializer with new init(url:schema:headers:logger:fetch:encoder:decoder:)"
)
public init(
url: URL,
schema: String? = nil,
headers: [String: String] = [:],
fetch: @escaping FetchHandler = { try await URLSession.shared.data(for: $0) },
encoder: JSONEncoder = PostgrestClient.Configuration.jsonEncoder,
decoder: JSONDecoder = PostgrestClient.Configuration.jsonDecoder
) {
self.init(
url: url,
schema: schema,
headers: headers,
logger: nil,
fetch: fetch,
encoder: encoder,
decoder: decoder
)
}
}
4 changes: 2 additions & 2 deletions Sources/PostgREST/PostgrestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class PostgrestBuilder: @unchecked Sendable {
request: Request
) {
self.configuration = configuration
http = HTTPClient(fetchHandler: configuration.fetch)
http = HTTPClient(logger: configuration.logger, fetchHandler: configuration.fetch)

mutableState = LockIsolated(
MutableState(
Expand Down Expand Up @@ -74,7 +74,7 @@ public class PostgrestBuilder: @unchecked Sendable {
do {
return try configuration.decoder.decode(T.self, from: data)
} catch {
debug("Fail to decode type '\(T.self) with error: \(error)")
configuration.logger?.error("Fail to decode type '\(T.self) with error: \(error)")
throw error
}
}
Expand Down
Loading

0 comments on commit 42ca887

Please sign in to comment.