Skip to content

Conversation

@chrfwow
Copy link
Contributor

@chrfwow chrfwow commented Sep 17, 2025

The addition of the hook data has resulted in a binary breaking change, which this PR attemts to fix

@chrfwow chrfwow requested review from a team as code owners September 17, 2025 14:30
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @chrfwow, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue by reverting a recently added 'hook data' feature that inadvertently introduced a binary breaking change. The changes simplify the OpenFeature SDK's hook mechanism by removing the HookData concept and consolidating HookContext into a single, more direct class. This ensures compatibility and reduces complexity in how hooks manage context during flag evaluation.

Highlights

  • Revert of "Hook Data" Feature: This pull request fully reverts the previously introduced 'feat: add hook data' change, which was identified as causing a binary breaking change in the SDK.
  • Simplification of HookContext: The HookContext has been refactored from an interface into a concrete class, directly incorporating its properties and eliminating the need for separate HookContextWithoutData and HookContextWithData classes.
  • Removal of HookData and Related Utilities: The HookData interface, its default implementation, and the Pair utility class have been entirely removed, streamlining the internal hook management mechanisms.
  • Streamlined Hook Execution Logic: The HookSupport class has been updated to reflect the removal of HookData, simplifying the methods responsible for executing before, after, error, and finallyAfter hooks during flag evaluation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@chrfwow chrfwow changed the title Revert "feat: add hook data (#1587)" fix: Revert "feat: add hook data (#1587)" Sep 17, 2025
@toddbaert toddbaert changed the title fix: Revert "feat: add hook data (#1587)" fix: revert hook data to resolve bytecode incompatibility Sep 17, 2025
@chrfwow chrfwow enabled auto-merge (squash) September 17, 2025 14:32
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request reverts the 'hook data' feature to fix a binary breaking change. The revert is comprehensive, removing related classes and simplifying the hook execution logic. However, I've identified a critical issue in OpenFeatureClient where the refactoring could lead to a NullPointerException during hook execution if an error occurs early in the process. I've provided suggestions to fix this by ensuring a valid HookContext is always available for all hook stages.

Comment on lines +167 to +192
List<Hook> mergedHooks = null;
HookContext<T> afterHookContext = null;

try {
final var stateManager = openfeatureApi.getFeatureProviderStateManager(this.domain);
var stateManager = openfeatureApi.getFeatureProviderStateManager(this.domain);
// provider must be accessed once to maintain a consistent reference
final var provider = stateManager.getProvider();
final var state = stateManager.getState();
hookContext =
HookContextWithoutData.from(key, type, this.getMetadata(), provider.getMetadata(), defaultValue);

// we are setting the evaluation context one after the other, so that we have a hook context in each
// possible exception case.
hookContext.setCtx(mergeEvaluationContext(ctx));
var provider = stateManager.getProvider();
var state = stateManager.getState();

mergedHooks = ObjectUtils.merge(
provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getMutableHooks());
hookDataPairs = hookSupport.getHookDataPairs(mergedHooks, type);
var mergedCtx = hookSupport.beforeHooks(type, hookContext, hookDataPairs, hints);
hookContext.setCtx(mergedCtx);

var mergedCtx = hookSupport.beforeHooks(
type,
HookContext.from(
key,
type,
this.getMetadata(),
provider.getMetadata(),
mergeEvaluationContext(ctx),
defaultValue),
mergedHooks,
hints);

afterHookContext =
HookContext.from(key, type, this.getMetadata(), provider.getMetadata(), mergedCtx, defaultValue);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This change introduces a potential NullPointerException. The afterHookContext variable is initialized to null and only assigned a value after beforeHooks completes successfully. If an exception is thrown during beforeHooks execution (or earlier, e.g., during hook merging), afterHookContext will remain null. This null value is then passed to errorHooks and afterAllHooks in the catch and finally blocks, which violates the OpenFeature specification requiring a non-null HookContext.

To fix this, we should ensure a valid HookContext is always available. The suggested change establishes a hookContext early and updates it immutably, preventing the null context issue. Subsequent comments will adjust the rest of the method to use this new hookContext variable.

        List<Hook> mergedHooks = null;
        HookContext<T> hookContext = null;

        try {
            var stateManager = openfeatureApi.getFeatureProviderStateManager(this.domain);
            // provider must be accessed once to maintain a consistent reference
            var provider = stateManager.getProvider();
            var state = stateManager.getState();

            mergedHooks = ObjectUtils.merge(
                    provider.getProviderHooks(), flagOptions.getHooks(), clientHooks, openfeatureApi.getMutableHooks());

            hookContext =
                    HookContext.from(
                            key,
                            type,
                            this.getMetadata(),
                            provider.getMetadata(),
                            mergeEvaluationContext(ctx),
                            defaultValue);

            var mergedCtx = hookSupport.beforeHooks(
                    type,
                    hookContext,
                    mergedHooks,
                    hints);

            hookContext = hookContext.withCtx(mergedCtx);

Comment on lines +210 to +212
hookSupport.errorHooks(type, afterHookContext, error, mergedHooks, hints);
} else {
hookSupport.afterHooks(type, hookContext, details, hookDataPairs, hints);
hookSupport.afterHooks(type, afterHookContext, details, mergedHooks, hints);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

As part of the fix for the potential NullPointerException identified in the previous comment, this should be updated to use the hookContext variable instead of afterHookContext.

Suggested change
hookSupport.errorHooks(type, afterHookContext, error, mergedHooks, hints);
} else {
hookSupport.afterHooks(type, hookContext, details, hookDataPairs, hints);
hookSupport.afterHooks(type, afterHookContext, details, mergedHooks, hints);
hookSupport.errorHooks(type, hookContext, error, mergedHooks, hints);
} else {
hookSupport.afterHooks(type, hookContext, details, mergedHooks, hints);

details.setErrorMessage(e.getMessage());
enrichDetailsWithErrorDefaults(defaultValue, details);
hookSupport.errorHooks(type, hookContext, e, hookDataPairs, hints);
hookSupport.errorHooks(type, afterHookContext, e, mergedHooks, hints);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

As part of the fix for the potential NullPointerException, this should be updated to use the hookContext variable instead of afterHookContext.

Suggested change
hookSupport.errorHooks(type, afterHookContext, e, mergedHooks, hints);
hookSupport.errorHooks(type, hookContext, e, mergedHooks, hints);

hookSupport.errorHooks(type, afterHookContext, e, mergedHooks, hints);
} finally {
hookSupport.afterAllHooks(type, hookContext, details, hookDataPairs, hints);
hookSupport.afterAllHooks(type, afterHookContext, details, mergedHooks, hints);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

As part of the fix for the potential NullPointerException, this should be updated to use the hookContext variable instead of afterHookContext.

Suggested change
hookSupport.afterAllHooks(type, afterHookContext, details, mergedHooks, hints);
hookSupport.afterAllHooks(type, hookContext, details, mergedHooks, hints);

Signed-off-by: christian.lutnik <[email protected]>
Signed-off-by: christian.lutnik <[email protected]>
@sonarqubecloud
Copy link

@codecov
Copy link

codecov bot commented Sep 17, 2025

Codecov Report

❌ Patch coverage is 95.45455% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.41%. Comparing base (6fb139f) to head (cd75471).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
src/main/java/dev/openfeature/sdk/HookSupport.java 90.00% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #1613      +/-   ##
============================================
+ Coverage     92.59%   93.41%   +0.81%     
+ Complexity      502      491      -11     
============================================
  Files            50       46       -4     
  Lines          1215     1184      -31     
  Branches        106      105       -1     
============================================
- Hits           1125     1106      -19     
+ Misses           60       48      -12     
  Partials         30       30              
Flag Coverage Δ
unittests 93.41% <95.45%> (+0.81%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@toddbaert toddbaert disabled auto-merge September 17, 2025 15:35
@toddbaert toddbaert merged commit 9845601 into open-feature:main Sep 17, 2025
14 of 15 checks passed
@chrfwow chrfwow deleted the revert-hook-data branch September 17, 2025 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants