feat: In-memory provider (#208)#226
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces an InMemoryProvider implementation for the Kotlin SDK, along with supporting classes like Flag and ContextEvaluator. It also integrates Gherkin-based E2E tests to ensure compliance with the OpenFeature specification. Feedback focuses on improving thread safety for the provider's internal state, ensuring type safety during flag evaluation (especially when using custom evaluators), resolving nullability mismatches in provider metadata, and enforcing invariants in the Flag data class to prevent invalid configurations.
d02500b to
e9af646
Compare
|
This is user-facing API change, so the PR name should use |
typotter
left a comment
There was a problem hiding this comment.
Solid implementation with a clean InMemoryProvider and a nice Flag/Builder API. Nothing major from my pass outside of what has already been flagged/is in discussion.
7f67cf9 to
b825f75
Compare
typotter
left a comment
There was a problem hiding this comment.
A few things I noticed on the latest commits — great progress overall.
typotter
left a comment
There was a problem hiding this comment.
A few paths still don't have unit test coverage:
- The
disabledflag path (returndefaultValuewithReason.DISABLED) contextEvaluatorreturningnull(fallback todefaultVariantwithReason.DEFAULT)updateFlags/updateFlagevent emission (thatProviderConfigurationChangedfires with the correctflagsChangedset)
b825f75 to
56cdedd
Compare
typotter
left a comment
There was a problem hiding this comment.
Thanks for the changes, Marcin
Sorry for missing this earlier, but I think we need to relook at the ContextEvaluator interface.
02ca4bc to
eaeb895
Compare
ebe0c00 to
eaa5335
Compare
Address review from @typotter: - Remove events.emit(ProviderReady) from FakeProvider.initialize; the SDK emits ProviderReady after initialize returns. - Soften the InMemoryProvider wording and link to open-feature/kotlin-sdk#226, where upstream in-memory provider support is tracked.
Code reviewFound 2 issues:
🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Volatile should work without changes. And should take effects on platform with multithreading. Project compiles fine on all non-JVM targets without failing.
Good catch, fixed case sensitivity issue |
Signed-off-by: Marcin Stepien <marcin.stepien@fluxon.com>
Signed-off-by: Marcin Stepien <marcin.stepien@fluxon.com>
Signed-off-by: Marcin Stepien <marcin.stepien@fluxon.com>
eaa5335 to
e7494ef
Compare
* feature_flags: add shared testing section to server and client _index Introduces a 'Testing with in-memory providers' section on both the server-side and client-side feature flag landing pages. Explains that the Datadog provider talks to Datadog's backend and is not appropriate for unit tests, and directs readers to per-language pages for concrete examples. * feature_flags: add Testing section to Go server-side page Teaches customers to swap DatadogProvider for OpenFeature's memprovider in unit tests. Uses named clients via SetNamedProviderAndWait so t.Parallel() is safe, and shows the ContextEvaluator pointer-to-func pattern. * feature_flags: add Testing section to Java server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider (Flag builder form) in JUnit 5 tests. Emphasizes OpenFeatureAPI singleton reset via shutdown() in @AfterEach and mentions Spring Boot test setup. * feature_flags: add Testing section to Python server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in pytest. Uses a function-scoped fixture with api.shutdown() teardown to prevent global API singleton leakage between tests. * feature_flags: add Testing section to Node.js server-side page Teaches customers to swap DatadogProvider for OpenFeature's TypedInMemoryProvider in Vitest tests. Shows OpenFeature.close() teardown and per-test putConfiguration() to keep tests isolated from each other. * feature_flags: add Testing section to .NET server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in xUnit via IAsyncLifetime. Notes cross-framework applicability (NUnit, MSTest) and the WebApplicationFactory integration path for ASP.NET Core. * feature_flags: add Testing section to Ruby server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in RSpec via an around hook that captures and restores the prior provider. Notes the Ruby SDK's plain-value flag hash (no variants) and the add_flag mutation API. * feature_flags: add Testing section to JavaScript client-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider from @openfeature/web-sdk in Vitest. Calls out the required flag shape (variants, defaultVariant, disabled) and the sync evaluation model. * feature_flags: add Testing section to React client-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in tests. Keeps the section minimal per the client-testing style: short paragraph plus a 10-line snippet, no RTL or component harness. * feature_flags: add Testing section to Android client-side page Kotlin/Android OpenFeature SDK does not ship an InMemoryProvider, so the section includes a ~30-line FakeProvider stub and walks through setting it up under runTest from kotlinx-coroutines-test. * feature_flags: add Testing section to iOS client-side page OpenFeature Swift SDK ships no InMemoryProvider and has no contrib package, so the section includes a ~50-line InMemoryTestProvider FeatureProvider stub. Uses async setProviderAndWait and clearProviderAndWait for teardown. * feature_flags: apply upstream OpenFeature SDK verification fixes Corrections from per-language expert review against current upstream SDKs: - Go: ContextEvaluator is already *func(...); drop the extra cast so the snippet compiles (the prior form produced a **func(...) conversion error). - Python: context_evaluator returns a FlagResolutionDetails, not a bare variant name. Fix prose wording. - .NET: InMemoryProvider's mutation method is UpdateFlagsAsync, not UpdateFlags. Fix prose wording. - JavaScript (client) and React: swap the deprecated InMemoryProvider for TypedInMemoryProvider (upstream marks the former @deprecated) and mention OpenFeatureTestProvider as the idiomatic React component-test wrapper. * feature_flags: frame iOS testing around OpenFeature and dd-openfeature-provider-swift Points readers at the Datadog OpenFeature bridge (dd-openfeature-provider-swift) as the production path for OpenFeature on iOS. Tests swap the bridge's DatadogProvider for a small custom FeatureProvider because the upstream OpenFeature Swift SDK does not ship an in-memory provider. * feature_flags: fix iOS test snippet against OpenFeature Swift SDK 0.3.0 Verification caught six compile errors in the InMemoryTestProvider stub: - initialize/onContextSet are 'async throws', not Future<Void, Never> - observe() returns AnyPublisher<ProviderEvent?, Never> (optional event) - ProviderEvent.ready has no associated value; use .ready not .ready(nil) - Reason case is .staticReason, not .static ('static' is reserved) - OpenFeatureAPI.shared.clearProvider() is sync and does not exist as clearProviderAndWait() in 0.3.0 Also update the prose callout to match. * feature_flags: clarify client-side provider fetches flags from Datadog's CDN Client-side SDKs reach Datadog's CDN to retrieve flag assignments, not the Remote Configuration backend used by server-side SDKs. Updates the testing-section callouts on the shared client _index and on each per-platform client page so the language is accurate. * feature_flags: soften testing guidance to present in-memory as one option In-memory providers are one testing approach; running the real Datadog provider against a dedicated test environment is equally valid. Updates the shared _index sections and each per-language page so they present both options up front, then proceed with the in-memory example as 'this section covers...' rather than 'do not use the Datadog provider'. * feature_flags: add OpenFeature usage section to iOS page Adds a new 'Use via OpenFeature' section covering install of dd-openfeature-provider-swift, OpenFeatureAPI setup, evaluation context, typed flag evaluation, and flag details. Existing FlagsClient-based sections are unchanged. Includes a 'not yet production-ready' callout since the bridge package is still in development. * feature_flags: note getIntegerValue returns Int64 in iOS OpenFeature section * Update content/en/feature_flags/client/_index.md Co-authored-by: Tyler Potter <tyler.potter@datadoghq.com> * feature_flags/android: drop ProviderReady emit, note kotlin-sdk#226 Address review from @typotter: - Remove events.emit(ProviderReady) from FakeProvider.initialize; the SDK emits ProviderReady after initialize returns. - Soften the InMemoryProvider wording and link to open-feature/kotlin-sdk#226, where upstream in-memory provider support is tracked. * feature_flags/server: mirror client step 3 rewording Apply @typotter's step 3 rewrite from client/_index.md to server/_index.md for consistency: convey that units under test get an injected OpenFeature client backed by the InMemoryProvider. * 1 picker only Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * remove dup from server Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * split sentence Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * action verb Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * with of Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * action Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * comma dotnet Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * comma nodejs Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * fast no promises Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * fast server no promises Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * ios bridge Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * these Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * Apply suggestions from code review Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> --------- Co-authored-by: Tyler Potter <tyler.potter@datadoghq.com> Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com>
* feature_flags: add shared testing section to server and client _index Introduces a 'Testing with in-memory providers' section on both the server-side and client-side feature flag landing pages. Explains that the Datadog provider talks to Datadog's backend and is not appropriate for unit tests, and directs readers to per-language pages for concrete examples. * feature_flags: add Testing section to Go server-side page Teaches customers to swap DatadogProvider for OpenFeature's memprovider in unit tests. Uses named clients via SetNamedProviderAndWait so t.Parallel() is safe, and shows the ContextEvaluator pointer-to-func pattern. * feature_flags: add Testing section to Java server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider (Flag builder form) in JUnit 5 tests. Emphasizes OpenFeatureAPI singleton reset via shutdown() in @AfterEach and mentions Spring Boot test setup. * feature_flags: add Testing section to Python server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in pytest. Uses a function-scoped fixture with api.shutdown() teardown to prevent global API singleton leakage between tests. * feature_flags: add Testing section to Node.js server-side page Teaches customers to swap DatadogProvider for OpenFeature's TypedInMemoryProvider in Vitest tests. Shows OpenFeature.close() teardown and per-test putConfiguration() to keep tests isolated from each other. * feature_flags: add Testing section to .NET server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in xUnit via IAsyncLifetime. Notes cross-framework applicability (NUnit, MSTest) and the WebApplicationFactory integration path for ASP.NET Core. * feature_flags: add Testing section to Ruby server-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in RSpec via an around hook that captures and restores the prior provider. Notes the Ruby SDK's plain-value flag hash (no variants) and the add_flag mutation API. * feature_flags: add Testing section to JavaScript client-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider from @openfeature/web-sdk in Vitest. Calls out the required flag shape (variants, defaultVariant, disabled) and the sync evaluation model. * feature_flags: add Testing section to React client-side page Teaches customers to swap DatadogProvider for OpenFeature's InMemoryProvider in tests. Keeps the section minimal per the client-testing style: short paragraph plus a 10-line snippet, no RTL or component harness. * feature_flags: add Testing section to Android client-side page Kotlin/Android OpenFeature SDK does not ship an InMemoryProvider, so the section includes a ~30-line FakeProvider stub and walks through setting it up under runTest from kotlinx-coroutines-test. * feature_flags: add Testing section to iOS client-side page OpenFeature Swift SDK ships no InMemoryProvider and has no contrib package, so the section includes a ~50-line InMemoryTestProvider FeatureProvider stub. Uses async setProviderAndWait and clearProviderAndWait for teardown. * feature_flags: apply upstream OpenFeature SDK verification fixes Corrections from per-language expert review against current upstream SDKs: - Go: ContextEvaluator is already *func(...); drop the extra cast so the snippet compiles (the prior form produced a **func(...) conversion error). - Python: context_evaluator returns a FlagResolutionDetails, not a bare variant name. Fix prose wording. - .NET: InMemoryProvider's mutation method is UpdateFlagsAsync, not UpdateFlags. Fix prose wording. - JavaScript (client) and React: swap the deprecated InMemoryProvider for TypedInMemoryProvider (upstream marks the former @deprecated) and mention OpenFeatureTestProvider as the idiomatic React component-test wrapper. * feature_flags: frame iOS testing around OpenFeature and dd-openfeature-provider-swift Points readers at the Datadog OpenFeature bridge (dd-openfeature-provider-swift) as the production path for OpenFeature on iOS. Tests swap the bridge's DatadogProvider for a small custom FeatureProvider because the upstream OpenFeature Swift SDK does not ship an in-memory provider. * feature_flags: fix iOS test snippet against OpenFeature Swift SDK 0.3.0 Verification caught six compile errors in the InMemoryTestProvider stub: - initialize/onContextSet are 'async throws', not Future<Void, Never> - observe() returns AnyPublisher<ProviderEvent?, Never> (optional event) - ProviderEvent.ready has no associated value; use .ready not .ready(nil) - Reason case is .staticReason, not .static ('static' is reserved) - OpenFeatureAPI.shared.clearProvider() is sync and does not exist as clearProviderAndWait() in 0.3.0 Also update the prose callout to match. * feature_flags: clarify client-side provider fetches flags from Datadog's CDN Client-side SDKs reach Datadog's CDN to retrieve flag assignments, not the Remote Configuration backend used by server-side SDKs. Updates the testing-section callouts on the shared client _index and on each per-platform client page so the language is accurate. * feature_flags: soften testing guidance to present in-memory as one option In-memory providers are one testing approach; running the real Datadog provider against a dedicated test environment is equally valid. Updates the shared _index sections and each per-language page so they present both options up front, then proceed with the in-memory example as 'this section covers...' rather than 'do not use the Datadog provider'. * feature_flags: add OpenFeature usage section to iOS page Adds a new 'Use via OpenFeature' section covering install of dd-openfeature-provider-swift, OpenFeatureAPI setup, evaluation context, typed flag evaluation, and flag details. Existing FlagsClient-based sections are unchanged. Includes a 'not yet production-ready' callout since the bridge package is still in development. * feature_flags: note getIntegerValue returns Int64 in iOS OpenFeature section * Update content/en/feature_flags/client/_index.md Co-authored-by: Tyler Potter <tyler.potter@datadoghq.com> * feature_flags/android: drop ProviderReady emit, note kotlin-sdk#226 Address review from @typotter: - Remove events.emit(ProviderReady) from FakeProvider.initialize; the SDK emits ProviderReady after initialize returns. - Soften the InMemoryProvider wording and link to open-feature/kotlin-sdk#226, where upstream in-memory provider support is tracked. * feature_flags/server: mirror client step 3 rewording Apply @typotter's step 3 rewrite from client/_index.md to server/_index.md for consistency: convey that units under test get an injected OpenFeature client backed by the InMemoryProvider. * 1 picker only Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * remove dup from server Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * split sentence Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * action verb Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * with of Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * action Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * comma dotnet Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * comma nodejs Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * fast no promises Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * fast server no promises Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * ios bridge Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * these Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> * Apply suggestions from code review Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com> --------- Co-authored-by: Tyler Potter <tyler.potter@datadoghq.com> Co-authored-by: Joe Peeples <joe.peeples@datadoghq.com>
InMemoryProvider, Gherkin based tests
This PR introduces the
InMemoryProviderfor the OpenFeature Kotlin SDK and validates its implementation using the official OpenFeature specification.InMemoryProviderfor managing and evaluating flags entirely in memory, includingContextEvaluatorandFlagmodels.open-feature/specrepository as a Git submodule to utilize the official cross-platform test suite.EvaluationSteps,GherkinSpecTest) using Cucumber to verify the new provider adheres to the OpenFeature Gherkin specification. Evaluation targets evaluation.feature file and ignores evaluation_v2.feature.ci.yamlworkflow to ensure Git submodules are checked out during CI test runs.Related Issues
Fixes #208
Notes
Because this PR introduces the
open-feature/specas a Git submodule, developers pulling or checking out this branch locally may need to initialize submodules by running: