Add optimizely module with validated Scala factory#140
Merged
Conversation
This was referenced May 13, 2026
… reason on variable fallback
84d73e2 to
20cd449
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements #133 (D1 — optimizely module scaffold) and #134 (D2 — validated factory).
Scope deviation from the original plan
The plan assumed depending on
dev.openfeature.contrib.providers:optimizely, but that artifact is not published to Maven Central — only present as source in the contrib repo. Rather than block on upstream's release timeline, this PR integrates with the Optimizely Java SDK directly viacom.optimizely.ab:core-api:4.2.2+core-httpclient-impl:4.2.2. Trade-offs:When
dev.openfeature.contrib.providers:optimizelyis eventually published, switching is a small refactor that this PR doesn't preclude.What lands
optimizely/sbt sub-project, aggregated into root, cross-built on Scala 2.13.16 + 3.3.4.OptimizelyFeatureProvider—EventProviderimplementation wrappingcom.optimizely.ab.Optimizely:initialize()registers an OptimizelyUpdateConfigNotificationhandler that counts down a latch on first fire sosetProviderAndWaitreturns cleanly; subsequent fires emitPROVIDER_CONFIGURATION_CHANGED.getStatemirrors the Optimizely client'sisValidstate.getEnabled; String / Int / Double → typed variable lookup viadecision.getVariables.getValue(variableKey, classOf[T]); Object →getVariables.toMapwrapped in a Structure). The variable key defaults to"value"and can be overridden per-evaluation via the context attributeopenfeature.variableKey, matching the contrib provider's convention so apps switching between integrations see consistent behaviour.targetingKey→TARGETING_KEY_MISSING, client not valid yet →PROVIDER_NOT_READY, Optimizely reasons containing "not found" →FLAG_NOT_FOUND.shutdown()removes the notification listener and (by default) closes the underlying client.OptimizelyProvider— Scala factory object with four shapes:make(sdkKey)— CDN datafile, validates SDK key.make(sdkKey, datafileUrl)— self-hosted Optimizely Agent, validates both.fromOptimizelyClient(client)— escape hatch; caller manages lifecycle (we don't close the client on shutdown).layer(...)ZLayer wrappers that surfaceFeatureFlagError.InvalidConfigurationat build time.[A-Za-z0-9_-]+, rejects obvious placeholders (YOUR_SDK_KEY,<sdk-key>,changeme, …).http/https, non-empty host.ContextTransformer— converts OFEvaluationContext(targetingKey +Value-wrapped attributes) to the(userId, Map[String, Object])pair Optimizely needs.Closes #133
Closes #134