Skip to content

Loading feature flags from new endpoint#738

Open
linglingye001 wants to merge 12 commits into
previewfrom
linglingye/exclude-classic-ff
Open

Loading feature flags from new endpoint#738
linglingye001 wants to merge 12 commits into
previewfrom
linglingye/exclude-classic-ff

Conversation

@linglingye001

@linglingye001 linglingye001 commented Jun 12, 2026

Copy link
Copy Markdown
Member

Previously, feature flags were loaded only as classic key-values prefixed with .appconfig.featureflag/. This PR registers a parallel "new feature flag" selector/watcher for each FeatureFlagSelector, fetches flags via the dedicated SDK endpoint, and synthesizes them back into the feature-management JSON schema so downstream parsing is unchanged. Classic flags can be turned off via a new opt-in flag ExcludeClassicFeatureFlags.

Changes:

  • Add opt-in behavior to exclude classic feature flags and prefer the new feature-flag endpoint as the source of truth.
  • Introduce “new feature-flag selector/watcher” markers and update selector equality/hash behavior accordingly.
  • Implement loading + change detection for the new feature-flag endpoint, including synthesis into the existing feature-management JSON schema.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds support for loading feature flags from Azure App Configuration’s newer feature-flag endpoint and introduces an option to exclude “classic” feature flags stored under the .appconfig.featureflag/ key namespace.

Changes:

  • Add opt-in behavior to exclude classic feature flags and prefer the new feature-flag endpoint as the source of truth.
  • Introduce “new feature-flag selector/watcher” markers and update selector equality/hash behavior accordingly.
  • Implement loading + change detection for the new feature-flag endpoint, including synthesis into the existing feature-management JSON schema.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueWatcher.cs Adds a flag to distinguish watchers targeting the new feature-flag endpoint.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Models/KeyValueSelector.cs Adds a flag to distinguish selectors for the new endpoint and updates equality/hash logic.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagSettingConverter.cs New converter that synthesizes ConfigurationSetting instances from SDK FeatureFlag models using the existing feature-management JSON schema.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/FeatureManagement/FeatureFlagOptions.cs Adds a new public option to exclude classic feature flags.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/Extensions/ConfigurationClientExtensions.cs Adds change detection for the new feature-flag endpoint by comparing page ETags.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationProvider.cs Routes loading/refresh logic between classic key-values and the new feature-flag endpoint; ensures new flags win on key conflicts.
src/Microsoft.Extensions.Configuration.AzureAppConfiguration/AzureAppConfigurationOptions.cs Updates UseFeatureFlags to register new-endpoint selectors/watchers (and optionally omit classic flags); changes default SDK service version.

Comment on lines +350 to +354
// The new endpoint uses null to mean "any name". A bare "*" is equivalent.
if (string.IsNullOrEmpty(nameFilter) || nameFilter == "*")
{
nameFilter = null;
}
Comment on lines 590 to 593
private static ConfigurationClientOptions GetDefaultClientOptions()
{
var clientOptions = new ConfigurationClientOptions(ConfigurationClientOptions.ServiceVersion.V2023_11_01);
var clientOptions = new ConfigurationClientOptions(ConfigurationClientOptions.ServiceVersion.V2026_05_01_Preview);
clientOptions.Retry.MaxRetries = MaxRetries;
Comment on lines +38 to +44
/// <summary>
/// When set to true, classic feature flags (key-values whose key is prefixed with
/// ".appconfig.featureflag/") are not loaded from the configuration store. Only feature flags
/// returned by the new feature-flag endpoint will be loaded.
/// Defaults to false; classic flags are loaded alongside new flags for backward compatibility.
/// </summary>
public bool ExcludeClassicFeatureFlags { get; set; } = false;
@linglingye001 linglingye001 marked this pull request as ready for review June 23, 2026 01:23
@linglingye001 linglingye001 changed the title Exclude classic feature flags Loading feature flags from new endpoint Jun 23, 2026

foreach (SdkFeatureFlag ff in page.Values)
{
ConfigurationSetting synthesized = FeatureFlagSettingConverter.ToConfigurationSetting(ff);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The new feature flags shouldn't be converted to configuration setting. When we load a new feature flag, we should break it down into it's associated key-value pairs, like the feature flag adapter does for classic feature flags, and inject that into the configuration directly.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I saw some updates in the PR. Is this one still being worked on?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, working in progess

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Updated, new feature flags now map to the internal model (provider FeatureFlag) and share the ff adapter's logic.

@jimmyca15

Copy link
Copy Markdown
Member

This needs to go into the preview branch.

@linglingye001 linglingye001 force-pushed the linglingye/exclude-classic-ff branch from 708b5b1 to 56f906e Compare June 25, 2026 09:50
@zhiyuanliang-ms zhiyuanliang-ms changed the base branch from main to preview June 25, 2026 09:58
/// configured <see cref="ClientOptions"/> so that the feature-flag client communicates with the
/// same store, audience, transport and retry behavior as the configuration client.
/// </summary>
internal FeatureFlagClientOptions GetFeatureFlagClientOptions()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we're going to separate the options, I think it will be better to have FeatureFlagClientOptions as a property just like ClientOptions.

And whenever ConfigureClientOptions is used, we can reflect the changes on FeatureFlagClientOptions.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Makes consumption equal.


namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
{
internal class ClientWrapper

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How about we introduce IAppConfigurationClient which exposes the methods that we need to use from both FeatureFlagClient and ConfigurationClient. And the implementation AppConfigurationClient can contain both internally.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That way the provider can still work with a single client.

// Emit configuration for feature flags loaded from the standalone feature-flag endpoint.
// These are mapped directly from the SDK model to feature-management key-values without
// round-tripping through a synthesized ConfigurationSetting.
if (sdkFFs != null && sdkFFs.Count > 0)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think that there should be a separate method to add feature flags into data.

private void ProcessFeatureFlags(
    Dictionary<string, ConfigurationSetting> data,
    IEnumerable<FeatureFlag> featureFlags);

if (key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
SdkFeatureFlag featureFlag = kvp.Value;

FeatureManagement.FeatureFlag model = SdkFeatureFlagModelMapper.MapToModel(featureFlag);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There shouldn't be an intermediate state. We should go straight from FeatureFlag to the IEnumerable<KeyValuePair<string, string>> that should go into configuration.

using Azure;
using Azure.Data.AppConfiguration;
using Microsoft.Extensions.Configuration.AzureAppConfiguration.Models;
using SdkFeatureFlag = Azure.Data.AppConfiguration.FeatureFlag;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's avoid introducing this concept of 'SdkFeatureFlag'.

Going forward 'FeatureFlag' refers to the new flags.

All previous naming for old flags should be renamed to ClassicFeatureFlag. Including the type here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we do need some separation, we can use the terms classic feature flag and standalone feature flag.

return data;
}

private async Task LoadFeatureFlags(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd like to keep classic feature flags and standalone feature flags separate.

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