feat: Working on cli client and tests
Some checks failed
CI / Run Tests (push) Failing after 3m7s

This commit is contained in:
2024-11-17 22:23:44 -05:00
parent 6472d3cd1e
commit ce18c44363
12 changed files with 287 additions and 73 deletions

View File

@@ -14,25 +14,38 @@ public extension DependencyValues {
}
}
/// Represents the interface needed for the command line application.
///
///
@DependencyClient
public struct CliClient {
/// Parse a log level from the given `EnvVars`.
public var logLevel: @Sendable (EnvVars) -> Logger.Level = { _ in .debug }
/// Generate the `EnvVars` with the given parameters.
public var makeEnvVars: @Sendable (EnvVarsRequest) async throws -> EnvVars
/// Generate the `MQTTClient` with the given parameters.
public var makeClient: @Sendable (ClientRequest) throws -> MQTTClient
/// Attempt to parse a string to an `MQTTClient.Version`.
public var parseMqttClientVersion: @Sendable (String) -> MQTTClient.Version?
/// Represents the parameters needed to create an `MQTTClient`.
///
///
public struct ClientRequest: Sendable {
public let envVars: EnvVars
public let environment: EnvVars
public let eventLoopGroup: MultiThreadedEventLoopGroup
public let logger: Logger?
public init(
envVars: EnvVars,
environment: EnvVars,
eventLoopGroup: MultiThreadedEventLoopGroup,
logger: Logger?
) {
self.envVars = envVars
self.environment = environment
self.eventLoopGroup = eventLoopGroup
self.logger = logger
}
@@ -63,7 +76,7 @@ extension CliClient: DependencyKey {
Self(
logLevel: { Logger.Level.from(environment: $0) },
makeEnvVars: {
try EnvVars.load(
try await EnvVars.load(
dotEnvFile: $0.envFilePath,
logger: $0.logger,
version: $0.mqttClientVersion
@@ -71,7 +84,7 @@ extension CliClient: DependencyKey {
},
makeClient: {
MQTTClient(
envVars: $0.envVars,
environment: $0.environment,
eventLoopGroup: $0.eventLoopGroup,
logger: $0.logger
)
@@ -81,32 +94,44 @@ extension CliClient: DependencyKey {
}
}
// MARK: - Helpers
extension EnvironmentDependency {
func dotEnvDict(path: String?) async throws -> [String: String] {
guard let path,
let file = FileType(path: path)
else { return [:] }
return try await load(file)
}
}
extension EnvVars {
/// Load the `EnvVars` from the environment.
///
/// - Paramaters:
/// - dotEnvFile: An optional environment file to load.
/// - logger: An optional logger to use for debugging.
/// - version: A version that is specified from command line, ignoring any environment variable.
static func load(
dotEnvFile: String?,
logger: Logger?,
version: String?
) throws -> EnvVars {
) async throws -> EnvVars {
@Dependency(\.environment) var environment
let defaultEnvVars = EnvVars()
let encoder = JSONEncoder()
let decoder = JSONDecoder()
if let dotEnvFile {
try DotEnv.load(path: dotEnvFile)
}
let defaultEnvDict = (try? encoder.encode(defaultEnvVars))
.flatMap { try? decoder.decode([String: String].self, from: $0) }
?? [:]
let dotEnvDict = try await environment.dotEnvDict(path: dotEnvFile)
let envVarsDict = defaultEnvDict
.merging(ProcessInfo.processInfo.environment, uniquingKeysWith: { $1 })
.merging(environment.processInfo(), uniquingKeysWith: { $1 })
.merging(dotEnvDict, uniquingKeysWith: { $1 })
var envVars = (try? JSONSerialization.data(withJSONObject: envVarsDict))
.flatMap { try? decoder.decode(EnvVars.self, from: $0) }
@@ -125,7 +150,7 @@ extension EnvVars {
@_spi(Internal)
public extension MQTTClient {
convenience init(
envVars: EnvVars,
environment envVars: EnvVars,
eventLoopGroup: EventLoopGroup,
logger: Logger?
) {

View File

@@ -0,0 +1,71 @@
import Dependencies
import DependenciesMacros
import DotEnv
import Foundation
import Models
@_spi(Internal)
public extension DependencyValues {
var environment: EnvironmentDependency {
get { self[EnvironmentDependency.self] }
set { self[EnvironmentDependency.self] = newValue }
}
}
/// Responsible for loading environment variables and files.
///
///
@_spi(Internal)
@DependencyClient
public struct EnvironmentDependency: Sendable {
/// Load the variables based on the request.
public var load: @Sendable (FileType) async throws -> [String: String] = { _ in [:] }
public var processInfo: @Sendable () -> [String: String] = { [:] }
public enum FileType: Equatable {
case dotEnv(path: String)
case json(path: String)
init?(path: String) {
let strings = path.split(separator: ".")
guard let ext = strings.last else {
return nil
}
switch ext {
case "env":
self = .dotEnv(path: path)
case "json":
self = .json(path: path)
default:
return nil
}
}
}
}
@_spi(Internal)
extension EnvironmentDependency: DependencyKey {
public static let testValue: EnvironmentDependency = Self()
public static let liveValue: EnvironmentDependency = Self(
load: { file in
switch file {
case let .dotEnv(path: path):
let file = try DotEnv.read(path: path)
return file.lines.reduce(into: [String: String]()) { partialResult, line in
partialResult[line.key] = line.value
}
case let .json(path: path):
let url = URL(filePath: path)
return try JSONDecoder().decode(
[String: String].self,
from: Data(contentsOf: url)
)
}
},
processInfo: { ProcessInfo.processInfo.environment }
)
}