From 6359d013bd22b1f7ce30d2289a61eb0eee723cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 12 Aug 2022 15:56:21 +0300 Subject: [PATCH] Introduce ISimpleStateCheckerSerializer and implement serialization of global feature requirements. --- .../ISimpleStateChecker.cs | 2 +- .../ISimpleStateCheckerSerializer.cs | 10 ++++ ...SimpleStateCheckerSerializerContributor.cs | 15 +++++ .../SimpleStateCheckerSerializer.cs | 56 +++++++++++++++++++ .../SimpleStateCheckerSerializerExtensions.cs | 38 +++++++++++++ ...SimpleStateCheckerSerializerContributor.cs | 54 ++++++++++++++++++ ...RequireGlobalFeaturesSimpleStateChecker.cs | 18 +++--- ...StateCheckerSerializerContributor_Tests.cs | 46 +++++++++++++++ .../PermissionDefinitionSerializer.cs | 20 +++---- ...p.PermissionManagement.Domain.Tests.csproj | 2 + .../AbpPermissionManagementTestModule.cs | 7 ++- .../PermissionDefinitionSerializer_Tests.cs | 6 +- 12 files changed, 251 insertions(+), 23 deletions(-) create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs create mode 100644 framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs create mode 100644 framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs index f1a2e40425..e919b981a6 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateChecker.cs @@ -6,4 +6,4 @@ public interface ISimpleStateChecker where TState : IHasSimpleStateCheckers { Task IsEnabledAsync(SimpleStateCheckerContext context); -} +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs new file mode 100644 index 0000000000..30d1ada0f2 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializer.cs @@ -0,0 +1,10 @@ +namespace Volo.Abp.SimpleStateChecking; + +public interface ISimpleStateCheckerSerializer +{ + public string Serialize(ISimpleStateChecker checker) + where TState : IHasSimpleStateCheckers; + + public ISimpleStateChecker Deserialize(string value) + where TState : IHasSimpleStateCheckers; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs new file mode 100644 index 0000000000..b36c41347e --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/ISimpleStateCheckerSerializerContributor.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Nodes; +using JetBrains.Annotations; + +namespace Volo.Abp.SimpleStateChecking; + +public interface ISimpleStateCheckerSerializerContributor +{ + [CanBeNull] + public string SerializeToJson(ISimpleStateChecker checker) + where TState : IHasSimpleStateCheckers; + + [CanBeNull] + public ISimpleStateChecker Deserialize(JsonObject jsonObject) + where TState : IHasSimpleStateCheckers; +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs new file mode 100644 index 0000000000..5943dd4509 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializer.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Text.Json.Nodes; +using JetBrains.Annotations; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.SimpleStateChecking; + +public class SimpleStateCheckerSerializer : + ISimpleStateCheckerSerializer, + ITransientDependency +{ + private readonly IEnumerable _contributors; + + public SimpleStateCheckerSerializer(IEnumerable contributors) + { + _contributors = contributors; + } + + [CanBeNull] + public string Serialize(ISimpleStateChecker checker) + where TState : IHasSimpleStateCheckers + { + foreach (var contributor in _contributors) + { + var result = contributor.SerializeToJson(checker); + if (result != null) + { + return result; + } + } + + return null; + } + + [CanBeNull] + public ISimpleStateChecker Deserialize(string value) + where TState : IHasSimpleStateCheckers + { + var jsonObject = JsonNode.Parse(value) as JsonObject; + if (jsonObject == null) + { + throw new AbpException("The value is not a JSON object: " + value); + } + + foreach (var contributor in _contributors) + { + var result = contributor.Deserialize(jsonObject); + if (result != null) + { + return result; + } + } + + return null; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs new file mode 100644 index 0000000000..b43a3d9b98 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/SimpleStateChecking/SimpleStateCheckerSerializerExtensions.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Volo.Abp.SimpleStateChecking; + +public static class SimpleStateCheckerSerializerExtensions +{ + public static string Serialize( + this ISimpleStateCheckerSerializer serializer, + IList> stateCheckers) + where TState : IHasSimpleStateCheckers + { + switch (stateCheckers.Count) + { + case 0: + return null; + case 1: + return $"[{serializer.Serialize(stateCheckers.Single())}]"; + default: + var stringBuilder = new StringBuilder("["); + + for (var i = 0; i < stateCheckers.Count; i++) + { + if (i > 0) + { + stringBuilder.Append(","); + } + + stringBuilder.Append(serializer.Serialize(stateCheckers[i])); + } + + stringBuilder.Append("]"); + + return stringBuilder.ToString(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs new file mode 100644 index 0000000000..0ce0a0bfcd --- /dev/null +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor.cs @@ -0,0 +1,54 @@ +using System.Linq; +using System.Text.Json.Nodes; +using Volo.Abp.DependencyInjection; +using Volo.Abp.SimpleStateChecking; + +namespace Volo.Abp.GlobalFeatures; + +public class GlobalFeaturesSimpleStateCheckerSerializerContributor : + ISimpleStateCheckerSerializerContributor, + ITransientDependency +{ + public string SerializeToJson(ISimpleStateChecker checker) + where TState : IHasSimpleStateCheckers + { + if (checker is not RequireGlobalFeaturesSimpleStateChecker globalFeaturesSimpleStateChecker) + { + return null; + } + + var jsonObject = new JsonObject { + ["T"] = "GF", + ["A"] = globalFeaturesSimpleStateChecker.RequiresAll + }; + + var nameArray = new JsonArray(); + foreach (var globalFeatureName in globalFeaturesSimpleStateChecker.GlobalFeatureNames) + { + nameArray.Add(globalFeatureName); + } + + jsonObject["N"] = nameArray; + return jsonObject.ToJsonString(); + } + + public ISimpleStateChecker Deserialize(JsonObject jsonObject) + where TState : IHasSimpleStateCheckers + { + if (jsonObject["T"]?.ToString() != "GF") + { + return null; + } + + var nameArray = jsonObject["N"] as JsonArray; + if (nameArray == null) + { + throw new AbpException("'N' is not an array in the serialized state checker! JsonObject: " + jsonObject.ToJsonString()); + } + + return new RequireGlobalFeaturesSimpleStateChecker( + (bool?)jsonObject["A"] ?? false, + nameArray.Select(x => x.ToString()).ToArray() + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs index 77fdaa0e1e..d37aba8397 100644 --- a/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs +++ b/framework/src/Volo.Abp.GlobalFeatures/Volo/Abp/GlobalFeatures/RequireGlobalFeaturesSimpleStateChecker.cs @@ -8,8 +8,8 @@ namespace Volo.Abp.GlobalFeatures; public class RequireGlobalFeaturesSimpleStateChecker : ISimpleStateChecker where TState : IHasSimpleStateCheckers { - private readonly string[] _globalFeatureNames; - private readonly bool _requiresAll; + public string[] GlobalFeatureNames { get; } + public bool RequiresAll { get; } public RequireGlobalFeaturesSimpleStateChecker(params string[] globalFeatureNames) : this(true, globalFeatureNames) @@ -20,23 +20,23 @@ public class RequireGlobalFeaturesSimpleStateChecker : ISimpleStateCheck { Check.NotNullOrEmpty(globalFeatureNames, nameof(globalFeatureNames)); - _requiresAll = requiresAll; - _globalFeatureNames = globalFeatureNames; + RequiresAll = requiresAll; + GlobalFeatureNames = globalFeatureNames; } public RequireGlobalFeaturesSimpleStateChecker(bool requiresAll, params Type[] globalFeatureNames) { Check.NotNullOrEmpty(globalFeatureNames, nameof(globalFeatureNames)); - _requiresAll = requiresAll; - _globalFeatureNames = globalFeatureNames.Select(GlobalFeatureNameAttribute.GetName).ToArray(); + RequiresAll = requiresAll; + GlobalFeatureNames = globalFeatureNames.Select(GlobalFeatureNameAttribute.GetName).ToArray(); } public Task IsEnabledAsync(SimpleStateCheckerContext context) { - var isEnabled = _requiresAll - ? _globalFeatureNames.All(x => GlobalFeatureManager.Instance.IsEnabled(x)) - : _globalFeatureNames.Any(x => GlobalFeatureManager.Instance.IsEnabled(x)); + var isEnabled = RequiresAll + ? GlobalFeatureNames.All(x => GlobalFeatureManager.Instance.IsEnabled(x)) + : GlobalFeatureNames.Any(x => GlobalFeatureManager.Instance.IsEnabled(x)); return Task.FromResult(isEnabled); } diff --git a/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs new file mode 100644 index 0000000000..46064bd11e --- /dev/null +++ b/framework/test/Volo.Abp.GlobalFeatures.Tests/Volo/Abp/GlobalFeatures/GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Text.Json.Nodes; +using Shouldly; +using Volo.Abp.SimpleStateChecking; +using Xunit; + +namespace Volo.Abp.GlobalFeatures; + +public class GlobalFeaturesSimpleStateCheckerSerializerContributor_Tests +{ + [Fact] + public void Should_Serialize_RequireGlobalFeaturesSimpleStateChecker() + { + var serializer = new GlobalFeaturesSimpleStateCheckerSerializerContributor(); + + var result = serializer.SerializeToJson( + new RequireGlobalFeaturesSimpleStateChecker( + "FeatureA", + "FeatureB" + ) + ); + + result.ShouldBe("{\"T\":\"GF\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}"); + } + + [Fact] + public void Should_Deserialize_RequireGlobalFeaturesSimpleStateChecker() + { + var serializer = new GlobalFeaturesSimpleStateCheckerSerializerContributor(); + + var jsonObject = (JsonObject)JsonNode.Parse("{\"T\":\"GF\",\"A\":true,\"N\":[\"FeatureA\",\"FeatureB\"]}"); + var checker = serializer.Deserialize(jsonObject); + + checker.ShouldBeOfType>(); + var globalFeaturesSimpleStateChecker = checker as RequireGlobalFeaturesSimpleStateChecker; + globalFeaturesSimpleStateChecker.ShouldNotBeNull(); + globalFeaturesSimpleStateChecker.RequiresAll.ShouldBeTrue(); + globalFeaturesSimpleStateChecker.GlobalFeatureNames[0].ShouldBe("FeatureA"); + globalFeaturesSimpleStateChecker.GlobalFeatureNames[1].ShouldBe("FeatureB"); + } + + private class MyState : IHasSimpleStateCheckers + { + public List> StateCheckers { get; } = new(); + } +} \ No newline at end of file diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs index b2922f198e..47abbc97eb 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Domain/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Localization; using Volo.Abp.Authorization.Permissions; @@ -14,13 +15,16 @@ namespace Volo.Abp.PermissionManagement; public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, ITransientDependency { + protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; } protected IGuidGenerator GuidGenerator { get; } protected IStringLocalizerFactory StringLocalizerFactory { get; } public PermissionDefinitionSerializer( IGuidGenerator guidGenerator, - IStringLocalizerFactory stringLocalizerFactory) + IStringLocalizerFactory stringLocalizerFactory, + ISimpleStateCheckerSerializer stateCheckerSerializer) { + StateCheckerSerializer = stateCheckerSerializer; GuidGenerator = guidGenerator; StringLocalizerFactory = stringLocalizerFactory; } @@ -52,7 +56,8 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I } } - public Task DeserializeAsync(PermissionDefinitionRecord permissionRecord) + public Task DeserializeAsync( + PermissionDefinitionRecord permissionRecord) { throw new System.NotImplementedException(); } @@ -67,16 +72,9 @@ public class PermissionDefinitionSerializer : IPermissionDefinitionSerializer, I throw new System.NotImplementedException(); } - protected virtual string SerializeStateCheckers(IEnumerable> stateCheckers) + protected virtual string SerializeStateCheckers(List> stateCheckers) { - //TODO: Serialize state checker - - if(!stateCheckers.Any()) - { - return null; - } - - return null; + return StateCheckerSerializer.Serialize(stateCheckers); } protected virtual string SerializeProviders(ICollection providers) diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj index 9b0cd2f1c0..8e77b2d56c 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo.Abp.PermissionManagement.Domain.Tests.csproj @@ -12,6 +12,8 @@ + + diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementTestModule.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementTestModule.cs index 24945f83b0..bf32af126d 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementTestModule.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/AbpPermissionManagementTestModule.cs @@ -2,6 +2,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EntityFrameworkCore; +using Volo.Abp.Features; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.EntityFrameworkCore; using Volo.Abp.Uow; @@ -10,7 +12,10 @@ namespace Volo.Abp.PermissionManagement; [DependsOn( typeof(AbpPermissionManagementEntityFrameworkCoreModule), - typeof(AbpPermissionManagementTestBaseModule))] + typeof(AbpPermissionManagementTestBaseModule), + typeof(AbpFeaturesModule), + typeof(AbpGlobalFeaturesModule) + )] public class AbpPermissionManagementTestModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs index 36de460470..35a7961e11 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.Domain.Tests/Volo/Abp/PermissionManagement/PermissionDefinitionSerializer_Tests.cs @@ -2,6 +2,8 @@ using Shouldly; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Data; +using Volo.Abp.Features; +using Volo.Abp.GlobalFeatures; using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Xunit; @@ -30,7 +32,8 @@ public class PermissionDefinitionSerializer_Tests : PermissionTestBase MultiTenancySides.Tenant ) .WithProviders("ProviderA", "ProviderB") - .WithProperty("CustomProperty2", "CustomValue2"); + .WithProperty("CustomProperty2", "CustomValue2") + .RequireGlobalFeatures("GlobalFeature1", "GlobalFeature2"); // Act @@ -47,6 +50,7 @@ public class PermissionDefinitionSerializer_Tests : PermissionTestBase permissionRecord.GetProperty("CustomProperty2").ShouldBe("CustomValue2"); permissionRecord.Providers.ShouldBe("ProviderA,ProviderB"); permissionRecord.MultiTenancySide.ShouldBe(MultiTenancySides.Tenant); + permissionRecord.StateCheckers.ShouldBe("[{\"T\":\"GF\",\"A\":true,\"N\":[\"GlobalFeature1\",\"GlobalFeature2\"]}]"); } private static PermissionGroupDefinition CreatePermissionGroup1(