From 1914b884e068d8e29624c6ca86f780b351f55990 Mon Sep 17 00:00:00 2001 From: Fabrizio Demaria Date: Mon, 22 Jan 2024 14:39:58 +0100 Subject: [PATCH] Add global events Signed-off-by: Fabrizio Demaria --- Sources/OpenFeature/EventHandler.swift | 4 +++ Sources/OpenFeature/OpenFeatureAPI.swift | 12 +++++-- .../DeveloperExperienceTests.swift | 35 +++++++++++-------- .../ProviderEventsTests.swift | 8 ++--- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Sources/OpenFeature/EventHandler.swift b/Sources/OpenFeature/EventHandler.swift index dad6388..2c338d4 100644 --- a/Sources/OpenFeature/EventHandler.swift +++ b/Sources/OpenFeature/EventHandler.swift @@ -23,6 +23,10 @@ public protocol EventPublisher { func observe() -> CurrentValueSubject } +public protocol GlobalEventPublisher { + func observe() -> PassthroughSubject +} + public protocol EventSender { func send(_ event: ProviderEvent) } diff --git a/Sources/OpenFeature/OpenFeatureAPI.swift b/Sources/OpenFeature/OpenFeatureAPI.swift index 9536bce..b397886 100644 --- a/Sources/OpenFeature/OpenFeatureAPI.swift +++ b/Sources/OpenFeature/OpenFeatureAPI.swift @@ -3,11 +3,13 @@ import Combine /// A global singleton which holds base configuration for the OpenFeature library. /// Configuration here will be shared across all ``Client``s. -public class OpenFeatureAPI: EventPublisher { +public class OpenFeatureAPI: GlobalEventPublisher { private var _provider: FeatureProvider? private var _context: EvaluationContext? private(set) var hooks: [any Hook] = [] private(set) var handlers: [Handler] = [] + private var providerObserver: AnyCancellable? + private var globalEventState = PassthroughSubject() /// The ``OpenFeatureAPI`` singleton static public let shared = OpenFeatureAPI() @@ -21,6 +23,9 @@ public class OpenFeatureAPI: EventPublisher { public func setProvider(provider: FeatureProvider, initialContext: EvaluationContext?) { self._provider = provider + self.providerObserver = provider.observe().sink { event in + self.globalEventState.send(event) + } if let context = initialContext { self._context = context } @@ -33,6 +38,7 @@ public class OpenFeatureAPI: EventPublisher { public func clearProvider() { self._provider = nil + self.providerObserver = nil } public func setEvaluationContext(evaluationContext: EvaluationContext) { @@ -65,8 +71,8 @@ public class OpenFeatureAPI: EventPublisher { self.hooks.removeAll() } - public func observe() -> CurrentValueSubject { - getProvider()!.observe() // TODO Fix! + public func observe() -> PassthroughSubject { + return globalEventState } struct Handler { diff --git a/Tests/OpenFeatureTests/DeveloperExperienceTests.swift b/Tests/OpenFeatureTests/DeveloperExperienceTests.swift index 74e19c8..07bb017 100644 --- a/Tests/OpenFeatureTests/DeveloperExperienceTests.swift +++ b/Tests/OpenFeatureTests/DeveloperExperienceTests.swift @@ -3,8 +3,6 @@ import XCTest @testable import OpenFeature final class DeveloperExperienceTests: XCTestCase { - let readyExpectation = XCTestExpectation(description: "Ready") - func testNoProviderSet() { OpenFeatureAPI.shared.clearProvider() let client = OpenFeatureAPI.shared.getClient() @@ -21,19 +19,26 @@ final class DeveloperExperienceTests: XCTestCase { XCTAssertFalse(flagValue) } -// func testObserveProviderReady() { -// let cancellable = OpenFeatureAPI.shared.observe().sink { notification in -// switch notification.name { -// case ProviderEvent.ready.notificationName: -// self.readyExpectation.fulfill() -// default: -// XCTFail("Unexpected event") -// } -// } -// OpenFeatureAPI.shared.setProvider(provider: DoSomethingProvider()) -// wait(for: [readyExpectation], timeout: 5) -// XCTAssertNotNil(cancellable) -// } + func testObserveProviderReady() { + let readyExpectation = XCTestExpectation(description: "Ready") + let errorExpectation = XCTestExpectation(description: "Error") + let staleExpectation = XCTestExpectation(description: "Stale") + let eventState = OpenFeatureAPI.shared.observe().sink { event in + switch event { + case ProviderEvent.ready: + readyExpectation.fulfill() + case ProviderEvent.error: + errorExpectation.fulfill() + case ProviderEvent.stale: + staleExpectation.fulfill() + default: + XCTFail("Unexpected event") + } + } + OpenFeatureAPI.shared.setProvider(provider: DoSomethingProvider()) + wait(for: [readyExpectation], timeout: 5) + XCTAssertNotNil(eventState) + } func testClientHooks() { OpenFeatureAPI.shared.setProvider(provider: NoOpProvider()) diff --git a/Tests/OpenFeatureTests/ProviderEventsTests.swift b/Tests/OpenFeatureTests/ProviderEventsTests.swift index 5356652..648ae9f 100644 --- a/Tests/OpenFeatureTests/ProviderEventsTests.swift +++ b/Tests/OpenFeatureTests/ProviderEventsTests.swift @@ -4,19 +4,19 @@ import XCTest final class ProviderEventsTests: XCTestCase { let provider = DoSomethingProvider() - let readyExpectation = XCTestExpectation(description: "Ready") func testReadyEventSent() { - let sink = provider + let readyExpectation = XCTestExpectation(description: "Ready") + let eventState = provider .observe() .filter { event in event == ProviderEvent.ready } .sink { _ in - self.readyExpectation.fulfill() + readyExpectation.fulfill() } OpenFeatureAPI.shared.setProvider(provider: provider) wait(for: [readyExpectation], timeout: 5) - XCTAssertNotNil(sink) + XCTAssertNotNil(eventState) } }