Skip to content

Commit

Permalink
Refactor config type names
Browse files Browse the repository at this point in the history
  • Loading branch information
shilgapira committed Aug 17, 2023
1 parent 8e1df7f commit 59f24eb
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 124 deletions.
5 changes: 5 additions & 0 deletions src/docs/DescopeKit.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ on the [Descope website](https://descope.com).
- ``DeliveryMethod``
- ``OAuthProvider``

### Development

- ``DescopeLogger``
- ``DescopeNetworkClient``

### Others

- ``DescopeSDK``
Expand Down
2 changes: 1 addition & 1 deletion src/internal/http/DescopeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class DescopeClient: HTTPClient {

init(config: DescopeConfig) {
self.config = config
super.init(baseURL: config.baseURL, logger: config.logger, networking: config.networking)
super.init(baseURL: config.baseURL, logger: config.logger, networkClient: config.networkClient)
}

// MARK: - OTP
Expand Down
14 changes: 7 additions & 7 deletions src/internal/http/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import Foundation

class HTTPClient {
let baseURL: String
let logger: DescopeConfig.Logger?
let networking: DescopeConfig.Networking
let logger: DescopeLogger?
let networkClient: DescopeNetworkClient

init(baseURL: String, logger: DescopeConfig.Logger?, networking: DescopeConfig.Networking?) {
init(baseURL: String, logger: DescopeLogger?, networkClient: DescopeNetworkClient?) {
self.baseURL = baseURL
self.logger = logger
self.networking = networking ?? DefaultNetworking()
self.networkClient = networkClient ?? DefaultNetworkClient()
}

// Convenience response functions
Expand Down Expand Up @@ -112,7 +112,7 @@ class HTTPClient {

private func sendRequest(_ request: URLRequest) async throws -> (Data, URLResponse) {
do {
return try await networking.call(request: request)
return try await networkClient.call(request: request)
} catch {
logger?.log(.error, "Network call failed with network error", request.url, error)
throw DescopeError.networkError.with(cause: error)
Expand Down Expand Up @@ -182,14 +182,14 @@ private func mergeHeaders(_ headers: [String: String], with defaults: [String: S

// Network

private class DefaultNetworking: DescopeConfig.Networking {
private class DefaultNetworkClient: DescopeNetworkClient {
private let session = makeURLSession()

deinit {
session.finishTasksAndInvalidate()
}

override func call(request: URLRequest) async throws -> (Data, URLResponse) {
func call(request: URLRequest) async throws -> (Data, URLResponse) {
return try await session.data(for: request)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/internal/routes/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ protocol Route {
}

extension Route {
func log(_ level: DescopeConfig.Logger.Level, _ message: StaticString, _ values: Any?...) {
func log(_ level: DescopeLogger.Level, _ message: StaticString, _ values: Any?...) {
client.config.logger?.log(level, message, values)
}
}
Expand Down
212 changes: 102 additions & 110 deletions src/sdk/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ public struct DescopeConfig {
///
/// The default value of this property is `nil` and thus logging will be completely
/// disabled. During development if you encounter any issues you can create an
/// instance of the `Logger` class to enable logging.
/// instance of the ``DescopeLogger`` class to enable logging.
///
/// Descope.config = DescopeConfig(projectId: "...", logger: DescopeConfig.Logger())
/// Descope.config = DescopeConfig(projectId: "...", logger: DescopeLogger())
///
/// If your application uses some logging framework or third party service you can forward
/// the Descope SDK log messages to it by creating a new subclass of `Logger` and
/// the Descope SDK log messages to it by creating a new subclass of ``DescopeLogger`` and
/// overriding the `output` method.
public var logger: Logger?
public var logger: DescopeLogger?

/// An optional object to override how HTTP requests are performed.
///
Expand All @@ -30,7 +30,7 @@ public struct DescopeConfig {
/// This property can be useful to test code that uses the Descope SDK without any
/// network requests actually taking place. In most other cases there shouldn't be
/// any need to use it.
public var networking: Networking? = nil
public var networkClient: DescopeNetworkClient? = nil

/// Creates a new ``DescopeConfig`` object.
///
Expand All @@ -39,121 +39,113 @@ public struct DescopeConfig {
/// the Descope console.
/// - baseURL: An optional override for the URL of the Descope server, in case it
/// needs to be accessed through a CNAME record.
public init(projectId: String, baseURL: String? = nil, logger: Logger? = nil) {
/// - logger: An optional object to enable logging in the Descope SDK.
public init(projectId: String, baseURL: String? = nil, logger: DescopeLogger? = nil) {
self.projectId = projectId
self.baseURL = baseURL ?? self.baseURL
self.logger = logger
}
}

/// Optional features primarily for testing and debugging.
extension DescopeConfig {
/// The `Logger` class can be used to customize logging functionality in the Descope SDK.
///
/// The default behavior is for log messages to be written to the standard output using
/// the `print()` function.
///
/// To customize how logging functions in the Descope SDK create a subclass of `Logger`
/// and override the ``output(level:message:debug:)`` method. See the documentation
/// for that method for more details.
open class Logger {
/// The severity of a log message.
public enum Level: Int {
case error, info, debug
}

/// The maximum log level that should be printed.
public let level: Level

/// Creates a new `Logger` object.
public init(level: Level = .debug) {
self.level = level
}

/// Formats the log message and prints it.
///
/// Override this method to customize how to handle log messages from the Descope SDK.
///
/// - Parameters:
/// - message: This parameter is guaranteed to be a constant compile-time string, so
/// you can assume it doesn't contain private user data or secrets and that it can
/// be sent to whatever logging target or service you use.
/// - debug: This array has runtime values that might be useful when debugging
/// issues with the Descope SDK. Since it might contain sensitive information
/// its contents are only provided in `debug` builds. In `release` builds it
/// is always an empty array.
open func output(level: Level, message: StaticString, debug: [Any]) {
var text = "[\(DescopeSDK.name)] \(message)"
if !debug.isEmpty {
text += " (" + debug.map { String(describing: $0) }.joined(separator: ", ") + ")"
}
print(text)
}

/// Called by other code in the Descope SDK to output log messages.
public func log(_ level: Level, _ message: StaticString, _ values: Any?...) {
guard level.rawValue <= self.level.rawValue else { return }
#if DEBUG
output(level: level, message: message, debug: values.compactMap { $0 })
#else
output(level: level, message: message, debug: [])
#endif
}
/// The ``DescopeLogger`` class can be used to customize logging functionality in the Descope SDK.
///
/// The default behavior is for log messages to be written to the standard output using
/// the `print()` function.
///
/// You can also customize how logging functions in the Descope SDK by creating a subclass
/// of ``DescopeLogger`` and overriding the ``output(level:message:debug:)`` method. See the
/// documentation for that method for more details.
open class DescopeLogger {
/// The severity of a log message.
public enum Level: Int {
case error, info, debug
}

/// The `Networking` abstract class can be used to override how HTTP requests
/// are performed by the SDK when calling the Descope server.
///
/// Create a new subclass of `Networking` and override the ``call(request:)``
/// method and either return the appropriate HTTP response values or throw an error.
///
/// For example, when testing code that uses the Descope SDK we might want to make
/// sure no network requests are actually made. A simple `Networking` subclass
/// that always throws an error might look like this:
///
/// class TestNetworking: DescopeConfig.Networking {
/// var error: DescopeError = .networkError
///
/// override func call(request: URLRequest) async throws -> (Data, URLResponse) {
/// throw error
/// }
/// }
///
/// The method signature is intentionally identical to the `data(for:)` method
/// in `URLSession`, so if for example all we want is for network requests made by
/// the Descope SDK to use the same `URLSession` instance we use elsewhere we can
/// use code such as this:
///
/// let config = DescopeConfig(projectId: "...")
/// config.networking = AppNetworking(session: appSession)
/// let descopeSDK = DescopeSDK(config: config)
///
/// // ... elsewhere
///
/// class AppNetworking: DescopeConfig.Networking {
/// let session: URLSession
/// The maximum log level that should be printed.
public let level: Level

/// Creates a new ``DescopeLogger`` object.
public init(level: Level = .debug) {
self.level = level
}

/// Formats the log message and prints it.
///
/// init(_ session: URLSession) {
/// self.session = session
/// }
/// Override this method to customize how to handle log messages from the Descope SDK.
///
/// override func call(request: URLRequest) async throws -> (Data, URLResponse) {
/// return try await session.data(for: request)
/// }
/// }
open class Networking {
/// Creates a new `Networking` object.
public init() {
}

/// Loads data using a `URLRequest` and returns the `data` and `response`.
///
/// - Note: The code that calls this function expects the response object to be an
/// instance of the `HTTPURLResponse` class and will throw an error if it's not.
/// This isn't reflected here to keep this simple to use and aligned with the
/// types in the `data(for:)` method in `URLSession`.
open func call(request: URLRequest) async throws -> (Data, URLResponse) {
throw DescopeError.networkError.with(message: "Custom implementations must override the call(request:) method")
/// - Parameters:
/// - message: This parameter is guaranteed to be a constant compile-time string, so
/// you can assume it doesn't contain private user data or secrets and that it can
/// be sent to whatever logging target or service you use.
/// - debug: This array has runtime values that might be useful when debugging
/// issues with the Descope SDK. Since it might contain sensitive information
/// its contents are only provided in `debug` builds. In `release` builds it
/// is always an empty array.
open func output(level: Level, message: StaticString, debug: [Any]) {
var text = "[\(DescopeSDK.name)] \(message)"
if !debug.isEmpty {
text += " (" + debug.map { String(describing: $0) }.joined(separator: ", ") + ")"
}
print(text)
}

/// Called by other code in the Descope SDK to output log messages.
public func log(_ level: Level, _ message: StaticString, _ values: Any?...) {
guard level.rawValue <= self.level.rawValue else { return }
#if DEBUG
output(level: level, message: message, debug: values.compactMap { $0 })
#else
output(level: level, message: message, debug: [])
#endif
}
}

/// The ``DescopeNetworkClient`` protocol can be used to override how HTTP requests
/// are performed by the SDK when calling the Descope server.
///
/// Your code should implement the ``call(request:)`` method and either return the
/// appropriate HTTP response values or throw an error.
///
/// For example, when testing code that uses the Descope SDK we might want to make
/// sure no network requests are actually made. A simple `DescopeNetworkClient`
/// implementation that always throws an error might look like this:
///
/// class FailingNetworkClient: DescopeNetworkClient {
/// var error: DescopeError = .networkError
///
/// func call(request: URLRequest) async throws -> (Data, URLResponse) {
/// throw error
/// }
/// }
///
/// The method signature is intentionally identical to the `data(for:)` method
/// in `URLSession`, so if for example all we want is for network requests made by
/// the Descope SDK to use the same `URLSession` instance we use elsewhere we can
/// use code such as this:
///
/// var config = DescopeConfig(projectId: "...")
/// config.networkClient = AppNetworkClient(session: appSession)
/// let descopeSDK = DescopeSDK(config: config)
///
/// // ... elsewhere
///
/// class AppNetworkClient: DescopeNetworkClient {
/// let session: URLSession
///
/// init(_ session: URLSession) {
/// self.session = session
/// }
///
/// func call(request: URLRequest) async throws -> (Data, URLResponse) {
/// return try await session.data(for: request)
/// }
/// }
public protocol DescopeNetworkClient {
/// Loads data using a `URLRequest` and returns the `data` and `response`.
///
/// - Note: The code that calls this function expects the response object to be an
/// instance of the `HTTPURLResponse` class and will throw an error if it's not.
/// This isn't reflected in the function signature to keep this simple to use
/// and aligned with the types in the `data(for:)` method in `URLSession`.
func call(request: URLRequest) async throws -> (Data, URLResponse)
}
2 changes: 1 addition & 1 deletion test/http/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import XCTest
@testable import DescopeKit

class TestHttpMethods: XCTestCase {
let client = HTTPClient(baseURL: "http://example", logger: nil, networking: MockHTTP.networking)
let client = HTTPClient(baseURL: "http://example", logger: nil, networkClient: MockHTTP.networkClient)

func testGet() async throws {
MockHTTP.push(json: MockResponse.json, headers: MockResponse.headers) { request in
Expand Down
6 changes: 3 additions & 3 deletions test/mocks/MockHTTP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ extension MockHTTP {
return URLSession(configuration: sessionConfig)
}()

static var networking: DescopeConfig.Networking = {
class Client: DescopeConfig.Networking {
override func call(request: URLRequest) async throws -> (Data, URLResponse) {
static var networkClient: DescopeNetworkClient = {
class Client: DescopeNetworkClient {
func call(request: URLRequest) async throws -> (Data, URLResponse) {
return try await session.data(for: request)
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/routes/AccessKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ private let jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiYXIiLCJuYW1l
class TestAccessKey: XCTestCase {
func testTokenDecoding() async throws {
var config = DescopeConfig(projectId: "foo")
config.networking = MockHTTP.networking
config.networkClient = MockHTTP.networkClient
let descope = DescopeSDK(config: config)

MockHTTP.push(json: ["sessionJwt": jwt]) { request in
Expand Down

0 comments on commit 59f24eb

Please sign in to comment.