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