Skip to content
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let package = Package(
targets: ["OFREP"])
],
dependencies: [
.package(url: "https://github.com/open-feature/swift-sdk.git", from: "0.2.0")
.package(url: "https://github.com/open-feature/swift-sdk.git", from: "0.3.0"),
],
targets: [
.target(
Expand Down
3 changes: 2 additions & 1 deletion Sources/GOFeatureFlag/hook/data_collector_bool_hook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class BooleanHook: Hook {
self.dataCollectorMngr.appendFeatureEvent(event: event)
}

func finallyAfter<HookValue>(ctx: HookContext<HookValue>, hints: [String: Any]) {
func finally<HookValue>(
ctx: HookContext<HookValue>, details: FlagEvaluationDetails<HookValue>, hints: [String: Any]) {
return
}
}
3 changes: 2 additions & 1 deletion Sources/GOFeatureFlag/hook/data_collector_double_hook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class DoubleHook: Hook {
self.dataCollectorMngr.appendFeatureEvent(event: event)
}

func finallyAfter<HookValue>(ctx: HookContext<HookValue>, hints: [String: Any]) {
func finally<HookValue>(
ctx: HookContext<HookValue>, details: FlagEvaluationDetails<HookValue>, hints: [String: Any]) {
return
}
}
3 changes: 2 additions & 1 deletion Sources/GOFeatureFlag/hook/data_collector_integer_hook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class IntegerHook: Hook {
self.dataCollectorMngr.appendFeatureEvent(event: event)
}

func finallyAfter<HookValue>(ctx: HookContext<HookValue>, hints: [String: Any]) {
func finally<HookValue>(
ctx: HookContext<HookValue>, details: FlagEvaluationDetails<HookValue>, hints: [String: Any]) {
return
}
}
3 changes: 2 additions & 1 deletion Sources/GOFeatureFlag/hook/data_collector_object_hook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class ObjectHook: Hook {
self.dataCollectorMngr.appendFeatureEvent(event: event)
}

func finallyAfter<HookValue>(ctx: HookContext<HookValue>, hints: [String: Any]) {
func finally<HookValue>(
ctx: HookContext<HookValue>, details: FlagEvaluationDetails<HookValue>, hints: [String: Any]) {
return
}
}
3 changes: 2 additions & 1 deletion Sources/GOFeatureFlag/hook/data_collector_string_hook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class StringHook: Hook {
self.dataCollectorMngr.appendFeatureEvent(event: event)
}

func finallyAfter<HookValue>(ctx: HookContext<HookValue>, hints: [String: Any]) {
func finally<HookValue>(
ctx: HookContext<HookValue>, details: FlagEvaluationDetails<HookValue>, hints: [String: Any]) {
return
}
}
10 changes: 5 additions & 5 deletions Sources/GOFeatureFlag/provider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
)
}

public func initialize(initialContext: (any OpenFeature.EvaluationContext)?) {
public func initialize(initialContext: OpenFeature.EvaluationContext?) async throws {
self.hooks = dataCollectorMngr.getHooks()
self.ofrepProvider.initialize(initialContext: initialContext)
try await self.ofrepProvider.initialize(initialContext: initialContext)

if self.options.dataCollectorInterval > 0 {
self.hooks.append(BooleanHook(dataCollectorMngr: self.dataCollectorMngr))
Expand All @@ -54,8 +54,8 @@

public func onContextSet(
oldContext: (any OpenFeature.EvaluationContext)?,
newContext: any OpenFeature.EvaluationContext) {
self.ofrepProvider.onContextSet(
newContext: any OpenFeature.EvaluationContext) async throws {
try await self.ofrepProvider.onContextSet(

Check warning on line 58 in Sources/GOFeatureFlag/provider.swift

View check run for this annotation

Codecov / codecov/patch

Sources/GOFeatureFlag/provider.swift#L57-L58

Added lines #L57 - L58 were not covered by tests
oldContext: oldContext,
newContext: newContext)
}
Expand Down Expand Up @@ -115,7 +115,7 @@
context: context)
}

public func observe() -> AnyPublisher<OpenFeature.ProviderEvent, Never> {
public func observe() -> AnyPublisher<OpenFeature.ProviderEvent?, Never> {

Check warning on line 118 in Sources/GOFeatureFlag/provider.swift

View check run for this annotation

Codecov / codecov/patch

Sources/GOFeatureFlag/provider.swift#L118

Added line #L118 was not covered by tests
return self.ofrepProvider.observe()
}
}
77 changes: 38 additions & 39 deletions Sources/OFREP/ofrep.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}

public class OfrepProvider: FeatureProvider {
private let eventHandler = EventHandler(ProviderEvent.notReady)
private let eventHandler = EventHandler()
private var evaluationContext: OpenFeature.EvaluationContext?

private var options: OfrepProviderOptions
Expand All @@ -26,54 +26,53 @@
self.ofrepAPI = OfrepAPI(networkingService: networkService, options: self.options)
}

public func observe() -> AnyPublisher<OpenFeature.ProviderEvent, Never> {
return eventHandler.observe()
}

public var hooks: [any Hook] = []
public var metadata: ProviderMetadata = Metadata()

public func initialize(initialContext: (any OpenFeature.EvaluationContext)?) {
public func observe() -> AnyPublisher<OpenFeature.ProviderEvent?, Never> {
return eventHandler.observe()
}

Check warning on line 35 in Sources/OFREP/ofrep.swift

View workflow job for this annotation

GitHub Actions / Swift Lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
public func initialize(initialContext: (any OpenFeature.EvaluationContext)?) async throws {
self.evaluationContext = initialContext
Task {
do {
let status = try await self.evaluateFlags(context: self.evaluationContext)
if self.options.pollInterval > 0 {
self.startPolling(pollInterval: self.options.pollInterval)
}
do {
let status = try await self.evaluateFlags(context: self.evaluationContext)
if self.options.pollInterval > 0 {
self.startPolling(pollInterval: self.options.pollInterval)
}

if status == .successWithChanges {
self.eventHandler.send(.ready)
return
}
self.eventHandler.send(.error)
} catch {
// TODO: Should be FATAL here
self.eventHandler.send(.error)
if status == .successWithChanges {
return
}

Check warning on line 47 in Sources/OFREP/ofrep.swift

View workflow job for this annotation

GitHub Actions / Swift Lint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
throw OpenFeatureError.generalError(message: "impossible to initialize the provider, receive unknown status")

Check warning on line 48 in Sources/OFREP/ofrep.swift

View workflow job for this annotation

GitHub Actions / Swift Lint

Line Length Violation: Line should be 120 characters or less; currently it has 121 characters (line_length)
} catch {
switch error {
case OfrepError.apiUnauthorizedError, OfrepError.forbiddenError:
throw OpenFeatureError.providerFatalError(message: error.localizedDescription)
default:
throw error
}
}
}

public func onContextSet(oldContext: (any OpenFeature.EvaluationContext)?,
newContext: any OpenFeature.EvaluationContext) {
self.eventHandler.send(.stale)
newContext: any OpenFeature.EvaluationContext) async throws {
self.evaluationContext = newContext
Task {
do {
let status = try await self.evaluateFlags(context: newContext)
if(status == .successWithChanges || status == .successNoChanges ) {
self.eventHandler.send(.ready)
}
} catch let error as OfrepError {
switch error {
case .apiTooManyRequestsError:
return // we want to stay stale in that case so we ignore the error.
default:
throw error
}
} catch {
self.eventHandler.send(.error)
do {
let status = try await self.evaluateFlags(context: newContext)
if(status == .successWithChanges || status == .successNoChanges ) {
return
}
} catch let error as OfrepError {
switch error {
case .apiTooManyRequestsError:
return // we want to stay stale in that case so we ignore the error.
default:
throw error
}
} catch {
throw error

Check warning on line 75 in Sources/OFREP/ofrep.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OFREP/ofrep.swift#L67-L75

Added lines #L67 - L75 were not covered by tests
}
}

Expand Down Expand Up @@ -282,10 +281,10 @@
weakSelf.eventHandler.send(.stale)
throw error
default:
weakSelf.eventHandler.send(.error)
throw error

Check warning on line 284 in Sources/OFREP/ofrep.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OFREP/ofrep.swift#L284

Added line #L284 was not covered by tests
}
} catch {
weakSelf.eventHandler.send(.error)
throw error

Check warning on line 287 in Sources/OFREP/ofrep.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OFREP/ofrep.swift#L287

Added line #L287 was not covered by tests
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/GOFeatureFlagTests/goff_api_tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class GoffApiTests: XCTestCase {
FeatureEvent(kind: "feature", userKey: "981f2662-1fb4-4732-ac6d-8399d9205aa9", creationDate: Int64(Date().timeIntervalSince1970), key: "flag-1", variation: "enabled", value: JSONValue.bool(true), default: false, version: nil, source: "PROVIDER_CACHE")
]
do {
let (response, _) = try await goffAPI.postDataCollector(events: events)
_ = try await goffAPI.postDataCollector(events: events)
XCTFail("Expected to throw GoFeatureFlagError.apiUnauthorizedError, but no error was thrown.")
} catch let error as GoFeatureFlagError {
switch error {
Expand All @@ -52,7 +52,7 @@ class GoffApiTests: XCTestCase {
FeatureEvent(kind: "feature", userKey: "981f2662-1fb4-4732-ac6d-8399d9205aa9", creationDate: Int64(Date().timeIntervalSince1970), key: "flag-1", variation: "enabled", value: JSONValue.bool(true), default: false, version: nil, source: "PROVIDER_CACHE")
]
do {
let (response, _) = try await goffAPI.postDataCollector(events: events)
_ = try await goffAPI.postDataCollector(events: events)
XCTFail("Expected to throw GoFeatureFlagError.forbiddenError, but no error was thrown.")
} catch let error as GoFeatureFlagError {
switch error {
Expand Down
17 changes: 9 additions & 8 deletions Tests/GOFeatureFlagTests/provider_tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,19 @@ class GoFeatureFlagProviderTests: XCTestCase {
let evaluationCtx = MutableContext(targetingKey: "ede04e44-463d-40d1-8fc0-b1d6855578d0")
let api = OpenFeatureAPI()
await api.setProviderAndWait(provider: provider, initialContext: evaluationCtx)
XCTAssertEqual(api.getProviderStatus(), ProviderStatus.ready)


let client = api.getClient()

let expectation = self.expectation(description: "Waiting for delay")
_ = client.getBooleanDetails(key: "my-flag", defaultValue: false)
_ = client.getBooleanDetails(key: "my-flag", defaultValue: false)
_ = client.getIntegerDetails(key: "int-flag", defaultValue: 1)
_ = client.getDoubleDetails(key: "double-flag", defaultValue: 1.0)
_ = client.getStringDetails(key: "string-flag", defaultValue: "default")
_ = client.getObjectDetails(key: "object-flag", defaultValue: Value.null)

let expectation = self.expectation(description: "Waiting for delay")

DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) { expectation.fulfill() }
DispatchQueue.global().asyncAfter(deadline: .now() + 1.5) { expectation.fulfill() }
await fulfillment(of: [expectation], timeout: 3.0)

XCTAssertEqual(1, mockNetworkService.dataCollectorCallCounter)
Expand Down Expand Up @@ -149,8 +150,8 @@ class GoFeatureFlagProviderTests: XCTestCase {
_ = client.getIntegerDetails(key: "int-flag", defaultValue: 1)

let expectation = self.expectation(description: "Waiting for delay")
DispatchQueue.global().asyncAfter(deadline: .now() + 3.0) { expectation.fulfill() }
await fulfillment(of: [expectation], timeout: 4.0)
DispatchQueue.global().asyncAfter(deadline: .now() + 4.0) { expectation.fulfill() }
await fulfillment(of: [expectation], timeout: 5.0)

XCTAssertEqual(1, mockNetworkService.dataCollectorCallCounter)
XCTAssertEqual(3, mockNetworkService.dataCollectorEventCounter)
Expand All @@ -160,8 +161,8 @@ class GoFeatureFlagProviderTests: XCTestCase {
_ = client.getObjectDetails(key: "object-flag", defaultValue: Value.null)

let expectation2 = self.expectation(description: "Waiting for delay")
DispatchQueue.global().asyncAfter(deadline: .now() + 3.0) { expectation2.fulfill() }
await fulfillment(of: [expectation2], timeout: 4.0)
DispatchQueue.global().asyncAfter(deadline: .now() + 4.0) { expectation2.fulfill() }
await fulfillment(of: [expectation2], timeout: 5.0)

XCTAssertEqual(2, mockNetworkService.dataCollectorCallCounter)
XCTAssertEqual(6, mockNetworkService.dataCollectorEventCounter)
Expand Down
Loading
Loading