Skip to content

Respect AllowedProviders/Providers in Feature/Setting management#25462

Merged
EngincanV merged 3 commits into
rel-10.4from
fix/feature-allowed-providers-respected-by-management
Jun 1, 2026
Merged

Respect AllowedProviders/Providers in Feature/Setting management#25462
EngincanV merged 3 commits into
rel-10.4from
fix/feature-allowed-providers-respected-by-management

Conversation

@maliming
Copy link
Copy Markdown
Member

@maliming maliming commented May 22, 2026

Fixes https://abp.io/support/questions/10684. Feature/Setting management now respects AllowedProviders / Providers to align with IFeatureChecker / ISettingProvider.

- FeatureManager.SetAsync: throw when providerName is not in feature.AllowedProviders
- FeatureManager.GetAllWithProviderAsync / GetOrNullInternalAsync: filter providers chain by feature.AllowedProviders (aligning with FeatureChecker)
- FeatureAppService.GetAsync: filter features by AllowedProviders and add parent chain check to prevent orphan child entries (aligning with PermissionAppService)
- SettingManager.SetAsync: throw when providerName is not in setting.Providers
- SettingManager.GetAllAsync / GetOrNullInternalAsync: filter providers chain by setting.Providers (aligning with SettingProvider)
Copilot AI review requested due to automatic review settings May 22, 2026 03:48
@maliming maliming added this to the 10.4-patch milestone May 22, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 tightens feature/setting management to respect each definition’s allowed provider list: writes reject disallowed providers, and reads filter the provider chain so behavior aligns more closely with IFeatureChecker / ISettingProvider. It also updates the Feature Management UI read model to omit definitions outside the active management provider (and avoids returning orphan children).

Changes:

  • Enforce provider compatibility for SettingManager / FeatureManager read+write paths (provider-chain filtering + write-time rejection).
  • Update FeatureAppService.GetAsync to filter out disallowed features (and orphan children) for the current management provider.
  • Add/extend test definitions and test cases covering allowed/disallowed provider scenarios and stale-fallback edge cases.

Reviewed changes

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

Show a summary per file
File Description
modules/setting-management/test/Volo.Abp.SettingManagement.Tests/Volo/Abp/SettingManagement/SettingManager_Basic_Tests.cs Adds tests for disallowed provider writes and read filtering behavior in setting management.
modules/setting-management/test/Volo.Abp.SettingManagement.TestBase/Volo/Abp/SettingManagement/TestSettingDefinitionProvider.cs Introduces settings restricted to specific providers for test coverage.
modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs Filters provider chains by SettingDefinition.Providers and rejects disallowed provider writes.
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs Adds provider-restricted feature definitions (including parent/child cases) for tests.
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs Adds tests for disallowed provider writes and filtered read behavior in FeatureManager.
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs Adds tests asserting the app service hides disallowed features and orphan children.
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs Filters provider chains by FeatureDefinition.AllowedProviders and rejects disallowed provider writes.
modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs Filters features returned from GetAsync based on management provider and parent inclusion.
Comments suppressed due to low confidence (2)

modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs:144

  • The new provider compatibility exception message is grammatically incorrect ("has not compatible") and is also evaluated before checking whether the provider actually exists. For a restricted setting, passing an unknown providerName will now throw the compatibility exception instead of the existing "Unknown setting value provider" error, which can hide misconfiguration. Consider checking provider existence first, then throwing a clearer message like "is not compatible with" (optionally including allowed providers).
        var setting = await SettingDefinitionManager.GetAsync(name);

        if (setting.Providers.Any() && !setting.Providers.Contains(providerName))
        {
            throw new AbpException(
                $"The setting named '{name}' has not compatible with the provider named '{providerName}'");
        }

        var providers = Enumerable
            .Reverse(Providers)
            .SkipWhile(p => p.Name != providerName)
            .ToList();

        if (!providers.Any())
        {
            throw new AbpException($"Unknown setting value provider: {providerName}");
        }

modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs:163

  • The new compatibility exception message is grammatically incorrect ("has not compatible") and is checked before verifying the provider exists. For a feature with restricted AllowedProviders, calling SetAsync with an unregistered providerName will throw the compatibility exception instead of the existing "Unknown feature value provider" error, which is misleading. Consider checking provider existence first, then throwing a clearer compatibility message (e.g., "is not compatible with") when the provider is registered but not allowed.
        var feature = await FeatureDefinitionManager.GetAsync(name);

        if (feature.ValueType?.Validator.IsValid(value) == false)
        {
            throw new FeatureValueInvalidException(feature.DisplayName.Localize(StringLocalizerFactory));
        }

        if (feature.AllowedProviders.Any() && !feature.AllowedProviders.Contains(providerName))
        {
            throw new AbpException(
                $"The feature named '{name}' has not compatible with the provider named '{providerName}'");
        }

        var providers = Enumerable
            .Reverse(Providers)
            .SkipWhile(p => p.Name != providerName)
            .ToList();

        if (!providers.Any())
        {
            throw new AbpException($"Unknown feature value provider: {providerName}");
        }

- SettingManager.GetAllAsync / FeatureManager.GetAllWithProviderAsync: drop top-level continue and rely on the provider chain filter so allowed upstream providers can still be read via inheritance (e.g. GetAllForUserAsync now returns a Global-only setting)
- FeatureAppService.GetAsync: switch includedFeatures to HashSet for O(1) parent lookup
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (3)

modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs:144

  • SetAsync checks setting.Providers compatibility before verifying providerName exists in the configured management providers. If providerName is actually unknown and the setting has a restricted provider list, this will throw the “not compatible” exception instead of the existing “Unknown setting value provider” error. Consider performing the unknown-provider check first (or only running the compatibility check after providers.Any() is validated).
        var setting = await SettingDefinitionManager.GetAsync(name);

        if (setting.Providers.Any() && !setting.Providers.Contains(providerName))
        {
            throw new AbpException(
                $"The setting named '{name}' has not compatible with the provider named '{providerName}'");
        }

        var providers = Enumerable
            .Reverse(Providers)
            .SkipWhile(p => p.Name != providerName)
            .ToList();

        if (!providers.Any())
        {
            throw new AbpException($"Unknown setting value provider: {providerName}");
        }

modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs:107

  • In GetAllAsync, the non-inherited branch calls settingProviderList[0].GetOrNullAsync(setting, providerKey) even when the first allowed provider is not the requested providerName (after filtering by setting.Providers). This can pass a user/tenant key to a different provider type and read the wrong row. Pass the key only when the provider matches the requested provider (otherwise null), similar to the inherited branch logic.
            else
            {
                value = await settingProviderList[0].GetOrNullAsync(
                    setting,
                    providerKey
                );
            }

modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs:163

  • SetAsync checks feature.AllowedProviders compatibility before verifying providerName exists in the configured management providers. If providerName is unknown and the feature has AllowedProviders configured, this throws the “not compatible” exception instead of the existing “Unknown feature value provider” error. Consider validating provider existence first, then enforcing the AllowedProviders restriction.
        var feature = await FeatureDefinitionManager.GetAsync(name);

        if (feature.ValueType?.Validator.IsValid(value) == false)
        {
            throw new FeatureValueInvalidException(feature.DisplayName.Localize(StringLocalizerFactory));
        }

        if (feature.AllowedProviders.Any() && !feature.AllowedProviders.Contains(providerName))
        {
            throw new AbpException(
                $"The feature named '{name}' has not compatible with the provider named '{providerName}'");
        }

        var providers = Enumerable
            .Reverse(Providers)
            .SkipWhile(p => p.Name != providerName)
            .ToList();

        if (!providers.Any())
        {
            throw new AbpException($"Unknown feature value provider: {providerName}");
        }

Replace 'has not compatible with' with 'is not compatible with' in FeatureManager, SettingManager and PermissionManager (the latter is the original phrasing this PR initially mirrored).
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

modules/setting-management/src/Volo.Abp.SettingManagement.Domain/Volo/Abp/SettingManagement/SettingManager.cs:107

  • In GetAllAsync, for non-inherited settings (setting.IsInherited == false) the code reads from settingProviderList[0]. Since providerList is ordered from upstream -> current provider (e.g., DefaultValue/Configuration/.../User), this can ignore the requested provider and cause GetAllForUserAsync/GetAllGlobalAsync to miss values that actually exist for that provider. For non-inherited settings, this should only query the current providerName (and if the setting’s Providers list doesn’t include providerName, it should be skipped).
                    {
                        value = providerValue;
                    }
                }
            }
            else
            {
                value = await settingProviderList[0].GetOrNullAsync(
                    setting,
                    providerKey
                );
            }

@maliming maliming requested a review from EngincanV May 22, 2026 04:23
@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

❌ Patch coverage is 43.26241% with 80 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.40%. Comparing base (9ddc151) to head (957e0c9).
⚠️ Report is 16 commits behind head on rel-10.4.

Files with missing lines Patch % Lines
...Volo/Abp/FeatureManagement/FeatureManager_Tests.cs 0.00% 79 Missing ⚠️
...Volo/Abp/PermissionManagement/PermissionManager.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           rel-10.4   #25462    +/-   ##
==========================================
  Coverage     49.39%   49.40%            
==========================================
  Files          3670     3670            
  Lines        123594   123735   +141     
  Branches       9452     9463    +11     
==========================================
+ Hits          61055    61127    +72     
- Misses        60705    60780    +75     
+ Partials       1834     1828     -6     

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@EngincanV EngincanV merged commit 4470056 into rel-10.4 Jun 1, 2026
4 of 5 checks passed
@EngincanV EngincanV deleted the fix/feature-allowed-providers-respected-by-management branch June 1, 2026 06:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants