mirror of https://github.com/abpframework/abp.git
14 changed files with 264 additions and 12 deletions
@ -0,0 +1,77 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.SimpleStateChecking; |
||||
|
|
||||
|
namespace Volo.Abp.Features; |
||||
|
|
||||
|
public class RequireFeaturesSimpleBatchStateChecker<TState> : SimpleBatchStateCheckerBase<TState> |
||||
|
where TState : IHasSimpleStateCheckers<TState> |
||||
|
{ |
||||
|
public static RequireFeaturesSimpleBatchStateChecker<TState> Current => _current.Value!; |
||||
|
private static readonly AsyncLocal<RequireFeaturesSimpleBatchStateChecker<TState>> _current = new(); |
||||
|
|
||||
|
private readonly List<RequireFeaturesSimpleBatchStateCheckerModel<TState>> _models; |
||||
|
|
||||
|
static RequireFeaturesSimpleBatchStateChecker() |
||||
|
{ |
||||
|
_current.Value = new RequireFeaturesSimpleBatchStateChecker<TState>(); |
||||
|
} |
||||
|
|
||||
|
public RequireFeaturesSimpleBatchStateChecker() |
||||
|
{ |
||||
|
_models = new List<RequireFeaturesSimpleBatchStateCheckerModel<TState>>(); |
||||
|
} |
||||
|
|
||||
|
public RequireFeaturesSimpleBatchStateChecker<TState> AddCheckModels( |
||||
|
params RequireFeaturesSimpleBatchStateCheckerModel<TState>[] models) |
||||
|
{ |
||||
|
Check.NotNullOrEmpty(models, nameof(models)); |
||||
|
|
||||
|
_models.AddRange(models); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public static IDisposable Use(RequireFeaturesSimpleBatchStateChecker<TState> checker) |
||||
|
{ |
||||
|
var previousValue = Current; |
||||
|
_current.Value = checker; |
||||
|
return new DisposeAction(() => _current.Value = previousValue); |
||||
|
} |
||||
|
|
||||
|
public override async Task<SimpleStateCheckerResult<TState>> IsEnabledAsync( |
||||
|
SimpleBatchStateCheckerContext<TState> context) |
||||
|
{ |
||||
|
var featureChecker = context.ServiceProvider.GetRequiredService<IFeatureChecker>(); |
||||
|
|
||||
|
var result = new SimpleStateCheckerResult<TState>(context.States); |
||||
|
|
||||
|
var relevantModels = _models |
||||
|
.Where(x => context.States.Any(s => s.Equals(x.State))) |
||||
|
.ToList(); |
||||
|
|
||||
|
var features = relevantModels.SelectMany(x => x.FeatureNames).Distinct().ToArray(); |
||||
|
var featureValues = await featureChecker.IsEnabledAsync(features); |
||||
|
|
||||
|
foreach (var state in context.States) |
||||
|
{ |
||||
|
var model = relevantModels.FirstOrDefault(x => x.State.Equals(state)); |
||||
|
if (model != null) |
||||
|
{ |
||||
|
if (model.RequiresAll) |
||||
|
{ |
||||
|
result[state] = model.FeatureNames.All(x => featureValues.TryGetValue(x, out var v) && v); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result[state] = model.FeatureNames.Any(x => featureValues.TryGetValue(x, out var v) && v); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using Volo.Abp.SimpleStateChecking; |
||||
|
|
||||
|
namespace Volo.Abp.Features; |
||||
|
|
||||
|
public class RequireFeaturesSimpleBatchStateCheckerModel<TState> |
||||
|
where TState : IHasSimpleStateCheckers<TState> |
||||
|
{ |
||||
|
public TState State { get; } |
||||
|
|
||||
|
public string[] FeatureNames { get; } |
||||
|
|
||||
|
public bool RequiresAll { get; } |
||||
|
|
||||
|
public RequireFeaturesSimpleBatchStateCheckerModel(TState state, string[] featureNames, bool requiresAll = true) |
||||
|
{ |
||||
|
Check.NotNull(state, nameof(state)); |
||||
|
Check.NotNullOrEmpty(featureNames, nameof(featureNames)); |
||||
|
|
||||
|
State = state; |
||||
|
FeatureNames = featureNames; |
||||
|
RequiresAll = requiresAll; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.MultiTenancy; |
||||
|
using Volo.Abp.SimpleStateChecking; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.Features; |
||||
|
|
||||
|
public class RequireFeaturesSimpleBatchStateChecker_Tests : FeatureTestBase |
||||
|
{ |
||||
|
private readonly ISimpleStateCheckerManager<MyStateEntity> _simpleStateCheckerManager; |
||||
|
private readonly ICurrentTenant _currentTenant; |
||||
|
|
||||
|
public RequireFeaturesSimpleBatchStateChecker_Tests() |
||||
|
{ |
||||
|
_simpleStateCheckerManager = GetRequiredService<ISimpleStateCheckerManager<MyStateEntity>>(); |
||||
|
_currentTenant = GetRequiredService<ICurrentTenant>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Switch_Current_Checker_Test() |
||||
|
{ |
||||
|
var checker = RequireFeaturesSimpleBatchStateChecker<MyStateEntity2>.Current; |
||||
|
checker.ShouldNotBeNull(); |
||||
|
|
||||
|
RequireFeaturesSimpleBatchStateChecker<MyStateEntity2> checker2 = null; |
||||
|
|
||||
|
using (RequireFeaturesSimpleBatchStateChecker<MyStateEntity2>.Use(new RequireFeaturesSimpleBatchStateChecker<MyStateEntity2>())) |
||||
|
{ |
||||
|
checker2 = RequireFeaturesSimpleBatchStateChecker<MyStateEntity2>.Current; |
||||
|
checker2.ShouldNotBeNull(); |
||||
|
checker2.ShouldNotBe(checker); |
||||
|
} |
||||
|
|
||||
|
checker2.ShouldNotBeNull(); |
||||
|
checker2.ShouldNotBe(checker); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task RequireFeaturesSimpleBatchStateChecker_Test() |
||||
|
{ |
||||
|
// Tenant1: BooleanTestFeature1=true, BooleanTestFeature2=true
|
||||
|
// Tenant2: no boolean features set → false
|
||||
|
using (_currentTenant.Change(TestFeatureStore.Tenant1Id)) |
||||
|
{ |
||||
|
var myStateEntities = new MyStateEntity[] |
||||
|
{ |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: true, batchCheck: true, "BooleanTestFeature1"), |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: true, batchCheck: true, "BooleanTestFeature2"), |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: true, batchCheck: true, "BooleanTestFeature1", "BooleanTestFeature2"), |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: true, batchCheck: true, "BooleanTestFeature1", "BooleanTestFeature2"), |
||||
|
}; |
||||
|
|
||||
|
var result = await _simpleStateCheckerManager.IsEnabledAsync(myStateEntities); |
||||
|
|
||||
|
result.Count.ShouldBe(myStateEntities.Length); |
||||
|
|
||||
|
result[myStateEntities[0]].ShouldBeTrue(); |
||||
|
result[myStateEntities[1]].ShouldBeTrue(); |
||||
|
result[myStateEntities[2]].ShouldBeTrue(); |
||||
|
result[myStateEntities[3]].ShouldBeTrue(); |
||||
|
} |
||||
|
|
||||
|
using (_currentTenant.Change(TestFeatureStore.Tenant2Id)) |
||||
|
{ |
||||
|
var myStateEntities = new MyStateEntity[] |
||||
|
{ |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: true, batchCheck: true, "BooleanTestFeature1"), |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: true, batchCheck: true, "BooleanTestFeature2"), |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: true, batchCheck: true, "BooleanTestFeature1", "BooleanTestFeature2"), |
||||
|
new MyStateEntity().RequireFeatures(requiresAll: false, batchCheck: true, "BooleanTestFeature1", "BooleanTestFeature2"), |
||||
|
}; |
||||
|
|
||||
|
var result = await _simpleStateCheckerManager.IsEnabledAsync(myStateEntities); |
||||
|
|
||||
|
result.Count.ShouldBe(myStateEntities.Length); |
||||
|
|
||||
|
result[myStateEntities[0]].ShouldBeFalse(); |
||||
|
result[myStateEntities[1]].ShouldBeFalse(); |
||||
|
result[myStateEntities[2]].ShouldBeFalse(); |
||||
|
result[myStateEntities[3]].ShouldBeFalse(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class MyStateEntity : IHasSimpleStateCheckers<MyStateEntity> |
||||
|
{ |
||||
|
public List<ISimpleStateChecker<MyStateEntity>> StateCheckers { get; } |
||||
|
|
||||
|
public MyStateEntity() |
||||
|
{ |
||||
|
StateCheckers = new List<ISimpleStateChecker<MyStateEntity>>(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class MyStateEntity2 : IHasSimpleStateCheckers<MyStateEntity2> |
||||
|
{ |
||||
|
public List<ISimpleStateChecker<MyStateEntity2>> StateCheckers { get; } |
||||
|
|
||||
|
public MyStateEntity2() |
||||
|
{ |
||||
|
StateCheckers = new List<ISimpleStateChecker<MyStateEntity2>>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue