Skip to content

Conversation

@kinyoklion
Copy link
Member

This PR

Adds support for hook data.
open-feature/spec#273

Related Issues

Notes

I realized that the 4.6.1 section of the spec wasn't consistent with the expected usage. Basically it over-specifies the typing of the hook data matching that of the evaluation context. That is one possible approach, it would just mean a bit more work on the part of the hook implementers.

In the earlier example in the spec I put a Span in the hook data:

  public Optional<EvaluationContext> before(HookContext context, HookHints hints) {
    SpanBuilder builder = tracer.spanBuilder('sample')
    .setParent(Context.current().with(Span.current()));
    Span span = builder.startSpan()
    context.hookData.set("span", span);
  }
  public void after(HookContext context, FlagEvaluationDetails details, HookHints hints) {
    // Only accessible by this hook for this specific evaluation.
    Object value = context.hookData.get("span");
    if (value instanceof Span) {
      Span span = (Span) value;
      span.end();
    }
  }

This is only possible if the hook data allows specification of any object instead of being limited to the immutable types of a context. For hooks hook data this is safe because only the hook mutating the data will have access to that data. Additionally the execution of the hook will be in sequence with the evaluation (likely in a single thread).

The alternative would be to store data in the hook, and use the hook data to know when to remove it.
Something like this:

  public Optional<EvaluationContext> before(HookContext context, HookHints hints) {
    SpanBuilder builder = tracer.spanBuilder('sample')
    .setParent(Context.current().with(Span.current()));
    Span span = builder.startSpan()

    String storageId = Uuid();
    this.tmpData.set(storageId, span);
    context.hookData.set("span", storageId);
  }
  public void after(HookContext context, FlagEvaluationDetails details, HookHints hints) {
    // Only accessible by this hook for this specific evaluation.
    Object value = context.hookData.get("span");
    if (value) {
      String id = value.AsString();
      Span span= this.tmpData.get(id);
      span.end();
    }
  }

Follow-up Tasks

How to test

@codecov
Copy link

codecov bot commented Feb 24, 2025

Codecov Report

Attention: Patch coverage is 98.72611% with 2 lines in your changes missing coverage. Please review.

Project coverage is 86.47%. Comparing base (568722a) to head (90374f4).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/OpenFeature/OpenFeatureClient.cs 92.59% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #387      +/-   ##
==========================================
+ Coverage   85.69%   86.47%   +0.77%     
==========================================
  Files          39       42       +3     
  Lines        1601     1671      +70     
  Branches      171      177       +6     
==========================================
+ Hits         1372     1445      +73     
+ Misses        191      187       -4     
- Partials       38       39       +1     

☔ 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.

@kinyoklion kinyoklion force-pushed the rlamb/add-hook-data-support branch from 1264589 to 7b827b7 Compare February 24, 2025 00:03
@kinyoklion kinyoklion force-pushed the rlamb/add-hook-data-support branch 4 times, most recently from 0af05fd to 04b20c6 Compare February 24, 2025 00:57
@kinyoklion kinyoklion force-pushed the rlamb/add-hook-data-support branch from 04b20c6 to a8a7f59 Compare February 24, 2025 01:13
@beeme1mr
Copy link
Member

Hey @kinyoklion, thanks for kicking this off. I'll cut some GitHub issues for other languages once this PR is ready so we can use this as a reference.

@toddbaert toddbaert requested review from askpt and chrfwow March 7, 2025 18:52
kinyoklion and others added 2 commits March 16, 2025 18:33
Co-authored-by: chrfwow <[email protected]>
Signed-off-by: Ryan Lamb <[email protected]>
@kinyoklion kinyoklion marked this pull request as ready for review March 17, 2025 01:44
@kinyoklion kinyoklion requested a review from a team as a code owner March 17, 2025 01:44
Signed-off-by: Ryan Lamb <[email protected]>
@kinyoklion
Copy link
Member Author

@toddbaert I would like to get your thoughts on the spec divergence in the description. If we are in agreement I can make a minor change to the spec wording.

@toddbaert
Copy link
Member

toddbaert commented Mar 21, 2025

This is only possible if the hook data allows specification of any object instead of being limited to the immutable types of a context. For hooks hook data this is safe because only the hook mutating the data will have access to that data. Additionally the execution of the hook will be in sequence with the evaluation (likely in a single thread).

@kinyoklion I agree with your assertion that allowing any object is relatively safe in this case, and certainly more ergonomic. I think we should adjust the spec accordingly.

@toddbaert toddbaert self-requested a review March 21, 2025 18:33
@toddbaert toddbaert requested a review from beeme1mr March 21, 2025 19:03
Signed-off-by: Ryan Lamb <[email protected]>
Copy link
Member

@beeme1mr beeme1mr left a comment

Choose a reason for hiding this comment

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

Looks good to me!

@kinyoklion could you please mentioning the new functionality briefly in the hook section of the readme and update the spec based on this comment?

@kinyoklion
Copy link
Member Author

Looks good to me!

@kinyoklion could you please mentioning the new functionality briefly in the hook section of the readme and update the spec based on this comment?

open-feature/spec#307

Also I added an example of a "timing" hook using hook data to the readme.

@kinyoklion kinyoklion added this pull request to the merge queue Apr 18, 2025
Merged via the queue into main with commit 4563512 Apr 18, 2025
17 checks passed
toddbaert pushed a commit to open-feature/spec that referenced this pull request Apr 24, 2025
## This PR
Loosens the value definition for hook data.

In the example within the spec, and within the implementation in the
dotnet-sdk, the intent was to allow any type of data. For example
creating an open telemetry span in before, storing it hook data, and
ending it in after.

### Notes
Related to: open-feature/dotnet-sdk#387

---------

Signed-off-by: Ryan Lamb <[email protected]>
github-merge-queue bot pushed a commit that referenced this pull request Apr 28, 2025
🤖 I have created a release *beep* *boop*
---


##
[2.5.0](v2.4.0...v2.5.0)
(2025-04-25)


### ✨ New Features

* Add support for hook data.
([#387](#387))
([4563512](4563512))


### 🧹 Chore

* add NuGet auditing
([#454](#454))
([42ab536](42ab536))
* Change file scoped namespaces and cleanup job
([#453](#453))
([1e74a04](1e74a04))
* **deps:** update codecov/codecov-action action to v5.4.2
([#432](#432))
([c692ec2](c692ec2))
* **deps:** update github/codeql-action digest to 28deaed
([#446](#446))
([dfecd0c](dfecd0c))
* **deps:** update spec digest to 18cde17
([#395](#395))
([5608dfb](5608dfb))
* **deps:** update spec digest to 2ba05d8
([#452](#452))
([eb688c4](eb688c4))
* **deps:** update spec digest to 36944c6
([#450](#450))
([e162169](e162169))
* **deps:** update spec digest to d27e000
([#455](#455))
([e0ec8ca](e0ec8ca))
* packages read in release please
([1acc00f](1acc00f))
* update release permissions
([d0bf40b](d0bf40b))
* **workflows:** Add permissions for contents and pull-requests
([#439](#439))
([568722a](568722a))


### 📚 Documentation

* update documentation on SetProviderAsync
([#449](#449))
([858b286](858b286))
* Update README with spec version
([#437](#437))
([7318b81](7318b81)),
closes [#204](#204)


### 🔄 Refactoring

* InMemoryProvider throwing when types mismatched
([#442](#442))
([8ecf50d](8ecf50d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Signed-off-by: OpenFeature Bot <[email protected]>
WeihanLi pushed a commit to WeihanLi/openfeature-dotnet-sdk that referenced this pull request May 14, 2025
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->

## This PR

Adds support for hook data.
open-feature/spec#273

### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->

### Notes
<!-- any additional notes for this PR -->

I realized that the 4.6.1 section of the spec wasn't consistent with the
expected usage. Basically it over-specifies the typing of the hook data
matching that of the evaluation context. That is one possible approach,
it would just mean a bit more work on the part of the hook implementers.

In the earlier example in the spec I put a `Span` in the hook data:
```
  public Optional<EvaluationContext> before(HookContext context, HookHints hints) {
    SpanBuilder builder = tracer.spanBuilder('sample')
    .setParent(Context.current().with(Span.current()));
    Span span = builder.startSpan()
    context.hookData.set("span", span);
  }
  public void after(HookContext context, FlagEvaluationDetails details, HookHints hints) {
    // Only accessible by this hook for this specific evaluation.
    Object value = context.hookData.get("span");
    if (value instanceof Span) {
      Span span = (Span) value;
      span.end();
    }
  }
```

This is only possible if the hook data allows specification of any
`object` instead of being limited to the immutable types of a context.
For hooks hook data this is safe because only the hook mutating the data
will have access to that data. Additionally the execution of the hook
will be in sequence with the evaluation (likely in a single thread).

The alternative would be to store data in the hook, and use the hook
data to know when to remove it.
Something like this:
```
  public Optional<EvaluationContext> before(HookContext context, HookHints hints) {
    SpanBuilder builder = tracer.spanBuilder('sample')
    .setParent(Context.current().with(Span.current()));
    Span span = builder.startSpan()

    String storageId = Uuid();
    this.tmpData.set(storageId, span);
    context.hookData.set("span", storageId);
  }
  public void after(HookContext context, FlagEvaluationDetails details, HookHints hints) {
    // Only accessible by this hook for this specific evaluation.
    Object value = context.hookData.get("span");
    if (value) {
      String id = value.AsString();
      Span span= this.tmpData.get(id);
      span.end();
    }
  }
```

### Follow-up Tasks
<!-- anything that is related to this PR but not done here should be
noted under this section -->
<!-- if there is a need for a new issue, please link it here -->

### How to test
<!-- if applicable, add testing instructions under this section -->

---------

Signed-off-by: Ryan Lamb <[email protected]>
Co-authored-by: chrfwow <[email protected]>
Signed-off-by: Weihan Li <[email protected]>
WeihanLi pushed a commit to WeihanLi/openfeature-dotnet-sdk that referenced this pull request May 14, 2025
🤖 I have created a release *beep* *boop*
---

##
[2.5.0](open-feature/dotnet-sdk@v2.4.0...v2.5.0)
(2025-04-25)

### ✨ New Features

* Add support for hook data.
([open-feature#387](open-feature#387))
([4563512](open-feature@4563512))

### 🧹 Chore

* add NuGet auditing
([open-feature#454](open-feature#454))
([42ab536](open-feature@42ab536))
* Change file scoped namespaces and cleanup job
([open-feature#453](open-feature#453))
([1e74a04](open-feature@1e74a04))
* **deps:** update codecov/codecov-action action to v5.4.2
([open-feature#432](open-feature#432))
([c692ec2](open-feature@c692ec2))
* **deps:** update github/codeql-action digest to 28deaed
([open-feature#446](open-feature#446))
([dfecd0c](open-feature@dfecd0c))
* **deps:** update spec digest to 18cde17
([open-feature#395](open-feature#395))
([5608dfb](open-feature@5608dfb))
* **deps:** update spec digest to 2ba05d8
([open-feature#452](open-feature#452))
([eb688c4](open-feature@eb688c4))
* **deps:** update spec digest to 36944c6
([open-feature#450](open-feature#450))
([e162169](open-feature@e162169))
* **deps:** update spec digest to d27e000
([open-feature#455](open-feature#455))
([e0ec8ca](open-feature@e0ec8ca))
* packages read in release please
([1acc00f](open-feature@1acc00f))
* update release permissions
([d0bf40b](open-feature@d0bf40b))
* **workflows:** Add permissions for contents and pull-requests
([open-feature#439](open-feature#439))
([568722a](open-feature@568722a))

### 📚 Documentation

* update documentation on SetProviderAsync
([open-feature#449](open-feature#449))
([858b286](open-feature@858b286))
* Update README with spec version
([open-feature#437](open-feature#437))
([7318b81](open-feature@7318b81)),
closes [open-feature#204](open-feature#204)

### 🔄 Refactoring

* InMemoryProvider throwing when types mismatched
([open-feature#442](open-feature#442))
([8ecf50d](open-feature@8ecf50d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Signed-off-by: OpenFeature Bot <[email protected]>
Signed-off-by: Weihan Li <[email protected]>
@askpt askpt deleted the rlamb/add-hook-data-support branch July 3, 2025 16:58
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.

7 participants