Skip to content

Commit

Permalink
feat: state and event for onContextChanged
Browse files Browse the repository at this point in the history
Signed-off-by: Fabrizio Demaria <[email protected]>
  • Loading branch information
fabriziodemaria committed Sep 6, 2024
1 parent 96f51a7 commit 40d0812
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 5 deletions.
12 changes: 8 additions & 4 deletions Sources/OpenFeature/OpenFeatureAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ public class OpenFeatureAPI {
default:
providerStatus = .error
eventHandler.send(.error(message: error.localizedDescription))

}
}
}
Expand All @@ -59,12 +58,17 @@ public class OpenFeatureAPI {
}

public func setEvaluationContext(evaluationContext: EvaluationContext) {
let oldContext = self._context
self._context = evaluationContext
providerStatus = .reconciling
eventHandler.send(.reconciling)
do {
let oldContext = self._context
self._context = evaluationContext
try getProvider()?.onContextSet(oldContext: oldContext, newContext: evaluationContext)
providerStatus = .ready
eventHandler.send(.contextChanged)
} catch {
// TODO Handle errors
providerStatus = .error
eventHandler.send(.error(message: error.localizedDescription))
}
}

Expand Down
26 changes: 26 additions & 0 deletions Tests/OpenFeatureTests/DeveloperExperienceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@ final class DeveloperExperienceTests: XCTestCase {
XCTAssertNotNil(eventState)
}

func testSetEvaluationContext() {
let contextChangedExpectation = XCTestExpectation(description: "Context Changed")
let reconcilingExpectation = XCTestExpectation(description: "Reconciling")
let observer = OpenFeatureAPI.shared.observe().sink { event in
switch event {
case .reconciling:
reconcilingExpectation.fulfill()
case .ready:
break
case .contextChanged:
contextChangedExpectation.fulfill()
default:
XCTFail("Unexpected event")
}
}
let semaphore = DispatchSemaphore(value: 0)
OpenFeatureAPI.shared.setProvider(provider: StaggeredProvider(onContextSetSemaphore: semaphore))
Task {
OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: MutableContext(attributes: [:]))
}
wait(for: [reconcilingExpectation], timeout: 2)
semaphore.signal()
wait(for: [contextChangedExpectation], timeout: 2)
XCTAssertNotNil(observer)
}

func testSetProviderAndWait() async {
let readyExpectation = XCTestExpectation(description: "Ready")
let errorExpectation = XCTestExpectation(description: "Error")
Expand Down
1 change: 0 additions & 1 deletion Tests/OpenFeatureTests/Helpers/DoSomethingProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import OpenFeature
class DoSomethingProvider: FeatureProvider {
public static let name = "Something"
private let eventHandler = EventHandler()
private var holdit: AnyCancellable?

func onContextSet(oldContext: OpenFeature.EvaluationContext?, newContext: OpenFeature.EvaluationContext) {
}
Expand Down
79 changes: 79 additions & 0 deletions Tests/OpenFeatureTests/Helpers/StaggeredProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import Combine
import Foundation
import OpenFeature

class StaggeredProvider: FeatureProvider {
public static let name = "Something"
private let eventHandler = EventHandler()
private let onContextSetSemaphore: DispatchSemaphore?

init(onContextSetSemaphore: DispatchSemaphore?) {
self.onContextSetSemaphore = onContextSetSemaphore
}

func onContextSet(oldContext: OpenFeature.EvaluationContext?, newContext: OpenFeature.EvaluationContext) {
onContextSetSemaphore?.wait()
}

func initialize(initialContext: OpenFeature.EvaluationContext?) {
}

var hooks: [any OpenFeature.Hook] = []
var metadata: OpenFeature.ProviderMetadata = DoMetadata()

func getBooleanEvaluation(key: String, defaultValue: Bool, context: EvaluationContext?) throws
-> ProviderEvaluation<
Bool
>
{
return ProviderEvaluation(value: !defaultValue, flagMetadata: DoSomethingProvider.flagMetadataMap)
}

func getStringEvaluation(key: String, defaultValue: String, context: EvaluationContext?) throws
-> ProviderEvaluation<
String
>
{
return ProviderEvaluation(
value: String(defaultValue.reversed()), flagMetadata: DoSomethingProvider.flagMetadataMap)
}

func getIntegerEvaluation(key: String, defaultValue: Int64, context: EvaluationContext?) throws
-> ProviderEvaluation<
Int64
>
{
return ProviderEvaluation(value: defaultValue * 100, flagMetadata: DoSomethingProvider.flagMetadataMap)
}

func getDoubleEvaluation(key: String, defaultValue: Double, context: EvaluationContext?) throws
-> ProviderEvaluation<
Double
>
{
return ProviderEvaluation(value: defaultValue * 100, flagMetadata: DoSomethingProvider.flagMetadataMap)
}

func getObjectEvaluation(key: String, defaultValue: Value, context: EvaluationContext?) throws
-> ProviderEvaluation<
Value
>
{
return ProviderEvaluation(value: .null, flagMetadata: DoSomethingProvider.flagMetadataMap)
}

func observe() -> AnyPublisher<ProviderEvent?, Never> {
eventHandler.observe()
}

public struct DoMetadata: ProviderMetadata {
public var name: String? = DoSomethingProvider.name
}

public static let flagMetadataMap = [
"int-metadata": FlagMetadataValue.integer(99),
"double-metadata": FlagMetadataValue.double(98.4),
"string-metadata": FlagMetadataValue.string("hello-world"),
"boolean-metadata": FlagMetadataValue.boolean(true),
]
}

0 comments on commit 40d0812

Please sign in to comment.