Skip to content

Conversation

@toddbaert
Copy link
Member

Deprecates the selector field in favor of a the Flagd-selector header. This:

  • allows infrastructure to easily manipulate this field
    • this is highly valuable; admins can use infrastructure (Envoy) to manipulate or inject this header easily, granting them the ability to control the returned flag-set
  • makes things consistent (OFREP already uses -H Flagd-Selector for the reasons above)
  • old functionality is not broken - we just check this header first.

@toddbaert toddbaert requested a review from a team as a code owner October 29, 2025 19:22
@gemini-code-assist
Copy link

Summary of Changes

Hello @toddbaert, 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 introduces a significant update to how flag selectors are managed, deprecating the selector field in the SyncFlagsRequest protobuf message. The primary goal is to transition to using the Flagd-selector metadata header, which offers improved flexibility for infrastructure-level manipulation and aligns with existing patterns for consistency. The change ensures that existing integrations continue to function by prioritizing the new header while maintaining support for the older field, thereby providing a smoother migration path.

Highlights

  • Field Deprecation: The selector field within the SyncFlagsRequest protobuf message has been officially deprecated.
  • New Selector Mechanism: The preferred method for conveying selector information is now the Flagd-selector metadata header, which offers enhanced flexibility for infrastructure manipulation.
  • Backward Compatibility: Existing functionality remains intact, as the system will prioritize checking the new Flagd-selector header but will fall back to the deprecated selector field if the header is not present.
  • Infrastructure Flexibility: This change allows infrastructure components like Envoy to easily manipulate or inject the selector, providing greater control over the returned flag-set.
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.

Copy link

@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 deprecates the selector field in SyncFlagsRequest within the legacy sync.v1 proto definition, favoring a flagd-selector header instead. The change itself is correct. However, this deprecation is not applied consistently. The selector field also exists in FetchAllFlagsRequest in the same file, and in both SyncFlagsRequest and FetchAllFlagsRequest in the current flagd.sync.v1 API definition, but these have not been updated. For API consistency, it's important to apply this deprecation across all relevant messages. I've left a comment with more details.

@open-feature open-feature deleted a comment from gemini-code-assist bot Oct 29, 2025
@toddbaert toddbaert force-pushed the feat/deprecate-selector branch from fad0550 to 37febd6 Compare October 29, 2025 19:29
@toddbaert toddbaert force-pushed the feat/deprecate-selector branch from 37febd6 to 7e11b77 Compare October 29, 2025 19:29
@toddbaert
Copy link
Member Author

/gemini review

Copy link

@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 deprecates the selector field in SyncFlagsRequest and FetchAllFlagsRequest messages within the flagd.sync.v1 protobuf definition. The change is in favor of using a Flagd-Selector header, which aligns with the OFREP specification and provides more flexibility for infrastructure-level manipulation. The changes are correct, but I've suggested improving the comments associated with the deprecation to be more informative for API consumers by indicating the replacement mechanism.

// filtering mechanism.
// ex:- selector: 'source=database,app=weatherapp'
string selector = 2;
string selector = 2 [deprecated = true]; // remember to reserve field number 2 if this is removed;

Choose a reason for hiding this comment

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

medium

The comment explaining the deprecation could be more helpful to users of this API. It's good practice to mention what should be used as a replacement. The information about reserving the field is a good implementation detail for maintainers, but guiding users is more important for a deprecation message.

Suggested change
string selector = 2 [deprecated = true]; // remember to reserve field number 2 if this is removed;
string selector = 2 [deprecated = true]; // Deprecated: Use the 'Flagd-Selector' header instead.

// filtering mechanism.
// ex:- selector: 'source=database,app=weatherapp'
string selector = 2;
string selector = 2 [deprecated = true]; // remember to reserve field number 2 if this is removed;

Choose a reason for hiding this comment

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

medium

The comment explaining the deprecation could be more helpful to users of this API. It's good practice to mention what should be used as a replacement. The information about reserving the field is a good implementation detail for maintainers, but guiding users is more important for a deprecation message.

Suggested change
string selector = 2 [deprecated = true]; // remember to reserve field number 2 if this is removed;
string selector = 2 [deprecated = true]; // Deprecated: Use the 'Flagd-Selector' header instead.

toddbaert added a commit to open-feature/flagd that referenced this pull request Nov 5, 2025
)

## Problem

Currently, flagd services have inconsistent handling of selectors:

- **Sync Service**: Uses `selector` field from the request body
- **Evaluation Service** (v1 & v2): Uses `Flagd-Selector` header
- **OFREP Service**: Uses `Flagd-Selector` header

This inconsistency makes the API surface less predictable and can lead
to confusion for users.

## Solution

This PR normalizes selector handling by updating the sync service to
accept the `Flagd-Selector` header as the primary method, aligning it
with the evaluation and OFREP services. The implementation maintains
full backward compatibility by falling back to the request body selector
when the header is not present.

## Changes

### Core Implementation

Modified `flagd/pkg/service/flag-sync/handler.go`:
- Added `getSelectorExpression()` helper method that checks for
`Flagd-Selector` header (via gRPC metadata) first
- Falls back to request body `selector` field if header is not present
- Logs a deprecation warning when the request body selector is used
- Applied the new logic to both `SyncFlags()` and `FetchAllFlags()`
methods

### Test Coverage

Added 5 comprehensive test cases in `handler_test.go`:
- Header-based selector (verifies no deprecation warning)
- Request body selector (verifies backward compatibility and deprecation
warning)
- Header precedence (verifies header takes priority when both are
provided)
- FetchAllFlags with header selector
- FetchAllFlags with request body selector

All existing tests continue to pass.

## Migration Path

The change is fully backward compatible. Users can migrate at their
convenience:

**Current (deprecated):**
```go
req := &syncv1.FetchAllFlagsRequest{
    Selector: "source:my-source",
}
resp, err := client.FetchAllFlags(ctx, req)
```

**Recommended:**
```go
md := metadata.New(map[string]string{
    "Flagd-Selector": "source:my-source",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
req := &syncv1.FetchAllFlagsRequest{}
resp, err := client.FetchAllFlags(ctx, req)
```

## Benefits

- **Consistency**: All flagd services now use the same `Flagd-Selector`
header pattern
- **Backward Compatibility**: Existing clients continue to work without
modification
- **Clear Migration Path**: Deprecation warnings guide users to adopt
the new approach
- **Aligns with Standards**: Using headers/metadata for query/filter
parameters is a common gRPC pattern

## Breaking Changes

None. The request body selector will be removed in a future major
version (e.g., v2.0.0).

## Related

Addresses #[issue_number]

<!-- START COPILOT CODING AGENT SUFFIX -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>[FEATURE] Normalize selector handling across sync,
evaluation, and OFREP services</issue_title>
> <issue_description>### Requirements
> 
> ### Problem
> Currently, we have inconsistent handling of selectors across different
flagd services:
> 
> - **Sync Service** (`flagd/pkg/service/flag-sync/handler.go:34`): Uses
`req.GetSelector()` from the request body
> - **Evaluation Service**: Uses `flagd-selector` header
> - **OFREP Service**: Uses `flagd-selector` header
> 
> This inconsistency can lead to confusion for users and makes the API
surface less predictable.
> 
> ### Proposal
> Normalize selector handling across all services by:
> 
> 1. **Preferred approach**: Use the `flagd-selector` header as the
primary method for all services
> 2. **Backward compatibility**: For the sync service, continue to
accept `selector` in the request body as a fallback for a deprecation
period
> 
> ### Implementation Strategy
> 1. Update sync service to check for `flagd-selector` header first
> 2. Fall back to request body `selector` field if header is not present
> 3. Log deprecation warning when using request body selector
> 4. Document the change and migration path
> 5. Remove request body selector support in a future major version
(e.g., v2.0.0)
> 
> ### Benefits
> - Consistent API across all flagd services
> - Easier to understand and use for developers
> - Aligns with common patterns for query/filter parameters in gRPC
metadata
> 
> ### Breaking Changes
> None initially (backward compatible), but request body selector would
be removed in a future major release.
> 
> ### Additional Context
> The sync service currently processes the selector at:
> ```go
> selectorExpression := req.GetSelector()
> selector := store.NewSelector(selectorExpression)
> ```
> 
> This should be updated to prioritize metadata/header values while
maintaining backward compatibility.</issue_description>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> </comments>
> 


</details>

Fixes #1814

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for
you](https://github.com/open-feature/flagd/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot)
— coding agent works faster and does higher quality work when set up for
your repo.

Relates to: open-feature/flagd-schemas#199

---------

Signed-off-by: Simon Schrottner <[email protected]>
Signed-off-by: Todd Baert <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: aepfli <[email protected]>
Co-authored-by: Simon Schrottner <[email protected]>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Todd Baert <[email protected]>
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