Browse Source

use file-scoped namespacing for featureManagement module

pull/10696/head
Ahmet Çotur 5 years ago
parent
commit
3afa5f1a5c
  1. 43
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs
  2. 23
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureDto.cs
  3. 19
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureGroupDto.cs
  4. 17
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementPermissions.cs
  5. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs
  6. 33
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeaturePermissionDefinitionProvider.cs
  7. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureProviderDto.cs
  8. 9
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/GetFeatureListResultDto.cs
  9. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/IFeatureAppService.cs
  10. 117
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/NewtonsoftStringValueTypeJsonConverter.cs
  11. 43
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs
  12. 51
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs
  13. 69
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs
  14. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/UpdateFeatureDto.cs
  15. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/UpdateFeaturesDto.cs
  16. 29
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationModule.cs
  17. 161
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs
  18. 15
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementAppServiceBase.cs
  19. 13
      modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementApplicationAutoMapperProfile.cs
  20. 15
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor.Server/AbpFeatureManagementBlazorServerModule.cs
  21. 17
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor.WebAssembly/AbpFeatureManagementBlazorWebAssemblyModule.cs
  22. 23
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementBlazorModule.cs
  23. 13
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementComponentBase.cs
  24. 251
      modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor.cs
  25. 45
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs
  26. 9
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureManagementDomainErrorCodes.cs
  27. 15
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureValueInvalidException.cs
  28. 11
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/AbpFeatureManagementResource.cs
  29. 37
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs
  30. 39
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs
  31. 35
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagerExtensions.cs
  32. 37
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs
  33. 43
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagerExtensions.cs
  34. 13
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementDbProperties.cs
  35. 21
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs
  36. 69
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs
  37. 169
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs
  38. 315
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManager.cs
  39. 23
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValue.cs
  40. 21
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValueWithGrantedProvider.cs
  41. 29
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureStore.cs
  42. 67
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValue.cs
  43. 35
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItem.cs
  44. 45
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator.cs
  45. 39
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs
  46. 23
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueProviderInfo.cs
  47. 19
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs
  48. 15
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementStore.cs
  49. 17
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManager.cs
  50. 35
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs
  51. 37
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs
  52. 43
      modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagerExtensions.cs
  53. 27
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreModule.cs
  54. 73
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs
  55. 29
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContext.cs
  56. 48
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContextModelCreatingExtensions.cs
  57. 13
      modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/IFeatureManagementDbContext.cs
  58. 7
      modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/ClientProxies/FeaturesClientProxy.cs
  59. 31
      modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiClientModule.cs
  60. 45
      modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs
  61. 41
      modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs
  62. 23
      modules/feature-management/src/Volo.Abp.FeatureManagement.Installer/Volo/Abp/FeatureManagement/AbpFeatureManagementInstallerModule.cs
  63. 29
      modules/feature-management/src/Volo.Abp.FeatureManagement.Installer/Volo/Abp/FeatureManagement/FeatureManagementInstallerPipelineBuilder.cs
  64. 25
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbModule.cs
  65. 21
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContext.cs
  66. 23
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContextExtensions.cs
  67. 13
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/IFeatureManagementMongoDbContext.cs
  68. 67
      modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs
  69. 67
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/AbpFeatureManagementWebModule.cs
  70. 13
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/FeatureManagementWebAutoMapperProfile.cs
  71. 115
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml.cs
  72. 27
      modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json
  73. 95
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs
  74. 9
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestBase.cs
  75. 15
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestModule.cs
  76. 15
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/NewtonsoftStringValueJsonConverter_Tests.cs
  77. 63
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs
  78. 15
      modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/SystemTextJsonStringValueJsonConverter_Tests.cs
  79. 15
      modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainTestModule.cs
  80. 9
      modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManagementDomainTestBase.cs
  81. 270
      modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs
  82. 126
      modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator_Tests.cs
  83. 53
      modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs
  84. 9
      modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementStore_Tests.cs
  85. 7
      modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureValueRepositoryTests.cs
  86. 34
      modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs
  87. 9
      modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs
  88. 9
      modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs
  89. 27
      modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs
  90. 13
      modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoTestCollection.cs
  91. 195
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs
  92. 13
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBase.cs
  93. 55
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs
  94. 9
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs
  95. 369
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs
  96. 109
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs
  97. 25
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs
  98. 93
      modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs

43
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationContractsModule.cs

@ -8,32 +8,31 @@ using Volo.Abp.Json.SystemTextJson;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpDddApplicationContractsModule),
typeof(AbpAuthorizationAbstractionsModule),
typeof(AbpJsonModule)
)]
public class AbpFeatureManagementApplicationContractsModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpDddApplicationContractsModule),
typeof(AbpAuthorizationAbstractionsModule),
typeof(AbpJsonModule)
)]
public class AbpFeatureManagementApplicationContractsModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) Configure<AbpVirtualFileSystemOptions>(options =>
{ {
Configure<AbpVirtualFileSystemOptions>(options => options.FileSets.AddEmbedded<AbpFeatureManagementApplicationContractsModule>();
{ });
options.FileSets.AddEmbedded<AbpFeatureManagementApplicationContractsModule>();
});
Configure<AbpNewtonsoftJsonSerializerOptions>(options => Configure<AbpNewtonsoftJsonSerializerOptions>(options =>
{ {
options.Converters.Add<NewtonsoftStringValueTypeJsonConverter>(); options.Converters.Add<NewtonsoftStringValueTypeJsonConverter>();
}); });
Configure<AbpSystemTextJsonSerializerOptions>(options => Configure<AbpSystemTextJsonSerializerOptions>(options =>
{ {
options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter()); options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter());
}); });
}
} }
} }

23
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureDto.cs

@ -1,23 +1,22 @@
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureDto
{ {
public class FeatureDto public string Name { get; set; }
{
public string Name { get; set; }
public string DisplayName { get; set; } public string DisplayName { get; set; }
public string Value { get; set; } public string Value { get; set; }
public FeatureProviderDto Provider { get; set; } public FeatureProviderDto Provider { get; set; }
public string Description { get; set; } public string Description { get; set; }
public IStringValueType ValueType { get; set; } public IStringValueType ValueType { get; set; }
public int Depth { get; set; } public int Depth { get; set; }
public string ParentName { get; set; } public string ParentName { get; set; }
}
} }

19
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureGroupDto.cs

@ -1,18 +1,17 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureGroupDto
{ {
public class FeatureGroupDto public string Name { get; set; }
{
public string Name { get; set; }
public string DisplayName { get; set; } public string DisplayName { get; set; }
public List<FeatureDto> Features { get; set; } public List<FeatureDto> Features { get; set; }
public string GetNormalizedGroupName() public string GetNormalizedGroupName()
{ {
return Name.Replace(".", "_"); return Name.Replace(".", "_");
}
} }
} }

17
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementPermissions.cs

@ -1,16 +1,15 @@
using Volo.Abp.Reflection; using Volo.Abp.Reflection;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementPermissions
{ {
public class FeatureManagementPermissions public const string GroupName = "FeatureManagement";
{
public const string GroupName = "FeatureManagement";
public const string ManageHostFeatures = GroupName + ".ManageHostFeatures"; public const string ManageHostFeatures = GroupName + ".ManageHostFeatures";
public static string[] GetAll() public static string[] GetAll()
{ {
return ReflectionHelper.GetPublicConstantsRecursively(typeof(FeatureManagementPermissions)); return ReflectionHelper.GetPublicConstantsRecursively(typeof(FeatureManagementPermissions));
}
} }
} }

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureManagementRemoteServiceConsts.cs

@ -1,9 +1,8 @@
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementRemoteServiceConsts
{ {
public class FeatureManagementRemoteServiceConsts public const string RemoteServiceName = "AbpFeatureManagement";
{
public const string RemoteServiceName = "AbpFeatureManagement";
public const string ModuleName = "featureManagement"; public const string ModuleName = "featureManagement";
}
} }

33
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeaturePermissionDefinitionProvider.cs

@ -3,26 +3,25 @@ using Volo.Abp.FeatureManagement.Localization;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeaturePermissionDefinitionProvider : PermissionDefinitionProvider
{ {
public class FeaturePermissionDefinitionProvider : PermissionDefinitionProvider public override void Define(IPermissionDefinitionContext context)
{ {
public override void Define(IPermissionDefinitionContext context) var featureManagementGroup = context.AddGroup(
{ FeatureManagementPermissions.GroupName,
var featureManagementGroup = context.AddGroup( L("Permission:FeatureManagement"),
FeatureManagementPermissions.GroupName, multiTenancySide: MultiTenancySides.Host);
L("Permission:FeatureManagement"),
multiTenancySide: MultiTenancySides.Host);
featureManagementGroup.AddPermission( featureManagementGroup.AddPermission(
FeatureManagementPermissions.ManageHostFeatures, FeatureManagementPermissions.ManageHostFeatures,
L("Permission:FeatureManagement.ManageHostFeatures"), L("Permission:FeatureManagement.ManageHostFeatures"),
multiTenancySide: MultiTenancySides.Host); multiTenancySide: MultiTenancySides.Host);
} }
private static LocalizableString L(string name) private static LocalizableString L(string name)
{ {
return LocalizableString.Create<AbpFeatureManagementResource>(name); return LocalizableString.Create<AbpFeatureManagementResource>(name);
}
} }
} }

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/FeatureProviderDto.cs

@ -1,9 +1,8 @@
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureProviderDto
{ {
public class FeatureProviderDto public string Name { get; set; }
{
public string Name { get; set; }
public string Key { get; set; } public string Key { get; set; }
}
} }

9
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/GetFeatureListResultDto.cs

@ -1,9 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class GetFeatureListResultDto
{ {
public class GetFeatureListResultDto public List<FeatureGroupDto> Groups { get; set; }
{
public List<FeatureGroupDto> Groups { get; set; }
}
} }

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/IFeatureAppService.cs

@ -2,12 +2,11 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public interface IFeatureAppService : IApplicationService
{ {
public interface IFeatureAppService : IApplicationService Task<GetFeatureListResultDto> GetAsync([NotNull] string providerName, string providerKey);
{
Task<GetFeatureListResultDto> GetAsync([NotNull] string providerName, string providerKey);
Task UpdateAsync([NotNull] string providerName, string providerKey, UpdateFeaturesDto input); Task UpdateAsync([NotNull] string providerName, string providerKey, UpdateFeaturesDto input);
}
} }

117
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/NewtonsoftStringValueTypeJsonConverter.cs

@ -7,84 +7,83 @@ using Newtonsoft.Json.Linq;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.JsonConverters namespace Volo.Abp.FeatureManagement.JsonConverters;
public class NewtonsoftStringValueTypeJsonConverter : JsonConverter, ITransientDependency
{ {
public class NewtonsoftStringValueTypeJsonConverter : JsonConverter, ITransientDependency public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{ {
public override bool CanWrite => false; return objectType == typeof(IStringValueType);
}
public override bool CanConvert(Type objectType) public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("This method should not be called to write (since CanWrite is false).");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{ {
return objectType == typeof(IStringValueType); return null;
} }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) var jsonObject = JObject.Load(reader);
var stringValue = CreateStringValueTypeByName(jsonObject, jsonObject["name"].ToString());
foreach (var o in serializer.Deserialize<Dictionary<string, object>>(
new JsonTextReader(new StringReader(jsonObject["properties"].ToString()))))
{ {
throw new NotImplementedException("This method should not be called to write (since CanWrite is false)."); stringValue[o.Key] = o.Value;
} }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) stringValue.Validator = CreateValueValidatorByName(jsonObject["validator"], jsonObject["validator"]["name"].ToString());
foreach (var o in serializer.Deserialize<Dictionary<string, object>>(
new JsonTextReader(new StringReader(jsonObject["validator"]["properties"].ToString()))))
{ {
if (reader.TokenType != JsonToken.StartObject) stringValue.Validator[o.Key] = o.Value;
{ }
return null;
}
var jsonObject = JObject.Load(reader);
var stringValue = CreateStringValueTypeByName(jsonObject, jsonObject["name"].ToString()); return stringValue;
foreach (var o in serializer.Deserialize<Dictionary<string, object>>( }
new JsonTextReader(new StringReader(jsonObject["properties"].ToString()))))
{
stringValue[o.Key] = o.Value;
}
stringValue.Validator = CreateValueValidatorByName(jsonObject["validator"], jsonObject["validator"]["name"].ToString()); protected virtual IStringValueType CreateStringValueTypeByName(JObject jObject, string name)
foreach (var o in serializer.Deserialize<Dictionary<string, object>>( {
new JsonTextReader(new StringReader(jsonObject["validator"]["properties"].ToString())))) if (name == "SelectionStringValueType")
{
var selectionStringValueType = new SelectionStringValueType();
if (jObject["itemSource"].HasValues)
{ {
stringValue.Validator[o.Key] = o.Value; selectionStringValueType.ItemSource = new StaticSelectionStringValueItemSource(jObject["itemSource"]["items"]
.Select(item => new LocalizableSelectionStringValueItem()
{
Value = item["value"].ToString(),
DisplayText = new LocalizableStringInfo(item["displayText"]["resourceName"].ToString(), item["displayText"]["name"].ToString())
}).ToArray());
} }
return stringValue; return selectionStringValueType;
} }
protected virtual IStringValueType CreateStringValueTypeByName(JObject jObject, string name) return name switch
{ {
if (name == "SelectionStringValueType") "FreeTextStringValueType" => new FreeTextStringValueType(),
{ "ToggleStringValueType" => new ToggleStringValueType(),
var selectionStringValueType = new SelectionStringValueType(); _ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!")
if (jObject["itemSource"].HasValues) };
{ }
selectionStringValueType.ItemSource = new StaticSelectionStringValueItemSource(jObject["itemSource"]["items"]
.Select(item => new LocalizableSelectionStringValueItem()
{
Value = item["value"].ToString(),
DisplayText = new LocalizableStringInfo(item["displayText"]["resourceName"].ToString(), item["displayText"]["name"].ToString())
}).ToArray());
}
return selectionStringValueType;
}
return name switch
{
"FreeTextStringValueType" => new FreeTextStringValueType(),
"ToggleStringValueType" => new ToggleStringValueType(),
_ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!")
};
}
protected virtual IValueValidator CreateValueValidatorByName(JToken jObject, string name) protected virtual IValueValidator CreateValueValidatorByName(JToken jObject, string name)
{
return name switch
{ {
return name switch "NULL" => new AlwaysValidValueValidator(),
{ "BOOLEAN" => new BooleanValueValidator(),
"NULL" => new AlwaysValidValueValidator(), "NUMERIC" => new NumericValueValidator(),
"BOOLEAN" => new BooleanValueValidator(), "STRING" => new StringValueValidator(),
"NUMERIC" => new NumericValueValidator(), _ => throw new ArgumentException($"{nameof(IValueValidator)} named {name} was not found!")
"STRING" => new StringValueValidator(), };
_ => throw new ArgumentException($"{nameof(IValueValidator)} named {name} was not found!")
};
}
} }
} }

43
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/SelectionStringValueItemSourceJsonConverter.cs

@ -4,34 +4,33 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.JsonConverters namespace Volo.Abp.FeatureManagement.JsonConverters;
public class SelectionStringValueItemSourceJsonConverter : JsonConverter<ISelectionStringValueItemSource>
{ {
public class SelectionStringValueItemSourceJsonConverter : JsonConverter<ISelectionStringValueItemSource> public override ISelectionStringValueItemSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
public override ISelectionStringValueItemSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) var rootElement = JsonDocument.ParseValue(ref reader).RootElement;
{ var itemsJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x =>
var rootElement = JsonDocument.ParseValue(ref reader).RootElement; x.Name.Equals(nameof(ISelectionStringValueItemSource.Items), StringComparison.OrdinalIgnoreCase));
var itemsJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x =>
x.Name.Equals(nameof(ISelectionStringValueItemSource.Items), StringComparison.OrdinalIgnoreCase));
if (itemsJsonProperty.Value.ValueKind == JsonValueKind.Array)
{
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
var selectionStringValueItem = if (itemsJsonProperty.Value.ValueKind == JsonValueKind.Array)
JsonSerializer.Deserialize<LocalizableSelectionStringValueItem[]>(itemsJsonProperty.Value.GetRawText(), newOptions) ?? {
Array.Empty<LocalizableSelectionStringValueItem>(); var newOptions = JsonSerializerOptionsHelper.Create(options, this);
return new StaticSelectionStringValueItemSource(selectionStringValueItem.As<ISelectionStringValueItem[]>()); var selectionStringValueItem =
} JsonSerializer.Deserialize<LocalizableSelectionStringValueItem[]>(itemsJsonProperty.Value.GetRawText(), newOptions) ??
Array.Empty<LocalizableSelectionStringValueItem>();
throw new JsonException($"Can't to get the {nameof(ISelectionStringValueItemSource.Items)} property of {nameof(ISelectionStringValueItemSource)}!"); return new StaticSelectionStringValueItemSource(selectionStringValueItem.As<ISelectionStringValueItem[]>());
} }
public override void Write(Utf8JsonWriter writer, ISelectionStringValueItemSource value, JsonSerializerOptions options) throw new JsonException($"Can't to get the {nameof(ISelectionStringValueItemSource.Items)} property of {nameof(ISelectionStringValueItemSource)}!");
{ }
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions); public override void Write(Utf8JsonWriter writer, ISelectionStringValueItemSource value, JsonSerializerOptions options)
} {
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
} }
} }

51
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/StringValueTypeJsonConverter.cs

@ -4,38 +4,37 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.JsonConverters namespace Volo.Abp.FeatureManagement.JsonConverters;
public class StringValueTypeJsonConverter : JsonConverter<IStringValueType>
{ {
public class StringValueTypeJsonConverter : JsonConverter<IStringValueType> public override IStringValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
public override IStringValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) var rootElement = JsonDocument.ParseValue(ref reader).RootElement;
{
var rootElement = JsonDocument.ParseValue(ref reader).RootElement;
var nameJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IStringValueType.Name), StringComparison.OrdinalIgnoreCase));
if (nameJsonProperty.Value.ValueKind == JsonValueKind.String)
{
var name = nameJsonProperty.Value.GetString();
var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(), var nameJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IStringValueType.Name), StringComparison.OrdinalIgnoreCase));
new SelectionStringValueItemSourceJsonConverter()); if (nameJsonProperty.Value.ValueKind == JsonValueKind.String)
{
var name = nameJsonProperty.Value.GetString();
return name switch var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ValueValidatorJsonConverter(),
{ new SelectionStringValueItemSourceJsonConverter());
"SelectionStringValueType" => JsonSerializer.Deserialize<SelectionStringValueType>(rootElement.GetRawText(), newOptions),
"FreeTextStringValueType" => JsonSerializer.Deserialize<FreeTextStringValueType>(rootElement.GetRawText(), newOptions),
"ToggleStringValueType" => JsonSerializer.Deserialize<ToggleStringValueType>(rootElement.GetRawText(), newOptions),
_ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!")
};
}
throw new JsonException($"Can't to get the {nameof(IStringValueType.Name)} property of {nameof(IStringValueType)}!"); return name switch
{
"SelectionStringValueType" => JsonSerializer.Deserialize<SelectionStringValueType>(rootElement.GetRawText(), newOptions),
"FreeTextStringValueType" => JsonSerializer.Deserialize<FreeTextStringValueType>(rootElement.GetRawText(), newOptions),
"ToggleStringValueType" => JsonSerializer.Deserialize<ToggleStringValueType>(rootElement.GetRawText(), newOptions),
_ => throw new ArgumentException($"{nameof(IStringValueType)} named {name} was not found!")
};
} }
public override void Write(Utf8JsonWriter writer, IStringValueType value, JsonSerializerOptions options) throw new JsonException($"Can't to get the {nameof(IStringValueType.Name)} property of {nameof(IStringValueType)}!");
{ }
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions); public override void Write(Utf8JsonWriter writer, IStringValueType value, JsonSerializerOptions options)
} {
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
} }
} }

69
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/JsonConverters/ValueValidatorJsonConverter.cs

@ -6,55 +6,54 @@ using System.Text.Json.Serialization;
using Volo.Abp.Json.SystemTextJson.JsonConverters; using Volo.Abp.Json.SystemTextJson.JsonConverters;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.JsonConverters namespace Volo.Abp.FeatureManagement.JsonConverters;
public class ValueValidatorJsonConverter : JsonConverter<IValueValidator>
{ {
public class ValueValidatorJsonConverter : JsonConverter<IValueValidator> public override IValueValidator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
public override IValueValidator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) var rootElement = JsonDocument.ParseValue(ref reader).RootElement;
var nameJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Name), StringComparison.OrdinalIgnoreCase));
if (nameJsonProperty.Value.ValueKind == JsonValueKind.String)
{ {
var rootElement = JsonDocument.ParseValue(ref reader).RootElement; var valueValidator = CreateValueValidatorByName(nameJsonProperty.Value.GetString());
var nameJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Name), StringComparison.OrdinalIgnoreCase)); var propertiesJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Properties), StringComparison.OrdinalIgnoreCase));
if (nameJsonProperty.Value.ValueKind == JsonValueKind.String) if (propertiesJsonProperty.Value.ValueKind == JsonValueKind.Object)
{ {
var valueValidator = CreateValueValidatorByName(nameJsonProperty.Value.GetString()); var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter());
var properties = JsonSerializer.Deserialize<IDictionary<string, object>>(propertiesJsonProperty.Value.GetRawText(), newOptions);
var propertiesJsonProperty = rootElement.EnumerateObject().FirstOrDefault(x => x.Name.Equals(nameof(IValueValidator.Properties), StringComparison.OrdinalIgnoreCase)); if (properties != null && properties.Any())
if (propertiesJsonProperty.Value.ValueKind == JsonValueKind.Object)
{ {
var newOptions = JsonSerializerOptionsHelper.Create(options, this, new ObjectToInferredTypesConverter()); foreach (var property in properties)
var properties = JsonSerializer.Deserialize<IDictionary<string, object>>(propertiesJsonProperty.Value.GetRawText(), newOptions);
if (properties != null && properties.Any())
{ {
foreach (var property in properties) valueValidator.Properties[property.Key] = property.Value;
{
valueValidator.Properties[property.Key] = property.Value;
}
} }
} }
return valueValidator;
} }
throw new JsonException($"Can't to get the {nameof(IValueValidator.Name)} property of {nameof(IValueValidator)}!"); return valueValidator;
} }
public override void Write(Utf8JsonWriter writer, IValueValidator value, JsonSerializerOptions options) throw new JsonException($"Can't to get the {nameof(IValueValidator.Name)} property of {nameof(IValueValidator)}!");
{ }
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
}
protected virtual IValueValidator CreateValueValidatorByName(string name) public override void Write(Utf8JsonWriter writer, IValueValidator value, JsonSerializerOptions options)
{
var newOptions = JsonSerializerOptionsHelper.Create(options, this);
JsonSerializer.Serialize(writer, value, value.GetType(), newOptions);
}
protected virtual IValueValidator CreateValueValidatorByName(string name)
{
return name switch
{ {
return name switch "NULL" => new AlwaysValidValueValidator(),
{ "BOOLEAN" => new BooleanValueValidator(),
"NULL" => new AlwaysValidValueValidator(), "NUMERIC" => new NumericValueValidator(),
"BOOLEAN" => new BooleanValueValidator(), "STRING" => new StringValueValidator(),
"NUMERIC" => new NumericValueValidator(), _ => throw new ArgumentException($"{nameof(IValueValidator)} named {name} was not found!")
"STRING" => new StringValueValidator(), };
_ => throw new ArgumentException($"{nameof(IValueValidator)} named {name} was not found!")
};
}
} }
} }

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/UpdateFeatureDto.cs

@ -1,9 +1,8 @@
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class UpdateFeatureDto
{ {
public class UpdateFeatureDto public string Name { get; set; }
{
public string Name { get; set; }
public string Value { get; set; } public string Value { get; set; }
}
} }

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Application.Contracts/Volo/Abp/FeatureManagement/UpdateFeaturesDto.cs

@ -1,9 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class UpdateFeaturesDto
{ {
public class UpdateFeaturesDto public List<UpdateFeatureDto> Features { get; set; }
{ }
public List<UpdateFeatureDto> Features { get; set; }
}
}

29
modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/AbpFeatureManagementApplicationModule.cs

@ -3,23 +3,22 @@ using Volo.Abp.Application;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementDomainModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpAutoMapperModule),
typeof(AbpDddApplicationModule)
)]
public class AbpFeatureManagementApplicationModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementDomainModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpAutoMapperModule),
typeof(AbpDddApplicationModule)
)]
public class AbpFeatureManagementApplicationModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) context.Services.AddAutoMapperObjectMapper<AbpFeatureManagementApplicationModule>();
Configure<AbpAutoMapperOptions>(options =>
{ {
context.Services.AddAutoMapperObjectMapper<AbpFeatureManagementApplicationModule>(); options.AddProfile<FeatureManagementApplicationAutoMapperProfile>(validate: true);
Configure<AbpAutoMapperOptions>(options => });
{
options.AddProfile<FeatureManagementApplicationAutoMapperProfile>(validate: true);
});
}
} }
} }

161
modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureAppService.cs

@ -1,124 +1,121 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[Authorize]
public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppService
{ {
[Authorize] protected FeatureManagementOptions Options { get; }
public class FeatureAppService : FeatureManagementAppServiceBase, IFeatureAppService protected IFeatureManager FeatureManager { get; }
protected IFeatureDefinitionManager FeatureDefinitionManager { get; }
public FeatureAppService(IFeatureManager featureManager,
IFeatureDefinitionManager featureDefinitionManager,
IOptions<FeatureManagementOptions> options)
{ {
protected FeatureManagementOptions Options { get; } FeatureManager = featureManager;
protected IFeatureManager FeatureManager { get; } FeatureDefinitionManager = featureDefinitionManager;
protected IFeatureDefinitionManager FeatureDefinitionManager { get; } Options = options.Value;
}
public FeatureAppService(IFeatureManager featureManager, public virtual async Task<GetFeatureListResultDto> GetAsync([NotNull] string providerName, string providerKey)
IFeatureDefinitionManager featureDefinitionManager, {
IOptions<FeatureManagementOptions> options) await CheckProviderPolicy(providerName, providerKey);
{
FeatureManager = featureManager;
FeatureDefinitionManager = featureDefinitionManager;
Options = options.Value;
}
public virtual async Task<GetFeatureListResultDto> GetAsync([NotNull] string providerName, string providerKey) var result = new GetFeatureListResultDto
{ {
await CheckProviderPolicy(providerName, providerKey); Groups = new List<FeatureGroupDto>()
};
var result = new GetFeatureListResultDto foreach (var group in FeatureDefinitionManager.GetGroups())
{
var groupDto = new FeatureGroupDto
{ {
Groups = new List<FeatureGroupDto>() Name = group.Name,
DisplayName = group.DisplayName.Localize(StringLocalizerFactory),
Features = new List<FeatureDto>()
}; };
foreach (var group in FeatureDefinitionManager.GetGroups()) foreach (var featureDefinition in group.GetFeaturesWithChildren())
{ {
var groupDto = new FeatureGroupDto if (providerName == TenantFeatureValueProvider.ProviderName &&
CurrentTenant.Id == null &&
providerKey == null &&
!featureDefinition.IsAvailableToHost)
{ {
Name = group.Name, continue;
DisplayName = group.DisplayName.Localize(StringLocalizerFactory), }
Features = new List<FeatureDto>()
};
foreach (var featureDefinition in group.GetFeaturesWithChildren()) var feature = await FeatureManager.GetOrNullWithProviderAsync(featureDefinition.Name, providerName, providerKey);
groupDto.Features.Add(new FeatureDto
{ {
if (providerName == TenantFeatureValueProvider.ProviderName && Name = featureDefinition.Name,
CurrentTenant.Id == null && DisplayName = featureDefinition.DisplayName?.Localize(StringLocalizerFactory),
providerKey == null && ValueType = featureDefinition.ValueType,
!featureDefinition.IsAvailableToHost) Description = featureDefinition.Description?.Localize(StringLocalizerFactory),
ParentName = featureDefinition.Parent?.Name,
Value = feature.Value,
Provider = new FeatureProviderDto
{ {
continue; Name = feature.Provider?.Name,
Key = feature.Provider?.Key
} }
});
}
var feature = await FeatureManager.GetOrNullWithProviderAsync(featureDefinition.Name, providerName, providerKey); SetFeatureDepth(groupDto.Features, providerName, providerKey);
groupDto.Features.Add(new FeatureDto
{
Name = featureDefinition.Name,
DisplayName = featureDefinition.DisplayName?.Localize(StringLocalizerFactory),
ValueType = featureDefinition.ValueType,
Description = featureDefinition.Description?.Localize(StringLocalizerFactory),
ParentName = featureDefinition.Parent?.Name,
Value = feature.Value,
Provider = new FeatureProviderDto
{
Name = feature.Provider?.Name,
Key = feature.Provider?.Key
}
});
}
SetFeatureDepth(groupDto.Features, providerName, providerKey); result.Groups.Add(groupDto);
}
result.Groups.Add(groupDto); return result;
} }
return result; public virtual async Task UpdateAsync([NotNull] string providerName, string providerKey, UpdateFeaturesDto input)
} {
await CheckProviderPolicy(providerName, providerKey);
public virtual async Task UpdateAsync([NotNull] string providerName, string providerKey, UpdateFeaturesDto input) foreach (var feature in input.Features)
{ {
await CheckProviderPolicy(providerName, providerKey); await FeatureManager.SetAsync(feature.Name, feature.Value, providerName, providerKey);
foreach (var feature in input.Features)
{
await FeatureManager.SetAsync(feature.Name, feature.Value, providerName, providerKey);
}
} }
}
protected virtual void SetFeatureDepth(List<FeatureDto> features, string providerName, string providerKey, protected virtual void SetFeatureDepth(List<FeatureDto> features, string providerName, string providerKey,
FeatureDto parentFeature = null, int depth = 0) FeatureDto parentFeature = null, int depth = 0)
{
foreach (var feature in features)
{ {
foreach (var feature in features) if ((parentFeature == null && feature.ParentName == null) || (parentFeature != null && parentFeature.Name == feature.ParentName))
{ {
if ((parentFeature == null && feature.ParentName == null) || (parentFeature != null && parentFeature.Name == feature.ParentName)) feature.Depth = depth;
{ SetFeatureDepth(features, providerName, providerKey, feature, depth + 1);
feature.Depth = depth;
SetFeatureDepth(features, providerName, providerKey, feature, depth + 1);
}
} }
} }
}
protected virtual async Task CheckProviderPolicy(string providerName, string providerKey) protected virtual async Task CheckProviderPolicy(string providerName, string providerKey)
{
string policyName;
if (providerName == TenantFeatureValueProvider.ProviderName && CurrentTenant.Id == null && providerKey == null)
{ {
string policyName; policyName = "FeatureManagement.ManageHostFeatures";
if (providerName == TenantFeatureValueProvider.ProviderName && CurrentTenant.Id == null && providerKey == null ) }
{ else
policyName = "FeatureManagement.ManageHostFeatures"; {
} policyName = Options.ProviderPolicies.GetOrDefault(providerName);
else if (policyName.IsNullOrEmpty())
{ {
policyName = Options.ProviderPolicies.GetOrDefault(providerName); throw new AbpException($"No policy defined to get/set permissions for the provider '{providerName}'. Use {nameof(FeatureManagementOptions)} to map the policy.");
if (policyName.IsNullOrEmpty())
{
throw new AbpException($"No policy defined to get/set permissions for the provider '{providerName}'. Use {nameof(FeatureManagementOptions)} to map the policy.");
}
} }
await AuthorizationService.CheckAsync(policyName);
} }
await AuthorizationService.CheckAsync(policyName);
} }
} }

15
modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementAppServiceBase.cs

@ -1,14 +1,13 @@
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.FeatureManagement.Localization; using Volo.Abp.FeatureManagement.Localization;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public abstract class FeatureManagementAppServiceBase : ApplicationService
{ {
public abstract class FeatureManagementAppServiceBase : ApplicationService protected FeatureManagementAppServiceBase()
{ {
protected FeatureManagementAppServiceBase() ObjectMapperContext = typeof(AbpFeatureManagementApplicationModule);
{ LocalizationResource = typeof(AbpFeatureManagementResource);
ObjectMapperContext = typeof(AbpFeatureManagementApplicationModule);
LocalizationResource = typeof(AbpFeatureManagementResource);
}
} }
} }

13
modules/feature-management/src/Volo.Abp.FeatureManagement.Application/Volo/Abp/FeatureManagement/FeatureManagementApplicationAutoMapperProfile.cs

@ -1,12 +1,11 @@
using AutoMapper; using AutoMapper;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementApplicationAutoMapperProfile : Profile
{ {
public class FeatureManagementApplicationAutoMapperProfile : Profile public FeatureManagementApplicationAutoMapperProfile()
{ {
public FeatureManagementApplicationAutoMapperProfile()
{
}
} }
} }

15
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor.Server/AbpFeatureManagementBlazorServerModule.cs

@ -1,14 +1,13 @@
using Volo.Abp.AspNetCore.Components.Server.Theming; using Volo.Abp.AspNetCore.Components.Server.Theming;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement.Blazor.Server namespace Volo.Abp.FeatureManagement.Blazor.Server;
[DependsOn(
typeof(AbpFeatureManagementBlazorModule),
typeof(AbpAspNetCoreComponentsServerThemingModule)
)]
public class AbpFeatureManagementBlazorServerModule : AbpModule
{ {
[DependsOn(
typeof(AbpFeatureManagementBlazorModule),
typeof(AbpAspNetCoreComponentsServerThemingModule)
)]
public class AbpFeatureManagementBlazorServerModule : AbpModule
{
}
} }

17
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor.WebAssembly/AbpFeatureManagementBlazorWebAssemblyModule.cs

@ -1,14 +1,13 @@
using Volo.Abp.AspNetCore.Components.WebAssembly.Theming; using Volo.Abp.AspNetCore.Components.WebAssembly.Theming;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement.Blazor.WebAssembly namespace Volo.Abp.FeatureManagement.Blazor.WebAssembly;
[DependsOn(
typeof(AbpFeatureManagementBlazorModule),
typeof(AbpAspNetCoreComponentsWebAssemblyThemingModule),
typeof(AbpFeatureManagementHttpApiClientModule)
)]
public class AbpFeatureManagementBlazorWebAssemblyModule : AbpModule
{ {
[DependsOn(
typeof(AbpFeatureManagementBlazorModule),
typeof(AbpAspNetCoreComponentsWebAssemblyThemingModule),
typeof(AbpFeatureManagementHttpApiClientModule)
)]
public class AbpFeatureManagementBlazorWebAssemblyModule : AbpModule
{
}
} }

23
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementBlazorModule.cs

@ -3,16 +3,15 @@ using Volo.Abp.AutoMapper;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement.Blazor namespace Volo.Abp.FeatureManagement.Blazor;
[DependsOn(
typeof(AbpAspNetCoreComponentsWebThemingModule),
typeof(AbpAutoMapperModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpFeaturesModule)
)]
public class AbpFeatureManagementBlazorModule : AbpModule
{ {
[DependsOn(
typeof(AbpAspNetCoreComponentsWebThemingModule), }
typeof(AbpAutoMapperModule),
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpFeaturesModule)
)]
public class AbpFeatureManagementBlazorModule : AbpModule
{
}
}

13
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/AbpFeatureManagementComponentBase.cs

@ -1,13 +1,12 @@
using Volo.Abp.AspNetCore.Components; using Volo.Abp.AspNetCore.Components;
using Volo.Abp.FeatureManagement.Localization; using Volo.Abp.FeatureManagement.Localization;
namespace Volo.Abp.FeatureManagement.Blazor namespace Volo.Abp.FeatureManagement.Blazor;
public abstract class AbpFeatureManagementComponentBase : AbpComponentBase
{ {
public abstract class AbpFeatureManagementComponentBase : AbpComponentBase protected AbpFeatureManagementComponentBase()
{ {
protected AbpFeatureManagementComponentBase() LocalizationResource = typeof(AbpFeatureManagementResource);
{
LocalizationResource = typeof(AbpFeatureManagementResource);
}
} }
} }

251
modules/feature-management/src/Volo.Abp.FeatureManagement.Blazor/Components/FeatureManagementModal.razor.cs

@ -13,196 +13,195 @@ using Volo.Abp.Features;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.Blazor.Components namespace Volo.Abp.FeatureManagement.Blazor.Components;
public partial class FeatureManagementModal
{ {
public partial class FeatureManagementModal [Inject] protected IFeatureAppService FeatureAppService { get; set; }
{
[Inject] protected IFeatureAppService FeatureAppService { get; set; }
[Inject] protected IUiMessageService UiMessageService { get; set; } [Inject] protected IUiMessageService UiMessageService { get; set; }
[Inject] protected IStringLocalizerFactory HtmlLocalizerFactory { get; set; } [Inject] protected IStringLocalizerFactory HtmlLocalizerFactory { get; set; }
[Inject] protected IOptions<AbpLocalizationOptions> LocalizationOptions { get; set; } [Inject] protected IOptions<AbpLocalizationOptions> LocalizationOptions { get; set; }
[Inject] private ICurrentApplicationConfigurationCacheResetService CurrentApplicationConfigurationCacheResetService { get; set; } [Inject] private ICurrentApplicationConfigurationCacheResetService CurrentApplicationConfigurationCacheResetService { get; set; }
protected Modal Modal; protected Modal Modal;
protected string ProviderName; protected string ProviderName;
protected string ProviderKey; protected string ProviderKey;
protected string SelectedTabName; protected string SelectedTabName;
protected List<FeatureGroupDto> Groups { get; set; } protected List<FeatureGroupDto> Groups { get; set; }
protected Dictionary<string, bool> ToggleValues; protected Dictionary<string, bool> ToggleValues;
protected Dictionary<string, string> SelectionStringValues; protected Dictionary<string, string> SelectionStringValues;
public virtual async Task OpenAsync([NotNull]string providerName, string providerKey = null) public virtual async Task OpenAsync([NotNull] string providerName, string providerKey = null)
{
try
{ {
try ProviderName = providerName;
{ ProviderKey = providerKey;
ProviderName = providerName;
ProviderKey = providerKey;
ToggleValues = new Dictionary<string, bool>(); ToggleValues = new Dictionary<string, bool>();
SelectionStringValues = new Dictionary<string, string>(); SelectionStringValues = new Dictionary<string, string>();
Groups = (await FeatureAppService.GetAsync(ProviderName, ProviderKey))?.Groups; Groups = (await FeatureAppService.GetAsync(ProviderName, ProviderKey))?.Groups;
Groups ??= new List<FeatureGroupDto>(); Groups ??= new List<FeatureGroupDto>();
if (Groups.Any()) if (Groups.Any())
{ {
SelectedTabName = GetNormalizedGroupName(Groups.First().Name); SelectedTabName = GetNormalizedGroupName(Groups.First().Name);
} }
foreach (var featureGroupDto in Groups) foreach (var featureGroupDto in Groups)
{
foreach (var featureDto in featureGroupDto.Features)
{ {
foreach (var featureDto in featureGroupDto.Features) if (featureDto.ValueType is ToggleStringValueType)
{ {
if (featureDto.ValueType is ToggleStringValueType) ToggleValues.Add(featureDto.Name, bool.Parse(featureDto.Value));
{
ToggleValues.Add(featureDto.Name, bool.Parse(featureDto.Value));
}
if (featureDto.ValueType is SelectionStringValueType)
{
SelectionStringValues.Add(featureDto.Name, featureDto.Value);
}
} }
}
await InvokeAsync(Modal.Show); if (featureDto.ValueType is SelectionStringValueType)
} {
catch (Exception ex) SelectionStringValues.Add(featureDto.Name, featureDto.Value);
{ }
await HandleErrorAsync(ex); }
} }
}
public virtual Task CloseModal() await InvokeAsync(Modal.Show);
}
catch (Exception ex)
{ {
return InvokeAsync(Modal.Hide); await HandleErrorAsync(ex);
} }
}
protected virtual async Task SaveAsync() public virtual Task CloseModal()
{
return InvokeAsync(Modal.Hide);
}
protected virtual async Task SaveAsync()
{
try
{ {
try var features = new UpdateFeaturesDto
{ {
var features = new UpdateFeaturesDto Features = Groups.SelectMany(g => g.Features).Select(f => new UpdateFeatureDto
{ {
Features = Groups.SelectMany(g => g.Features).Select(f => new UpdateFeatureDto Name = f.Name,
{ Value = f.ValueType is ToggleStringValueType ? ToggleValues[f.Name].ToString() :
Name = f.Name, f.ValueType is SelectionStringValueType ? SelectionStringValues[f.Name] : f.Value
Value = f.ValueType is ToggleStringValueType ? ToggleValues[f.Name].ToString() : }).ToList()
f.ValueType is SelectionStringValueType ? SelectionStringValues[f.Name] : f.Value };
}).ToList()
};
await FeatureAppService.UpdateAsync(ProviderName, ProviderKey, features); await FeatureAppService.UpdateAsync(ProviderName, ProviderKey, features);
await CurrentApplicationConfigurationCacheResetService.ResetAsync(); await CurrentApplicationConfigurationCacheResetService.ResetAsync();
await InvokeAsync(Modal.Hide); await InvokeAsync(Modal.Hide);
}
catch (Exception ex)
{
await HandleErrorAsync(ex);
}
} }
catch (Exception ex)
protected virtual string GetNormalizedGroupName(string name)
{ {
return "FeatureGroup_" + name.Replace(".", "_"); await HandleErrorAsync(ex);
} }
}
protected virtual string GetNormalizedGroupName(string name)
{
return "FeatureGroup_" + name.Replace(".", "_");
}
protected virtual string GetFeatureStyles(FeatureDto feature)
{
return $"margin-left: {feature.Depth * 20}px";
}
protected virtual bool IsDisabled(string providerName)
{
return providerName != ProviderName && providerName != DefaultValueFeatureValueProvider.ProviderName;
}
protected virtual string GetFeatureStyles(FeatureDto feature) protected virtual async Task OnFeatureValueChangedAsync(string value, FeatureDto feature)
{
if (feature?.ValueType?.Validator.IsValid(value) == true)
{ {
return $"margin-left: {feature.Depth * 20}px"; feature.Value = value;
} }
else
protected virtual bool IsDisabled(string providerName)
{ {
return providerName != ProviderName && providerName != DefaultValueFeatureValueProvider.ProviderName; await UiMessageService.Warn(L["Volo.Abp.FeatureManagement:InvalidFeatureValue", feature.DisplayName]);
} }
}
protected virtual Task OnSelectedValueChangedAsync(bool value, FeatureDto feature)
{
ToggleValues[feature.Name] = value;
protected virtual async Task OnFeatureValueChangedAsync(string value, FeatureDto feature) if (value)
{ {
if (feature?.ValueType?.Validator.IsValid(value) == true) CheckParents(feature.ParentName);
{
feature.Value = value;
}
else
{
await UiMessageService.Warn(L["Volo.Abp.FeatureManagement:InvalidFeatureValue", feature.DisplayName]);
}
} }
else
protected virtual Task OnSelectedValueChangedAsync(bool value, FeatureDto feature)
{ {
ToggleValues[feature.Name] = value; UncheckChildren(feature.Name);
}
if (value) return Task.CompletedTask;
{ }
CheckParents(feature.ParentName);
}
else
{
UncheckChildren(feature.Name);
}
return Task.CompletedTask; protected virtual void CheckParents(string parentName)
{
if (parentName.IsNullOrWhiteSpace())
{
return;
} }
protected virtual void CheckParents(string parentName) foreach (var featureGroupDto in Groups)
{ {
if (parentName.IsNullOrWhiteSpace()) foreach (var featureDto in featureGroupDto.Features)
{ {
return; if (featureDto.Name == parentName && ToggleValues.ContainsKey(featureDto.Name))
}
foreach (var featureGroupDto in Groups)
{
foreach (var featureDto in featureGroupDto.Features)
{ {
if (featureDto.Name == parentName && ToggleValues.ContainsKey(featureDto.Name)) ToggleValues[featureDto.Name] = true;
{ CheckParents(featureDto.ParentName);
ToggleValues[featureDto.Name] = true;
CheckParents(featureDto.ParentName);
}
} }
} }
} }
}
protected virtual void UncheckChildren(string featureName) protected virtual void UncheckChildren(string featureName)
{
foreach (var featureGroupDto in Groups)
{ {
foreach (var featureGroupDto in Groups) foreach (var featureDto in featureGroupDto.Features)
{ {
foreach (var featureDto in featureGroupDto.Features) if (featureDto.ParentName == featureName && ToggleValues.ContainsKey(featureDto.Name))
{ {
if (featureDto.ParentName == featureName && ToggleValues.ContainsKey(featureDto.Name)) ToggleValues[featureDto.Name] = false;
{ UncheckChildren(featureDto.Name);
ToggleValues[featureDto.Name] = false;
UncheckChildren(featureDto.Name);
}
} }
} }
} }
}
protected virtual IStringLocalizer CreateStringLocalizer(string resourceName) protected virtual IStringLocalizer CreateStringLocalizer(string resourceName)
{ {
var resource = LocalizationOptions.Value.Resources.Values.FirstOrDefault(x => x.ResourceName == resourceName); var resource = LocalizationOptions.Value.Resources.Values.FirstOrDefault(x => x.ResourceName == resourceName);
return HtmlLocalizerFactory.Create(resource != null ? resource.ResourceType : LocalizationOptions.Value.DefaultResourceType); return HtmlLocalizerFactory.Create(resource != null ? resource.ResourceType : LocalizationOptions.Value.DefaultResourceType);
} }
protected virtual Task ClosingModal(ModalClosingEventArgs eventArgs) protected virtual Task ClosingModal(ModalClosingEventArgs eventArgs)
{ {
eventArgs.Cancel = eventArgs.CloseReason == CloseReason.FocusLostClosing; eventArgs.Cancel = eventArgs.CloseReason == CloseReason.FocusLostClosing;
return Task.CompletedTask; return Task.CompletedTask;
}
} }
} }

45
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainSharedModule.cs

@ -6,33 +6,32 @@ using Volo.Abp.Validation;
using Volo.Abp.Validation.Localization; using Volo.Abp.Validation.Localization;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpValidationModule)
)]
public class AbpFeatureManagementDomainSharedModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpValidationModule)
)]
public class AbpFeatureManagementDomainSharedModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) Configure<AbpVirtualFileSystemOptions>(options =>
{ {
Configure<AbpVirtualFileSystemOptions>(options => options.FileSets.AddEmbedded<AbpFeatureManagementDomainSharedModule>();
{ });
options.FileSets.AddEmbedded<AbpFeatureManagementDomainSharedModule>();
});
Configure<AbpLocalizationOptions>(options => Configure<AbpLocalizationOptions>(options =>
{ {
options.Resources options.Resources
.Add<AbpFeatureManagementResource>("en") .Add<AbpFeatureManagementResource>("en")
.AddBaseTypes( .AddBaseTypes(
typeof(AbpValidationResource) typeof(AbpValidationResource)
).AddVirtualJson("Volo/Abp/FeatureManagement/Localization/Domain"); ).AddVirtualJson("Volo/Abp/FeatureManagement/Localization/Domain");
}); });
Configure<AbpExceptionLocalizationOptions>(options => Configure<AbpExceptionLocalizationOptions>(options =>
{ {
options.MapCodeNamespace("Volo.Abp.FeatureManagement", typeof(AbpFeatureManagementResource)); options.MapCodeNamespace("Volo.Abp.FeatureManagement", typeof(AbpFeatureManagementResource));
}); });
}
} }
} }

9
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureManagementDomainErrorCodes.cs

@ -1,9 +1,6 @@
using System; namespace Volo.Abp.FeatureManagement;
namespace Volo.Abp.FeatureManagement public static class FeatureManagementDomainErrorCodes
{ {
public static class FeatureManagementDomainErrorCodes public const string FeatureValueInvalid = "Volo.Abp.FeatureManagement:InvalidFeatureValue";
{
public const string FeatureValueInvalid = "Volo.Abp.FeatureManagement:InvalidFeatureValue";
}
} }

15
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/FeatureValueInvalidException.cs

@ -1,14 +1,13 @@
using System; using System;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[Serializable]
public class FeatureValueInvalidException : BusinessException
{ {
[Serializable] public FeatureValueInvalidException(string name) :
public class FeatureValueInvalidException : BusinessException base(FeatureManagementDomainErrorCodes.FeatureValueInvalid)
{ {
public FeatureValueInvalidException(string name) : WithData("0", name);
base(FeatureManagementDomainErrorCodes.FeatureValueInvalid)
{
WithData("0", name);
}
} }
} }

11
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain.Shared/Volo/Abp/FeatureManagement/Localization/AbpFeatureManagementResource.cs

@ -1,10 +1,9 @@
using Volo.Abp.Localization; using Volo.Abp.Localization;
namespace Volo.Abp.FeatureManagement.Localization namespace Volo.Abp.FeatureManagement.Localization;
[LocalizationResourceName("AbpFeatureManagement")]
public class AbpFeatureManagementResource
{ {
[LocalizationResourceName("AbpFeatureManagement")]
public class AbpFeatureManagementResource
{
}
} }

37
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainModule.cs

@ -4,31 +4,30 @@ using Volo.Abp.Features;
using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Localization.ExceptionHandling;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpFeaturesModule),
typeof(AbpCachingModule)
)]
public class AbpFeatureManagementDomainModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementDomainSharedModule),
typeof(AbpFeaturesModule),
typeof(AbpCachingModule)
)]
public class AbpFeatureManagementDomainModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) Configure<FeatureManagementOptions>(options =>
{ {
Configure<FeatureManagementOptions>(options => options.Providers.Add<DefaultValueFeatureManagementProvider>();
{ options.Providers.Add<EditionFeatureManagementProvider>();
options.Providers.Add<DefaultValueFeatureManagementProvider>();
options.Providers.Add<EditionFeatureManagementProvider>();
//TODO: Should be moved to the Tenant Management module //TODO: Should be moved to the Tenant Management module
options.Providers.Add<TenantFeatureManagementProvider>(); options.Providers.Add<TenantFeatureManagementProvider>();
options.ProviderPolicies[TenantFeatureValueProvider.ProviderName] = "AbpTenantManagement.Tenants.ManageFeatures"; options.ProviderPolicies[TenantFeatureValueProvider.ProviderName] = "AbpTenantManagement.Tenants.ManageFeatures";
}); });
Configure<AbpExceptionLocalizationOptions>(options => Configure<AbpExceptionLocalizationOptions>(options =>
{ {
options.MapCodeNamespace("AbpFeatureManagement", typeof(AbpFeatureManagementResource)); options.MapCodeNamespace("AbpFeatureManagement", typeof(AbpFeatureManagementResource));
}); });
}
} }
} }

39
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagementProvider.cs

@ -2,30 +2,29 @@
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class DefaultValueFeatureManagementProvider : IFeatureManagementProvider, ISingletonDependency
{ {
public class DefaultValueFeatureManagementProvider : IFeatureManagementProvider, ISingletonDependency public string Name => DefaultValueFeatureValueProvider.ProviderName;
{
public string Name => DefaultValueFeatureValueProvider.ProviderName;
public bool Compatible(string providerName) public bool Compatible(string providerName)
{ {
return providerName == Name; return providerName == Name;
} }
public virtual Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey) public virtual Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey)
{ {
return Task.FromResult(feature.DefaultValue); return Task.FromResult(feature.DefaultValue);
} }
public virtual Task SetAsync(FeatureDefinition feature, string value, string providerKey) public virtual Task SetAsync(FeatureDefinition feature, string value, string providerKey)
{ {
throw new AbpException($"Can not set default value of a feature. It is only possible while defining the feature in a {typeof(IFeatureDefinitionProvider)} implementation."); throw new AbpException($"Can not set default value of a feature. It is only possible while defining the feature in a {typeof(IFeatureDefinitionProvider)} implementation.");
} }
public virtual Task ClearAsync(FeatureDefinition feature, string providerKey) public virtual Task ClearAsync(FeatureDefinition feature, string providerKey)
{ {
throw new AbpException($"Can not clear default value of a feature. It is only possible while defining the feature in a {typeof(IFeatureDefinitionProvider)} implementation."); throw new AbpException($"Can not clear default value of a feature. It is only possible while defining the feature in a {typeof(IFeatureDefinitionProvider)} implementation.");
}
} }
} }

35
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/DefaultValueFeatureManagerExtensions.cs

@ -3,28 +3,27 @@ using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public static class DefaultValueFeatureManagerExtensions
{ {
public static class DefaultValueFeatureManagerExtensions public static Task<string> GetOrNullDefaultAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true)
{ {
public static Task<string> GetOrNullDefaultAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true) return featureManager.GetOrNullAsync(name, DefaultValueFeatureValueProvider.ProviderName, null, fallback);
{ }
return featureManager.GetOrNullAsync(name, DefaultValueFeatureValueProvider.ProviderName, null, fallback);
}
public static Task<List<FeatureNameValue>> GetAllDefaultAsync(this IFeatureManager featureManager, bool fallback = true) public static Task<List<FeatureNameValue>> GetAllDefaultAsync(this IFeatureManager featureManager, bool fallback = true)
{ {
return featureManager.GetAllAsync(DefaultValueFeatureValueProvider.ProviderName, null, fallback); return featureManager.GetAllAsync(DefaultValueFeatureValueProvider.ProviderName, null, fallback);
} }
public static Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true) public static Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync(this IFeatureManager featureManager, [NotNull] string name, bool fallback = true)
{ {
return featureManager.GetOrNullWithProviderAsync(name, DefaultValueFeatureValueProvider.ProviderName, null, fallback); return featureManager.GetOrNullWithProviderAsync(name, DefaultValueFeatureValueProvider.ProviderName, null, fallback);
} }
public static Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync(this IFeatureManager featureManager, bool fallback = true) public static Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync(this IFeatureManager featureManager, bool fallback = true)
{ {
return featureManager.GetAllWithProviderAsync(DefaultValueFeatureValueProvider.ProviderName, null, fallback); return featureManager.GetAllWithProviderAsync(DefaultValueFeatureValueProvider.ProviderName, null, fallback);
}
} }
} }

37
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagementProvider.cs

@ -4,30 +4,29 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.Security.Claims; using Volo.Abp.Security.Claims;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class EditionFeatureManagementProvider : FeatureManagementProvider, ITransientDependency
{ {
public class EditionFeatureManagementProvider : FeatureManagementProvider, ITransientDependency public override string Name => EditionFeatureValueProvider.ProviderName;
{
public override string Name => EditionFeatureValueProvider.ProviderName;
protected ICurrentPrincipalAccessor PrincipalAccessor { get; } protected ICurrentPrincipalAccessor PrincipalAccessor { get; }
public EditionFeatureManagementProvider( public EditionFeatureManagementProvider(
IFeatureManagementStore store, IFeatureManagementStore store,
ICurrentPrincipalAccessor principalAccessor) ICurrentPrincipalAccessor principalAccessor)
: base(store) : base(store)
{ {
PrincipalAccessor = principalAccessor; PrincipalAccessor = principalAccessor;
} }
protected override Task<string> NormalizeProviderKeyAsync(string providerKey) protected override Task<string> NormalizeProviderKeyAsync(string providerKey)
{
if (providerKey != null)
{ {
if (providerKey != null) return Task.FromResult(providerKey);
{
return Task.FromResult(providerKey);
}
return Task.FromResult(PrincipalAccessor.Principal?.FindEditionId()?.ToString("N"));
} }
return Task.FromResult(PrincipalAccessor.Principal?.FindEditionId()?.ToString("N"));
} }
} }

43
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/EditionFeatureManagerExtensions.cs

@ -4,33 +4,32 @@ using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public static class EditionFeatureManagerExtensions
{ {
public static class EditionFeatureManagerExtensions public static Task<string> GetOrNullForEditionAsync(this IFeatureManager featureManager, [NotNull] string name, Guid editionId, bool fallback = true)
{ {
public static Task<string> GetOrNullForEditionAsync(this IFeatureManager featureManager, [NotNull] string name, Guid editionId, bool fallback = true) return featureManager.GetOrNullAsync(name, EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback);
{ }
return featureManager.GetOrNullAsync(name, EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback);
}
public static Task<List<FeatureNameValue>> GetAllForEditionAsync(this IFeatureManager featureManager, Guid editionId, bool fallback = true) public static Task<List<FeatureNameValue>> GetAllForEditionAsync(this IFeatureManager featureManager, Guid editionId, bool fallback = true)
{ {
return featureManager.GetAllAsync(EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback); return featureManager.GetAllAsync(EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback);
} }
public static Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderForEditionAsync(this IFeatureManager featureManager, [NotNull] string name, Guid editionId, bool fallback = true) public static Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderForEditionAsync(this IFeatureManager featureManager, [NotNull] string name, Guid editionId, bool fallback = true)
{ {
return featureManager.GetOrNullWithProviderAsync(name, EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback); return featureManager.GetOrNullWithProviderAsync(name, EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback);
} }
public static Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderForEditionAsync(this IFeatureManager featureManager, Guid editionId, bool fallback = true) public static Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderForEditionAsync(this IFeatureManager featureManager, Guid editionId, bool fallback = true)
{ {
return featureManager.GetAllWithProviderAsync(EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback); return featureManager.GetAllWithProviderAsync(EditionFeatureValueProvider.ProviderName, editionId.ToString(), fallback);
} }
public static Task SetForEditionAsync(this IFeatureManager featureManager, Guid editionId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) public static Task SetForEditionAsync(this IFeatureManager featureManager, Guid editionId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false)
{ {
return featureManager.SetAsync(name, value, EditionFeatureValueProvider.ProviderName, editionId.ToString(), forceToSet); return featureManager.SetAsync(name, value, EditionFeatureValueProvider.ProviderName, editionId.ToString(), forceToSet);
}
} }
} }

13
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementDbProperties.cs

@ -1,13 +1,12 @@
using Volo.Abp.Data; using Volo.Abp.Data;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public static class FeatureManagementDbProperties
{ {
public static class FeatureManagementDbProperties public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix;
{
public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix;
public static string DbSchema { get; set; } = AbpCommonDbProperties.DbSchema; public static string DbSchema { get; set; } = AbpCommonDbProperties.DbSchema;
public const string ConnectionStringName = "AbpFeatureManagement"; public const string ConnectionStringName = "AbpFeatureManagement";
}
} }

21
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementOptions.cs

@ -1,18 +1,17 @@
using System.Collections.Generic; using System.Collections.Generic;
using Volo.Abp.Collections; using Volo.Abp.Collections;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementOptions
{ {
public class FeatureManagementOptions public ITypeList<IFeatureManagementProvider> Providers { get; }
{
public ITypeList<IFeatureManagementProvider> Providers { get; }
public Dictionary<string, string> ProviderPolicies { get; } public Dictionary<string, string> ProviderPolicies { get; }
public FeatureManagementOptions() public FeatureManagementOptions()
{ {
Providers = new TypeList<IFeatureManagementProvider>(); Providers = new TypeList<IFeatureManagementProvider>();
ProviderPolicies = new Dictionary<string, string>(); ProviderPolicies = new Dictionary<string, string>();
}
} }
} }

69
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementProvider.cs

@ -1,42 +1,41 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public abstract class FeatureManagementProvider : IFeatureManagementProvider
{ {
public abstract class FeatureManagementProvider : IFeatureManagementProvider public abstract string Name { get; }
protected IFeatureManagementStore Store { get; }
protected FeatureManagementProvider(IFeatureManagementStore store)
{
Store = store;
}
public virtual bool Compatible(string providerName)
{
return providerName == Name;
}
public virtual async Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey)
{
return await Store.GetOrNullAsync(feature.Name, Name, await NormalizeProviderKeyAsync(providerKey));
}
public virtual async Task SetAsync(FeatureDefinition feature, string value, string providerKey)
{
await Store.SetAsync(feature.Name, value, Name, await NormalizeProviderKeyAsync(providerKey));
}
public virtual async Task ClearAsync(FeatureDefinition feature, string providerKey)
{
await Store.DeleteAsync(feature.Name, Name, await NormalizeProviderKeyAsync(providerKey));
}
protected virtual Task<string> NormalizeProviderKeyAsync(string providerKey)
{ {
public abstract string Name { get; } return Task.FromResult(providerKey);
protected IFeatureManagementStore Store { get; }
protected FeatureManagementProvider(IFeatureManagementStore store)
{
Store = store;
}
public virtual bool Compatible(string providerName)
{
return providerName == Name;
}
public virtual async Task<string> GetOrNullAsync(FeatureDefinition feature, string providerKey)
{
return await Store.GetOrNullAsync(feature.Name, Name, await NormalizeProviderKeyAsync(providerKey));
}
public virtual async Task SetAsync(FeatureDefinition feature, string value, string providerKey)
{
await Store.SetAsync(feature.Name, value, Name, await NormalizeProviderKeyAsync(providerKey));
}
public virtual async Task ClearAsync(FeatureDefinition feature, string providerKey)
{
await Store.DeleteAsync(feature.Name, Name, await NormalizeProviderKeyAsync(providerKey));
}
protected virtual Task<string> NormalizeProviderKeyAsync(string providerKey)
{
return Task.FromResult(providerKey);
}
} }
} }

169
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureManagementStore.cs

@ -7,115 +7,114 @@ using Volo.Abp.Features;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Uow; using Volo.Abp.Uow;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementStore : IFeatureManagementStore, ITransientDependency
{ {
public class FeatureManagementStore : IFeatureManagementStore, ITransientDependency protected IDistributedCache<FeatureValueCacheItem> Cache { get; }
protected IFeatureDefinitionManager FeatureDefinitionManager { get; }
protected IFeatureValueRepository FeatureValueRepository { get; }
protected IGuidGenerator GuidGenerator { get; }
public FeatureManagementStore(
IFeatureValueRepository featureValueRepository,
IGuidGenerator guidGenerator,
IDistributedCache<FeatureValueCacheItem> cache,
IFeatureDefinitionManager featureDefinitionManager)
{ {
protected IDistributedCache<FeatureValueCacheItem> Cache { get; } FeatureValueRepository = featureValueRepository;
protected IFeatureDefinitionManager FeatureDefinitionManager { get; } GuidGenerator = guidGenerator;
protected IFeatureValueRepository FeatureValueRepository { get; } Cache = cache;
protected IGuidGenerator GuidGenerator { get; } FeatureDefinitionManager = featureDefinitionManager;
}
public FeatureManagementStore(
IFeatureValueRepository featureValueRepository, [UnitOfWork]
IGuidGenerator guidGenerator, public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
IDistributedCache<FeatureValueCacheItem> cache, {
IFeatureDefinitionManager featureDefinitionManager) var cacheItem = await GetCacheItemAsync(name, providerName, providerKey);
return cacheItem.Value;
}
[UnitOfWork]
public virtual async Task SetAsync(string name, string value, string providerName, string providerKey)
{
var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey);
if (featureValue == null)
{ {
FeatureValueRepository = featureValueRepository; featureValue = new FeatureValue(GuidGenerator.Create(), name, value, providerName, providerKey);
GuidGenerator = guidGenerator; await FeatureValueRepository.InsertAsync(featureValue);
Cache = cache;
FeatureDefinitionManager = featureDefinitionManager;
} }
else
[UnitOfWork]
public virtual async Task<string> GetOrNullAsync(string name, string providerName, string providerKey)
{ {
var cacheItem = await GetCacheItemAsync(name, providerName, providerKey); featureValue.Value = value;
return cacheItem.Value; await FeatureValueRepository.UpdateAsync(featureValue);
} }
[UnitOfWork] await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new FeatureValueCacheItem(featureValue?.Value), considerUow: true);
public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) }
{
var featureValue = await FeatureValueRepository.FindAsync(name, providerName, providerKey);
if (featureValue == null)
{
featureValue = new FeatureValue(GuidGenerator.Create(), name, value, providerName, providerKey);
await FeatureValueRepository.InsertAsync(featureValue);
}
else
{
featureValue.Value = value;
await FeatureValueRepository.UpdateAsync(featureValue);
}
await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new FeatureValueCacheItem(featureValue?.Value), considerUow: true); [UnitOfWork]
public virtual async Task DeleteAsync(string name, string providerName, string providerKey)
{
var featureValues = await FeatureValueRepository.FindAllAsync(name, providerName, providerKey);
foreach (var featureValue in featureValues)
{
await FeatureValueRepository.DeleteAsync(featureValue);
await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true);
} }
}
[UnitOfWork] protected virtual async Task<FeatureValueCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey)
public virtual async Task DeleteAsync(string name, string providerName, string providerKey) {
var cacheKey = CalculateCacheKey(name, providerName, providerKey);
var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null)
{ {
var featureValues = await FeatureValueRepository.FindAllAsync(name, providerName, providerKey); return cacheItem;
foreach (var featureValue in featureValues)
{
await FeatureValueRepository.DeleteAsync(featureValue);
await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true);
}
} }
protected virtual async Task<FeatureValueCacheItem> GetCacheItemAsync(string name, string providerName, string providerKey) cacheItem = new FeatureValueCacheItem(null);
{
var cacheKey = CalculateCacheKey(name, providerName, providerKey);
var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true);
if (cacheItem != null) await SetCacheItemsAsync(providerName, providerKey, name, cacheItem);
{
return cacheItem;
}
cacheItem = new FeatureValueCacheItem(null); return cacheItem;
}
await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); private async Task SetCacheItemsAsync(
string providerName,
string providerKey,
string currentName,
FeatureValueCacheItem currentCacheItem)
{
var featureDefinitions = FeatureDefinitionManager.GetAll();
var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value);
return cacheItem; var cacheItems = new List<KeyValuePair<string, FeatureValueCacheItem>>();
}
private async Task SetCacheItemsAsync( foreach (var featureDefinition in featureDefinitions)
string providerName,
string providerKey,
string currentName,
FeatureValueCacheItem currentCacheItem)
{ {
var featureDefinitions = FeatureDefinitionManager.GetAll(); var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name);
var featuresDictionary = (await FeatureValueRepository.GetListAsync(providerName, providerKey))
.ToDictionary(s => s.Name, s => s.Value);
var cacheItems = new List<KeyValuePair<string, FeatureValueCacheItem>>(); cacheItems.Add(
new KeyValuePair<string, FeatureValueCacheItem>(
CalculateCacheKey(featureDefinition.Name, providerName, providerKey),
new FeatureValueCacheItem(featureValue)
)
);
foreach (var featureDefinition in featureDefinitions) if (featureDefinition.Name == currentName)
{ {
var featureValue = featuresDictionary.GetOrDefault(featureDefinition.Name); currentCacheItem.Value = featureValue;
cacheItems.Add(
new KeyValuePair<string, FeatureValueCacheItem>(
CalculateCacheKey(featureDefinition.Name, providerName, providerKey),
new FeatureValueCacheItem(featureValue)
)
);
if (featureDefinition.Name == currentName)
{
currentCacheItem.Value = featureValue;
}
} }
await Cache.SetManyAsync(cacheItems, considerUow: true);
} }
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) await Cache.SetManyAsync(cacheItems, considerUow: true);
{ }
return FeatureValueCacheItem.CalculateCacheKey(name, providerName, providerKey);
} protected virtual string CalculateCacheKey(string name, string providerName, string providerKey)
{
return FeatureValueCacheItem.CalculateCacheKey(name, providerName, providerKey);
} }
} }

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

@ -8,209 +8,208 @@ using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManager : IFeatureManager, ISingletonDependency
{ {
public class FeatureManager : IFeatureManager, ISingletonDependency protected IFeatureDefinitionManager FeatureDefinitionManager { get; }
protected List<IFeatureManagementProvider> Providers => _lazyProviders.Value;
protected FeatureManagementOptions Options { get; }
protected IStringLocalizerFactory StringLocalizerFactory { get; }
private readonly Lazy<List<IFeatureManagementProvider>> _lazyProviders;
public FeatureManager(
IOptions<FeatureManagementOptions> options,
IServiceProvider serviceProvider,
IFeatureDefinitionManager featureDefinitionManager,
IStringLocalizerFactory stringLocalizerFactory)
{ {
protected IFeatureDefinitionManager FeatureDefinitionManager { get; } FeatureDefinitionManager = featureDefinitionManager;
protected List<IFeatureManagementProvider> Providers => _lazyProviders.Value; StringLocalizerFactory = stringLocalizerFactory;
protected FeatureManagementOptions Options { get; } Options = options.Value;
protected IStringLocalizerFactory StringLocalizerFactory { get; }
//TODO: Instead, use IHybridServiceScopeFactory and create a scope..?
private readonly Lazy<List<IFeatureManagementProvider>> _lazyProviders;
_lazyProviders = new Lazy<List<IFeatureManagementProvider>>(
public FeatureManager( () => Options
IOptions<FeatureManagementOptions> options, .Providers
IServiceProvider serviceProvider, .Select(c => serviceProvider.GetRequiredService(c) as IFeatureManagementProvider)
IFeatureDefinitionManager featureDefinitionManager, .ToList(),
IStringLocalizerFactory stringLocalizerFactory) true
{ );
FeatureDefinitionManager = featureDefinitionManager; }
StringLocalizerFactory = stringLocalizerFactory;
Options = options.Value;
//TODO: Instead, use IHybridServiceScopeFactory and create a scope..?
_lazyProviders = new Lazy<List<IFeatureManagementProvider>>(
() => Options
.Providers
.Select(c => serviceProvider.GetRequiredService(c) as IFeatureManagementProvider)
.ToList(),
true
);
}
public virtual async Task<string> GetOrNullAsync( public virtual async Task<string> GetOrNullAsync(
string name, string name,
string providerName, string providerName,
string providerKey, string providerKey,
bool fallback = true) bool fallback = true)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
Check.NotNull(providerName, nameof(providerName)); Check.NotNull(providerName, nameof(providerName));
return (await GetOrNullInternalAsync(name, providerName, providerKey, fallback)).Value; return (await GetOrNullInternalAsync(name, providerName, providerKey, fallback)).Value;
} }
public virtual async Task<List<FeatureNameValue>> GetAllAsync( public virtual async Task<List<FeatureNameValue>> GetAllAsync(
string providerName, string providerName,
string providerKey, string providerKey,
bool fallback = true) bool fallback = true)
{ {
return (await GetAllWithProviderAsync(providerName, providerKey, fallback)) return (await GetAllWithProviderAsync(providerName, providerKey, fallback))
.Select(x => new FeatureNameValue(x.Name, x.Value)).ToList(); .Select(x => new FeatureNameValue(x.Name, x.Value)).ToList();
} }
public async Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync(string name, public async Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync(string name,
string providerName, string providerKey, bool fallback = true) string providerName, string providerKey, bool fallback = true)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
Check.NotNull(providerName, nameof(providerName)); Check.NotNull(providerName, nameof(providerName));
return await GetOrNullInternalAsync(name, providerName, providerKey, fallback); return await GetOrNullInternalAsync(name, providerName, providerKey, fallback);
} }
public async Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync(string providerName, public async Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync(string providerName,
string providerKey, bool fallback = true) string providerKey, bool fallback = true)
{ {
Check.NotNull(providerName, nameof(providerName)); Check.NotNull(providerName, nameof(providerName));
var featureDefinitions = FeatureDefinitionManager.GetAll(); var featureDefinitions = FeatureDefinitionManager.GetAll();
var providers = Enumerable.Reverse(Providers).SkipWhile(c => c.Name != providerName); var providers = Enumerable.Reverse(Providers).SkipWhile(c => c.Name != providerName);
if (!fallback) if (!fallback)
{ {
providers = providers.TakeWhile(c => c.Name == providerName); providers = providers.TakeWhile(c => c.Name == providerName);
} }
var providerList = providers.ToList(); var providerList = providers.ToList();
if (!providerList.Any()) if (!providerList.Any())
{ {
return new List<FeatureNameValueWithGrantedProvider>(); return new List<FeatureNameValueWithGrantedProvider>();
} }
var featureValues = new Dictionary<string, FeatureNameValueWithGrantedProvider>(); var featureValues = new Dictionary<string, FeatureNameValueWithGrantedProvider>();
foreach (var feature in featureDefinitions) foreach (var feature in featureDefinitions)
{
var featureNameValueWithGrantedProvider = new FeatureNameValueWithGrantedProvider(feature.Name, null);
foreach (var provider in providerList)
{ {
var featureNameValueWithGrantedProvider = new FeatureNameValueWithGrantedProvider(feature.Name, null); string pk = null;
foreach (var provider in providerList) if (provider.Compatible(providerName))
{ {
string pk = null; pk = providerKey;
if (provider.Compatible(providerName))
{
pk = providerKey;
}
var value = await provider.GetOrNullAsync(feature, pk);
if (value != null)
{
featureNameValueWithGrantedProvider.Value = value;
featureNameValueWithGrantedProvider.Provider = new FeatureValueProviderInfo(provider.Name, pk);
break;
}
} }
if (featureNameValueWithGrantedProvider.Value != null) var value = await provider.GetOrNullAsync(feature, pk);
if (value != null)
{ {
featureValues[feature.Name] = featureNameValueWithGrantedProvider; featureNameValueWithGrantedProvider.Value = value;
featureNameValueWithGrantedProvider.Provider = new FeatureValueProviderInfo(provider.Name, pk);
break;
} }
} }
return featureValues.Values.ToList(); if (featureNameValueWithGrantedProvider.Value != null)
{
featureValues[feature.Name] = featureNameValueWithGrantedProvider;
}
} }
public virtual async Task SetAsync( return featureValues.Values.ToList();
string name, }
string value,
string providerName,
string providerKey,
bool forceToSet = false)
{
Check.NotNull(name, nameof(name));
Check.NotNull(providerName, nameof(providerName));
var feature = FeatureDefinitionManager.Get(name); public virtual async Task SetAsync(
string name,
string value,
string providerName,
string providerKey,
bool forceToSet = false)
{
Check.NotNull(name, nameof(name));
Check.NotNull(providerName, nameof(providerName));
if (feature?.ValueType?.Validator.IsValid(value) == false) var feature = FeatureDefinitionManager.Get(name);
{
throw new FeatureValueInvalidException(feature.DisplayName.Localize(StringLocalizerFactory));
}
var providers = Enumerable if (feature?.ValueType?.Validator.IsValid(value) == false)
.Reverse(Providers) {
.SkipWhile(p => p.Name != providerName) throw new FeatureValueInvalidException(feature.DisplayName.Localize(StringLocalizerFactory));
.ToList(); }
if (!providers.Any()) var providers = Enumerable
{ .Reverse(Providers)
return; .SkipWhile(p => p.Name != providerName)
} .ToList();
if (providers.Count > 1 && !forceToSet && value != null) if (!providers.Any())
{
return;
}
if (providers.Count > 1 && !forceToSet && value != null)
{
var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null);
if (fallbackValue.Value == value)
{ {
var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); //Clear the value if it's same as it's fallback value
if (fallbackValue.Value == value) value = null;
{
//Clear the value if it's same as it's fallback value
value = null;
}
} }
}
providers = providers providers = providers
.TakeWhile(p => p.Name == providerName) .TakeWhile(p => p.Name == providerName)
.ToList(); //Getting list for case of there are more than one provider with same providerName .ToList(); //Getting list for case of there are more than one provider with same providerName
if (value == null) if (value == null)
{
foreach (var provider in providers)
{ {
foreach (var provider in providers) await provider.ClearAsync(feature, providerKey);
{
await provider.ClearAsync(feature, providerKey);
}
} }
else }
else
{
foreach (var provider in providers)
{ {
foreach (var provider in providers) await provider.SetAsync(feature, value, providerKey);
{
await provider.SetAsync(feature, value, providerKey);
}
} }
} }
}
protected virtual async Task<FeatureNameValueWithGrantedProvider> GetOrNullInternalAsync( protected virtual async Task<FeatureNameValueWithGrantedProvider> GetOrNullInternalAsync(
string name, string name,
string providerName, string providerName,
string providerKey, string providerKey,
bool fallback = true) //TODO: Fallback is not used bool fallback = true) //TODO: Fallback is not used
{
var feature = FeatureDefinitionManager.Get(name);
var providers = Enumerable
.Reverse(Providers);
if (providerName != null)
{ {
var feature = FeatureDefinitionManager.Get(name); providers = providers.SkipWhile(c => c.Name != providerName);
var providers = Enumerable }
.Reverse(Providers);
if (providerName != null) var featureNameValueWithGrantedProvider = new FeatureNameValueWithGrantedProvider(name, null);
foreach (var provider in providers)
{
string pk = null;
if (provider.Compatible(providerName))
{ {
providers = providers.SkipWhile(c => c.Name != providerName); pk = providerKey;
} }
var featureNameValueWithGrantedProvider = new FeatureNameValueWithGrantedProvider(name, null); var value = await provider.GetOrNullAsync(feature, pk);
foreach (var provider in providers) if (value != null)
{ {
string pk = null; featureNameValueWithGrantedProvider.Value = value;
if (provider.Compatible(providerName)) featureNameValueWithGrantedProvider.Provider = new FeatureValueProviderInfo(provider.Name, pk);
{ break;
pk = providerKey;
}
var value = await provider.GetOrNullAsync(feature, pk);
if (value != null)
{
featureNameValueWithGrantedProvider.Value = value;
featureNameValueWithGrantedProvider.Provider = new FeatureValueProviderInfo(provider.Name, pk);
break;
}
} }
return featureNameValueWithGrantedProvider;
} }
return featureNameValueWithGrantedProvider;
} }
} }

23
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValue.cs

@ -1,19 +1,18 @@
using System; using System;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[Serializable]
public class FeatureNameValue : NameValue
{ {
[Serializable] public FeatureNameValue()
public class FeatureNameValue : NameValue
{ {
public FeatureNameValue()
{
} }
public FeatureNameValue(string name, string value) public FeatureNameValue(string name, string value)
{ {
Name = name; Name = name;
Value = value; Value = value;
}
} }
} }

21
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureNameValueWithGrantedProvider.cs

@ -1,19 +1,18 @@
using System; using System;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[Serializable]
public class FeatureNameValueWithGrantedProvider : NameValue
{ {
[Serializable] public FeatureValueProviderInfo Provider { get; set; }
public class FeatureNameValueWithGrantedProvider : NameValue
{
public FeatureValueProviderInfo Provider { get; set; }
public FeatureNameValueWithGrantedProvider([NotNull] string name, string value) public FeatureNameValueWithGrantedProvider([NotNull] string name, string value)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
Name = name; Name = name;
Value = value; Value = value;
}
} }
} }

29
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureStore.cs

@ -2,23 +2,22 @@
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureStore : IFeatureStore, ITransientDependency
{ {
public class FeatureStore : IFeatureStore, ITransientDependency protected IFeatureManagementStore FeatureManagementStore { get; }
{
protected IFeatureManagementStore FeatureManagementStore { get; }
public FeatureStore(IFeatureManagementStore featureManagementStore) public FeatureStore(IFeatureManagementStore featureManagementStore)
{ {
FeatureManagementStore = featureManagementStore; FeatureManagementStore = featureManagementStore;
} }
public virtual Task<string> GetOrNullAsync( public virtual Task<string> GetOrNullAsync(
string name, string name,
string providerName, string providerName,
string providerKey) string providerKey)
{ {
return FeatureManagementStore.GetOrNullAsync(name, providerName, providerKey); return FeatureManagementStore.GetOrNullAsync(name, providerName, providerKey);
}
} }
} }

67
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValue.cs

@ -2,41 +2,40 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureValue : Entity<Guid>, IAggregateRoot<Guid>
{ {
public class FeatureValue : Entity<Guid>, IAggregateRoot<Guid> [NotNull]
public virtual string Name { get; protected set; }
[NotNull]
public virtual string Value { get; internal set; }
[NotNull]
public virtual string ProviderName { get; protected set; }
[CanBeNull]
public virtual string ProviderKey { get; protected set; }
protected FeatureValue()
{ {
[NotNull]
public virtual string Name { get; protected set; } }
[NotNull] public FeatureValue(
public virtual string Value { get; internal set; } Guid id,
[NotNull] string name,
[NotNull] [NotNull] string value,
public virtual string ProviderName { get; protected set; } [NotNull] string providerName,
[CanBeNull] string providerKey)
[CanBeNull] {
public virtual string ProviderKey { get; protected set; } Check.NotNull(name, nameof(name));
protected FeatureValue() Id = id;
{ Name = Check.NotNullOrWhiteSpace(name, nameof(name));
Value = Check.NotNullOrWhiteSpace(value, nameof(value));
} ProviderName = Check.NotNullOrWhiteSpace(providerName, nameof(providerName));
ProviderKey = providerKey;
public FeatureValue(
Guid id,
[NotNull] string name,
[NotNull] string value,
[NotNull] string providerName,
[CanBeNull] string providerKey)
{
Check.NotNull(name, nameof(name));
Id = id;
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
Value = Check.NotNullOrWhiteSpace(value, nameof(value));
ProviderName = Check.NotNullOrWhiteSpace(providerName, nameof(providerName));
ProviderKey = providerKey;
}
} }
} }

35
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItem.cs

@ -1,27 +1,26 @@
using System; using System;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[Serializable]
[IgnoreMultiTenancy]
public class FeatureValueCacheItem
{ {
[Serializable] public string Value { get; set; }
[IgnoreMultiTenancy]
public class FeatureValueCacheItem
{
public string Value { get; set; }
public FeatureValueCacheItem() public FeatureValueCacheItem()
{ {
} }
public FeatureValueCacheItem(string value) public FeatureValueCacheItem(string value)
{ {
Value = value; Value = value;
} }
public static string CalculateCacheKey(string name, string providerName, string providerKey) public static string CalculateCacheKey(string name, string providerName, string providerKey)
{ {
return "pn:" + providerName + ",pk:" + providerKey + ",n:" + name; return "pn:" + providerName + ",pk:" + providerKey + ",n:" + name;
}
} }
} }

45
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator.cs

@ -4,33 +4,32 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus; using Volo.Abp.EventBus;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureValueCacheItemInvalidator :
ILocalEventHandler<EntityChangedEventData<FeatureValue>>,
ITransientDependency
{ {
public class FeatureValueCacheItemInvalidator : protected IDistributedCache<FeatureValueCacheItem> Cache { get; }
ILocalEventHandler<EntityChangedEventData<FeatureValue>>,
ITransientDependency
{
protected IDistributedCache<FeatureValueCacheItem> Cache { get; }
public FeatureValueCacheItemInvalidator(IDistributedCache<FeatureValueCacheItem> cache) public FeatureValueCacheItemInvalidator(IDistributedCache<FeatureValueCacheItem> cache)
{ {
Cache = cache; Cache = cache;
} }
public virtual async Task HandleEventAsync(EntityChangedEventData<FeatureValue> eventData) public virtual async Task HandleEventAsync(EntityChangedEventData<FeatureValue> eventData)
{ {
var cacheKey = CalculateCacheKey( var cacheKey = CalculateCacheKey(
eventData.Entity.Name, eventData.Entity.Name,
eventData.Entity.ProviderName, eventData.Entity.ProviderName,
eventData.Entity.ProviderKey eventData.Entity.ProviderKey
); );
await Cache.RemoveAsync(cacheKey, considerUow: true); await Cache.RemoveAsync(cacheKey, considerUow: true);
} }
protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) protected virtual string CalculateCacheKey(string name, string providerName, string providerKey)
{ {
return FeatureValueCacheItem.CalculateCacheKey(name, providerName, providerKey); return FeatureValueCacheItem.CalculateCacheKey(name, providerName, providerKey);
}
} }
} }

39
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueConsts.cs

@ -1,25 +1,24 @@
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public static class FeatureValueConsts
{ {
public static class FeatureValueConsts /// <summary>
{ /// Default value: 128
/// <summary> /// </summary>
/// Default value: 128 public static int MaxNameLength { get; set; } = 128;
/// </summary>
public static int MaxNameLength { get; set;} = 128;
/// <summary> /// <summary>
/// Default value: 64 /// Default value: 64
/// </summary> /// </summary>
public static int MaxProviderNameLength { get; set;} = 64; public static int MaxProviderNameLength { get; set; } = 64;
/// <summary> /// <summary>
/// Default value: 64 /// Default value: 64
/// </summary> /// </summary>
public static int MaxProviderKeyLength { get; set;} = 64; public static int MaxProviderKeyLength { get; set; } = 64;
/// <summary> /// <summary>
/// Default value: 128 /// Default value: 128
/// </summary> /// </summary>
public static int MaxValueLength { get; set;} = 128; public static int MaxValueLength { get; set; } = 128;
}
} }

23
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/FeatureValueProviderInfo.cs

@ -1,21 +1,20 @@
using System; using System;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[Serializable]
public class FeatureValueProviderInfo
{ {
[Serializable] public string Name { get; }
public class FeatureValueProviderInfo
{
public string Name { get; }
public string Key { get; } public string Key { get; }
public FeatureValueProviderInfo([NotNull]string name, string key) public FeatureValueProviderInfo([NotNull] string name, string key)
{ {
Check.NotNull(name, nameof(name)); Check.NotNull(name, nameof(name));
Name = name; Name = name;
Key = key; Key = key;
}
} }
} }

19
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementProvider.cs

@ -2,19 +2,18 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public interface IFeatureManagementProvider
{ {
public interface IFeatureManagementProvider string Name { get; }
{
string Name { get; }
//TODO: Other better method name. //TODO: Other better method name.
bool Compatible(string providerName); bool Compatible(string providerName);
Task<string> GetOrNullAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey); Task<string> GetOrNullAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey);
Task SetAsync([NotNull] FeatureDefinition feature, [NotNull] string value, [CanBeNull] string providerKey); Task SetAsync([NotNull] FeatureDefinition feature, [NotNull] string value, [CanBeNull] string providerKey);
Task ClearAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey); Task ClearAsync([NotNull] FeatureDefinition feature, [CanBeNull] string providerKey);
}
} }

15
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManagementStore.cs

@ -1,13 +1,12 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public interface IFeatureManagementStore
{ {
public interface IFeatureManagementStore Task<string> GetOrNullAsync(string name, string providerName, string providerKey);
{
Task<string> GetOrNullAsync(string name, string providerName, string providerKey);
Task SetAsync(string name, string value, string providerName, string providerKey); Task SetAsync(string name, string value, string providerName, string providerKey);
Task DeleteAsync(string name, string providerName, string providerKey); Task DeleteAsync(string name, string providerName, string providerKey);
} }
}

17
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureManager.cs

@ -2,18 +2,17 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public interface IFeatureManager
{ {
public interface IFeatureManager Task<string> GetOrNullAsync([NotNull] string name, [NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true);
{
Task<string> GetOrNullAsync([NotNull]string name, [NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true);
Task<List<FeatureNameValue>> GetAllAsync([NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true); Task<List<FeatureNameValue>> GetAllAsync([NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true);
Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync([NotNull]string name, [NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true); Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderAsync([NotNull] string name, [NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true);
Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync([NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true); Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderAsync([NotNull] string providerName, [CanBeNull] string providerKey, bool fallback = true);
Task SetAsync([NotNull] string name, [CanBeNull] string value, [NotNull] string providerName, [CanBeNull] string providerKey, bool forceToSet = false); Task SetAsync([NotNull] string name, [CanBeNull] string value, [NotNull] string providerName, [CanBeNull] string providerKey, bool forceToSet = false);
}
} }

35
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/IFeatureValueRepository.cs

@ -4,25 +4,24 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public interface IFeatureValueRepository : IBasicRepository<FeatureValue, Guid>
{ {
public interface IFeatureValueRepository : IBasicRepository<FeatureValue, Guid> Task<FeatureValue> FindAsync(
{ string name,
Task<FeatureValue> FindAsync( string providerName,
string name, string providerKey,
string providerName, CancellationToken cancellationToken = default);
string providerKey,
CancellationToken cancellationToken = default);
Task<List<FeatureValue>> FindAllAsync( Task<List<FeatureValue>> FindAllAsync(
string name, string name,
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
Task<List<FeatureValue>> GetListAsync( Task<List<FeatureValue>> GetListAsync(
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default); CancellationToken cancellationToken = default);
}
} }

37
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagementProvider.cs

@ -3,30 +3,29 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class TenantFeatureManagementProvider : FeatureManagementProvider, ITransientDependency
{ {
public class TenantFeatureManagementProvider : FeatureManagementProvider, ITransientDependency public override string Name => TenantFeatureValueProvider.ProviderName;
{
public override string Name => TenantFeatureValueProvider.ProviderName;
protected ICurrentTenant CurrentTenant { get; } protected ICurrentTenant CurrentTenant { get; }
public TenantFeatureManagementProvider( public TenantFeatureManagementProvider(
IFeatureManagementStore store, IFeatureManagementStore store,
ICurrentTenant currentTenant) ICurrentTenant currentTenant)
: base(store) : base(store)
{ {
CurrentTenant = currentTenant; CurrentTenant = currentTenant;
} }
protected override Task<string> NormalizeProviderKeyAsync(string providerKey) protected override Task<string> NormalizeProviderKeyAsync(string providerKey)
{
if (providerKey != null)
{ {
if (providerKey != null) return Task.FromResult(providerKey);
{
return Task.FromResult(providerKey);
}
return Task.FromResult(CurrentTenant.Id?.ToString());
} }
return Task.FromResult(CurrentTenant.Id?.ToString());
} }
} }

43
modules/feature-management/src/Volo.Abp.FeatureManagement.Domain/Volo/Abp/FeatureManagement/TenantFeatureManagerExtensions.cs

@ -4,33 +4,32 @@ using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Volo.Abp.Features; using Volo.Abp.Features;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public static class TenantFeatureManagerExtensions
{ {
public static class TenantFeatureManagerExtensions public static Task<string> GetOrNullForTenantAsync(this IFeatureManager featureManager, [NotNull] string name, Guid tenantId, bool fallback = true)
{ {
public static Task<string> GetOrNullForTenantAsync(this IFeatureManager featureManager, [NotNull] string name, Guid tenantId, bool fallback = true) return featureManager.GetOrNullAsync(name, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback);
{ }
return featureManager.GetOrNullAsync(name, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback);
}
public static Task<List<FeatureNameValue>> GetAllForTenantAsync(this IFeatureManager featureManager, Guid tenantId, bool fallback = true) public static Task<List<FeatureNameValue>> GetAllForTenantAsync(this IFeatureManager featureManager, Guid tenantId, bool fallback = true)
{ {
return featureManager.GetAllAsync(TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback); return featureManager.GetAllAsync(TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback);
} }
public static Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderForTenantAsync(this IFeatureManager featureManager, [NotNull] string name, Guid tenantId, bool fallback = true) public static Task<FeatureNameValueWithGrantedProvider> GetOrNullWithProviderForTenantAsync(this IFeatureManager featureManager, [NotNull] string name, Guid tenantId, bool fallback = true)
{ {
return featureManager.GetOrNullWithProviderAsync(name, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback); return featureManager.GetOrNullWithProviderAsync(name, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback);
} }
public static Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderForTenantAsync(this IFeatureManager featureManager, Guid tenantId, bool fallback = true) public static Task<List<FeatureNameValueWithGrantedProvider>> GetAllWithProviderForTenantAsync(this IFeatureManager featureManager, Guid tenantId, bool fallback = true)
{ {
return featureManager.GetAllWithProviderAsync(TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback); return featureManager.GetAllWithProviderAsync(TenantFeatureValueProvider.ProviderName, tenantId.ToString(), fallback);
} }
public static Task SetForTenantAsync(this IFeatureManager featureManager, Guid tenantId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false) public static Task SetForTenantAsync(this IFeatureManager featureManager, Guid tenantId, [NotNull] string name, [CanBeNull] string value, bool forceToSet = false)
{ {
return featureManager.SetAsync(name, value, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), forceToSet); return featureManager.SetAsync(name, value, TenantFeatureValueProvider.ProviderName, tenantId.ToString(), forceToSet);
}
} }
} }

27
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreModule.cs

@ -2,22 +2,21 @@
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
[DependsOn(
typeof(AbpFeatureManagementDomainModule),
typeof(AbpEntityFrameworkCoreModule)
)]
public class AbpFeatureManagementEntityFrameworkCoreModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementDomainModule),
typeof(AbpEntityFrameworkCoreModule)
)]
public class AbpFeatureManagementEntityFrameworkCoreModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) context.Services.AddAbpDbContext<FeatureManagementDbContext>(options =>
{ {
context.Services.AddAbpDbContext<FeatureManagementDbContext>(options => options.AddDefaultRepositories<IFeatureManagementDbContext>();
{
options.AddDefaultRepositories<IFeatureManagementDbContext>();
options.AddRepository<FeatureValue, EfCoreFeatureValueRepository>(); options.AddRepository<FeatureValue, EfCoreFeatureValueRepository>();
}); });
}
} }
} }

73
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/EfCoreFeatureValueRepository.cs

@ -7,47 +7,46 @@ using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
public class EfCoreFeatureValueRepository : EfCoreRepository<IFeatureManagementDbContext, FeatureValue, Guid>, IFeatureValueRepository
{ {
public class EfCoreFeatureValueRepository : EfCoreRepository<IFeatureManagementDbContext, FeatureValue, Guid>, IFeatureValueRepository public EfCoreFeatureValueRepository(IDbContextProvider<IFeatureManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{ {
public EfCoreFeatureValueRepository(IDbContextProvider<IFeatureManagementDbContext> dbContextProvider) }
: base(dbContextProvider)
{
}
public virtual async Task<FeatureValue> FindAsync( public virtual async Task<FeatureValue> FindAsync(
string name, string name,
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return await (await GetDbSetAsync()) return await (await GetDbSetAsync())
.OrderBy(x => x.Id) .OrderBy(x => x.Id)
.FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, GetCancellationToken(cancellationToken)); .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, GetCancellationToken(cancellationToken));
} }
public async Task<List<FeatureValue>> FindAllAsync( public async Task<List<FeatureValue>> FindAllAsync(
string name, string name,
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return await (await GetDbSetAsync()) return await (await GetDbSetAsync())
.Where( .Where(
s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey
).ToListAsync(GetCancellationToken(cancellationToken)); ).ToListAsync(GetCancellationToken(cancellationToken));
} }
public virtual async Task<List<FeatureValue>> GetListAsync( public virtual async Task<List<FeatureValue>> GetListAsync(
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return await (await GetDbSetAsync()) return await (await GetDbSetAsync())
.Where( .Where(
s => s.ProviderName == providerName && s.ProviderKey == providerKey s => s.ProviderName == providerName && s.ProviderKey == providerKey
).ToListAsync(GetCancellationToken(cancellationToken)); ).ToListAsync(GetCancellationToken(cancellationToken));
}
} }
} }

29
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContext.cs

@ -3,25 +3,24 @@ using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
[IgnoreMultiTenancy]
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public class FeatureManagementDbContext : AbpDbContext<FeatureManagementDbContext>, IFeatureManagementDbContext
{ {
[IgnoreMultiTenancy] public DbSet<FeatureValue> FeatureValues { get; set; }
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public class FeatureManagementDbContext : AbpDbContext<FeatureManagementDbContext>, IFeatureManagementDbContext
{
public DbSet<FeatureValue> FeatureValues { get; set; }
public FeatureManagementDbContext(DbContextOptions<FeatureManagementDbContext> options) public FeatureManagementDbContext(DbContextOptions<FeatureManagementDbContext> options)
: base(options) : base(options)
{ {
} }
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
base.OnModelCreating(builder); base.OnModelCreating(builder);
builder.ConfigureFeatureManagement(); builder.ConfigureFeatureManagement();
}
} }
} }

48
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementDbContextModelCreatingExtensions.cs

@ -1,38 +1,36 @@
using System; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.Modeling;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
public static class FeatureManagementDbContextModelCreatingExtensions
{ {
public static class FeatureManagementDbContextModelCreatingExtensions public static void ConfigureFeatureManagement(
this ModelBuilder builder)
{ {
public static void ConfigureFeatureManagement( Check.NotNull(builder, nameof(builder));
this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
if (builder.IsTenantOnlyDatabase()) if (builder.IsTenantOnlyDatabase())
{ {
return; return;
} }
builder.Entity<FeatureValue>(b => builder.Entity<FeatureValue>(b =>
{ {
b.ToTable(FeatureManagementDbProperties.DbTablePrefix + "FeatureValues", FeatureManagementDbProperties.DbSchema); b.ToTable(FeatureManagementDbProperties.DbTablePrefix + "FeatureValues", FeatureManagementDbProperties.DbSchema);
b.ConfigureByConvention(); b.ConfigureByConvention();
b.Property(x => x.Name).HasMaxLength(FeatureValueConsts.MaxNameLength).IsRequired(); b.Property(x => x.Name).HasMaxLength(FeatureValueConsts.MaxNameLength).IsRequired();
b.Property(x => x.Value).HasMaxLength(FeatureValueConsts.MaxValueLength).IsRequired(); b.Property(x => x.Value).HasMaxLength(FeatureValueConsts.MaxValueLength).IsRequired();
b.Property(x => x.ProviderName).HasMaxLength(FeatureValueConsts.MaxProviderNameLength); b.Property(x => x.ProviderName).HasMaxLength(FeatureValueConsts.MaxProviderNameLength);
b.Property(x => x.ProviderKey).HasMaxLength(FeatureValueConsts.MaxProviderKeyLength); b.Property(x => x.ProviderKey).HasMaxLength(FeatureValueConsts.MaxProviderKeyLength);
b.HasIndex(x => new { x.Name, x.ProviderName, x.ProviderKey }).IsUnique(true); b.HasIndex(x => new { x.Name, x.ProviderName, x.ProviderKey }).IsUnique(true);
b.ApplyObjectExtensionMappings(); b.ApplyObjectExtensionMappings();
}); });
builder.TryConfigureObjectExtensions<FeatureManagementDbContext>(); builder.TryConfigureObjectExtensions<FeatureManagementDbContext>();
}
} }
} }

13
modules/feature-management/src/Volo.Abp.FeatureManagement.EntityFrameworkCore/Volo/Abp/FeatureManagement/EntityFrameworkCore/IFeatureManagementDbContext.cs

@ -3,12 +3,11 @@ using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
[IgnoreMultiTenancy]
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public interface IFeatureManagementDbContext : IEfCoreDbContext
{ {
[IgnoreMultiTenancy] DbSet<FeatureValue> FeatureValues { get; }
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public interface IFeatureManagementDbContext : IEfCoreDbContext
{
DbSet<FeatureValue> FeatureValues { get; }
}
} }

7
modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/ClientProxies/FeaturesClientProxy.cs

@ -1,8 +1,7 @@
// This file is part of FeaturesClientProxy, you can customize it here // This file is part of FeaturesClientProxy, you can customize it here
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace Volo.Abp.FeatureManagement.ClientProxies namespace Volo.Abp.FeatureManagement.ClientProxies;
public partial class FeaturesClientProxy
{ {
public partial class FeaturesClientProxy
{
}
} }

31
modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi.Client/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiClientModule.cs

@ -3,24 +3,23 @@ using Volo.Abp.Http.Client;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpHttpClientModule))]
public class AbpFeatureManagementHttpApiClientModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpHttpClientModule))]
public class AbpFeatureManagementHttpApiClientModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) context.Services.AddStaticHttpClientProxies(
{ typeof(AbpFeatureManagementApplicationContractsModule).Assembly,
context.Services.AddStaticHttpClientProxies( FeatureManagementRemoteServiceConsts.RemoteServiceName
typeof(AbpFeatureManagementApplicationContractsModule).Assembly, );
FeatureManagementRemoteServiceConsts.RemoteServiceName
);
Configure<AbpVirtualFileSystemOptions>(options => Configure<AbpVirtualFileSystemOptions>(options =>
{ {
options.FileSets.AddEmbedded<AbpFeatureManagementHttpApiClientModule>(); options.FileSets.AddEmbedded<AbpFeatureManagementHttpApiClientModule>();
}); });
}
} }
} }

45
modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/AbpFeatureManagementHttpApiModule.cs

@ -8,34 +8,33 @@ using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.FeatureManagement.JsonConverters; using Volo.Abp.FeatureManagement.JsonConverters;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpAspNetCoreMvcModule))]
public class AbpFeatureManagementHttpApiModule : AbpModule
{ {
[DependsOn( public override void PreConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpAspNetCoreMvcModule))]
public class AbpFeatureManagementHttpApiModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context) PreConfigure<IMvcBuilder>(mvcBuilder =>
{ {
PreConfigure<IMvcBuilder>(mvcBuilder => mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpFeatureManagementHttpApiModule).Assembly);
{ });
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpFeatureManagementHttpApiModule).Assembly); }
});
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpLocalizationOptions>(options =>
{ {
Configure<AbpLocalizationOptions>(options => options.Resources
{ .Get<AbpFeatureManagementResource>()
options.Resources .AddBaseTypes(typeof(AbpUiResource));
.Get<AbpFeatureManagementResource>() });
.AddBaseTypes(typeof(AbpUiResource));
});
Configure<JsonOptions>(options => Configure<JsonOptions>(options =>
{ {
options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter()); options.JsonSerializerOptions.Converters.AddIfNotContains(new StringValueTypeJsonConverter());
}); });
}
} }
} }

41
modules/feature-management/src/Volo.Abp.FeatureManagement.HttpApi/Volo/Abp/FeatureManagement/FeaturesController.cs

@ -2,30 +2,29 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[RemoteService(Name = FeatureManagementRemoteServiceConsts.RemoteServiceName)]
[Area(FeatureManagementRemoteServiceConsts.ModuleName)]
[Route("api/feature-management/features")]
public class FeaturesController : AbpControllerBase, IFeatureAppService
{ {
[RemoteService(Name = FeatureManagementRemoteServiceConsts.RemoteServiceName)] protected IFeatureAppService FeatureAppService { get; }
[Area(FeatureManagementRemoteServiceConsts.ModuleName)]
[Route("api/feature-management/features")]
public class FeaturesController : AbpControllerBase, IFeatureAppService
{
protected IFeatureAppService FeatureAppService { get; }
public FeaturesController(IFeatureAppService featureAppService) public FeaturesController(IFeatureAppService featureAppService)
{ {
FeatureAppService = featureAppService; FeatureAppService = featureAppService;
} }
[HttpGet] [HttpGet]
public virtual Task<GetFeatureListResultDto> GetAsync(string providerName, string providerKey) public virtual Task<GetFeatureListResultDto> GetAsync(string providerName, string providerKey)
{ {
return FeatureAppService.GetAsync(providerName, providerKey); return FeatureAppService.GetAsync(providerName, providerKey);
} }
[HttpPut] [HttpPut]
public virtual Task UpdateAsync(string providerName, string providerKey, UpdateFeaturesDto input) public virtual Task UpdateAsync(string providerName, string providerKey, UpdateFeaturesDto input)
{ {
return FeatureAppService.UpdateAsync(providerName, providerKey, input); return FeatureAppService.UpdateAsync(providerName, providerKey, input);
}
} }
} }

23
modules/feature-management/src/Volo.Abp.FeatureManagement.Installer/Volo/Abp/FeatureManagement/AbpFeatureManagementInstallerModule.cs

@ -2,20 +2,19 @@
using Volo.Abp.Studio; using Volo.Abp.Studio;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpStudioModuleInstallerModule),
typeof(AbpVirtualFileSystemModule)
)]
public class AbpFeatureManagementInstallerModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpStudioModuleInstallerModule),
typeof(AbpVirtualFileSystemModule)
)]
public class AbpFeatureManagementInstallerModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) Configure<AbpVirtualFileSystemOptions>(options =>
{ {
Configure<AbpVirtualFileSystemOptions>(options => options.FileSets.AddEmbedded<AbpFeatureManagementInstallerModule>();
{ });
options.FileSets.AddEmbedded<AbpFeatureManagementInstallerModule>();
});
}
} }
} }

29
modules/feature-management/src/Volo.Abp.FeatureManagement.Installer/Volo/Abp/FeatureManagement/FeatureManagementInstallerPipelineBuilder.cs

@ -3,22 +3,21 @@ using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Studio.ModuleInstalling; using Volo.Abp.Studio.ModuleInstalling;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)]
[ExposeServices(typeof(IModuleInstallingPipelineBuilder))]
public class FeatureManagementInstallerPipelineBuilder : ModuleInstallingPipelineBuilderBase, IModuleInstallingPipelineBuilder, ITransientDependency
{ {
[Dependency(ServiceLifetime.Transient, ReplaceServices = true)] public async Task<ModuleInstallingPipeline> BuildAsync(ModuleInstallingContext context)
[ExposeServices(typeof(IModuleInstallingPipelineBuilder))]
public class FeatureManagementInstallerPipelineBuilder : ModuleInstallingPipelineBuilderBase, IModuleInstallingPipelineBuilder, ITransientDependency
{ {
public async Task<ModuleInstallingPipeline> BuildAsync(ModuleInstallingContext context) context.AddEfCoreConfigurationMethodDeclaration(
{ new EfCoreConfigurationMethodDeclaration(
context.AddEfCoreConfigurationMethodDeclaration( "Volo.Abp.FeatureManagement.EntityFrameworkCore",
new EfCoreConfigurationMethodDeclaration( "ConfigureFeatureManagement"
"Volo.Abp.FeatureManagement.EntityFrameworkCore", )
"ConfigureFeatureManagement" );
)
); return GetBasePipeline(context);
return GetBasePipeline(context);
}
} }
} }

25
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbModule.cs

@ -2,22 +2,21 @@
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
[DependsOn(
typeof(AbpFeatureManagementDomainModule),
typeof(AbpMongoDbModule)
)]
public class AbpFeatureManagementMongoDbModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementDomainModule),
typeof(AbpMongoDbModule)
)]
public class AbpFeatureManagementMongoDbModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) context.Services.AddMongoDbContext<FeatureManagementMongoDbContext>(options =>
{ {
context.Services.AddMongoDbContext<FeatureManagementMongoDbContext>(options => options.AddDefaultRepositories<IFeatureManagementMongoDbContext>();
{
options.AddDefaultRepositories<IFeatureManagementMongoDbContext>();
options.AddRepository<FeatureValue, MongoFeatureValueRepository>(); options.AddRepository<FeatureValue, MongoFeatureValueRepository>();
}); });
}
} }
} }

21
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContext.cs

@ -3,19 +3,18 @@ using Volo.Abp.Data;
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
[IgnoreMultiTenancy]
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public class FeatureManagementMongoDbContext : AbpMongoDbContext, IFeatureManagementMongoDbContext
{ {
[IgnoreMultiTenancy] public IMongoCollection<FeatureValue> FeatureValues => Collection<FeatureValue>();
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public class FeatureManagementMongoDbContext : AbpMongoDbContext, IFeatureManagementMongoDbContext
{
public IMongoCollection<FeatureValue> FeatureValues => Collection<FeatureValue>();
protected override void CreateModel(IMongoModelBuilder modelBuilder) protected override void CreateModel(IMongoModelBuilder modelBuilder)
{ {
base.CreateModel(modelBuilder); base.CreateModel(modelBuilder);
modelBuilder.ConfigureFeatureManagement(); modelBuilder.ConfigureFeatureManagement();
}
} }
} }

23
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementMongoDbContextExtensions.cs

@ -1,18 +1,17 @@
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
public static class FeatureManagementMongoDbContextExtensions
{ {
public static class FeatureManagementMongoDbContextExtensions public static void ConfigureFeatureManagement(
this IMongoModelBuilder builder)
{ {
public static void ConfigureFeatureManagement( Check.NotNull(builder, nameof(builder));
this IMongoModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
builder.Entity<FeatureValue>(b => builder.Entity<FeatureValue>(b =>
{ {
b.CollectionName = FeatureManagementDbProperties.DbTablePrefix + "FeatureValues"; b.CollectionName = FeatureManagementDbProperties.DbTablePrefix + "FeatureValues";
}); });
}
} }
} }

13
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/IFeatureManagementMongoDbContext.cs

@ -3,12 +3,11 @@ using Volo.Abp.Data;
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
[IgnoreMultiTenancy]
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public interface IFeatureManagementMongoDbContext : IAbpMongoDbContext
{ {
[IgnoreMultiTenancy] IMongoCollection<FeatureValue> FeatureValues { get; }
[ConnectionStringName(FeatureManagementDbProperties.ConnectionStringName)]
public interface IFeatureManagementMongoDbContext : IAbpMongoDbContext
{
IMongoCollection<FeatureValue> FeatureValues { get; }
}
} }

67
modules/feature-management/src/Volo.Abp.FeatureManagement.MongoDB/Volo/Abp/FeatureManagement/MongoDB/MongoFeatureValueRepository.cs

@ -7,45 +7,44 @@ using MongoDB.Driver.Linq;
using Volo.Abp.Domain.Repositories.MongoDB; using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
public class MongoFeatureValueRepository : MongoDbRepository<IFeatureManagementMongoDbContext, FeatureValue, Guid>, IFeatureValueRepository
{ {
public class MongoFeatureValueRepository : MongoDbRepository<IFeatureManagementMongoDbContext, FeatureValue, Guid>, IFeatureValueRepository public MongoFeatureValueRepository(IMongoDbContextProvider<IFeatureManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{ {
public MongoFeatureValueRepository(IMongoDbContextProvider<IFeatureManagementMongoDbContext> dbContextProvider)
: base(dbContextProvider)
{
} }
public virtual async Task<FeatureValue> FindAsync( public virtual async Task<FeatureValue> FindAsync(
string name, string name,
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return await (await GetMongoQueryableAsync(cancellationToken)) return await (await GetMongoQueryableAsync(cancellationToken))
.OrderBy(x => x.Id) .OrderBy(x => x.Id)
.FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, GetCancellationToken(cancellationToken)); .FirstOrDefaultAsync(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey, GetCancellationToken(cancellationToken));
} }
public async Task<List<FeatureValue>> FindAllAsync( public async Task<List<FeatureValue>> FindAllAsync(
string name, string name,
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return await (await GetMongoQueryableAsync(cancellationToken)) return await (await GetMongoQueryableAsync(cancellationToken))
.Where(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey).ToListAsync(GetCancellationToken(cancellationToken)); .Where(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey).ToListAsync(GetCancellationToken(cancellationToken));
} }
public virtual async Task<List<FeatureValue>> GetListAsync( public virtual async Task<List<FeatureValue>> GetListAsync(
string providerName, string providerName,
string providerKey, string providerKey,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return await (await GetMongoQueryableAsync(cancellationToken)) return await (await GetMongoQueryableAsync(cancellationToken))
.Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey) .Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey)
.ToListAsync(GetCancellationToken(cancellationToken)); .ToListAsync(GetCancellationToken(cancellationToken));
}
} }
} }

67
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/AbpFeatureManagementWebModule.cs

@ -8,50 +8,49 @@ using Volo.Abp.Http.ProxyScripting.Generators.JQuery;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpAspNetCoreMvcUiThemeSharedModule),
typeof(AbpAutoMapperModule)
)]
public class AbpFeatureManagementWebModule : AbpModule
{ {
[DependsOn( public override void PreConfigureServices(ServiceConfigurationContext context)
typeof(AbpFeatureManagementApplicationContractsModule),
typeof(AbpAspNetCoreMvcUiThemeSharedModule),
typeof(AbpAutoMapperModule)
)]
public class AbpFeatureManagementWebModule : AbpModule
{ {
public override void PreConfigureServices(ServiceConfigurationContext context) context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
{ {
context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => options.AddAssemblyResource(typeof(AbpFeatureManagementResource), typeof(AbpFeatureManagementWebModule).Assembly);
{ });
options.AddAssemblyResource(typeof(AbpFeatureManagementResource), typeof(AbpFeatureManagementWebModule).Assembly);
});
PreConfigure<IMvcBuilder>(mvcBuilder => PreConfigure<IMvcBuilder>(mvcBuilder =>
{ {
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpFeatureManagementWebModule).Assembly); mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpFeatureManagementWebModule).Assembly);
}); });
} }
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{ {
Configure<AbpVirtualFileSystemOptions>(options => options.FileSets.AddEmbedded<AbpFeatureManagementWebModule>();
{ });
options.FileSets.AddEmbedded<AbpFeatureManagementWebModule>();
});
context.Services.AddAutoMapperObjectMapper<AbpFeatureManagementWebModule>(); context.Services.AddAutoMapperObjectMapper<AbpFeatureManagementWebModule>();
Configure<AbpAutoMapperOptions>(options => Configure<AbpAutoMapperOptions>(options =>
{ {
options.AddProfile<FeatureManagementWebAutoMapperProfile>(validate: true); options.AddProfile<FeatureManagementWebAutoMapperProfile>(validate: true);
}); });
Configure<RazorPagesOptions>(options => Configure<RazorPagesOptions>(options =>
{ {
//Configure authorization. //Configure authorization.
}); });
Configure<DynamicJavaScriptProxyOptions>(options => Configure<DynamicJavaScriptProxyOptions>(options =>
{ {
options.DisableModule(FeatureManagementRemoteServiceConsts.ModuleName); options.DisableModule(FeatureManagementRemoteServiceConsts.ModuleName);
}); });
}
} }
} }

13
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/FeatureManagementWebAutoMapperProfile.cs

@ -1,12 +1,11 @@
using AutoMapper; using AutoMapper;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementWebAutoMapperProfile : Profile
{ {
public class FeatureManagementWebAutoMapperProfile : Profile public FeatureManagementWebAutoMapperProfile()
{ {
public FeatureManagementWebAutoMapperProfile() //Create mappings.
{
//Create mappings.
}
} }
} }

115
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml.cs

@ -9,86 +9,85 @@ using Volo.Abp.EventBus.Local;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement.Web.Pages.FeatureManagement namespace Volo.Abp.FeatureManagement.Web.Pages.FeatureManagement;
public class FeatureManagementModal : AbpPageModel
{ {
public class FeatureManagementModal : AbpPageModel [Required]
{ [HiddenInput]
[Required] [BindProperty(SupportsGet = true)]
[HiddenInput] public string ProviderName { get; set; }
[BindProperty(SupportsGet = true)]
public string ProviderName { get; set; }
[HiddenInput] [HiddenInput]
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string ProviderKey { get; set; } public string ProviderKey { get; set; }
[BindProperty] [BindProperty]
public List<FeatureGroupViewModel> FeatureGroups { get; set; } public List<FeatureGroupViewModel> FeatureGroups { get; set; }
public GetFeatureListResultDto FeatureListResultDto { get; set; } public GetFeatureListResultDto FeatureListResultDto { get; set; }
protected IFeatureAppService FeatureAppService { get; } protected IFeatureAppService FeatureAppService { get; }
protected ILocalEventBus LocalEventBus { get; } protected ILocalEventBus LocalEventBus { get; }
public FeatureManagementModal( public FeatureManagementModal(
IFeatureAppService featureAppService, IFeatureAppService featureAppService,
ILocalEventBus localEventBus) ILocalEventBus localEventBus)
{ {
ObjectMapperContext = typeof(AbpFeatureManagementWebModule); ObjectMapperContext = typeof(AbpFeatureManagementWebModule);
FeatureAppService = featureAppService; FeatureAppService = featureAppService;
LocalEventBus = localEventBus; LocalEventBus = localEventBus;
} }
public virtual async Task<IActionResult> OnGetAsync() public virtual async Task<IActionResult> OnGetAsync()
{ {
ValidateModel(); ValidateModel();
FeatureListResultDto = await FeatureAppService.GetAsync(ProviderName, ProviderKey); FeatureListResultDto = await FeatureAppService.GetAsync(ProviderName, ProviderKey);
return Page(); return Page();
} }
public virtual async Task<IActionResult> OnPostAsync() public virtual async Task<IActionResult> OnPostAsync()
{
var features = new UpdateFeaturesDto
{ {
var features = new UpdateFeaturesDto Features = FeatureGroups.SelectMany(g => g.Features).Select(f => new UpdateFeatureDto
{ {
Features = FeatureGroups.SelectMany(g => g.Features).Select(f => new UpdateFeatureDto Name = f.Name,
{ Value = f.Type == nameof(ToggleStringValueType) ? f.BoolValue.ToString() : f.Value
Name = f.Name, }).ToList()
Value = f.Type == nameof(ToggleStringValueType) ? f.BoolValue.ToString() : f.Value };
}).ToList()
};
await FeatureAppService.UpdateAsync(ProviderName, ProviderKey, features); await FeatureAppService.UpdateAsync(ProviderName, ProviderKey, features);
await LocalEventBus.PublishAsync( await LocalEventBus.PublishAsync(
new CurrentApplicationConfigurationCacheResetEventData() new CurrentApplicationConfigurationCacheResetEventData()
); );
return NoContent(); return NoContent();
} }
public virtual bool IsDisabled(string providerName) public virtual bool IsDisabled(string providerName)
{ {
return providerName != ProviderName && providerName != DefaultValueFeatureValueProvider.ProviderName; return providerName != ProviderName && providerName != DefaultValueFeatureValueProvider.ProviderName;
} }
public class FeatureGroupViewModel public class FeatureGroupViewModel
{ {
public List<FeatureViewModel> Features { get; set; } public List<FeatureViewModel> Features { get; set; }
} }
public class FeatureViewModel public class FeatureViewModel
{ {
public string Name { get; set; } public string Name { get; set; }
public string Value { get; set; } public string Value { get; set; }
public bool BoolValue { get; set; } public bool BoolValue { get; set; }
public string Type { get; set; } public string Type { get; set; }
}
} }
} }

27
modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Properties/launchSettings.json

@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:50650/",
"sslPort": 44341
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Volo.Abp.FeatureManagement.Web": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}

95
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureAppService_Tests.cs

@ -9,71 +9,70 @@ using Volo.Abp.Features;
using Volo.Abp.Users; using Volo.Abp.Users;
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureAppService_Tests : FeatureManagementApplicationTestBase
{ {
public class FeatureAppService_Tests : FeatureManagementApplicationTestBase private readonly IFeatureAppService _featureAppService;
{ private readonly IFeatureValueRepository _featureValueRepository;
private readonly IFeatureAppService _featureAppService; private ICurrentUser _currentUser;
private readonly IFeatureValueRepository _featureValueRepository; private readonly FeatureManagementTestData _testData;
private ICurrentUser _currentUser;
private readonly FeatureManagementTestData _testData;
public FeatureAppService_Tests() public FeatureAppService_Tests()
{ {
_featureAppService = GetRequiredService<IFeatureAppService>(); _featureAppService = GetRequiredService<IFeatureAppService>();
_featureValueRepository = GetRequiredService<IFeatureValueRepository>(); _featureValueRepository = GetRequiredService<IFeatureValueRepository>();
_testData = GetRequiredService<FeatureManagementTestData>(); _testData = GetRequiredService<FeatureManagementTestData>();
} }
protected override void AfterAddApplication(IServiceCollection services) protected override void AfterAddApplication(IServiceCollection services)
{ {
_currentUser = Substitute.For<ICurrentUser>(); _currentUser = Substitute.For<ICurrentUser>();
services.AddSingleton(_currentUser); services.AddSingleton(_currentUser);
} }
[Fact] [Fact]
public async Task GetAsync() public async Task GetAsync()
{ {
Login(_testData.User1Id); Login(_testData.User1Id);
var featureList = await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName, var featureList = await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()); TestEditionIds.Regular.ToString());
featureList.ShouldNotBeNull(); featureList.ShouldNotBeNull();
featureList.Groups.SelectMany(g =>g .Features).ShouldContain(feature => feature.Name == TestFeatureDefinitionProvider.SocialLogins); featureList.Groups.SelectMany(g => g.Features).ShouldContain(feature => feature.Name == TestFeatureDefinitionProvider.SocialLogins);
} }
[Fact] [Fact]
public async Task UpdateAsync() public async Task UpdateAsync()
{ {
Login(_testData.User1Id); Login(_testData.User1Id);
await _featureAppService.UpdateAsync(EditionFeatureValueProvider.ProviderName, await _featureAppService.UpdateAsync(EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString(), new UpdateFeaturesDto() TestEditionIds.Regular.ToString(), new UpdateFeaturesDto()
{
Features = new List<UpdateFeatureDto>()
{ {
Features = new List<UpdateFeatureDto>()
{
new UpdateFeatureDto() new UpdateFeatureDto()
{ {
Name = TestFeatureDefinitionProvider.SocialLogins, Name = TestFeatureDefinitionProvider.SocialLogins,
Value = false.ToString().ToLowerInvariant() Value = false.ToString().ToLowerInvariant()
} }
} }
}); });
(await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName, (await _featureAppService.GetAsync(EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).Groups.SelectMany(g => g.Features).Any(x => TestEditionIds.Regular.ToString())).Groups.SelectMany(g => g.Features).Any(x =>
x.Name == TestFeatureDefinitionProvider.SocialLogins && x.Name == TestFeatureDefinitionProvider.SocialLogins &&
x.Value == false.ToString().ToLowerInvariant()) x.Value == false.ToString().ToLowerInvariant())
.ShouldBeTrue(); .ShouldBeTrue();
} }
private void Login(Guid userId) private void Login(Guid userId)
{ {
_currentUser.Id.Returns(userId); _currentUser.Id.Returns(userId);
_currentUser.IsAuthenticated.Returns(true); _currentUser.IsAuthenticated.Returns(true);
}
} }
} }

9
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestBase.cs

@ -1,11 +1,6 @@
using System; namespace Volo.Abp.FeatureManagement;
using System.Collections.Generic;
using System.Text;
namespace Volo.Abp.FeatureManagement public class FeatureManagementApplicationTestBase : FeatureManagementTestBase<FeatureManagementApplicationTestModule>
{ {
public class FeatureManagementApplicationTestBase : FeatureManagementTestBase<FeatureManagementApplicationTestModule>
{
}
} }

15
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/FeatureManagementApplicationTestModule.cs

@ -1,13 +1,12 @@
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpFeatureManagementDomainTestModule)
)]
public class FeatureManagementApplicationTestModule : AbpModule
{ {
[DependsOn(
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpFeatureManagementDomainTestModule)
)]
public class FeatureManagementApplicationTestModule : AbpModule
{
}
} }

15
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/NewtonsoftStringValueJsonConverter_Tests.cs

@ -1,16 +1,15 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json; using Volo.Abp.Json;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class NewtonsoftStringValueJsonConverter_Tests : StringValueJsonConverter_Tests
{ {
public class NewtonsoftStringValueJsonConverter_Tests : StringValueJsonConverter_Tests protected override void AfterAddApplication(IServiceCollection services)
{ {
protected override void AfterAddApplication(IServiceCollection services) services.PreConfigure<AbpJsonOptions>(options =>
{ {
services.PreConfigure<AbpJsonOptions>(options => options.UseHybridSerializer = true;
{ });
options.UseHybridSerializer = true;
});
}
} }
} }

63
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/StringValueJsonConverter_Tests.cs

@ -5,23 +5,23 @@ using Volo.Abp.Json;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public abstract class StringValueJsonConverter_Tests : FeatureManagementApplicationTestBase
{ {
public abstract class StringValueJsonConverter_Tests : FeatureManagementApplicationTestBase private readonly IJsonSerializer _jsonSerializer;
{
private readonly IJsonSerializer _jsonSerializer;
public StringValueJsonConverter_Tests() public StringValueJsonConverter_Tests()
{ {
_jsonSerializer = GetRequiredService<IJsonSerializer>(); _jsonSerializer = GetRequiredService<IJsonSerializer>();
} }
[Fact] [Fact]
public void Should_Serialize_And_Deserialize() public void Should_Serialize_And_Deserialize()
{
var featureListDto = new GetFeatureListResultDto
{ {
var featureListDto = new GetFeatureListResultDto Groups = new List<FeatureGroupDto>
{
Groups = new List<FeatureGroupDto>
{ {
new FeatureGroupDto new FeatureGroupDto
{ {
@ -71,30 +71,29 @@ namespace Volo.Abp.FeatureManagement
} }
} }
} }
}; };
var serialized = _jsonSerializer.Serialize(featureListDto, indented: true); var serialized = _jsonSerializer.Serialize(featureListDto, indented: true);
var featureListDto2 = _jsonSerializer.Deserialize<GetFeatureListResultDto>(serialized); var featureListDto2 = _jsonSerializer.Deserialize<GetFeatureListResultDto>(serialized);
featureListDto2.ShouldNotBeNull(); featureListDto2.ShouldNotBeNull();
featureListDto2.Groups[0].Features[0].ValueType.ShouldBeOfType<FreeTextStringValueType>(); featureListDto2.Groups[0].Features[0].ValueType.ShouldBeOfType<FreeTextStringValueType>();
featureListDto2.Groups[0].Features[0].ValueType.Validator.ShouldBeOfType<BooleanValueValidator>(); featureListDto2.Groups[0].Features[0].ValueType.Validator.ShouldBeOfType<BooleanValueValidator>();
featureListDto2.Groups[0].Features[1].ValueType.ShouldBeOfType<SelectionStringValueType>(); featureListDto2.Groups[0].Features[1].ValueType.ShouldBeOfType<SelectionStringValueType>();
featureListDto2.Groups[0].Features[1].ValueType.Validator.ShouldBeOfType<AlwaysValidValueValidator>(); featureListDto2.Groups[0].Features[1].ValueType.Validator.ShouldBeOfType<AlwaysValidValueValidator>();
featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldBeOfType<LocalizableSelectionStringValueItem[]>(); featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldBeOfType<LocalizableSelectionStringValueItem[]>();
featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldContain(x => featureListDto2.Groups[0].Features[1].ValueType.As<SelectionStringValueType>().ItemSource.Items.ShouldContain(x =>
x.Value == "TestValue" && x.DisplayText.ResourceName == "TestResourceName" && x.Value == "TestValue" && x.DisplayText.ResourceName == "TestResourceName" &&
x.DisplayText.Name == "TestName"); x.DisplayText.Name == "TestName");
featureListDto2.Groups[0].Features[2].ValueType.ShouldBeOfType<ToggleStringValueType>(); featureListDto2.Groups[0].Features[2].ValueType.ShouldBeOfType<ToggleStringValueType>();
featureListDto2.Groups[0].Features[2].ValueType.Validator.ShouldBeOfType<NumericValueValidator>(); featureListDto2.Groups[0].Features[2].ValueType.Validator.ShouldBeOfType<NumericValueValidator>();
featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MaxValue.ShouldBe(1000); featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MaxValue.ShouldBe(1000);
featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MinValue.ShouldBe(10); featureListDto2.Groups[0].Features[2].ValueType.Validator.As<NumericValueValidator>().MinValue.ShouldBe(10);
featureListDto2.Groups[0].Features[3].Provider.Name.ShouldBe("FeatureName"); featureListDto2.Groups[0].Features[3].Provider.Name.ShouldBe("FeatureName");
featureListDto2.Groups[0].Features[3].Provider.Key.ShouldBe("FeatureKey"); featureListDto2.Groups[0].Features[3].Provider.Key.ShouldBe("FeatureKey");
}
} }
} }

15
modules/feature-management/test/Volo.Abp.FeatureManagement.Application.Tests/Volo/Abp/FeatureManagement/SystemTextJsonStringValueJsonConverter_Tests.cs

@ -1,16 +1,15 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Json; using Volo.Abp.Json;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class SystemTextJsonStringValueJsonConverter_Tests : StringValueJsonConverter_Tests
{ {
public class SystemTextJsonStringValueJsonConverter_Tests : StringValueJsonConverter_Tests protected override void AfterAddApplication(IServiceCollection services)
{ {
protected override void AfterAddApplication(IServiceCollection services) services.PreConfigure<AbpJsonOptions>(options =>
{ {
services.PreConfigure<AbpJsonOptions>(options => options.UseHybridSerializer = false;
{ });
options.UseHybridSerializer = false;
});
}
} }
} }

15
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/AbpFeatureManagementDomainTestModule.cs

@ -1,13 +1,12 @@
using Volo.Abp.FeatureManagement.EntityFrameworkCore; using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpFeatureManagementEntityFrameworkCoreTestModule)
)]
public class AbpFeatureManagementDomainTestModule : AbpModule
{ {
[DependsOn(
typeof(AbpFeatureManagementEntityFrameworkCoreTestModule)
)]
public class AbpFeatureManagementDomainTestModule : AbpModule
{
}
} }

9
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManagementDomainTestBase.cs

@ -1,7 +1,6 @@
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public abstract class FeatureManagementDomainTestBase : FeatureManagementTestBase<AbpFeatureManagementDomainTestModule>
{ {
public abstract class FeatureManagementDomainTestBase : FeatureManagementTestBase<AbpFeatureManagementDomainTestModule>
{
} }
}

270
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureManager_Tests.cs

@ -1,158 +1,156 @@
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManager_Tests : FeatureManagementDomainTestBase
{ {
public class FeatureManager_Tests : FeatureManagementDomainTestBase private readonly IFeatureManager _featureManager;
private readonly ICurrentTenant _currentTenant;
private readonly IFeatureChecker _featureChecker;
public FeatureManager_Tests()
{ {
private readonly IFeatureManager _featureManager; _featureManager = GetRequiredService<IFeatureManager>();
private readonly ICurrentTenant _currentTenant; _featureChecker = GetRequiredService<IFeatureChecker>();
private readonly IFeatureChecker _featureChecker; _currentTenant = GetRequiredService<ICurrentTenant>();
}
public FeatureManager_Tests() [Fact]
{ public async Task Should_Get_A_FeatureValue_For_A_Provider()
_featureManager = GetRequiredService<IFeatureManager>(); {
_featureChecker = GetRequiredService<IFeatureChecker>(); //Default values
_currentTenant = GetRequiredService<ICurrentTenant>();
} (await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.SocialLogins
)).ShouldBeNull();
(await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.DailyAnalysis
)).ShouldBe(false.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.ProjectCount
)).ShouldBe("1");
(await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.BackupCount
)).ShouldBe("0");
//"Enterprise" edition values
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.SocialLogins,
TestEditionIds.Enterprise
)).ShouldBe(true.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.DailyAnalysis,
TestEditionIds.Enterprise
)).ShouldBe(false.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.ProjectCount,
TestEditionIds.Enterprise
)).ShouldBe("3");
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.BackupCount,
TestEditionIds.Enterprise
)).ShouldBe("5");
//"Ultimate" edition values
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.SocialLogins,
TestEditionIds.Ultimate
)).ShouldBe(true.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.DailyAnalysis,
TestEditionIds.Ultimate
)).ShouldBe(true.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.ProjectCount,
TestEditionIds.Ultimate
)).ShouldBe("10");
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.BackupCount,
TestEditionIds.Ultimate
)).ShouldBe("10");
}
[Fact] [Fact]
public async Task Should_Get_A_FeatureValue_For_A_Provider() public async Task Should_Change_Feature_Value_And_Refresh_Cache()
{
var tenantId = Guid.NewGuid();
//It is "False" at the beginning
using (_currentTenant.Change(tenantId))
{ {
//Default values (await _featureChecker.IsEnabledAsync(TestFeatureDefinitionProvider.SocialLogins)).ShouldBeFalse();
(await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.SocialLogins
)).ShouldBeNull();
(await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.DailyAnalysis
)).ShouldBe(false.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.ProjectCount
)).ShouldBe("1");
(await _featureManager.GetOrNullDefaultAsync(
TestFeatureDefinitionProvider.BackupCount
)).ShouldBe("0");
//"Enterprise" edition values
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.SocialLogins,
TestEditionIds.Enterprise
)).ShouldBe(true.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.DailyAnalysis,
TestEditionIds.Enterprise
)).ShouldBe(false.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.ProjectCount,
TestEditionIds.Enterprise
)).ShouldBe("3");
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.BackupCount,
TestEditionIds.Enterprise
)).ShouldBe("5");
//"Ultimate" edition values
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.SocialLogins,
TestEditionIds.Ultimate
)).ShouldBe(true.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.DailyAnalysis,
TestEditionIds.Ultimate
)).ShouldBe(true.ToString().ToLowerInvariant());
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.ProjectCount,
TestEditionIds.Ultimate
)).ShouldBe("10");
(await _featureManager.GetOrNullForEditionAsync(
TestFeatureDefinitionProvider.BackupCount,
TestEditionIds.Ultimate
)).ShouldBe("10");
} }
[Fact] //Set to "True" by host for the tenant
public async Task Should_Change_Feature_Value_And_Refresh_Cache() using (_currentTenant.Change(null))
{ {
var tenantId = Guid.NewGuid(); (await _featureChecker.IsEnabledAsync(TestFeatureDefinitionProvider.SocialLogins)).ShouldBeFalse();
await _featureManager.SetForTenantAsync(tenantId, TestFeatureDefinitionProvider.SocialLogins, "True");
//It is "False" at the beginning (await _featureManager.GetOrNullForTenantAsync(TestFeatureDefinitionProvider.SocialLogins, tenantId)).ShouldBe("True");
using (_currentTenant.Change(tenantId))
{
(await _featureChecker.IsEnabledAsync(TestFeatureDefinitionProvider.SocialLogins)).ShouldBeFalse();
}
//Set to "True" by host for the tenant
using (_currentTenant.Change(null))
{
(await _featureChecker.IsEnabledAsync(TestFeatureDefinitionProvider.SocialLogins)).ShouldBeFalse();
await _featureManager.SetForTenantAsync(tenantId, TestFeatureDefinitionProvider.SocialLogins, "True");
(await _featureManager.GetOrNullForTenantAsync(TestFeatureDefinitionProvider.SocialLogins, tenantId)).ShouldBe("True");
}
//Now, it should be "True"
using (_currentTenant.Change(tenantId))
{
(await _featureChecker.IsEnabledAsync(TestFeatureDefinitionProvider.SocialLogins)).ShouldBeTrue();
}
} }
//Now, it should be "True"
[Fact] using (_currentTenant.Change(tenantId))
public async Task Should_Get_FeatureValues_With_Provider_For_A_Provider()
{ {
var featureNameValueWithGrantedProviders = await _featureManager.GetAllWithProviderAsync( (await _featureChecker.IsEnabledAsync(TestFeatureDefinitionProvider.SocialLogins)).ShouldBeTrue();
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Enterprise.ToString()
);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.SocialLogins
&& x.Value == true.ToString().ToLowerInvariant() &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.EmailSupport &&
x.Value == true.ToString().ToLowerInvariant() &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
//Default Value
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.DailyAnalysis &&
x.Value == false.ToString().ToLowerInvariant() &&
x.Provider.Name == DefaultValueFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.UserCount &&
x.Value == "20" &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.ProjectCount &&
x.Value == "3" &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.BackupCount &&
x.Value == "5" &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
} }
}
[Fact]
public async Task Should_Get_FeatureValues_With_Provider_For_A_Provider()
{
var featureNameValueWithGrantedProviders = await _featureManager.GetAllWithProviderAsync(
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Enterprise.ToString()
);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.SocialLogins
&& x.Value == true.ToString().ToLowerInvariant() &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.EmailSupport &&
x.Value == true.ToString().ToLowerInvariant() &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
//Default Value
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.DailyAnalysis &&
x.Value == false.ToString().ToLowerInvariant() &&
x.Provider.Name == DefaultValueFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.UserCount &&
x.Value == "20" &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.ProjectCount &&
x.Value == "3" &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
featureNameValueWithGrantedProviders.ShouldContain(x =>
x.Name == TestFeatureDefinitionProvider.BackupCount &&
x.Value == "5" &&
x.Provider.Name == EditionFeatureValueProvider.ProviderName);
} }
} }

126
modules/feature-management/test/Volo.Abp.FeatureManagement.Domain.Tests/Volo/Abp/FeatureManagement/FeatureValueCacheItemInvalidator_Tests.cs

@ -1,32 +1,65 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Settings;
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureValueCacheItemInvalidator_Tests : FeatureManagementTestBase<AbpFeatureManagementDomainTestModule>
{ {
public class FeatureValueCacheItemInvalidator_Tests : FeatureManagementTestBase<AbpFeatureManagementDomainTestModule> private IDistributedCache<FeatureValueCacheItem> _cache;
private IFeatureValueRepository _featureValueRepository;
private IFeatureManagementStore _featureManagementStore;
private ICurrentTenant _currentTenant;
public FeatureValueCacheItemInvalidator_Tests()
{ {
private IDistributedCache<FeatureValueCacheItem> _cache; _cache = GetRequiredService<IDistributedCache<FeatureValueCacheItem>>();
private IFeatureValueRepository _featureValueRepository; _featureValueRepository = GetRequiredService<IFeatureValueRepository>();
private IFeatureManagementStore _featureManagementStore; _featureManagementStore = GetRequiredService<IFeatureManagementStore>();
private ICurrentTenant _currentTenant; _currentTenant = GetRequiredService<ICurrentTenant>();
public FeatureValueCacheItemInvalidator_Tests() }
{
_cache = GetRequiredService<IDistributedCache<FeatureValueCacheItem>>(); [Fact]
_featureValueRepository = GetRequiredService<IFeatureValueRepository>(); public async Task Cache_Should_Invalidator_WhenFeatureChanged()
_featureManagementStore = GetRequiredService<IFeatureManagementStore>(); {
_currentTenant = GetRequiredService<ICurrentTenant>(); // Arrange cache feature.
} (await _featureManagementStore.GetOrNullAsync(
TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()
)
).ShouldNotBeNull();
var feature = await _featureValueRepository.FindAsync(
TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()
);
[Fact] // Act
public async Task Cache_Should_Invalidator_WhenFeatureChanged() await _featureValueRepository.DeleteAsync(feature);
// Assert
(await _cache.GetAsync(
FeatureValueCacheItem.CalculateCacheKey(
TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()
)
)
).ShouldBeNull();
}
[Fact]
public async Task Cache_Should_Invalidator_WhenSettingChanged_Between_Tenant_And_Host()
{
var tenantId = Guid.NewGuid();
using (_currentTenant.Change(tenantId))
{ {
// Arrange cache feature. // Arrange cache feature.
(await _featureManagementStore.GetOrNullAsync( (await _featureManagementStore.GetOrNullAsync(
@ -35,17 +68,19 @@ namespace Volo.Abp.FeatureManagement
TestEditionIds.Regular.ToString() TestEditionIds.Regular.ToString()
) )
).ShouldNotBeNull(); ).ShouldNotBeNull();
}
var feature = await _featureValueRepository.FindAsync( using (_currentTenant.Change(null))
TestFeatureDefinitionProvider.SocialLogins, {
await _featureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString(),
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString() TestEditionIds.Regular.ToString());
); }
// Act
await _featureValueRepository.DeleteAsync(feature);
// Assert using (_currentTenant.Change(tenantId))
{
// Arrange cache feature.
(await _cache.GetAsync( (await _cache.GetAsync(
FeatureValueCacheItem.CalculateCacheKey( FeatureValueCacheItem.CalculateCacheKey(
TestFeatureDefinitionProvider.SocialLogins, TestFeatureDefinitionProvider.SocialLogins,
@ -55,44 +90,5 @@ namespace Volo.Abp.FeatureManagement
) )
).ShouldBeNull(); ).ShouldBeNull();
} }
[Fact]
public async Task Cache_Should_Invalidator_WhenSettingChanged_Between_Tenant_And_Host()
{
var tenantId = Guid.NewGuid();
using (_currentTenant.Change(tenantId))
{
// Arrange cache feature.
(await _featureManagementStore.GetOrNullAsync(
TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()
)
).ShouldNotBeNull();
}
using (_currentTenant.Change(null))
{
await _featureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString(),
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
}
using (_currentTenant.Change(tenantId))
{
// Arrange cache feature.
(await _cache.GetAsync(
FeatureValueCacheItem.CalculateCacheKey(
TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()
)
)
).ShouldBeNull();
}
}
} }
} }

53
modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/AbpFeatureManagementEntityFrameworkCoreTestModule.cs

@ -6,38 +6,37 @@ using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Sqlite; using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
[DependsOn(
typeof(FeatureManagementTestBaseModule),
typeof(AbpFeatureManagementEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(FeatureManagementTestBaseModule),
typeof(AbpFeatureManagementEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class AbpFeatureManagementEntityFrameworkCoreTestModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) var sqliteConnection = CreateDatabaseAndGetConnection();
{
var sqliteConnection = CreateDatabaseAndGetConnection();
Configure<AbpDbContextOptions>(options => Configure<AbpDbContextOptions>(options =>
{
options.Configure(abpDbContextConfigurationContext =>
{ {
options.Configure(abpDbContextConfigurationContext => abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection);
{
abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection);
});
}); });
} });
}
private static SqliteConnection CreateDatabaseAndGetConnection()
{ private static SqliteConnection CreateDatabaseAndGetConnection()
var connection = new SqliteConnection("Data Source=:memory:"); {
connection.Open(); var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
new FeatureManagementDbContext(
new DbContextOptionsBuilder<FeatureManagementDbContext>().UseSqlite(connection).Options
).GetService<IRelationalDatabaseCreator>().CreateTables();
new FeatureManagementDbContext( return connection;
new DbContextOptionsBuilder<FeatureManagementDbContext>().UseSqlite(connection).Options
).GetService<IRelationalDatabaseCreator>().CreateTables();
return connection;
}
} }
} }

9
modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureManagementStore_Tests.cs

@ -1,11 +1,6 @@
using System; namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
using System.Collections.Generic;
using System.Text;
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore public class FeatureManagementStore_Tests : FeatureManagementStore_Tests<AbpFeatureManagementEntityFrameworkCoreTestModule>
{ {
public class FeatureManagementStore_Tests : FeatureManagementStore_Tests<AbpFeatureManagementEntityFrameworkCoreTestModule>
{
}
} }

7
modules/feature-management/test/Volo.Abp.FeatureManagement.EntityFrameworkCore.Tests/Volo/Abp/FeatureManagement/EntityFrameworkCore/FeatureValueRepositoryTests.cs

@ -1,7 +1,6 @@
namespace Volo.Abp.FeatureManagement.EntityFrameworkCore namespace Volo.Abp.FeatureManagement.EntityFrameworkCore;
public class FeatureValueRepositoryTests : FeatureValueRepository_Tests<AbpFeatureManagementEntityFrameworkCoreTestModule>
{ {
public class FeatureValueRepositoryTests : FeatureValueRepository_Tests<AbpFeatureManagementEntityFrameworkCoreTestModule>
{
}
} }

34
modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs

@ -1,27 +1,25 @@
using System; using System;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Uow;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
[DependsOn(
typeof(FeatureManagementTestBaseModule),
typeof(AbpFeatureManagementMongoDbModule)
)]
public class AbpFeatureManagementMongoDbTestModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(FeatureManagementTestBaseModule),
typeof(AbpFeatureManagementMongoDbModule)
)]
public class AbpFeatureManagementMongoDbTestModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) var stringArray = MongoDbFixture.ConnectionString.Split('?');
{ var connectionString = stringArray[0].EnsureEndsWith('/') +
var stringArray = MongoDbFixture.ConnectionString.Split('?'); "Db_" +
var connectionString = stringArray[0].EnsureEndsWith('/') + Guid.NewGuid().ToString("N") + "/?" + stringArray[1];
"Db_" +
Guid.NewGuid().ToString("N") + "/?" + stringArray[1];
Configure<AbpDbConnectionOptions>(options => Configure<AbpDbConnectionOptions>(options =>
{ {
options.ConnectionStrings.Default = connectionString; options.ConnectionStrings.Default = connectionString;
}); });
}
} }
} }

9
modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs

@ -1,10 +1,9 @@
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
[Collection(MongoTestCollection.Name)]
public class FeatureManagementStore_Tests : FeatureManagementStore_Tests<AbpFeatureManagementMongoDbTestModule>
{ {
[Collection(MongoTestCollection.Name)]
public class FeatureManagementStore_Tests : FeatureManagementStore_Tests<AbpFeatureManagementMongoDbTestModule>
{
}
} }

9
modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs

@ -1,10 +1,9 @@
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
[Collection(MongoTestCollection.Name)]
public class FeatureValueRepositoryTests : FeatureValueRepository_Tests<AbpFeatureManagementMongoDbTestModule>
{ {
[Collection(MongoTestCollection.Name)]
public class FeatureValueRepositoryTests : FeatureValueRepository_Tests<AbpFeatureManagementMongoDbTestModule>
{
}
} }

27
modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs

@ -1,22 +1,21 @@
using System; using System;
using Mongo2Go; using Mongo2Go;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
public class MongoDbFixture : IDisposable
{ {
public class MongoDbFixture : IDisposable private static readonly MongoDbRunner MongoDbRunner;
{ public static readonly string ConnectionString;
private static readonly MongoDbRunner MongoDbRunner;
public static readonly string ConnectionString;
static MongoDbFixture() static MongoDbFixture()
{ {
MongoDbRunner = MongoDbRunner.Start(singleNodeReplSet: true, singleNodeReplSetWaitTimeout: 20); MongoDbRunner = MongoDbRunner.Start(singleNodeReplSet: true, singleNodeReplSetWaitTimeout: 20);
ConnectionString = MongoDbRunner.ConnectionString; ConnectionString = MongoDbRunner.ConnectionString;
} }
public void Dispose() public void Dispose()
{ {
MongoDbRunner?.Dispose(); MongoDbRunner?.Dispose();
}
} }
} }

13
modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoTestCollection.cs

@ -1,10 +1,9 @@
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement.MongoDB namespace Volo.Abp.FeatureManagement.MongoDB;
[CollectionDefinition(Name)]
public class MongoTestCollection : ICollectionFixture<MongoDbFixture>
{ {
[CollectionDefinition(Name)] public const string Name = "MongoDB Collection";
public class MongoTestCollection : ICollectionFixture<MongoDbFixture> }
{
public const string Name = "MongoDB Collection";
}
}

195
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementStore_Tests.cs

@ -1,6 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Features; using Volo.Abp.Features;
@ -8,103 +6,127 @@ using Volo.Abp.Modularity;
using Volo.Abp.Uow; using Volo.Abp.Uow;
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public abstract class FeatureManagementStore_Tests<TStartupModule> : FeatureManagementTestBase<TStartupModule>
where TStartupModule : IAbpModule
{ {
public abstract class FeatureManagementStore_Tests<TStartupModule> : FeatureManagementTestBase<TStartupModule> private IFeatureManagementStore FeatureManagementStore { get; set; }
where TStartupModule : IAbpModule private IFeatureValueRepository FeatureValueRepository { get; set; }
private IUnitOfWorkManager UnitOfWorkManager { get; set; }
protected FeatureManagementStore_Tests()
{ {
private IFeatureManagementStore FeatureManagementStore { get; set; } FeatureManagementStore = GetRequiredService<IFeatureManagementStore>();
private IFeatureValueRepository FeatureValueRepository { get; set; } FeatureValueRepository = GetRequiredService<IFeatureValueRepository>();
private IUnitOfWorkManager UnitOfWorkManager { get; set; } UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
}
protected FeatureManagementStore_Tests() [Fact]
{ public async Task GetOrNullAsync()
FeatureManagementStore = GetRequiredService<IFeatureManagementStore>(); {
FeatureValueRepository = GetRequiredService<IFeatureValueRepository>(); // Act
UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>(); (await FeatureManagementStore.GetOrNullAsync(Guid.NewGuid().ToString(),
} EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull();
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull();
}
[Fact] [Fact]
public async Task GetOrNullAsync() public async Task Should_Get_Null_Where_Feature_Deleted()
{ {
// Act // Arrange
(await FeatureManagementStore.GetOrNullAsync(Guid.NewGuid().ToString(), (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull(); TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act
await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
// Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull();
}
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, [Fact]
EditionFeatureValueProvider.ProviderName, public async Task SetAsync()
TestEditionIds.Regular.ToString())).ShouldNotBeNull(); {
} // Arrange
(await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).Value.ShouldBe(true.ToString().ToLowerInvariant());
// Act
await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString().ToUpperInvariant(),
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
// Assert
(await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).Value.ShouldBe(false.ToString().ToUpperInvariant());
}
[Fact] [Fact]
public async Task Should_Get_Null_Where_Feature_Deleted() public async Task Set_In_UnitOfWork_Should_Be_Consistent()
{
using (UnitOfWorkManager.Begin())
{ {
// Arrange // Arrange
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull(); TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act // Act
await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins, await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString().ToUpperInvariant(),
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()); TestEditionIds.Regular.ToString());
// Assert // Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull(); TestEditionIds.Regular.ToString())).ShouldBe(false.ToString().ToUpperInvariant());
} }
}
[Fact] [Fact]
public async Task SetAsync() public async Task DeleteAsync()
{ {
// Arrange // Arrange
(await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, (await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).Value.ShouldBe(true.ToString().ToLowerInvariant()); TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act // Act
await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins, await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString().ToUpperInvariant(), EditionFeatureValueProvider.ProviderName,
EditionFeatureValueProvider.ProviderName, TestEditionIds.Regular.ToString());
TestEditionIds.Regular.ToString());
// Assert
(await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).Value.ShouldBe(false.ToString().ToUpperInvariant());
}
[Fact] // Assert
public async Task Set_In_UnitOfWork_Should_Be_Consistent() (await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins,
{ EditionFeatureValueProvider.ProviderName,
using (UnitOfWorkManager.Begin()) TestEditionIds.Regular.ToString())).ShouldBeNull();
{
// Arrange
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins, }
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act
await FeatureManagementStore.SetAsync(TestFeatureDefinitionProvider.SocialLogins,
false.ToString().ToUpperInvariant(),
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
// Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBe(false.ToString().ToUpperInvariant());
}
}
[Fact] [Fact]
public async Task DeleteAsync() public async Task Delete_In_UnitOfWork_Should_Be_Consistent()
{
using (var uow = UnitOfWorkManager.Begin())
{ {
// Arrange // Arrange
(await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull(); TestEditionIds.Regular.ToString())).ShouldNotBeNull();
@ -113,37 +135,12 @@ namespace Volo.Abp.FeatureManagement
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString()); TestEditionIds.Regular.ToString());
await uow.SaveChangesAsync();
// Assert // Assert
(await FeatureValueRepository.FindAsync(TestFeatureDefinitionProvider.SocialLogins, (await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull(); TestEditionIds.Regular.ToString())).ShouldBeNull();
}
[Fact]
public async Task Delete_In_UnitOfWork_Should_Be_Consistent()
{
using (var uow = UnitOfWorkManager.Begin())
{
// Arrange
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldNotBeNull();
// Act
await FeatureManagementStore.DeleteAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString());
await uow.SaveChangesAsync();
// Assert
(await FeatureManagementStore.GetOrNullAsync(TestFeatureDefinitionProvider.SocialLogins,
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Regular.ToString())).ShouldBeNull();
}
} }
} }
} }

13
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBase.cs

@ -1,14 +1,13 @@
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Testing; using Volo.Abp.Testing;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public abstract class FeatureManagementTestBase<TStartupModule> : AbpIntegratedTest<TStartupModule>
where TStartupModule : IAbpModule
{ {
public abstract class FeatureManagementTestBase<TStartupModule> : AbpIntegratedTest<TStartupModule> protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
where TStartupModule : IAbpModule
{ {
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) options.UseAutofac();
{
options.UseAutofac();
}
} }
} }

55
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestBaseModule.cs

@ -5,43 +5,42 @@ using Volo.Abp.Features;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Threading; using Volo.Abp.Threading;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
[DependsOn(
typeof(AbpAutofacModule),
typeof(AbpTestBaseModule),
typeof(AbpAuthorizationModule),
typeof(AbpFeatureManagementDomainModule)
)]
public class FeatureManagementTestBaseModule : AbpModule
{ {
[DependsOn( public override void ConfigureServices(ServiceConfigurationContext context)
typeof(AbpAutofacModule),
typeof(AbpTestBaseModule),
typeof(AbpAuthorizationModule),
typeof(AbpFeatureManagementDomainModule)
)]
public class FeatureManagementTestBaseModule : AbpModule
{ {
public override void ConfigureServices(ServiceConfigurationContext context) context.Services.AddAlwaysAllowAuthorization();
{ }
context.Services.AddAlwaysAllowAuthorization();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context) public override void OnApplicationInitialization(ApplicationInitializationContext context)
{ {
SeedTestData(context); SeedTestData(context);
} }
public override void PostConfigureServices(ServiceConfigurationContext context) public override void PostConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<FeatureManagementOptions>(options =>
{ {
context.Services.Configure<FeatureManagementOptions>(options =>
{
//TODO: Any value can pass. After completing the permission unit test, look at it again. //TODO: Any value can pass. After completing the permission unit test, look at it again.
options.ProviderPolicies[EditionFeatureValueProvider.ProviderName] = EditionFeatureValueProvider.ProviderName; options.ProviderPolicies[EditionFeatureValueProvider.ProviderName] = EditionFeatureValueProvider.ProviderName;
}); });
} }
private static void SeedTestData(ApplicationInitializationContext context) private static void SeedTestData(ApplicationInitializationContext context)
{
using (var scope = context.ServiceProvider.CreateScope())
{ {
using (var scope = context.ServiceProvider.CreateScope()) AsyncHelper.RunSync(() => scope.ServiceProvider
{ .GetRequiredService<FeatureManagementTestDataBuilder>()
AsyncHelper.RunSync(() => scope.ServiceProvider .BuildAsync());
.GetRequiredService<FeatureManagementTestDataBuilder>()
.BuildAsync());
}
} }
} }
} }

9
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestData.cs

@ -1,10 +1,9 @@
using System; using System;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementTestData : ISingletonDependency
{ {
public class FeatureManagementTestData : ISingletonDependency public Guid User1Id { get; } = Guid.NewGuid();
{
public Guid User1Id { get; } = Guid.NewGuid();
}
} }

369
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureManagementTestDataBuilder.cs

@ -3,191 +3,190 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.Guids; using Volo.Abp.Guids;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class FeatureManagementTestDataBuilder : ITransientDependency
{ {
public class FeatureManagementTestDataBuilder : ITransientDependency private readonly IFeatureValueRepository _featureValueRepository;
private readonly IGuidGenerator _guidGenerator;
private readonly FeatureManagementTestData _testData;
public FeatureManagementTestDataBuilder(
IGuidGenerator guidGenerator,
FeatureManagementTestData testData,
IFeatureValueRepository featureValueRepository)
{
_guidGenerator = guidGenerator;
_testData = testData;
_featureValueRepository = featureValueRepository;
}
public async Task BuildAsync()
{ {
private readonly IFeatureValueRepository _featureValueRepository; #region "Regular" edition features
private readonly IGuidGenerator _guidGenerator;
private readonly FeatureManagementTestData _testData; //SocialLogins
await _featureValueRepository.InsertAsync(
public FeatureManagementTestDataBuilder( new FeatureValue(
IGuidGenerator guidGenerator, _guidGenerator.Create(),
FeatureManagementTestData testData, TestFeatureDefinitionProvider.SocialLogins,
IFeatureValueRepository featureValueRepository) true.ToString().ToLowerInvariant(),
{ EditionFeatureValueProvider.ProviderName,
_guidGenerator = guidGenerator; TestEditionIds.Regular.ToString()
_testData = testData; )
_featureValueRepository = featureValueRepository; );
}
//UserCount
public async Task BuildAsync() await _featureValueRepository.InsertAsync(
{ new FeatureValue(
#region "Regular" edition features _guidGenerator.Create(),
TestFeatureDefinitionProvider.UserCount,
//SocialLogins "10",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Regular.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.SocialLogins, );
true.ToString().ToLowerInvariant(),
EditionFeatureValueProvider.ProviderName, //ProjectCount
TestEditionIds.Regular.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.ProjectCount,
//UserCount "1",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Regular.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.UserCount, );
"10",
EditionFeatureValueProvider.ProviderName, #endregion
TestEditionIds.Regular.ToString()
) #region "Enterprise" edition features
);
//SocialLogins
//ProjectCount await _featureValueRepository.InsertAsync(
await _featureValueRepository.InsertAsync( new FeatureValue(
new FeatureValue( _guidGenerator.Create(),
_guidGenerator.Create(), TestFeatureDefinitionProvider.SocialLogins,
TestFeatureDefinitionProvider.ProjectCount, true.ToString().ToLowerInvariant(),
"1", EditionFeatureValueProvider.ProviderName,
EditionFeatureValueProvider.ProviderName, TestEditionIds.Enterprise.ToString()
TestEditionIds.Regular.ToString() )
) );
);
//EmailSupport
#endregion await _featureValueRepository.InsertAsync(
new FeatureValue(
#region "Enterprise" edition features _guidGenerator.Create(),
TestFeatureDefinitionProvider.EmailSupport,
//SocialLogins true.ToString().ToLowerInvariant(),
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Enterprise.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.SocialLogins, );
true.ToString().ToLowerInvariant(),
EditionFeatureValueProvider.ProviderName, //UserCount
TestEditionIds.Enterprise.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.UserCount,
//EmailSupport "20",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Enterprise.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.EmailSupport, );
true.ToString().ToLowerInvariant(),
EditionFeatureValueProvider.ProviderName, //ProjectCount
TestEditionIds.Enterprise.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.ProjectCount,
//UserCount "3",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Enterprise.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.UserCount, );
"20",
EditionFeatureValueProvider.ProviderName, //BackupCount
TestEditionIds.Enterprise.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.BackupCount,
//ProjectCount "5",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Enterprise.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.ProjectCount, );
"3",
EditionFeatureValueProvider.ProviderName, #endregion
TestEditionIds.Enterprise.ToString()
) #region "Ultimate" edition features
);
//SocialLogins
//BackupCount await _featureValueRepository.InsertAsync(
await _featureValueRepository.InsertAsync( new FeatureValue(
new FeatureValue( _guidGenerator.Create(),
_guidGenerator.Create(), TestFeatureDefinitionProvider.SocialLogins,
TestFeatureDefinitionProvider.BackupCount, true.ToString().ToLowerInvariant(),
"5", EditionFeatureValueProvider.ProviderName,
EditionFeatureValueProvider.ProviderName, TestEditionIds.Ultimate.ToString()
TestEditionIds.Enterprise.ToString() )
) );
);
//EmailSupport
#endregion await _featureValueRepository.InsertAsync(
new FeatureValue(
#region "Ultimate" edition features _guidGenerator.Create(),
TestFeatureDefinitionProvider.EmailSupport,
//SocialLogins true.ToString().ToLowerInvariant(),
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Ultimate.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.SocialLogins, );
true.ToString().ToLowerInvariant(),
EditionFeatureValueProvider.ProviderName, //EmailSupport
TestEditionIds.Ultimate.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.DailyAnalysis,
//EmailSupport true.ToString().ToLowerInvariant(),
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Ultimate.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.EmailSupport, );
true.ToString().ToLowerInvariant(),
EditionFeatureValueProvider.ProviderName, //UserCount
TestEditionIds.Ultimate.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.UserCount,
//EmailSupport "100",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Ultimate.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.DailyAnalysis, );
true.ToString().ToLowerInvariant(),
EditionFeatureValueProvider.ProviderName, //ProjectCount
TestEditionIds.Ultimate.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.ProjectCount,
//UserCount "10",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Ultimate.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.UserCount, );
"100",
EditionFeatureValueProvider.ProviderName, //BackupCount
TestEditionIds.Ultimate.ToString() await _featureValueRepository.InsertAsync(
) new FeatureValue(
); _guidGenerator.Create(),
TestFeatureDefinitionProvider.BackupCount,
//ProjectCount "10",
await _featureValueRepository.InsertAsync( EditionFeatureValueProvider.ProviderName,
new FeatureValue( TestEditionIds.Ultimate.ToString()
_guidGenerator.Create(), )
TestFeatureDefinitionProvider.ProjectCount, );
"10",
EditionFeatureValueProvider.ProviderName, #endregion
TestEditionIds.Ultimate.ToString()
)
);
//BackupCount
await _featureValueRepository.InsertAsync(
new FeatureValue(
_guidGenerator.Create(),
TestFeatureDefinitionProvider.BackupCount,
"10",
EditionFeatureValueProvider.ProviderName,
TestEditionIds.Ultimate.ToString()
)
);
#endregion
}
} }
} }

109
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/FeatureValueRepository_Tests.cs

@ -4,73 +4,72 @@ using Volo.Abp.Features;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Xunit; using Xunit;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public abstract class FeatureValueRepository_Tests<TStartupModule> : FeatureManagementTestBase<TStartupModule>
where TStartupModule : IAbpModule
{ {
public abstract class FeatureValueRepository_Tests<TStartupModule> : FeatureManagementTestBase<TStartupModule> protected IFeatureValueRepository Repository { get; set; }
where TStartupModule : IAbpModule
{
protected IFeatureValueRepository Repository { get; set; }
protected FeatureValueRepository_Tests() protected FeatureValueRepository_Tests()
{ {
Repository = GetRequiredService<IFeatureValueRepository>(); Repository = GetRequiredService<IFeatureValueRepository>();
} }
[Fact] [Fact]
public async Task FindAsync() public async Task FindAsync()
{ {
//feature value does exists //feature value does exists
var featureValue = await Repository.FindAsync( var featureValue = await Repository.FindAsync(
TestFeatureDefinitionProvider.ProjectCount, TestFeatureDefinitionProvider.ProjectCount,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Enterprise.ToString() TestEditionIds.Enterprise.ToString()
); );
featureValue.ShouldNotBeNull(); featureValue.ShouldNotBeNull();
featureValue.Value.ShouldBe("3"); featureValue.Value.ShouldBe("3");
//feature value does not exists //feature value does not exists
featureValue = await Repository.FindAsync( featureValue = await Repository.FindAsync(
TestFeatureDefinitionProvider.ProjectCount, TestFeatureDefinitionProvider.ProjectCount,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
"undefined-edition-id" "undefined-edition-id"
); );
featureValue.ShouldBeNull(); featureValue.ShouldBeNull();
} }
[Fact] [Fact]
public async Task FindAllAsync() public async Task FindAllAsync()
{ {
var featureValues = await Repository.FindAllAsync( var featureValues = await Repository.FindAllAsync(
TestFeatureDefinitionProvider.ProjectCount, TestFeatureDefinitionProvider.ProjectCount,
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Enterprise.ToString() TestEditionIds.Enterprise.ToString()
); );
featureValues.Count.ShouldBe(1); featureValues.Count.ShouldBe(1);
} }
[Fact] [Fact]
public async Task GetListAsync() public async Task GetListAsync()
{ {
var featureValues = await Repository.GetListAsync( var featureValues = await Repository.GetListAsync(
EditionFeatureValueProvider.ProviderName, EditionFeatureValueProvider.ProviderName,
TestEditionIds.Enterprise.ToString() TestEditionIds.Enterprise.ToString()
); );
featureValues.Count.ShouldBeGreaterThan(0); featureValues.Count.ShouldBeGreaterThan(0);
featureValues.ShouldContain( featureValues.ShouldContain(
fv => fv.Name == TestFeatureDefinitionProvider.SocialLogins && fv => fv.Name == TestFeatureDefinitionProvider.SocialLogins &&
fv.Value == "true" fv.Value == "true"
); );
featureValues.ShouldContain( featureValues.ShouldContain(
fv => fv.Name == TestFeatureDefinitionProvider.ProjectCount && fv => fv.Name == TestFeatureDefinitionProvider.ProjectCount &&
fv.Value == "3" fv.Value == "3"
); );
}
} }
} }

25
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestEditionIds.cs

@ -1,18 +1,17 @@
using System; using System;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public static class TestEditionIds
{ {
public static class TestEditionIds public static Guid Regular { get; }
{ public static Guid Enterprise { get; }
public static Guid Regular { get; } public static Guid Ultimate { get; }
public static Guid Enterprise { get; }
public static Guid Ultimate { get; }
static TestEditionIds() static TestEditionIds()
{ {
Regular = Guid.Parse("532599ab-c0c0-4345-a04a-e322867b6e15"); Regular = Guid.Parse("532599ab-c0c0-4345-a04a-e322867b6e15");
Enterprise = Guid.Parse("27e50758-1feb-436a-be4f-cae8519e0cb2"); Enterprise = Guid.Parse("27e50758-1feb-436a-be4f-cae8519e0cb2");
Ultimate = Guid.Parse("6ea78c22-be32-497e-aaba-a2332c564c5e"); Ultimate = Guid.Parse("6ea78c22-be32-497e-aaba-a2332c564c5e");
}
} }
} }

93
modules/feature-management/test/Volo.Abp.FeatureManagement.TestBase/Volo/Abp/FeatureManagement/TestFeatureDefinitionProvider.cs

@ -1,54 +1,53 @@
using Volo.Abp.Features; using Volo.Abp.Features;
using Volo.Abp.Validation.StringValues; using Volo.Abp.Validation.StringValues;
namespace Volo.Abp.FeatureManagement namespace Volo.Abp.FeatureManagement;
public class TestFeatureDefinitionProvider : FeatureDefinitionProvider
{ {
public class TestFeatureDefinitionProvider : FeatureDefinitionProvider public const string SocialLogins = "SocialLogins";
public const string EmailSupport = "EmailSupport";
public const string DailyAnalysis = "DailyAnalysis";
public const string UserCount = "UserCount";
public const string ProjectCount = "ProjectCount";
public const string BackupCount = "BackupCount";
public override void Define(IFeatureDefinitionContext context)
{ {
public const string SocialLogins = "SocialLogins"; var group = context.AddGroup("TestGroup");
public const string EmailSupport = "EmailSupport";
public const string DailyAnalysis = "DailyAnalysis"; group.AddFeature(
public const string UserCount = "UserCount"; SocialLogins,
public const string ProjectCount = "ProjectCount"; valueType: new ToggleStringValueType()
public const string BackupCount = "BackupCount"; );
public override void Define(IFeatureDefinitionContext context) group.AddFeature(
{ EmailSupport,
var group = context.AddGroup("TestGroup"); valueType: new ToggleStringValueType()
);
group.AddFeature(
SocialLogins, group.AddFeature(
valueType: new ToggleStringValueType() DailyAnalysis,
); defaultValue: false.ToString().ToLowerInvariant(), //Optional, it is already false by default
valueType: new ToggleStringValueType()
group.AddFeature( );
EmailSupport,
valueType: new ToggleStringValueType() group.AddFeature(
); UserCount,
defaultValue: "1",
group.AddFeature( valueType: new FreeTextStringValueType(new NumericValueValidator(1, 1000))
DailyAnalysis, );
defaultValue: false.ToString().ToLowerInvariant(), //Optional, it is already false by default
valueType: new ToggleStringValueType() group.AddFeature(
); ProjectCount,
defaultValue: "1",
group.AddFeature( valueType: new FreeTextStringValueType(new NumericValueValidator(1, 10))
UserCount, );
defaultValue: "1",
valueType: new FreeTextStringValueType(new NumericValueValidator(1, 1000)) group.AddFeature(
); BackupCount,
defaultValue: "0",
group.AddFeature( valueType: new FreeTextStringValueType(new NumericValueValidator(0, 10))
ProjectCount, );
defaultValue: "1",
valueType: new FreeTextStringValueType(new NumericValueValidator(1, 10))
);
group.AddFeature(
BackupCount,
defaultValue: "0",
valueType: new FreeTextStringValueType(new NumericValueValidator(0, 10))
);
}
} }
} }

Loading…
Cancel
Save