From 839db7141e593bb99d0d5a19e09f56d5ea28b8af Mon Sep 17 00:00:00 2001 From: Fabrizio Demaria Date: Wed, 27 Mar 2024 16:48:03 +0100 Subject: [PATCH] Basic EvaluationContext wiring in events --- .../ConfidenceDemoApp/ConfidenceDemoApp.swift | 5 +- Package.swift | 10 +-- Sources/Confidence/Confidence.swift | 67 +++++++++++++------ .../Confidence/ConfidenceEventSender.swift | 2 + Sources/Confidence/Contextual.swift | 8 ++- .../ConfidenceClientOptions.swift | 15 +---- .../ConfidenceFeatureProvider.swift | 17 +++-- 7 files changed, 81 insertions(+), 43 deletions(-) rename Sources/{Confidence => ConfidenceProvider/ConfidenceClient}/ConfidenceClientOptions.swift (72%) diff --git a/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift b/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift index 7b953607..309fef04 100644 --- a/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift +++ b/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift @@ -36,6 +36,9 @@ extension ConfidenceDemoApp { // NOTE: Using a random UUID for each app start is not advised and can result in getting stale values. let ctx = MutableContext(targetingKey: UUID.init().uuidString, structure: MutableStructure()) - OpenFeatureAPI.shared.setProvider(provider: provider, initialContext: ctx) + Task { + await OpenFeatureAPI.shared.setProviderAndWait(provider: provider, initialContext: ctx) + confidence.send(eventName: "my_event") + } } } diff --git a/Package.swift b/Package.swift index 7e5811ae..74afe395 100644 --- a/Package.swift +++ b/Package.swift @@ -21,6 +21,11 @@ let package = Package( .package(url: "git@github.com:open-feature/swift-sdk.git", from: "0.1.0"), ], targets: [ + .target( + name: "Confidence", + dependencies: [], + plugins: [] + ), .target( name: "ConfidenceProvider", dependencies: [ @@ -29,11 +34,6 @@ let package = Package( ], plugins: [] ), - .target( - name: "Confidence", - dependencies: [], - plugins: [] - ), .testTarget( name: "ConfidenceProviderTests", dependencies: [ diff --git a/Sources/Confidence/Confidence.swift b/Sources/Confidence/Confidence.swift index a701b5de..ccb13d1a 100644 --- a/Sources/Confidence/Confidence.swift +++ b/Sources/Confidence/Confidence.swift @@ -3,16 +3,24 @@ import Foundation public class Confidence: ConfidenceEventSender { public var context: [String: String] public let clientSecret: String - public let options: ConfidenceClientOptions + public var timeout: TimeInterval + public var region: ConfidenceRegion + public var initializationStrategy: InitializationStrategy - init(clientSecret: String, options: ConfidenceClientOptions) { - self.clientSecret = clientSecret - self.options = options + init(clientSecret: String, + timeout: TimeInterval, + region: ConfidenceRegion, + initializationStrategy: InitializationStrategy) { self.context = [:] + self.clientSecret = clientSecret + self.timeout = timeout + self.region = region + self.initializationStrategy = initializationStrategy } + // TODO: Implement actual event uploading to the backend public func send(eventName: String) { - print("Sending \(eventName)") + print("Sending \(eventName) - Targeting key: \(context["targeting_key"] ?? "UNKNOWN")") } public func updateContextEntry(key: String, value: String) { @@ -27,37 +35,58 @@ public class Confidence: ConfidenceEventSender { context = [:] } + // TODO: Implement creation of child instances public func withContext(_ context: [String: String]) -> Self { - // TODO return self } } extension Confidence { - public struct Builder { + public class Builder { let clientSecret: String - var options: ConfidenceClientOptions + var timeout: TimeInterval = 10.0 + var region: ConfidenceRegion = .global + var initializationStrategy: InitializationStrategy = .fetchAndActivate public init(clientSecret: String) { self.clientSecret = clientSecret - self.options = ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: (clientSecret))) } - init(clientSecret: String, options: ConfidenceClientOptions) { - self.clientSecret = clientSecret - self.options = options + public func withTimeout(timeout: TimeInterval) -> Builder { + self.timeout = timeout + return self } - public func withOptions(options: ConfidenceClientOptions) -> Builder { - return Builder( - clientSecret: clientSecret, - options: options - ) + + public func withRegion(region: ConfidenceRegion) -> Builder { + self.region = region + return self + } + + public func withInitializationstrategy(initializationStrategy: InitializationStrategy) -> Builder { + self.initializationStrategy = initializationStrategy + return self } public func build() -> Confidence { - return Confidence(clientSecret: clientSecret, options: ConfidenceClientOptions()) + return Confidence( + clientSecret: clientSecret, + timeout: timeout, + region: region, + initializationStrategy: initializationStrategy + ) } } } + +public enum InitializationStrategy { + case fetchAndActivate, activateAndFetchAsync +} + +public enum ConfidenceRegion { + case global + case europe + case usa +} + + diff --git a/Sources/Confidence/ConfidenceEventSender.swift b/Sources/Confidence/ConfidenceEventSender.swift index 24d30e80..4747a543 100644 --- a/Sources/Confidence/ConfidenceEventSender.swift +++ b/Sources/Confidence/ConfidenceEventSender.swift @@ -1,5 +1,7 @@ import Foundation +/// Sends events to Confidence. Contextual data is appended to each event +// TODO: Add functions for sending events with payload public protocol ConfidenceEventSender: Contextual { func send(eventName: String) } diff --git a/Sources/Confidence/Contextual.swift b/Sources/Confidence/Contextual.swift index 1a3070ae..9b6859ce 100644 --- a/Sources/Confidence/Contextual.swift +++ b/Sources/Confidence/Contextual.swift @@ -1,11 +1,15 @@ import Foundation +/// A Contextual implementer maintains context data and can create child instances +/// that can still access their parent's data public protocol Contextual { - var context: [String: String] { get set } // TODO Introdue complex types + // TODO: Add complex type to the context Dictionary + var context: [String: String] { get set } func updateContextEntry(key: String, value: String) func removeContextEntry(key: String) func clearContext() - + /// Creates a child Contextual instance that still has access + /// to its parent context func withContext(_ context: [String: String]) -> Self } diff --git a/Sources/Confidence/ConfidenceClientOptions.swift b/Sources/ConfidenceProvider/ConfidenceClient/ConfidenceClientOptions.swift similarity index 72% rename from Sources/Confidence/ConfidenceClientOptions.swift rename to Sources/ConfidenceProvider/ConfidenceClient/ConfidenceClientOptions.swift index cc706cdc..f2ae97e8 100644 --- a/Sources/Confidence/ConfidenceClientOptions.swift +++ b/Sources/ConfidenceProvider/ConfidenceClient/ConfidenceClientOptions.swift @@ -1,4 +1,5 @@ import Foundation +import Confidence public struct ConfidenceClientOptions { public var credentials: ConfidenceClientCredentials // DEPRECATED @@ -7,12 +8,12 @@ public struct ConfidenceClientOptions { public var initializationStrategy: InitializationStrategy public init( - credentials: ConfidenceClientCredentials? = nil, + credentials: ConfidenceClientCredentials, timeout: TimeInterval? = nil, region: ConfidenceRegion? = nil, initializationStrategy: InitializationStrategy = .fetchAndActivate ) { - self.credentials = credentials ?? ConfidenceClientCredentials.clientSecret(secret: "") + self.credentials = credentials self.timeout = timeout ?? 10.0 self.region = region ?? .global self.initializationStrategy = initializationStrategy @@ -29,13 +30,3 @@ public enum ConfidenceClientCredentials { } } } - -public enum ConfidenceRegion { - case global - case europe - case usa -} - -public enum InitializationStrategy { - case fetchAndActivate, activateAndFetchAsync -} diff --git a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift index 07f3128f..4101e556 100644 --- a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift +++ b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift @@ -54,6 +54,8 @@ public class ConfidenceFeatureProvider: FeatureProvider { } if self.initializationStrategy == .activateAndFetchAsync { + // TODO: Set the entire context + confidence?.context = ["targeting_key": "CACHED"] eventHandler.send(.ready) } @@ -70,6 +72,8 @@ public class ConfidenceFeatureProvider: FeatureProvider { // signal the provider is ready after the network request is done if self.initializationStrategy == .fetchAndActivate { + // TODO: Set the entire context + confidence?.context = ["targeting_key": initialContext.getTargetingKey()] eventHandler.send(.ready) } } catch { @@ -111,6 +115,8 @@ public class ConfidenceFeatureProvider: FeatureProvider { // update the storage try await store(with: newContext, resolveResult: resolveResult, refreshCache: true) eventHandler.send(ProviderEvent.ready) + // TODO: Set the entire context + confidence?.context = ["targeting_key": newContext.getTargetingKey()] } catch { eventHandler.send(ProviderEvent.ready) // do nothing @@ -416,7 +422,8 @@ extension ConfidenceFeatureProvider { var applyStorage: Storage = DefaultStorage.resolverApplyCache() var confidence: Confidence? - /// Initializes the builder with the given credentails. DEPRECATED + /// DEPRECATED + /// Initializes the builder with the given credentails. /// /// OpenFeatureAPI.shared.setProvider(provider: /// ConfidenceFeatureProvider.Builder(credentials: .clientSecret(secret: "mysecret")) @@ -427,9 +434,11 @@ extension ConfidenceFeatureProvider { /// TODO public init(confidence: Confidence) { - self.options = ConfidenceClientOptions(credentials: ConfidenceClientCredentials - .clientSecret(secret: confidence.clientSecret)) - self.initializationStrategy = confidence.options.initializationStrategy + self.options = ConfidenceClientOptions( + credentials: ConfidenceClientCredentials.clientSecret(secret: confidence.clientSecret), + timeout: confidence.timeout, + region: confidence.region) + self.initializationStrategy = confidence.initializationStrategy self.confidence = confidence }