Skip to content

Commit

Permalink
feat: client processing provider state early
Browse files Browse the repository at this point in the history
Signed-off-by: Fabrizio Demaria <[email protected]>
  • Loading branch information
fabriziodemaria committed Sep 10, 2024
1 parent 3443a18 commit cabfae2
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 20 deletions.
11 changes: 11 additions & 0 deletions Sources/OpenFeature/OpenFeatureAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ public class OpenFeatureAPI {
self.hooks.removeAll()
}

public func getState() -> (provider: FeatureProvider?, evaluationContext: EvaluationContext?, providerStatus: ProviderStatus) {

Check warning on line 89 in Sources/OpenFeature/OpenFeatureAPI.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Large Tuple Violation: Tuples should have at most 2 members (large_tuple)
return self.stateManager.getState()
}

public func observe() -> AnyPublisher<ProviderEvent?, Never> {
return providerSubject.map { provider in
if let provider = provider {
Expand Down Expand Up @@ -181,4 +185,11 @@ struct SafeStateManager {
self.providerStatus = .notReady
}
}

// Method to read all values atomically
func getState() -> (provider: FeatureProvider?, evaluationContext: EvaluationContext?, providerStatus: ProviderStatus) {

Check warning on line 190 in Sources/OpenFeature/OpenFeatureAPI.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Indentation Width Violation: Code should be indented using one tab or 4 spaces (indentation_width)

Check warning on line 190 in Sources/OpenFeature/OpenFeatureAPI.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Large Tuple Violation: Tuples should have at most 2 members (large_tuple)
return queue.sync {
(provider: provider, evaluationContext: evaluationContext, providerStatus: providerStatus)
}
}
}
51 changes: 32 additions & 19 deletions Sources/OpenFeature/OpenFeatureClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,65 +68,78 @@ extension OpenFeatureClient {
defaultValue: T,
options: FlagEvaluationOptions?
) -> FlagEvaluationDetails<T> {
let openFeatureApiState = openFeatureApi.getState()
var details = FlagEvaluationDetails(flagKey: key, value: defaultValue)

if openFeatureApi.getProviderStatus() == .fatal {
switch (openFeatureApiState.providerStatus) {

Check warning on line 73 in Sources/OpenFeature/OpenFeatureClient.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Control Statement Violation: `if`, `for`, `guard`, `switch`, `while`, and `catch` statements shouldn't unnecessarily wrap their conditionals or arguments in parentheses (control_statement)
case .fatal:
details.errorCode = .providerFatal
details.errorMessage = "Fatal error reported by the Provider" // TODO Improve this message with error details
details.errorMessage = OpenFeatureError.providerFatalError(message: "unknown").description // TODO Improve this message with error details

Check warning on line 76 in Sources/OpenFeature/OpenFeatureClient.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Line Length Violation: Line should be 120 characters or less; currently it has 151 characters (line_length)
details.reason = Reason.error.rawValue
return details
case .notReady:
details.errorCode = .providerNotReady
details.errorMessage = OpenFeatureError.providerNotReadyError.description
details.reason = Reason.error.rawValue
return details
case .reconciling, .stale:
details.reason = Reason.stale.rawValue
return details
case .error:
details.errorCode = .general
details.errorMessage = OpenFeatureError.generalError(message: "unknown").description // TODO Improve this message with error details

Check warning on line 89 in Sources/OpenFeature/OpenFeatureClient.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Line Length Violation: Line should be 120 characters or less; currently it has 145 characters (line_length)
details.reason = Reason.error.rawValue
return details
case .ready:
return evaluateFlagReady(key: key, defaultValue: defaultValue, options: options, openFeatureApiState: openFeatureApiState)

Check warning on line 93 in Sources/OpenFeature/OpenFeatureClient.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Line Length Violation: Line should be 120 characters or less; currently it has 134 characters (line_length)
}
}

private func evaluateFlagReady<T: AllowedFlagValueType>(
key: String,
defaultValue: T,
options: FlagEvaluationOptions?,
openFeatureApiState: (provider: FeatureProvider?, evaluationContext: EvaluationContext?, providerStatus: ProviderStatus)

Check warning on line 101 in Sources/OpenFeature/OpenFeatureClient.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Large Tuple Violation: Tuples should have at most 2 members (large_tuple)

Check warning on line 101 in Sources/OpenFeature/OpenFeatureClient.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Line Length Violation: Line should be 120 characters or less; currently it has 128 characters (line_length)
) -> FlagEvaluationDetails<T> {
var details = FlagEvaluationDetails(flagKey: key, value: defaultValue)
let options = options ?? FlagEvaluationOptions(hooks: [], hookHints: [:])
let hints = options.hookHints
let context = openFeatureApi.getEvaluationContext()

let provider = openFeatureApi.getProvider() ?? NoOpProvider()
let context = openFeatureApiState.evaluationContext
let provider = openFeatureApiState.provider ?? NoOpProvider()
let hookCtx = HookContext(
flagKey: key,
type: T.flagValueType,
defaultValue: defaultValue,
ctx: context,
clientMetadata: self.metadata,
providerMetadata: provider.metadata)

hookLock.lock()
let mergedHooks = provider.hooks + options.hooks + hooks + openFeatureApi.hooks
hookLock.unlock()

do {
hookSupport.beforeHooks(flagValueType: T.flagValueType, hookCtx: hookCtx, hooks: mergedHooks, hints: hints)

let providerEval = try createProviderEvaluation(
key: key,
context: context,
defaultValue: defaultValue,
provider: provider)

let evalDetails = FlagEvaluationDetails<T>.from(providerEval: providerEval, flagKey: key)
details = evalDetails

details = FlagEvaluationDetails<T>.from(providerEval: providerEval, flagKey: key)
try hookSupport.afterHooks(
flagValueType: T.flagValueType, hookCtx: hookCtx, details: evalDetails, hooks: mergedHooks, hints: hints
flagValueType: T.flagValueType, hookCtx: hookCtx, details: details, hooks: mergedHooks, hints: hints
)
} catch {
logger.error("Unable to correctly evaluate flag with key \(key) due to exception \(error)")

if let error = error as? OpenFeatureError {
details.errorCode = error.errorCode()
} else {
details.errorCode = .general
}

details.errorMessage = "\(error)"
details.reason = Reason.error.rawValue

hookSupport.errorHooks(
flagValueType: T.flagValueType, hookCtx: hookCtx, error: error, hooks: mergedHooks, hints: hints)
}

hookSupport.afterAllHooks(
flagValueType: T.flagValueType, hookCtx: hookCtx, hooks: mergedHooks, hints: hints)

return details
}

Expand Down
2 changes: 1 addition & 1 deletion Tests/OpenFeatureTests/DeveloperExperienceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ final class DeveloperExperienceTests: XCTestCase {
let details = client.getDetails(key: "test", defaultValue: false)

XCTAssertEqual(details.errorCode, .providerFatal)
XCTAssertEqual(details.errorMessage, "Fatal error reported by the Provider")
XCTAssertEqual(details.errorMessage, "A fatal error occurred in the provider: unknown")
XCTAssertEqual(details.reason, Reason.error.rawValue)
}
}

0 comments on commit cabfae2

Please sign in to comment.