import Foundation public struct APITokenFetchStrategy: ProviderFetchStrategy { public typealias TokenResolver = @Sendable ([String: String]) -> String? public typealias MissingCredentialsError = @Sendable () -> (any Error & Sendable) public typealias UsageLoader = @Sendable (String, ProviderFetchContext) async throws -> UsageSnapshot public let id: String public let kind: ProviderFetchKind = .apiToken private let sourceLabel: String private let reportsMissingCredentials: Bool private let resolveToken: TokenResolver private let missingCredentialsError: MissingCredentialsError private let loadUsage: UsageLoader public init( id: String, sourceLabel: String = "api", reportsMissingCredentials: Bool = false, resolveToken: @escaping TokenResolver, missingCredentialsError: @escaping MissingCredentialsError, loadUsage: @escaping UsageLoader) { self.id = id self.sourceLabel = sourceLabel self.reportsMissingCredentials = reportsMissingCredentials self.resolveToken = resolveToken self.missingCredentialsError = missingCredentialsError self.loadUsage = loadUsage } public func isAvailable(_ context: ProviderFetchContext) async -> Bool { self.reportsMissingCredentials || self.resolveToken(context.env) != nil } public func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult { guard let token = self.resolveToken(context.env) else { throw self.missingCredentialsError() } return try await self.makeResult( usage: self.loadUsage(token, context), sourceLabel: self.sourceLabel) } public func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool { false }} extension ProviderFetchPlan { public static func apiToken( strategyID: String, sourceLabel: String = "api", reportsMissingCredentials: Bool = false, resolveToken: @escaping APITokenFetchStrategy.TokenResolver, missingCredentialsError: @escaping APITokenFetchStrategy.MissingCredentialsError, loadUsage: @escaping APITokenFetchStrategy.UsageLoader) -> ProviderFetchPlan { ProviderFetchPlan( sourceModes: [.auto, .api], pipeline: ProviderFetchPipeline(resolveStrategies: { _ in [APITokenFetchStrategy( id: strategyID, sourceLabel: sourceLabel, reportsMissingCredentials: reportsMissingCredentials, resolveToken: resolveToken, missingCredentialsError: missingCredentialsError, loadUsage: loadUsage)] })) }}
import Foundation
public struct APITokenFetchStrategy: ProviderFetchStrategy {
public typealias TokenResolver = @Sendable ([String: String]) -> String?
public typealias MissingCredentialsError = @Sendable () -> (any Error & Sendable)
public typealias UsageLoader = @Sendable (String, ProviderFetchContext) async throws -> UsageSnapshot
public let id: String
public let kind: ProviderFetchKind = .apiToken
private let sourceLabel: String
private let reportsMissingCredentials: Bool
private let resolveToken: TokenResolver
private let missingCredentialsError: MissingCredentialsError
private let loadUsage: UsageLoader
public init(
id: String,
sourceLabel: String = "api",
reportsMissingCredentials: Bool = false,
resolveToken: @escaping TokenResolver,
missingCredentialsError: @escaping MissingCredentialsError,
loadUsage: @escaping UsageLoader)
{
self.id = id
self.sourceLabel = sourceLabel
self.reportsMissingCredentials = reportsMissingCredentials
self.resolveToken = resolveToken
self.missingCredentialsError = missingCredentialsError
self.loadUsage = loadUsage
}
public func isAvailable(_ context: ProviderFetchContext) async -> Bool {
self.reportsMissingCredentials || self.resolveToken(context.env) != nil
}
public func fetch(_ context: ProviderFetchContext) async throws -> ProviderFetchResult {
guard let token = self.resolveToken(context.env) else {
throw self.missingCredentialsError()
}
return try await self.makeResult(
usage: self.loadUsage(token, context),
sourceLabel: self.sourceLabel)
}
public func shouldFallback(on _: Error, context _: ProviderFetchContext) -> Bool {
false
}
}
extension ProviderFetchPlan {
public static func apiToken(
strategyID: String,
sourceLabel: String = "api",
reportsMissingCredentials: Bool = false,
resolveToken: @escaping APITokenFetchStrategy.TokenResolver,
missingCredentialsError: @escaping APITokenFetchStrategy.MissingCredentialsError,
loadUsage: @escaping APITokenFetchStrategy.UsageLoader) -> ProviderFetchPlan
{
ProviderFetchPlan(
sourceModes: [.auto, .api],
pipeline: ProviderFetchPipeline(resolveStrategies: { _ in
[APITokenFetchStrategy(
id: strategyID,
sourceLabel: sourceLabel,
reportsMissingCredentials: reportsMissingCredentials,
resolveToken: resolveToken,
missingCredentialsError: missingCredentialsError,
loadUsage: loadUsage)]
}))
}
}