You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: normalize selector in sync (use header as in OFREP and RPC) (#1815)
## 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 <simon.schrottner@dynatrace.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: aepfli <9987394+aepfli@users.noreply.github.com>
Co-authored-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
0 commit comments