diff --git a/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift b/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift index e30d2679..07526a85 100644 --- a/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift +++ b/ConfidenceDemoApp/ConfidenceDemoApp/ConfidenceDemoApp.swift @@ -33,10 +33,14 @@ extension ConfidenceDemoApp { let provider = ConfidenceFeatureProvider(confidence: confidence) // 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()) + let ctx = MutableContext( + targetingKey: UUID.init().uuidString, + structure: MutableStructure.init(attributes: ["country": .string("SE")])) Task { await OpenFeatureAPI.shared.setProviderAndWait(provider: provider, initialContext: ctx) - confidence.send(definition: "my_event", payload: ConfidenceStruct()) + confidence.send( + definition: "my_event", + payload: ["my_string_field": ConfidenceValue(string: "hello_from_world")]) } } } diff --git a/Sources/Confidence/Confidence.swift b/Sources/Confidence/Confidence.swift index 0fa2f7ab..09d8208c 100644 --- a/Sources/Confidence/Confidence.swift +++ b/Sources/Confidence/Confidence.swift @@ -22,7 +22,7 @@ public class Confidence: ConfidenceEventSender { // TODO: Implement actual event uploading to the backend public func send(definition: String, payload: ConfidenceStruct) { - print("Sending \(definition) - Targeting key: \(payload)") + print("Sending: \"\(definition)\".\nMessage: \(payload)\nContext: \(context)") } public func updateContextEntry(key: String, value: ConfidenceValue) { diff --git a/Sources/Confidence/ConfidenceValue.swift b/Sources/Confidence/ConfidenceValue.swift index a6e0e3d0..29f580a0 100644 --- a/Sources/Confidence/ConfidenceValue.swift +++ b/Sources/Confidence/ConfidenceValue.swift @@ -2,8 +2,11 @@ import Foundation public typealias ConfidenceStruct = [String: ConfidenceValue] -public class ConfidenceValue: Equatable, Encodable { +public class ConfidenceValue: Equatable, Encodable, CustomStringConvertible { private let value: ConfidenceValueInternal + public var description: String { + return value.description + } public init(boolean: Bool) { self.value = .boolean(boolean) diff --git a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift index e224e623..6ccde58f 100644 --- a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift +++ b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift @@ -77,6 +77,7 @@ public class ConfidenceFeatureProvider: FeatureProvider { return } + self.updateConfidenceContext(context: initialContext) if self.initializationStrategy == .activateAndFetchAsync { eventHandler.send(.ready) } @@ -128,12 +129,14 @@ public class ConfidenceFeatureProvider: FeatureProvider { return } + self.updateConfidenceContext(context: newContext) Task { do { let resolveResult = try await resolve(context: newContext) // update the storage try await store(with: newContext, resolveResult: resolveResult, refreshCache: true) + eventHandler.send(ProviderEvent.ready) } catch { eventHandler.send(ProviderEvent.ready) @@ -142,6 +145,12 @@ public class ConfidenceFeatureProvider: FeatureProvider { } } + private func updateConfidenceContext(context: EvaluationContext) { + confidence?.updateContextEntry( + key: "open_feature", + value: ConfidenceValue(structure: ConfidenceTypeMapper.from(ctx: context))) + } + public func getBooleanEvaluation(key: String, defaultValue: Bool, context: EvaluationContext?) throws -> OpenFeature.ProviderEvaluation { diff --git a/Tests/ConfidenceProviderTests/ConfidenceFeatureProviderTest.swift b/Tests/ConfidenceProviderTests/ConfidenceFeatureProviderTest.swift index 5c449742..919f71f8 100644 --- a/Tests/ConfidenceProviderTests/ConfidenceFeatureProviderTest.swift +++ b/Tests/ConfidenceProviderTests/ConfidenceFeatureProviderTest.swift @@ -1,6 +1,7 @@ // swiftlint:disable type_body_length // swiftlint:disable file_length import Foundation +import Confidence import OpenFeature import XCTest @@ -898,6 +899,57 @@ class ConfidenceFeatureProviderTest: XCTestCase { XCTAssertEqual(evaluation.value, 5) } } + + func testConfidenceContextOnInitialize() throws { + let confidence = Confidence.Builder.init(clientSecret: "").build() + let provider = ConfidenceFeatureProvider(confidence: confidence) + + withExtendedLifetime( + provider.observe().sink { event in + if event == .ready { + self.readyExpectation.fulfill() + } + }) + { + provider.initialize(initialContext: MutableContext(targetingKey: "user1")) + wait(for: [readyExpectation], timeout: 5) + let context = confidence.context + let expected = [ + "open_feature": ConfidenceValue(structure: ["targeting_key": ConfidenceValue(string: "user1")]) + ] + XCTAssertEqual(context, expected) + } + } + + func testConfidenceContextOnContextChange() throws { + let confidence = Confidence.Builder.init(clientSecret: "").build() + let provider = ConfidenceFeatureProvider(confidence: confidence) + + let readyExpectation = self.expectation(description: "Waiting for init and ctx change to complete") + readyExpectation.expectedFulfillmentCount = 2 + + withExtendedLifetime( + provider.observe().sink { event in + if event == .ready { + readyExpectation.fulfill() + } + }) + { + let ctx1 = MutableContext(targetingKey: "user1") + let ctx2 = MutableContext(targetingKey: "user1", structure: MutableStructure(attributes: ["active": Value.boolean(true)])) + provider.initialize(initialContext: ctx1) + provider.onContextSet(oldContext: ctx1, newContext: ctx2) + wait(for: [readyExpectation], timeout: 5) + let context = confidence.context + let expected = [ + "open_feature": ConfidenceValue(structure: [ + "targeting_key": ConfidenceValue(string: "user1"), + "active": ConfidenceValue(boolean: true) + ]) + ] + XCTAssertEqual(context, expected) + } + } } final class DispatchQueueFake: DispatchQueueType { diff --git a/Tests/ConfidenceProviderTests/ConfidenceTypeMapperTest.swift b/Tests/ConfidenceProviderTests/ConfidenceTypeMapperTest.swift index 7fe525c4..70b3397b 100644 --- a/Tests/ConfidenceProviderTests/ConfidenceTypeMapperTest.swift +++ b/Tests/ConfidenceProviderTests/ConfidenceTypeMapperTest.swift @@ -34,7 +34,10 @@ class ValueConverterTest: XCTestCase { "dateList": .list([.date(date1), .date(date2)]), "nullList": .list([.null, .null]), "listList": .list([.list([.string("nested_value1")]), .list([.string("nested_value2")])]), - "structList": .list([.structure(["test": .string("nested_test1")]), .structure(["test": .string("nested_test2")])]) + "structList": .list([ + .structure(["test": .string("nested_test1")]), + .structure(["test": .string("nested_test2")]) + ]) ]))) let confidenceStruct = ConfidenceTypeMapper.from(ctx: openFeatureCtx) let expected = [