Browse Source

Rename Volo.Abp.OperationRateLimit to Volo.Abp.OperationRateLimiting

pull/25024/head
maliming 4 weeks ago
parent
commit
b92eda2419
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 4
      framework/Volo.Abp.slnx
  2. 8
      framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/AbpOperationRateLimitResource.cs
  3. 14
      framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitChecker.cs
  4. 8
      framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitFormatter.cs
  5. 11
      framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitPolicyProvider.cs
  6. 12
      framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitRule.cs
  7. 13
      framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitStore.cs
  8. 16
      framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitRuleDefinition.cs
  9. 0
      framework/src/Volo.Abp.OperationRateLimiting/FodyWeavers.xml
  10. 8
      framework/src/Volo.Abp.OperationRateLimiting/Volo.Abp.OperationRateLimiting.csproj
  11. 6
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingErrorCodes.cs
  12. 14
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingModule.cs
  13. 10
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingOptions.cs
  14. 8
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingResource.cs
  15. 14
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/IOperationRateLimitingChecker.cs
  16. 64
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingChecker.cs
  17. 6
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingContext.cs
  18. 6
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingResult.cs
  19. 4
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingRuleResult.cs
  20. 12
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Exceptions/AbpOperationRateLimitingException.cs
  21. 12
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Formatting/DefaultOperationRateLimitingFormatter.cs
  22. 8
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Formatting/IOperationRateLimitingFormatter.cs
  23. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/ar.json
  24. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/cs.json
  25. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/de.json
  26. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/el.json
  27. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/en-GB.json
  28. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/en.json
  29. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/es.json
  30. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/fa.json
  31. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/fi.json
  32. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/fr.json
  33. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/hi.json
  34. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/hr.json
  35. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/hu.json
  36. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/is.json
  37. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/it.json
  38. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/nl.json
  39. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/pl-PL.json
  40. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/pt-BR.json
  41. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/ro-RO.json
  42. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/ru.json
  43. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/sk.json
  44. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/sl.json
  45. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/sv.json
  46. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/tr.json
  47. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/vi.json
  48. 2
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/zh-Hans.json
  49. 14
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/DefaultOperationRateLimitingPolicyProvider.cs
  50. 11
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/IOperationRateLimitingPolicyProvider.cs
  51. 4
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingPartitionType.cs
  52. 6
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingPolicy.cs
  53. 34
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingPolicyBuilder.cs
  54. 52
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleBuilder.cs
  55. 16
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleDefinition.cs
  56. 46
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/FixedWindowOperationRateLimitingRule.cs
  57. 12
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/IOperationRateLimitingRule.cs
  58. 38
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/DistributedCacheOperationRateLimitingStore.cs
  59. 13
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/IOperationRateLimitingStore.cs
  60. 6
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/OperationRateLimitingCacheItem.cs
  61. 4
      framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/OperationRateLimitingStoreResult.cs
  62. 2
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo.Abp.OperationRateLimiting.Tests.csproj
  63. 26
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingException_Tests.cs
  64. 20
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingPhase2RaceTestModule.cs
  65. 8
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingTestModule.cs
  66. 10
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/DistributedCacheOperationRateLimitingStore_Tests.cs
  67. 36
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingCheckerFixes_Tests.cs
  68. 130
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingChecker_Tests.cs
  69. 38
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingFrontendIntegration_Tests.cs
  70. 28
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingMultiTenant_Tests.cs
  71. 54
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingPolicyBuilder_Tests.cs
  72. 4
      framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingTestBase.cs

4
framework/Volo.Abp.slnx

@ -169,7 +169,7 @@
<Project Path="src/Volo.Abp.TickerQ/Volo.Abp.TickerQ.csproj" />
<Project Path="src/Volo.Abp.BackgroundJobs.TickerQ/Volo.Abp.BackgroundJobs.TickerQ.csproj" />
<Project Path="src/Volo.Abp.BackgroundWorkers.TickerQ/Volo.Abp.BackgroundWorkers.TickerQ.csproj" />
<Project Path="src/Volo.Abp.OperationRateLimit/Volo.Abp.OperationRateLimit.csproj" />
<Project Path="src/Volo.Abp.OperationRateLimiting/Volo.Abp.OperationRateLimiting.csproj" />
</Folder>
<Folder Name="/test/">
<Project Path="test/AbpTestBase/AbpTestBase.csproj" />
@ -257,6 +257,6 @@
<Project Path="test/Volo.Abp.Uow.Tests/Volo.Abp.Uow.Tests.csproj" />
<Project Path="test/Volo.Abp.Validation.Tests/Volo.Abp.Validation.Tests.csproj" />
<Project Path="test/Volo.Abp.VirtualFileSystem.Tests/Volo.Abp.VirtualFileSystem.Tests.csproj" />
<Project Path="test/Volo.Abp.OperationRateLimit.Tests/Volo.Abp.OperationRateLimit.Tests.csproj" />
<Project Path="test/Volo.Abp.OperationRateLimiting.Tests/Volo.Abp.OperationRateLimiting.Tests.csproj" />
</Folder>
</Solution>

8
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/AbpOperationRateLimitResource.cs

@ -1,8 +0,0 @@
using Volo.Abp.Localization;
namespace Volo.Abp.OperationRateLimit;
[LocalizationResourceName("AbpOperationRateLimit")]
public class AbpOperationRateLimitResource
{
}

14
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitChecker.cs

@ -1,14 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimit;
public interface IOperationRateLimitChecker
{
Task CheckAsync(string policyName, OperationRateLimitContext? context = null);
Task<bool> IsAllowedAsync(string policyName, OperationRateLimitContext? context = null);
Task<OperationRateLimitResult> GetStatusAsync(string policyName, OperationRateLimitContext? context = null);
Task ResetAsync(string policyName, OperationRateLimitContext? context = null);
}

8
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitFormatter.cs

@ -1,8 +0,0 @@
using System;
namespace Volo.Abp.OperationRateLimit;
public interface IOperationRateLimitFormatter
{
string Format(TimeSpan duration);
}

11
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitPolicyProvider.cs

@ -1,11 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimit;
public interface IOperationRateLimitPolicyProvider
{
Task<OperationRateLimitPolicy> GetAsync(string policyName);
Task<List<OperationRateLimitPolicy>> GetListAsync();
}

12
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitRule.cs

@ -1,12 +0,0 @@
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimit;
public interface IOperationRateLimitRule
{
Task<OperationRateLimitRuleResult> AcquireAsync(OperationRateLimitContext context);
Task<OperationRateLimitRuleResult> CheckAsync(OperationRateLimitContext context);
Task ResetAsync(OperationRateLimitContext context);
}

13
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/IOperationRateLimitStore.cs

@ -1,13 +0,0 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimit;
public interface IOperationRateLimitStore
{
Task<OperationRateLimitStoreResult> IncrementAsync(string key, TimeSpan duration, int maxCount);
Task<OperationRateLimitStoreResult> GetAsync(string key, TimeSpan duration, int maxCount);
Task ResetAsync(string key);
}

16
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitRuleDefinition.cs

@ -1,16 +0,0 @@
using System;
namespace Volo.Abp.OperationRateLimit;
public class OperationRateLimitRuleDefinition
{
public TimeSpan Duration { get; set; }
public int MaxCount { get; set; }
public OperationRateLimitPartitionType PartitionType { get; set; }
public Func<OperationRateLimitContext, string>? CustomPartitionKeyResolver { get; set; }
public bool IsMultiTenant { get; set; }
}

0
framework/src/Volo.Abp.OperationRateLimit/FodyWeavers.xml → framework/src/Volo.Abp.OperationRateLimiting/FodyWeavers.xml

8
framework/src/Volo.Abp.OperationRateLimit/Volo.Abp.OperationRateLimit.csproj → framework/src/Volo.Abp.OperationRateLimiting/Volo.Abp.OperationRateLimiting.csproj

@ -7,8 +7,8 @@
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<AssemblyName>Volo.Abp.OperationRateLimit</AssemblyName>
<PackageId>Volo.Abp.OperationRateLimit</PackageId>
<AssemblyName>Volo.Abp.OperationRateLimiting</AssemblyName>
<PackageId>Volo.Abp.OperationRateLimiting</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
@ -17,8 +17,8 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Volo\Abp\OperationRateLimit\Localization\*.json" />
<EmbeddedResource Include="Volo\Abp\OperationRateLimit\Localization\*.json" />
<None Remove="Volo\Abp\OperationRateLimiting\Localization\*.json" />
<EmbeddedResource Include="Volo\Abp\OperationRateLimiting\Localization\*.json" />
</ItemGroup>
<ItemGroup>

6
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/AbpOperationRateLimitErrorCodes.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingErrorCodes.cs

@ -1,9 +1,9 @@
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public static class AbpOperationRateLimitErrorCodes
public static class AbpOperationRateLimitingErrorCodes
{
/// <summary>
/// Default error code for rate limit exceeded.
/// </summary>
public const string ExceedLimit = "Volo.Abp.OperationRateLimit:010001";
public const string ExceedLimit = "Volo.Abp.OperationRateLimiting:010001";
}

14
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/AbpOperationRateLimitModule.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingModule.cs

@ -7,7 +7,7 @@ using Volo.Abp.Modularity;
using Volo.Abp.Security;
using Volo.Abp.VirtualFileSystem;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
[DependsOn(
typeof(AbpCachingModule),
@ -16,27 +16,27 @@ namespace Volo.Abp.OperationRateLimit;
typeof(AbpAspNetCoreAbstractionsModule),
typeof(AbpDistributedLockingAbstractionsModule)
)]
public class AbpOperationRateLimitModule : AbpModule
public class AbpOperationRateLimitingModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpOperationRateLimitModule>();
options.FileSets.AddEmbedded<AbpOperationRateLimitingModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<AbpOperationRateLimitResource>("en")
.AddVirtualJson("/Volo/Abp/OperationRateLimit/Localization");
.Add<AbpOperationRateLimitingResource>("en")
.AddVirtualJson("/Volo/Abp/OperationRateLimiting/Localization");
});
Configure<AbpExceptionLocalizationOptions>(options =>
{
options.MapCodeNamespace(
"Volo.Abp.OperationRateLimit",
typeof(AbpOperationRateLimitResource));
"Volo.Abp.OperationRateLimiting",
typeof(AbpOperationRateLimitingResource));
});
}
}

10
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/AbpOperationRateLimitOptions.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingOptions.cs

@ -1,19 +1,19 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class AbpOperationRateLimitOptions
public class AbpOperationRateLimitingOptions
{
public bool IsEnabled { get; set; } = true;
public TimeSpan LockTimeout { get; set; } = TimeSpan.FromSeconds(5);
public Dictionary<string, OperationRateLimitPolicy> Policies { get; } = new();
public Dictionary<string, OperationRateLimitingPolicy> Policies { get; } = new();
public void AddPolicy(string name, Action<OperationRateLimitPolicyBuilder> configure)
public void AddPolicy(string name, Action<OperationRateLimitingPolicyBuilder> configure)
{
var builder = new OperationRateLimitPolicyBuilder(name);
var builder = new OperationRateLimitingPolicyBuilder(name);
configure(builder);
Policies[name] = builder.Build();
}

8
framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingResource.cs

@ -0,0 +1,8 @@
using Volo.Abp.Localization;
namespace Volo.Abp.OperationRateLimiting;
[LocalizationResourceName("AbpOperationRateLimiting")]
public class AbpOperationRateLimitingResource
{
}

14
framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/IOperationRateLimitingChecker.cs

@ -0,0 +1,14 @@
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimiting;
public interface IOperationRateLimitingChecker
{
Task CheckAsync(string policyName, OperationRateLimitingContext? context = null);
Task<bool> IsAllowedAsync(string policyName, OperationRateLimitingContext? context = null);
Task<OperationRateLimitingResult> GetStatusAsync(string policyName, OperationRateLimitingContext? context = null);
Task ResetAsync(string policyName, OperationRateLimitingContext? context = null);
}

64
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitChecker.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingChecker.cs

@ -9,23 +9,23 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientDependency
public class OperationRateLimitingChecker : IOperationRateLimitingChecker, ITransientDependency
{
protected AbpOperationRateLimitOptions Options { get; }
protected IOperationRateLimitPolicyProvider PolicyProvider { get; }
protected AbpOperationRateLimitingOptions Options { get; }
protected IOperationRateLimitingPolicyProvider PolicyProvider { get; }
protected IServiceProvider ServiceProvider { get; }
protected IOperationRateLimitStore Store { get; }
protected IOperationRateLimitingStore Store { get; }
protected ICurrentUser CurrentUser { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IWebClientInfoProvider WebClientInfoProvider { get; }
public OperationRateLimitChecker(
IOptions<AbpOperationRateLimitOptions> options,
IOperationRateLimitPolicyProvider policyProvider,
public OperationRateLimitingChecker(
IOptions<AbpOperationRateLimitingOptions> options,
IOperationRateLimitingPolicyProvider policyProvider,
IServiceProvider serviceProvider,
IOperationRateLimitStore store,
IOperationRateLimitingStore store,
ICurrentUser currentUser,
ICurrentTenant currentTenant,
IWebClientInfoProvider webClientInfoProvider)
@ -39,7 +39,7 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
WebClientInfoProvider = webClientInfoProvider;
}
public virtual async Task CheckAsync(string policyName, OperationRateLimitContext? context = null)
public virtual async Task CheckAsync(string policyName, OperationRateLimitingContext? context = null)
{
if (!Options.IsEnabled)
{
@ -52,7 +52,7 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
// Phase 1: Check ALL rules without incrementing to get complete status.
// Do not exit early: a later rule may have a larger RetryAfter that the caller needs to know about.
var checkResults = new List<OperationRateLimitRuleResult>();
var checkResults = new List<OperationRateLimitingRuleResult>();
foreach (var rule in rules)
{
checkResults.Add(await rule.CheckAsync(context));
@ -68,7 +68,7 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
// Phase 2: All rules pass - now increment all counters.
// Also guard against a concurrent race where another request consumed the last quota
// between Phase 1 and Phase 2.
var incrementResults = new List<OperationRateLimitRuleResult>();
var incrementResults = new List<OperationRateLimitingRuleResult>();
foreach (var rule in rules)
{
incrementResults.Add(await rule.AcquireAsync(context));
@ -81,7 +81,7 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
}
}
public virtual async Task<bool> IsAllowedAsync(string policyName, OperationRateLimitContext? context = null)
public virtual async Task<bool> IsAllowedAsync(string policyName, OperationRateLimitingContext? context = null)
{
if (!Options.IsEnabled)
{
@ -104,11 +104,11 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
return true;
}
public virtual async Task<OperationRateLimitResult> GetStatusAsync(string policyName, OperationRateLimitContext? context = null)
public virtual async Task<OperationRateLimitingResult> GetStatusAsync(string policyName, OperationRateLimitingContext? context = null)
{
if (!Options.IsEnabled)
{
return new OperationRateLimitResult
return new OperationRateLimitingResult
{
IsAllowed = true,
RemainingCount = int.MaxValue,
@ -120,7 +120,7 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
context = EnsureContext(context);
var policy = await PolicyProvider.GetAsync(policyName);
var rules = CreateRules(policy);
var ruleResults = new List<OperationRateLimitRuleResult>();
var ruleResults = new List<OperationRateLimitingRuleResult>();
foreach (var rule in rules)
{
@ -130,7 +130,7 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
return AggregateResults(ruleResults, policy);
}
public virtual async Task ResetAsync(string policyName, OperationRateLimitContext? context = null)
public virtual async Task ResetAsync(string policyName, OperationRateLimitingContext? context = null)
{
context = EnsureContext(context);
var policy = await PolicyProvider.GetAsync(policyName);
@ -142,20 +142,20 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
}
}
protected virtual OperationRateLimitContext EnsureContext(OperationRateLimitContext? context)
protected virtual OperationRateLimitingContext EnsureContext(OperationRateLimitingContext? context)
{
context ??= new OperationRateLimitContext();
context ??= new OperationRateLimitingContext();
context.ServiceProvider = ServiceProvider;
return context;
}
protected virtual List<IOperationRateLimitRule> CreateRules(OperationRateLimitPolicy policy)
protected virtual List<IOperationRateLimitingRule> CreateRules(OperationRateLimitingPolicy policy)
{
var rules = new List<IOperationRateLimitRule>();
var rules = new List<IOperationRateLimitingRule>();
for (var i = 0; i < policy.Rules.Count; i++)
{
rules.Add(new FixedWindowOperationRateLimitRule(
rules.Add(new FixedWindowOperationRateLimitingRule(
policy.Name,
i,
policy.Rules[i],
@ -167,15 +167,15 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
foreach (var customRuleType in policy.CustomRuleTypes)
{
rules.Add((IOperationRateLimitRule)ServiceProvider.GetRequiredService(customRuleType));
rules.Add((IOperationRateLimitingRule)ServiceProvider.GetRequiredService(customRuleType));
}
return rules;
}
protected virtual OperationRateLimitResult AggregateResults(
List<OperationRateLimitRuleResult> ruleResults,
OperationRateLimitPolicy policy)
protected virtual OperationRateLimitingResult AggregateResults(
List<OperationRateLimitingRuleResult> ruleResults,
OperationRateLimitingPolicy policy)
{
var isAllowed = ruleResults.All(r => r.IsAllowed);
var mostRestrictive = ruleResults
@ -183,7 +183,7 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
.ThenByDescending(r => r.RetryAfter ?? TimeSpan.Zero)
.First();
return new OperationRateLimitResult
return new OperationRateLimitingResult
{
IsAllowed = isAllowed,
RemainingCount = mostRestrictive.RemainingCount,
@ -201,13 +201,13 @@ public class OperationRateLimitChecker : IOperationRateLimitChecker, ITransientD
}
protected virtual void ThrowRateLimitException(
OperationRateLimitPolicy policy,
OperationRateLimitResult result,
OperationRateLimitContext context)
OperationRateLimitingPolicy policy,
OperationRateLimitingResult result,
OperationRateLimitingContext context)
{
var formatter = context.ServiceProvider.GetRequiredService<IOperationRateLimitFormatter>();
var formatter = context.ServiceProvider.GetRequiredService<IOperationRateLimitingFormatter>();
var exception = new AbpOperationRateLimitException(
var exception = new AbpOperationRateLimitingException(
policy.Name,
result,
policy.ErrorCode);

6
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitContext.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingContext.cs

@ -2,9 +2,9 @@ using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitContext
public class OperationRateLimitingContext
{
/// <summary>
/// Optional parameter passed by the caller.
@ -14,7 +14,7 @@ public class OperationRateLimitContext
public string? Parameter { get; set; }
/// <summary>
/// Additional properties that can be read by custom <see cref="IOperationRateLimitRule"/> implementations
/// Additional properties that can be read by custom <see cref="IOperationRateLimitingRule"/> implementations
/// and are forwarded to the exception's Data dictionary when the rate limit is exceeded.
/// </summary>
public Dictionary<string, object?> ExtraProperties { get; set; } = new();

6
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitResult.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingResult.cs

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitResult
public class OperationRateLimitingResult
{
public bool IsAllowed { get; set; }
@ -20,5 +20,5 @@ public class OperationRateLimitResult
/// <summary>
/// Detailed results per rule (for composite policies).
/// </summary>
public List<OperationRateLimitRuleResult>? RuleResults { get; set; }
public List<OperationRateLimitingRuleResult>? RuleResults { get; set; }
}

4
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitRuleResult.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Checker/OperationRateLimitingRuleResult.cs

@ -1,8 +1,8 @@
using System;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitRuleResult
public class OperationRateLimitingRuleResult
{
public string RuleName { get; set; } = default!;

12
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/AbpOperationRateLimitException.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Exceptions/AbpOperationRateLimitingException.cs

@ -1,21 +1,21 @@
using System;
using Volo.Abp.ExceptionHandling;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class AbpOperationRateLimitException : BusinessException, IHasHttpStatusCode
public class AbpOperationRateLimitingException : BusinessException, IHasHttpStatusCode
{
public string PolicyName { get; }
public OperationRateLimitResult Result { get; }
public OperationRateLimitingResult Result { get; }
public int HttpStatusCode => 429;
public AbpOperationRateLimitException(
public AbpOperationRateLimitingException(
string policyName,
OperationRateLimitResult result,
OperationRateLimitingResult result,
string? errorCode = null)
: base(code: errorCode ?? AbpOperationRateLimitErrorCodes.ExceedLimit)
: base(code: errorCode ?? AbpOperationRateLimitingErrorCodes.ExceedLimit)
{
PolicyName = policyName;
Result = result;

12
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/DefaultOperationRateLimitFormatter.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Formatting/DefaultOperationRateLimitingFormatter.cs

@ -2,15 +2,15 @@ using System;
using Microsoft.Extensions.Localization;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class DefaultOperationRateLimitFormatter
: IOperationRateLimitFormatter, ITransientDependency
public class DefaultOperationRateLimitingFormatter
: IOperationRateLimitingFormatter, ITransientDependency
{
protected IStringLocalizer<AbpOperationRateLimitResource> Localizer { get; }
protected IStringLocalizer<AbpOperationRateLimitingResource> Localizer { get; }
public DefaultOperationRateLimitFormatter(
IStringLocalizer<AbpOperationRateLimitResource> localizer)
public DefaultOperationRateLimitingFormatter(
IStringLocalizer<AbpOperationRateLimitingResource> localizer)
{
Localizer = localizer;
}

8
framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Formatting/IOperationRateLimitingFormatter.cs

@ -0,0 +1,8 @@
using System;
namespace Volo.Abp.OperationRateLimiting;
public interface IOperationRateLimitingFormatter
{
string Format(TimeSpan duration);
}

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/ar.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/ar.json

@ -1,7 +1,7 @@
{
"culture": "ar",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "تم تجاوز حد معدل العملية. يمكنك المحاولة مرة أخرى بعد {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "تم تجاوز حد معدل العملية. يمكنك المحاولة مرة أخرى بعد {RetryAfter}.",
"RetryAfter:Years": "{0} سنة/سنوات",
"RetryAfter:YearsAndMonths": "{0} سنة/سنوات و {1} شهر/أشهر",
"RetryAfter:Months": "{0} شهر/أشهر",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/cs.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/cs.json

@ -1,7 +1,7 @@
{
"culture": "cs",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Překročen limit rychlosti operace. Můžete to zkusit znovu za {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Překročen limit rychlosti operace. Můžete to zkusit znovu za {RetryAfter}.",
"RetryAfter:Years": "{0} rok(y/let)",
"RetryAfter:YearsAndMonths": "{0} rok(y/let) a {1} měsíc(e/ů)",
"RetryAfter:Months": "{0} měsíc(e/ů)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/de.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/de.json

@ -1,7 +1,7 @@
{
"culture": "de",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Betriebsratenlimit überschritten. Sie können es nach {RetryAfter} erneut versuchen.",
"Volo.Abp.OperationRateLimiting:010001": "Betriebsratenlimit überschritten. Sie können es nach {RetryAfter} erneut versuchen.",
"RetryAfter:Years": "{0} Jahr(e)",
"RetryAfter:YearsAndMonths": "{0} Jahr(e) und {1} Monat(e)",
"RetryAfter:Months": "{0} Monat(e)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/el.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/el.json

@ -1,7 +1,7 @@
{
"culture": "el",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Υπέρβαση ορίου ρυθμού λειτουργίας. Μπορείτε να δοκιμάσετε ξανά μετά από {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Υπέρβαση ορίου ρυθμού λειτουργίας. Μπορείτε να δοκιμάσετε ξανά μετά από {RetryAfter}.",
"RetryAfter:Years": "{0} έτος/η",
"RetryAfter:YearsAndMonths": "{0} έτος/η και {1} μήνας/ες",
"RetryAfter:Months": "{0} μήνας/ες",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/en-GB.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/en-GB.json

@ -1,7 +1,7 @@
{
"culture": "en-GB",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Operation rate limit exceeded. You can try again after {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Operation rate limit exceeded. You can try again after {RetryAfter}.",
"RetryAfter:Years": "{0} year(s)",
"RetryAfter:YearsAndMonths": "{0} year(s) and {1} month(s)",
"RetryAfter:Months": "{0} month(s)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/en.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/en.json

@ -1,7 +1,7 @@
{
"culture": "en",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Operation rate limit exceeded. You can try again after {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Operation rate limit exceeded. You can try again after {RetryAfter}.",
"RetryAfter:Years": "{0} year(s)",
"RetryAfter:YearsAndMonths": "{0} year(s) and {1} month(s)",
"RetryAfter:Months": "{0} month(s)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/es.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/es.json

@ -1,7 +1,7 @@
{
"culture": "es",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Se ha excedido el límite de tasa de operación. Puede intentarlo de nuevo después de {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Se ha excedido el límite de tasa de operación. Puede intentarlo de nuevo después de {RetryAfter}.",
"RetryAfter:Years": "{0} año(s)",
"RetryAfter:YearsAndMonths": "{0} año(s) y {1} mes(es)",
"RetryAfter:Months": "{0} mes(es)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/fa.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/fa.json

@ -1,7 +1,7 @@
{
"culture": "fa",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "محدودیت نرخ عملیات فراتر رفته است. می‌توانید بعد از {RetryAfter} دوباره تلاش کنید.",
"Volo.Abp.OperationRateLimiting:010001": "محدودیت نرخ عملیات فراتر رفته است. می‌توانید بعد از {RetryAfter} دوباره تلاش کنید.",
"RetryAfter:Years": "{0} سال",
"RetryAfter:YearsAndMonths": "{0} سال و {1} ماه",
"RetryAfter:Months": "{0} ماه",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/fi.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/fi.json

@ -1,7 +1,7 @@
{
"culture": "fi",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Toiminnon nopeusraja ylitetty. Voit yrittää uudelleen {RetryAfter} kuluttua.",
"Volo.Abp.OperationRateLimiting:010001": "Toiminnon nopeusraja ylitetty. Voit yrittää uudelleen {RetryAfter} kuluttua.",
"RetryAfter:Years": "{0} vuosi/vuotta",
"RetryAfter:YearsAndMonths": "{0} vuosi/vuotta ja {1} kuukausi/kuukautta",
"RetryAfter:Months": "{0} kuukausi/kuukautta",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/fr.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/fr.json

@ -1,7 +1,7 @@
{
"culture": "fr",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Limite de taux d'opération dépassée. Vous pouvez réessayer après {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Limite de taux d'opération dépassée. Vous pouvez réessayer après {RetryAfter}.",
"RetryAfter:Years": "{0} an(s)",
"RetryAfter:YearsAndMonths": "{0} an(s) et {1} mois",
"RetryAfter:Months": "{0} mois",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/hi.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/hi.json

@ -1,7 +1,7 @@
{
"culture": "hi",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "ऑपरेशन दर सीमा पार हो गई। आप {RetryAfter} के बाद पुनः प्रयास कर सकते हैं।",
"Volo.Abp.OperationRateLimiting:010001": "ऑपरेशन दर सीमा पार हो गई। आप {RetryAfter} के बाद पुनः प्रयास कर सकते हैं।",
"RetryAfter:Years": "{0} वर्ष",
"RetryAfter:YearsAndMonths": "{0} वर्ष और {1} महीना/महीने",
"RetryAfter:Months": "{0} महीना/महीने",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/hr.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/hr.json

@ -1,7 +1,7 @@
{
"culture": "hr",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Prekoračeno ograničenje brzine operacije. Možete pokušati ponovo nakon {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Prekoračeno ograničenje brzine operacije. Možete pokušati ponovo nakon {RetryAfter}.",
"RetryAfter:Years": "{0} godina/e",
"RetryAfter:YearsAndMonths": "{0} godina/e i {1} mjesec/i",
"RetryAfter:Months": "{0} mjesec/i",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/hu.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/hu.json

@ -1,7 +1,7 @@
{
"culture": "hu",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "A műveleti sebességkorlát túllépve. Újra próbálkozhat {RetryAfter} múlva.",
"Volo.Abp.OperationRateLimiting:010001": "A műveleti sebességkorlát túllépve. Újra próbálkozhat {RetryAfter} múlva.",
"RetryAfter:Years": "{0} év",
"RetryAfter:YearsAndMonths": "{0} év és {1} hónap",
"RetryAfter:Months": "{0} hónap",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/is.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/is.json

@ -1,7 +1,7 @@
{
"culture": "is",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Aðgerðarhraðatakmörk náð. Þú getur reynt aftur eftir {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Aðgerðarhraðatakmörk náð. Þú getur reynt aftur eftir {RetryAfter}.",
"RetryAfter:Years": "{0} ár",
"RetryAfter:YearsAndMonths": "{0} ár og {1} mánuð(ir)",
"RetryAfter:Months": "{0} mánuð(ur/ir)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/it.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/it.json

@ -1,7 +1,7 @@
{
"culture": "it",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Limite di frequenza operazione superato. Puoi riprovare dopo {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Limite di frequenza operazione superato. Puoi riprovare dopo {RetryAfter}.",
"RetryAfter:Years": "{0} anno/i",
"RetryAfter:YearsAndMonths": "{0} anno/i e {1} mese/i",
"RetryAfter:Months": "{0} mese/i",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/nl.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/nl.json

@ -1,7 +1,7 @@
{
"culture": "nl",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Bewerkingssnelheidslimiet overschreden. U kunt het opnieuw proberen na {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Bewerkingssnelheidslimiet overschreden. U kunt het opnieuw proberen na {RetryAfter}.",
"RetryAfter:Years": "{0} jaar",
"RetryAfter:YearsAndMonths": "{0} jaar en {1} maand(en)",
"RetryAfter:Months": "{0} maand(en)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/pl-PL.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/pl-PL.json

@ -1,7 +1,7 @@
{
"culture": "pl-PL",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Przekroczono limit częstotliwości operacji. Możesz spróbować ponownie po {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Przekroczono limit częstotliwości operacji. Możesz spróbować ponownie po {RetryAfter}.",
"RetryAfter:Years": "{0} rok/lat",
"RetryAfter:YearsAndMonths": "{0} rok/lat i {1} miesiąc/miesięcy",
"RetryAfter:Months": "{0} miesiąc/miesięcy",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/pt-BR.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/pt-BR.json

@ -1,7 +1,7 @@
{
"culture": "pt-BR",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Limite de taxa de operação excedido. Você pode tentar novamente após {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Limite de taxa de operação excedido. Você pode tentar novamente após {RetryAfter}.",
"RetryAfter:Years": "{0} ano(s)",
"RetryAfter:YearsAndMonths": "{0} ano(s) e {1} mês/meses",
"RetryAfter:Months": "{0} mês/meses",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/ro-RO.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/ro-RO.json

@ -1,7 +1,7 @@
{
"culture": "ro-RO",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Limita ratei de operare a fost depășită. Puteți încerca din nou după {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Limita ratei de operare a fost depășită. Puteți încerca din nou după {RetryAfter}.",
"RetryAfter:Years": "{0} an/ani",
"RetryAfter:YearsAndMonths": "{0} an/ani și {1} lună/luni",
"RetryAfter:Months": "{0} lună/luni",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/ru.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/ru.json

@ -1,7 +1,7 @@
{
"culture": "ru",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Превышен лимит частоты операций. Вы можете повторить попытку через {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Превышен лимит частоты операций. Вы можете повторить попытку через {RetryAfter}.",
"RetryAfter:Years": "{0} год/лет",
"RetryAfter:YearsAndMonths": "{0} год/лет и {1} месяц/месяцев",
"RetryAfter:Months": "{0} месяц/месяцев",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/sk.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/sk.json

@ -1,7 +1,7 @@
{
"culture": "sk",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Prekročený limit rýchlosti operácie. Môžete to skúsiť znova po {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Prekročený limit rýchlosti operácie. Môžete to skúsiť znova po {RetryAfter}.",
"RetryAfter:Years": "{0} rok/rokov",
"RetryAfter:YearsAndMonths": "{0} rok/rokov a {1} mesiac/mesiacov",
"RetryAfter:Months": "{0} mesiac/mesiacov",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/sl.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/sl.json

@ -1,7 +1,7 @@
{
"culture": "sl",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Presežena omejitev hitrosti operacije. Poskusite lahko znova čez {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Presežena omejitev hitrosti operacije. Poskusite lahko znova čez {RetryAfter}.",
"RetryAfter:Years": "{0} leto/let",
"RetryAfter:YearsAndMonths": "{0} leto/let in {1} mesec/mesecev",
"RetryAfter:Months": "{0} mesec/mesecev",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/sv.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/sv.json

@ -1,7 +1,7 @@
{
"culture": "sv",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Hastighetsgränsen för operationen har överskridits. Du kan försöka igen efter {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Hastighetsgränsen för operationen har överskridits. Du kan försöka igen efter {RetryAfter}.",
"RetryAfter:Years": "{0} år",
"RetryAfter:YearsAndMonths": "{0} år och {1} månad(er)",
"RetryAfter:Months": "{0} månad(er)",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/tr.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/tr.json

@ -1,7 +1,7 @@
{
"culture": "tr",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "İşlem hız sınırı aşıldı. {RetryAfter} sonra tekrar deneyebilirsiniz.",
"Volo.Abp.OperationRateLimiting:010001": "İşlem hız sınırı aşıldı. {RetryAfter} sonra tekrar deneyebilirsiniz.",
"RetryAfter:Years": "{0} yıl",
"RetryAfter:YearsAndMonths": "{0} yıl ve {1} ay",
"RetryAfter:Months": "{0} ay",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/vi.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/vi.json

@ -1,7 +1,7 @@
{
"culture": "vi",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "Đã vượt quá giới hạn tốc độ thao tác. Bạn có thể thử lại sau {RetryAfter}.",
"Volo.Abp.OperationRateLimiting:010001": "Đã vượt quá giới hạn tốc độ thao tác. Bạn có thể thử lại sau {RetryAfter}.",
"RetryAfter:Years": "{0} năm",
"RetryAfter:YearsAndMonths": "{0} năm và {1} tháng",
"RetryAfter:Months": "{0} tháng",

2
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/Localization/zh-Hans.json → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Localization/zh-Hans.json

@ -1,7 +1,7 @@
{
"culture": "zh-Hans",
"texts": {
"Volo.Abp.OperationRateLimit:010001": "操作频率超出限制。请在 {RetryAfter} 后重试。",
"Volo.Abp.OperationRateLimiting:010001": "操作频率超出限制。请在 {RetryAfter} 后重试。",
"RetryAfter:Years": "{0} 年",
"RetryAfter:YearsAndMonths": "{0} 年 {1} 个月",
"RetryAfter:Months": "{0} 个月",

14
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/DefaultOperationRateLimitPolicyProvider.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/DefaultOperationRateLimitingPolicyProvider.cs

@ -4,30 +4,30 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class DefaultOperationRateLimitPolicyProvider : IOperationRateLimitPolicyProvider, ITransientDependency
public class DefaultOperationRateLimitingPolicyProvider : IOperationRateLimitingPolicyProvider, ITransientDependency
{
protected AbpOperationRateLimitOptions Options { get; }
protected AbpOperationRateLimitingOptions Options { get; }
public DefaultOperationRateLimitPolicyProvider(IOptions<AbpOperationRateLimitOptions> options)
public DefaultOperationRateLimitingPolicyProvider(IOptions<AbpOperationRateLimitingOptions> options)
{
Options = options.Value;
}
public virtual Task<OperationRateLimitPolicy> GetAsync(string policyName)
public virtual Task<OperationRateLimitingPolicy> GetAsync(string policyName)
{
if (!Options.Policies.TryGetValue(policyName, out var policy))
{
throw new AbpException(
$"Operation rate limit policy '{policyName}' was not found. " +
$"Make sure to configure it using AbpOperationRateLimitOptions.AddPolicy().");
$"Make sure to configure it using AbpOperationRateLimitingOptions.AddPolicy().");
}
return Task.FromResult(policy);
}
public virtual Task<List<OperationRateLimitPolicy>> GetListAsync()
public virtual Task<List<OperationRateLimitingPolicy>> GetListAsync()
{
return Task.FromResult(Options.Policies.Values.ToList());
}

11
framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/IOperationRateLimitingPolicyProvider.cs

@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimiting;
public interface IOperationRateLimitingPolicyProvider
{
Task<OperationRateLimitingPolicy> GetAsync(string policyName);
Task<List<OperationRateLimitingPolicy>> GetListAsync();
}

4
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitPartitionType.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingPartitionType.cs

@ -1,6 +1,6 @@
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public enum OperationRateLimitPartitionType
public enum OperationRateLimitingPartitionType
{
Parameter,
CurrentUser,

6
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitPolicy.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingPolicy.cs

@ -1,15 +1,15 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitPolicy
public class OperationRateLimitingPolicy
{
public string Name { get; set; } = default!;
public string? ErrorCode { get; set; }
public List<OperationRateLimitRuleDefinition> Rules { get; set; } = new();
public List<OperationRateLimitingRuleDefinition> Rules { get; set; } = new();
public List<Type> CustomRuleTypes { get; set; } = new();
}

34
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitPolicyBuilder.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingPolicyBuilder.cs

@ -2,16 +2,16 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitPolicyBuilder
public class OperationRateLimitingPolicyBuilder
{
private readonly string _name;
private string? _errorCode;
private readonly List<OperationRateLimitRuleDefinition> _rules = new();
private readonly List<OperationRateLimitingRuleDefinition> _rules = new();
private readonly List<Type> _customRuleTypes = new();
public OperationRateLimitPolicyBuilder(string name)
public OperationRateLimitingPolicyBuilder(string name)
{
_name = Check.NotNullOrWhiteSpace(name, nameof(name));
}
@ -19,10 +19,10 @@ public class OperationRateLimitPolicyBuilder
/// <summary>
/// Add a built-in rule. Multiple rules are AND-combined.
/// </summary>
public OperationRateLimitPolicyBuilder AddRule(
Action<OperationRateLimitRuleBuilder> configure)
public OperationRateLimitingPolicyBuilder AddRule(
Action<OperationRateLimitingRuleBuilder> configure)
{
var builder = new OperationRateLimitRuleBuilder(this);
var builder = new OperationRateLimitingRuleBuilder(this);
configure(builder);
if (!builder.IsCommitted)
{
@ -34,8 +34,8 @@ public class OperationRateLimitPolicyBuilder
/// <summary>
/// Add a custom rule type (resolved from DI).
/// </summary>
public OperationRateLimitPolicyBuilder AddRule<TRule>()
where TRule : class, IOperationRateLimitRule
public OperationRateLimitingPolicyBuilder AddRule<TRule>()
where TRule : class, IOperationRateLimitingRule
{
_customRuleTypes.Add(typeof(TRule));
return this;
@ -45,10 +45,10 @@ public class OperationRateLimitPolicyBuilder
/// Shortcut: single-rule policy with fixed window.
/// Returns the rule builder for partition configuration.
/// </summary>
public OperationRateLimitRuleBuilder WithFixedWindow(
public OperationRateLimitingRuleBuilder WithFixedWindow(
TimeSpan duration, int maxCount)
{
var builder = new OperationRateLimitRuleBuilder(this);
var builder = new OperationRateLimitingRuleBuilder(this);
builder.WithFixedWindow(duration, maxCount);
return builder;
}
@ -56,18 +56,18 @@ public class OperationRateLimitPolicyBuilder
/// <summary>
/// Set a custom ErrorCode for this policy's exception.
/// </summary>
public OperationRateLimitPolicyBuilder WithErrorCode(string errorCode)
public OperationRateLimitingPolicyBuilder WithErrorCode(string errorCode)
{
_errorCode = Check.NotNullOrWhiteSpace(errorCode, nameof(errorCode));
return this;
}
internal void AddRuleDefinition(OperationRateLimitRuleDefinition definition)
internal void AddRuleDefinition(OperationRateLimitingRuleDefinition definition)
{
_rules.Add(definition);
}
internal OperationRateLimitPolicy Build()
internal OperationRateLimitingPolicy Build()
{
if (_rules.Count == 0 && _customRuleTypes.Count == 0)
{
@ -77,7 +77,7 @@ public class OperationRateLimitPolicyBuilder
}
var duplicate = _rules
.Where(r => r.PartitionType != OperationRateLimitPartitionType.Custom)
.Where(r => r.PartitionType != OperationRateLimitingPartitionType.Custom)
.GroupBy(r => (r.Duration, r.MaxCount, r.PartitionType, r.IsMultiTenant))
.FirstOrDefault(g => g.Count() > 1);
@ -91,11 +91,11 @@ public class OperationRateLimitPolicyBuilder
"Each rule in a policy must have a unique combination of these properties.");
}
return new OperationRateLimitPolicy
return new OperationRateLimitingPolicy
{
Name = _name,
ErrorCode = _errorCode,
Rules = new List<OperationRateLimitRuleDefinition>(_rules),
Rules = new List<OperationRateLimitingRuleDefinition>(_rules),
CustomRuleTypes = new List<Type>(_customRuleTypes)
};
}

52
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitRuleBuilder.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleBuilder.cs

@ -1,24 +1,24 @@
using System;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitRuleBuilder
public class OperationRateLimitingRuleBuilder
{
private readonly OperationRateLimitPolicyBuilder _policyBuilder;
private readonly OperationRateLimitingPolicyBuilder _policyBuilder;
private TimeSpan _duration;
private int _maxCount;
private OperationRateLimitPartitionType? _partitionType;
private Func<OperationRateLimitContext, string>? _customPartitionKeyResolver;
private OperationRateLimitingPartitionType? _partitionType;
private Func<OperationRateLimitingContext, string>? _customPartitionKeyResolver;
private bool _isMultiTenant;
internal bool IsCommitted { get; private set; }
internal OperationRateLimitRuleBuilder(OperationRateLimitPolicyBuilder policyBuilder)
internal OperationRateLimitingRuleBuilder(OperationRateLimitingPolicyBuilder policyBuilder)
{
_policyBuilder = policyBuilder;
}
public OperationRateLimitRuleBuilder WithFixedWindow(
public OperationRateLimitingRuleBuilder WithFixedWindow(
TimeSpan duration, int maxCount)
{
_duration = duration;
@ -26,7 +26,7 @@ public class OperationRateLimitRuleBuilder
return this;
}
public OperationRateLimitRuleBuilder WithMultiTenancy()
public OperationRateLimitingRuleBuilder WithMultiTenancy()
{
_isMultiTenant = true;
return this;
@ -35,9 +35,9 @@ public class OperationRateLimitRuleBuilder
/// <summary>
/// Use context.Parameter as partition key.
/// </summary>
public OperationRateLimitPolicyBuilder PartitionByParameter()
public OperationRateLimitingPolicyBuilder PartitionByParameter()
{
_partitionType = OperationRateLimitPartitionType.Parameter;
_partitionType = OperationRateLimitingPartitionType.Parameter;
CommitToPolicyBuilder();
return _policyBuilder;
}
@ -45,9 +45,9 @@ public class OperationRateLimitRuleBuilder
/// <summary>
/// Auto resolve from ICurrentUser.Id.
/// </summary>
public OperationRateLimitPolicyBuilder PartitionByCurrentUser()
public OperationRateLimitingPolicyBuilder PartitionByCurrentUser()
{
_partitionType = OperationRateLimitPartitionType.CurrentUser;
_partitionType = OperationRateLimitingPartitionType.CurrentUser;
CommitToPolicyBuilder();
return _policyBuilder;
}
@ -55,9 +55,9 @@ public class OperationRateLimitRuleBuilder
/// <summary>
/// Auto resolve from ICurrentTenant.Id.
/// </summary>
public OperationRateLimitPolicyBuilder PartitionByCurrentTenant()
public OperationRateLimitingPolicyBuilder PartitionByCurrentTenant()
{
_partitionType = OperationRateLimitPartitionType.CurrentTenant;
_partitionType = OperationRateLimitingPartitionType.CurrentTenant;
CommitToPolicyBuilder();
return _policyBuilder;
}
@ -65,9 +65,9 @@ public class OperationRateLimitRuleBuilder
/// <summary>
/// Auto resolve from IWebClientInfoProvider.ClientIpAddress.
/// </summary>
public OperationRateLimitPolicyBuilder PartitionByClientIp()
public OperationRateLimitingPolicyBuilder PartitionByClientIp()
{
_partitionType = OperationRateLimitPartitionType.ClientIp;
_partitionType = OperationRateLimitingPartitionType.ClientIp;
CommitToPolicyBuilder();
return _policyBuilder;
}
@ -76,9 +76,9 @@ public class OperationRateLimitRuleBuilder
/// Partition by email address.
/// Resolves from context.Parameter, falls back to ICurrentUser.Email.
/// </summary>
public OperationRateLimitPolicyBuilder PartitionByEmail()
public OperationRateLimitingPolicyBuilder PartitionByEmail()
{
_partitionType = OperationRateLimitPartitionType.Email;
_partitionType = OperationRateLimitingPartitionType.Email;
CommitToPolicyBuilder();
return _policyBuilder;
}
@ -87,9 +87,9 @@ public class OperationRateLimitRuleBuilder
/// Partition by phone number.
/// Resolves from context.Parameter, falls back to ICurrentUser.PhoneNumber.
/// </summary>
public OperationRateLimitPolicyBuilder PartitionByPhoneNumber()
public OperationRateLimitingPolicyBuilder PartitionByPhoneNumber()
{
_partitionType = OperationRateLimitPartitionType.PhoneNumber;
_partitionType = OperationRateLimitingPartitionType.PhoneNumber;
CommitToPolicyBuilder();
return _policyBuilder;
}
@ -97,10 +97,10 @@ public class OperationRateLimitRuleBuilder
/// <summary>
/// Custom partition key resolver from context.
/// </summary>
public OperationRateLimitPolicyBuilder PartitionBy(
Func<OperationRateLimitContext, string> keyResolver)
public OperationRateLimitingPolicyBuilder PartitionBy(
Func<OperationRateLimitingContext, string> keyResolver)
{
_partitionType = OperationRateLimitPartitionType.Custom;
_partitionType = OperationRateLimitingPartitionType.Custom;
_customPartitionKeyResolver = Check.NotNull(keyResolver, nameof(keyResolver));
CommitToPolicyBuilder();
return _policyBuilder;
@ -112,7 +112,7 @@ public class OperationRateLimitRuleBuilder
IsCommitted = true;
}
internal OperationRateLimitRuleDefinition Build()
internal OperationRateLimitingRuleDefinition Build()
{
if (_duration <= TimeSpan.Zero)
{
@ -135,14 +135,14 @@ public class OperationRateLimitRuleBuilder
"Call PartitionByParameter(), PartitionByCurrentUser(), PartitionByClientIp(), or another PartitionBy*() method.");
}
if (_partitionType == OperationRateLimitPartitionType.Custom && _customPartitionKeyResolver == null)
if (_partitionType == OperationRateLimitingPartitionType.Custom && _customPartitionKeyResolver == null)
{
throw new AbpException(
"Custom partition type requires a key resolver. " +
"Call PartitionBy(keyResolver) instead of setting partition type directly.");
}
return new OperationRateLimitRuleDefinition
return new OperationRateLimitingRuleDefinition
{
Duration = _duration,
MaxCount = _maxCount,

16
framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Policies/OperationRateLimitingRuleDefinition.cs

@ -0,0 +1,16 @@
using System;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitingRuleDefinition
{
public TimeSpan Duration { get; set; }
public int MaxCount { get; set; }
public OperationRateLimitingPartitionType PartitionType { get; set; }
public Func<OperationRateLimitingContext, string>? CustomPartitionKeyResolver { get; set; }
public bool IsMultiTenant { get; set; }
}

46
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/FixedWindowOperationRateLimitRule.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/FixedWindowOperationRateLimitingRule.cs

@ -3,25 +3,25 @@ using Volo.Abp.AspNetCore.WebClientInfo;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Users;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class FixedWindowOperationRateLimitRule : IOperationRateLimitRule
public class FixedWindowOperationRateLimitingRule : IOperationRateLimitingRule
{
private const string HostTenantKey = "host";
protected string PolicyName { get; }
protected int RuleIndex { get; }
protected OperationRateLimitRuleDefinition Definition { get; }
protected IOperationRateLimitStore Store { get; }
protected OperationRateLimitingRuleDefinition Definition { get; }
protected IOperationRateLimitingStore Store { get; }
protected ICurrentUser CurrentUser { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IWebClientInfoProvider WebClientInfoProvider { get; }
public FixedWindowOperationRateLimitRule(
public FixedWindowOperationRateLimitingRule(
string policyName,
int ruleIndex,
OperationRateLimitRuleDefinition definition,
IOperationRateLimitStore store,
OperationRateLimitingRuleDefinition definition,
IOperationRateLimitingStore store,
ICurrentUser currentUser,
ICurrentTenant currentTenant,
IWebClientInfoProvider webClientInfoProvider)
@ -35,8 +35,8 @@ public class FixedWindowOperationRateLimitRule : IOperationRateLimitRule
WebClientInfoProvider = webClientInfoProvider;
}
public virtual async Task<OperationRateLimitRuleResult> AcquireAsync(
OperationRateLimitContext context)
public virtual async Task<OperationRateLimitingRuleResult> AcquireAsync(
OperationRateLimitingContext context)
{
var partitionKey = ResolvePartitionKey(context);
var storeKey = BuildStoreKey(partitionKey);
@ -45,8 +45,8 @@ public class FixedWindowOperationRateLimitRule : IOperationRateLimitRule
return ToRuleResult(storeResult);
}
public virtual async Task<OperationRateLimitRuleResult> CheckAsync(
OperationRateLimitContext context)
public virtual async Task<OperationRateLimitingRuleResult> CheckAsync(
OperationRateLimitingContext context)
{
var partitionKey = ResolvePartitionKey(context);
var storeKey = BuildStoreKey(partitionKey);
@ -55,47 +55,47 @@ public class FixedWindowOperationRateLimitRule : IOperationRateLimitRule
return ToRuleResult(storeResult);
}
public virtual async Task ResetAsync(OperationRateLimitContext context)
public virtual async Task ResetAsync(OperationRateLimitingContext context)
{
var partitionKey = ResolvePartitionKey(context);
var storeKey = BuildStoreKey(partitionKey);
await Store.ResetAsync(storeKey);
}
protected virtual string ResolvePartitionKey(OperationRateLimitContext context)
protected virtual string ResolvePartitionKey(OperationRateLimitingContext context)
{
return Definition.PartitionType switch
{
OperationRateLimitPartitionType.Parameter =>
OperationRateLimitingPartitionType.Parameter =>
context.Parameter ?? throw new AbpException(
$"OperationRateLimitContext.Parameter is required for policy '{PolicyName}' (PartitionByParameter)."),
$"OperationRateLimitingContext.Parameter is required for policy '{PolicyName}' (PartitionByParameter)."),
OperationRateLimitPartitionType.CurrentUser =>
OperationRateLimitingPartitionType.CurrentUser =>
CurrentUser.Id?.ToString() ?? throw new AbpException(
$"Current user is not authenticated. Policy '{PolicyName}' requires PartitionByCurrentUser."),
OperationRateLimitPartitionType.CurrentTenant =>
OperationRateLimitingPartitionType.CurrentTenant =>
CurrentTenant.Id?.ToString() ?? HostTenantKey,
OperationRateLimitPartitionType.ClientIp =>
OperationRateLimitingPartitionType.ClientIp =>
WebClientInfoProvider.ClientIpAddress
?? throw new AbpException(
$"Client IP address could not be determined. Policy '{PolicyName}' requires PartitionByClientIp. " +
"Ensure IWebClientInfoProvider is properly configured."),
OperationRateLimitPartitionType.Email =>
OperationRateLimitingPartitionType.Email =>
context.Parameter
?? CurrentUser.Email
?? throw new AbpException(
$"Email is required for policy '{PolicyName}' (PartitionByEmail). Provide it via context.Parameter or ensure the user has an email."),
OperationRateLimitPartitionType.PhoneNumber =>
OperationRateLimitingPartitionType.PhoneNumber =>
context.Parameter
?? CurrentUser.PhoneNumber
?? throw new AbpException(
$"Phone number is required for policy '{PolicyName}' (PartitionByPhoneNumber). Provide it via context.Parameter or ensure the user has a phone number."),
OperationRateLimitPartitionType.Custom =>
OperationRateLimitingPartitionType.Custom =>
Definition.CustomPartitionKeyResolver!(context),
_ => throw new AbpException($"Unknown partition type: {Definition.PartitionType}")
@ -119,9 +119,9 @@ public class FixedWindowOperationRateLimitRule : IOperationRateLimitRule
return $"orl:t:{tenantId}:{PolicyName}:{ruleKey}:{partitionKey}";
}
protected virtual OperationRateLimitRuleResult ToRuleResult(OperationRateLimitStoreResult storeResult)
protected virtual OperationRateLimitingRuleResult ToRuleResult(OperationRateLimitingStoreResult storeResult)
{
return new OperationRateLimitRuleResult
return new OperationRateLimitingRuleResult
{
RuleName = $"{PolicyName}:Rule[{(long)Definition.Duration.TotalSeconds}s,{Definition.MaxCount},{Definition.PartitionType}]",
IsAllowed = storeResult.IsAllowed,

12
framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Rules/IOperationRateLimitingRule.cs

@ -0,0 +1,12 @@
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimiting;
public interface IOperationRateLimitingRule
{
Task<OperationRateLimitingRuleResult> AcquireAsync(OperationRateLimitingContext context);
Task<OperationRateLimitingRuleResult> CheckAsync(OperationRateLimitingContext context);
Task ResetAsync(OperationRateLimitingContext context);
}

38
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/DistributedCacheOperationRateLimitStore.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/DistributedCacheOperationRateLimitingStore.cs

@ -7,20 +7,20 @@ using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Timing;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore, ITransientDependency
public class DistributedCacheOperationRateLimitingStore : IOperationRateLimitingStore, ITransientDependency
{
protected IDistributedCache<OperationRateLimitCacheItem> Cache { get; }
protected IDistributedCache<OperationRateLimitingCacheItem> Cache { get; }
protected IClock Clock { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected AbpOperationRateLimitOptions Options { get; }
protected AbpOperationRateLimitingOptions Options { get; }
public DistributedCacheOperationRateLimitStore(
IDistributedCache<OperationRateLimitCacheItem> cache,
public DistributedCacheOperationRateLimitingStore(
IDistributedCache<OperationRateLimitingCacheItem> cache,
IClock clock,
IAbpDistributedLock distributedLock,
IOptions<AbpOperationRateLimitOptions> options)
IOptions<AbpOperationRateLimitingOptions> options)
{
Cache = cache;
Clock = clock;
@ -28,12 +28,12 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
Options = options.Value;
}
public virtual async Task<OperationRateLimitStoreResult> IncrementAsync(
public virtual async Task<OperationRateLimitingStoreResult> IncrementAsync(
string key, TimeSpan duration, int maxCount)
{
if (maxCount <= 0)
{
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = false,
CurrentCount = 0,
@ -43,7 +43,7 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
}
await using (var handle = await DistributedLock.TryAcquireAsync(
$"OperationRateLimit:{key}", Options.LockTimeout))
$"OperationRateLimiting:{key}", Options.LockTimeout))
{
if (handle == null)
{
@ -57,14 +57,14 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
if (cacheItem == null || now >= cacheItem.WindowStart.Add(duration))
{
cacheItem = new OperationRateLimitCacheItem { Count = 1, WindowStart = now };
cacheItem = new OperationRateLimitingCacheItem { Count = 1, WindowStart = now };
await Cache.SetAsync(key, cacheItem,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = duration
});
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = true,
CurrentCount = 1,
@ -75,7 +75,7 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
if (cacheItem.Count >= maxCount)
{
var retryAfter = cacheItem.WindowStart.Add(duration) - now;
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = false,
CurrentCount = cacheItem.Count,
@ -92,7 +92,7 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
AbsoluteExpirationRelativeToNow = expiration > TimeSpan.Zero ? expiration : duration
});
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = true,
CurrentCount = cacheItem.Count,
@ -101,12 +101,12 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
}
}
public virtual async Task<OperationRateLimitStoreResult> GetAsync(
public virtual async Task<OperationRateLimitingStoreResult> GetAsync(
string key, TimeSpan duration, int maxCount)
{
if (maxCount <= 0)
{
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = false,
CurrentCount = 0,
@ -120,7 +120,7 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
if (cacheItem == null || now >= cacheItem.WindowStart.Add(duration))
{
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = true,
CurrentCount = 0,
@ -131,7 +131,7 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
if (cacheItem.Count >= maxCount)
{
var retryAfter = cacheItem.WindowStart.Add(duration) - now;
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = false,
CurrentCount = cacheItem.Count,
@ -140,7 +140,7 @@ public class DistributedCacheOperationRateLimitStore : IOperationRateLimitStore,
};
}
return new OperationRateLimitStoreResult
return new OperationRateLimitingStoreResult
{
IsAllowed = true,
CurrentCount = cacheItem.Count,

13
framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/IOperationRateLimitingStore.cs

@ -0,0 +1,13 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.OperationRateLimiting;
public interface IOperationRateLimitingStore
{
Task<OperationRateLimitingStoreResult> IncrementAsync(string key, TimeSpan duration, int maxCount);
Task<OperationRateLimitingStoreResult> GetAsync(string key, TimeSpan duration, int maxCount);
Task ResetAsync(string key);
}

6
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitCacheItem.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/OperationRateLimitingCacheItem.cs

@ -2,11 +2,11 @@ using System;
using Volo.Abp.Caching;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
[CacheName("OperationRateLimit")]
[CacheName("OperationRateLimiting")]
[IgnoreMultiTenancy]
public class OperationRateLimitCacheItem
public class OperationRateLimitingCacheItem
{
public int Count { get; set; }

4
framework/src/Volo.Abp.OperationRateLimit/Volo/Abp/OperationRateLimit/OperationRateLimitStoreResult.cs → framework/src/Volo.Abp.OperationRateLimiting/Volo/Abp/OperationRateLimiting/Store/OperationRateLimitingStoreResult.cs

@ -1,8 +1,8 @@
using System;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitStoreResult
public class OperationRateLimitingStoreResult
{
public bool IsAllowed { get; set; }

2
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo.Abp.OperationRateLimit.Tests.csproj → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo.Abp.OperationRateLimiting.Tests.csproj

@ -11,7 +11,7 @@
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.ExceptionHandling\Volo.Abp.ExceptionHandling.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.OperationRateLimit\Volo.Abp.OperationRateLimit.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.OperationRateLimiting\Volo.Abp.OperationRateLimiting.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>

26
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/AbpOperationRateLimitException_Tests.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingException_Tests.cs

@ -2,14 +2,14 @@ using System;
using Shouldly;
using Xunit;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class AbpOperationRateLimitException_Tests
public class AbpOperationRateLimitingException_Tests
{
[Fact]
public void Should_Set_HttpStatusCode_To_429()
{
var result = new OperationRateLimitResult
var result = new OperationRateLimitingResult
{
IsAllowed = false,
MaxCount = 3,
@ -18,7 +18,7 @@ public class AbpOperationRateLimitException_Tests
RetryAfter = TimeSpan.FromMinutes(15)
};
var exception = new AbpOperationRateLimitException("TestPolicy", result);
var exception = new AbpOperationRateLimitingException("TestPolicy", result);
exception.HttpStatusCode.ShouldBe(429);
}
@ -26,7 +26,7 @@ public class AbpOperationRateLimitException_Tests
[Fact]
public void Should_Set_Default_ErrorCode()
{
var result = new OperationRateLimitResult
var result = new OperationRateLimitingResult
{
IsAllowed = false,
MaxCount = 3,
@ -34,15 +34,15 @@ public class AbpOperationRateLimitException_Tests
RemainingCount = 0
};
var exception = new AbpOperationRateLimitException("TestPolicy", result);
var exception = new AbpOperationRateLimitingException("TestPolicy", result);
exception.Code.ShouldBe(AbpOperationRateLimitErrorCodes.ExceedLimit);
exception.Code.ShouldBe(AbpOperationRateLimitingErrorCodes.ExceedLimit);
}
[Fact]
public void Should_Set_Custom_ErrorCode()
{
var result = new OperationRateLimitResult
var result = new OperationRateLimitingResult
{
IsAllowed = false,
MaxCount = 3,
@ -50,7 +50,7 @@ public class AbpOperationRateLimitException_Tests
RemainingCount = 0
};
var exception = new AbpOperationRateLimitException("TestPolicy", result, "App:Custom:Error");
var exception = new AbpOperationRateLimitingException("TestPolicy", result, "App:Custom:Error");
exception.Code.ShouldBe("App:Custom:Error");
}
@ -58,7 +58,7 @@ public class AbpOperationRateLimitException_Tests
[Fact]
public void Should_Include_Data_Properties()
{
var result = new OperationRateLimitResult
var result = new OperationRateLimitingResult
{
IsAllowed = false,
MaxCount = 3,
@ -68,7 +68,7 @@ public class AbpOperationRateLimitException_Tests
WindowDuration = TimeSpan.FromHours(1)
};
var exception = new AbpOperationRateLimitException("TestPolicy", result);
var exception = new AbpOperationRateLimitingException("TestPolicy", result);
exception.Data["PolicyName"].ShouldBe("TestPolicy");
exception.Data["MaxCount"].ShouldBe(3);
@ -82,7 +82,7 @@ public class AbpOperationRateLimitException_Tests
[Fact]
public void Should_Store_PolicyName_And_Result()
{
var result = new OperationRateLimitResult
var result = new OperationRateLimitingResult
{
IsAllowed = false,
MaxCount = 5,
@ -91,7 +91,7 @@ public class AbpOperationRateLimitException_Tests
RetryAfter = TimeSpan.FromHours(1)
};
var exception = new AbpOperationRateLimitException("MyPolicy", result);
var exception = new AbpOperationRateLimitingException("MyPolicy", result);
exception.PolicyName.ShouldBe("MyPolicy");
exception.Result.ShouldBeSameAs(result);

20
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/AbpOperationRateLimitPhase2RaceTestModule.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingPhase2RaceTestModule.cs

@ -6,18 +6,18 @@ using Volo.Abp.Autofac;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Modularity;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
/// <summary>
/// A mock store that simulates a concurrent race condition:
/// - GetAsync always says the quota is available (Phase 1 checks pass).
/// - IncrementAsync always says the quota is exhausted (Phase 2 finds another request consumed it).
/// </summary>
internal class RaceConditionSimulatorStore : IOperationRateLimitStore
internal class RaceConditionSimulatorStore : IOperationRateLimitingStore
{
public Task<OperationRateLimitStoreResult> GetAsync(string key, TimeSpan duration, int maxCount)
public Task<OperationRateLimitingStoreResult> GetAsync(string key, TimeSpan duration, int maxCount)
{
return Task.FromResult(new OperationRateLimitStoreResult
return Task.FromResult(new OperationRateLimitingStoreResult
{
IsAllowed = true,
CurrentCount = 0,
@ -25,10 +25,10 @@ internal class RaceConditionSimulatorStore : IOperationRateLimitStore
});
}
public Task<OperationRateLimitStoreResult> IncrementAsync(string key, TimeSpan duration, int maxCount)
public Task<OperationRateLimitingStoreResult> IncrementAsync(string key, TimeSpan duration, int maxCount)
{
// Simulate: between Phase 1 and Phase 2 another concurrent request consumed the last slot.
return Task.FromResult(new OperationRateLimitStoreResult
return Task.FromResult(new OperationRateLimitingStoreResult
{
IsAllowed = false,
CurrentCount = maxCount,
@ -44,19 +44,19 @@ internal class RaceConditionSimulatorStore : IOperationRateLimitStore
}
[DependsOn(
typeof(AbpOperationRateLimitModule),
typeof(AbpOperationRateLimitingModule),
typeof(AbpExceptionHandlingModule),
typeof(AbpTestBaseModule),
typeof(AbpAutofacModule)
)]
public class AbpOperationRateLimitPhase2RaceTestModule : AbpModule
public class AbpOperationRateLimitingPhase2RaceTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Replace(
ServiceDescriptor.Transient<IOperationRateLimitStore, RaceConditionSimulatorStore>());
ServiceDescriptor.Transient<IOperationRateLimitingStore, RaceConditionSimulatorStore>());
Configure<AbpOperationRateLimitOptions>(options =>
Configure<AbpOperationRateLimitingOptions>(options =>
{
options.AddPolicy("TestRacePolicy", policy =>
{

8
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/AbpOperationRateLimitTestModule.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/AbpOperationRateLimitingTestModule.cs

@ -6,15 +6,15 @@ using Volo.Abp.Autofac;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Modularity;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
[DependsOn(
typeof(AbpOperationRateLimitModule),
typeof(AbpOperationRateLimitingModule),
typeof(AbpExceptionHandlingModule),
typeof(AbpTestBaseModule),
typeof(AbpAutofacModule)
)]
public class AbpOperationRateLimitTestModule : AbpModule
public class AbpOperationRateLimitingTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
@ -22,7 +22,7 @@ public class AbpOperationRateLimitTestModule : AbpModule
mockWebClientInfoProvider.ClientIpAddress.Returns("127.0.0.1");
context.Services.AddSingleton<IWebClientInfoProvider>(mockWebClientInfoProvider);
Configure<AbpOperationRateLimitOptions>(options =>
Configure<AbpOperationRateLimitingOptions>(options =>
{
options.AddPolicy("TestSimple", policy =>
{

10
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/DistributedCacheOperationRateLimitStore_Tests.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/DistributedCacheOperationRateLimitingStore_Tests.cs

@ -3,15 +3,15 @@ using System.Threading.Tasks;
using Shouldly;
using Xunit;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class DistributedCacheOperationRateLimitStore_Tests : OperationRateLimitTestBase
public class DistributedCacheOperationRateLimitingStore_Tests : OperationRateLimitingTestBase
{
private readonly IOperationRateLimitStore _store;
private readonly IOperationRateLimitingStore _store;
public DistributedCacheOperationRateLimitStore_Tests()
public DistributedCacheOperationRateLimitingStore_Tests()
{
_store = GetRequiredService<IOperationRateLimitStore>();
_store = GetRequiredService<IOperationRateLimitingStore>();
}
[Fact]

36
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/OperationRateLimitCheckerFixes_Tests.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingCheckerFixes_Tests.cs

@ -4,19 +4,19 @@ using Shouldly;
using Volo.Abp.Testing;
using Xunit;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
/// <summary>
/// Tests for Fix #6: Phase 1 in CheckAsync now checks ALL rules before throwing,
/// so RetryAfter is the maximum across all blocking rules and RuleResults is complete.
/// </summary>
public class OperationRateLimitCheckerPhase1_Tests : OperationRateLimitTestBase
public class OperationRateLimitingCheckerPhase1_Tests : OperationRateLimitingTestBase
{
private readonly IOperationRateLimitChecker _checker;
private readonly IOperationRateLimitingChecker _checker;
public OperationRateLimitCheckerPhase1_Tests()
public OperationRateLimitingCheckerPhase1_Tests()
{
_checker = GetRequiredService<IOperationRateLimitChecker>();
_checker = GetRequiredService<IOperationRateLimitingChecker>();
}
[Fact]
@ -25,14 +25,14 @@ public class OperationRateLimitCheckerPhase1_Tests : OperationRateLimitTestBase
// TestCompositeMaxRetryAfter: Rule0 (5-min window, max=1), Rule1 (2-hr window, max=1)
// Both rules use PartitionByParameter with the same key, so one request exhausts both.
var param = $"max-retry-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// First request: both rules go from 0 to 1 (exhausted, since maxCount=1)
await _checker.CheckAsync("TestCompositeMaxRetryAfter", context);
// Second request: both Rule0 and Rule1 are blocking.
// Phase 1 checks all rules → RetryAfter must be the larger one (~2 hours).
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCompositeMaxRetryAfter", context);
});
@ -46,12 +46,12 @@ public class OperationRateLimitCheckerPhase1_Tests : OperationRateLimitTestBase
public async Task Should_Include_All_Rules_In_RuleResults_When_Multiple_Rules_Block()
{
var param = $"all-rules-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// Exhaust both rules
await _checker.CheckAsync("TestCompositeMaxRetryAfter", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCompositeMaxRetryAfter", context);
});
@ -69,12 +69,12 @@ public class OperationRateLimitCheckerPhase1_Tests : OperationRateLimitTestBase
// TestCompositePartialBlock: Rule0 (max=1) blocks, Rule1 (max=100) is still within limit.
// RuleResults must contain BOTH rules so callers get the full picture.
var param = $"partial-block-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// Exhaust only Rule0 (max=1)
await _checker.CheckAsync("TestCompositePartialBlock", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCompositePartialBlock", context);
});
@ -102,8 +102,8 @@ public class OperationRateLimitCheckerPhase1_Tests : OperationRateLimitTestBase
/// Uses a mock store that simulates a concurrent race condition:
/// GetAsync (Phase 1) always reports quota available, but IncrementAsync (Phase 2) returns denied.
/// </summary>
public class OperationRateLimitCheckerPhase2Race_Tests
: AbpIntegratedTest<AbpOperationRateLimitPhase2RaceTestModule>
public class OperationRateLimitingCheckerPhase2Race_Tests
: AbpIntegratedTest<AbpOperationRateLimitingPhase2RaceTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
@ -116,10 +116,10 @@ public class OperationRateLimitCheckerPhase2Race_Tests
// The mock store always returns IsAllowed=true in GetAsync (Phase 1 passes)
// but always returns IsAllowed=false in IncrementAsync (simulates concurrent exhaustion).
// Before Fix #1, CheckAsync would silently succeed. After the fix it must throw.
var checker = GetRequiredService<IOperationRateLimitChecker>();
var context = new OperationRateLimitContext { Parameter = "race-test" };
var checker = GetRequiredService<IOperationRateLimitingChecker>();
var context = new OperationRateLimitingContext { Parameter = "race-test" };
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestRacePolicy", context);
});
@ -134,8 +134,8 @@ public class OperationRateLimitCheckerPhase2Race_Tests
{
// IsAllowedAsync is read-only and does not call IncrementAsync,
// so it should not be affected by the mock store's deny-on-increment behavior.
var checker = GetRequiredService<IOperationRateLimitChecker>();
var context = new OperationRateLimitContext { Parameter = "is-allowed-race" };
var checker = GetRequiredService<IOperationRateLimitingChecker>();
var context = new OperationRateLimitingContext { Parameter = "is-allowed-race" };
// Should return true because GetAsync always returns allowed in the mock store
var allowed = await checker.IsAllowedAsync("TestRacePolicy", context);

130
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/OperationRateLimitChecker_Tests.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingChecker_Tests.cs

@ -6,21 +6,21 @@ using Shouldly;
using Volo.Abp.Security.Claims;
using Xunit;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public class OperationRateLimitingChecker_Tests : OperationRateLimitingTestBase
{
private readonly IOperationRateLimitChecker _checker;
private readonly IOperationRateLimitingChecker _checker;
public OperationRateLimitChecker_Tests()
public OperationRateLimitingChecker_Tests()
{
_checker = GetRequiredService<IOperationRateLimitChecker>();
_checker = GetRequiredService<IOperationRateLimitingChecker>();
}
[Fact]
public async Task Should_Allow_Within_Limit()
{
var context = new OperationRateLimitContext { Parameter = "test@example.com" };
var context = new OperationRateLimitingContext { Parameter = "test@example.com" };
// Should not throw for 3 requests (max is 3)
await _checker.CheckAsync("TestSimple", context);
@ -32,13 +32,13 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Reject_When_Exceeded()
{
var param = $"exceed-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestSimple", context);
});
@ -46,14 +46,14 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
exception.PolicyName.ShouldBe("TestSimple");
exception.Result.IsAllowed.ShouldBeFalse();
exception.HttpStatusCode.ShouldBe(429);
exception.Code.ShouldBe(AbpOperationRateLimitErrorCodes.ExceedLimit);
exception.Code.ShouldBe(AbpOperationRateLimitingErrorCodes.ExceedLimit);
}
[Fact]
public async Task Should_Return_Correct_RemainingCount()
{
var param = $"remaining-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
var status = await _checker.GetStatusAsync("TestSimple", context);
status.IsAllowed.ShouldBeTrue();
@ -73,13 +73,13 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Return_Correct_RetryAfter()
{
var param = $"retry-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestSimple", context);
});
@ -100,8 +100,8 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var context = new OperationRateLimitContext { Parameter = $"composite-{Guid.NewGuid()}" };
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
var context = new OperationRateLimitingContext { Parameter = $"composite-{Guid.NewGuid()}" };
// Should pass: both rules within limits
await checker.CheckAsync("TestComposite", context);
@ -123,16 +123,16 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
var param = $"composite-reject-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await checker.CheckAsync("TestComposite", context);
await checker.CheckAsync("TestComposite", context);
await checker.CheckAsync("TestComposite", context);
// 4th request: Rule1 (max 3 per hour by parameter) should fail
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestComposite", context);
});
@ -146,14 +146,14 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Reset_Counter()
{
var param = $"reset-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
// Should be at limit
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestSimple", context);
});
@ -169,12 +169,12 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Use_Custom_ErrorCode()
{
var param = $"custom-error-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestCustomErrorCode", context);
await _checker.CheckAsync("TestCustomErrorCode", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCustomErrorCode", context);
});
@ -194,7 +194,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
[Fact]
public async Task Should_Skip_When_Disabled()
{
var options = GetRequiredService<Microsoft.Extensions.Options.IOptions<AbpOperationRateLimitOptions>>();
var options = GetRequiredService<Microsoft.Extensions.Options.IOptions<AbpOperationRateLimitingOptions>>();
var originalValue = options.Value.IsEnabled;
try
@ -202,7 +202,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
options.Value.IsEnabled = false;
var param = $"disabled-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// Should pass unlimited times
for (var i = 0; i < 100; i++)
@ -220,7 +220,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Work_With_IsAllowedAsync()
{
var param = $"is-allowed-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// IsAllowedAsync does not consume quota
(await _checker.IsAllowedAsync("TestSimple", context)).ShouldBeTrue();
@ -245,8 +245,8 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
var param1 = $"param1-{Guid.NewGuid()}";
var param2 = $"param2-{Guid.NewGuid()}";
var context1 = new OperationRateLimitContext { Parameter = param1 };
var context2 = new OperationRateLimitContext { Parameter = param2 };
var context1 = new OperationRateLimitingContext { Parameter = param1 };
var context2 = new OperationRateLimitingContext { Parameter = param2 };
// Consume all for param1
await _checker.CheckAsync("TestSimple", context1);
@ -262,7 +262,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Support_ExtraProperties_In_Exception_Data()
{
var param = $"extra-{Guid.NewGuid()}";
var context = new OperationRateLimitContext
var context = new OperationRateLimitingContext
{
Parameter = param,
ExtraProperties =
@ -276,7 +276,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestSimple", context);
});
@ -291,13 +291,13 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Partition_By_Email_Via_Parameter()
{
var email = $"email-param-{Guid.NewGuid()}@example.com";
var context = new OperationRateLimitContext { Parameter = email };
var context = new OperationRateLimitingContext { Parameter = email };
await _checker.CheckAsync("TestEmailBased", context);
await _checker.CheckAsync("TestEmailBased", context);
await _checker.CheckAsync("TestEmailBased", context);
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestEmailBased", context);
});
@ -315,16 +315,16 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
// No Parameter set, should fall back to ICurrentUser.Email
var context = new OperationRateLimitContext();
var context = new OperationRateLimitingContext();
await checker.CheckAsync("TestEmailBased", context);
await checker.CheckAsync("TestEmailBased", context);
await checker.CheckAsync("TestEmailBased", context);
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestEmailBased", context);
});
@ -336,13 +336,13 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Partition_By_PhoneNumber_Via_Parameter()
{
var phone = $"phone-param-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = phone };
var context = new OperationRateLimitingContext { Parameter = phone };
await _checker.CheckAsync("TestPhoneNumberBased", context);
await _checker.CheckAsync("TestPhoneNumberBased", context);
await _checker.CheckAsync("TestPhoneNumberBased", context);
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestPhoneNumberBased", context);
});
@ -360,16 +360,16 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
// No Parameter set, should fall back to ICurrentUser.PhoneNumber
var context = new OperationRateLimitContext();
var context = new OperationRateLimitingContext();
await checker.CheckAsync("TestPhoneNumberBased", context);
await checker.CheckAsync("TestPhoneNumberBased", context);
await checker.CheckAsync("TestPhoneNumberBased", context);
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestPhoneNumberBased", context);
});
@ -381,7 +381,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Throw_When_Email_Not_Available()
{
// No Parameter and no authenticated user
var context = new OperationRateLimitContext();
var context = new OperationRateLimitingContext();
await Assert.ThrowsAsync<AbpException>(async () =>
{
@ -403,16 +403,16 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
var param = $"no-waste-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// 2 successful requests (Rule1: 2/5, Rule2: 2/2)
await checker.CheckAsync("TestCompositeRule2First", context);
await checker.CheckAsync("TestCompositeRule2First", context);
// 3rd request: Rule2 blocks (2/2 at max)
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestCompositeRule2First", context);
});
@ -442,7 +442,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
// TestCompositeParamIp: Rule1 (Parameter, 5/hour), Rule2 (ClientIp, 3/hour)
// IP limit (3) is lower, should trigger first
var param = $"param-ip-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// 3 successful requests
await _checker.CheckAsync("TestCompositeParamIp", context);
@ -450,7 +450,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
await _checker.CheckAsync("TestCompositeParamIp", context);
// 4th: IP rule blocks (3/3)
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCompositeParamIp", context);
});
@ -475,8 +475,8 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
// but share the same Rule2 (IP) counter
var param1 = $"share-ip-1-{Guid.NewGuid()}";
var param2 = $"share-ip-2-{Guid.NewGuid()}";
var context1 = new OperationRateLimitContext { Parameter = param1 };
var context2 = new OperationRateLimitContext { Parameter = param2 };
var context1 = new OperationRateLimitingContext { Parameter = param1 };
var context2 = new OperationRateLimitingContext { Parameter = param2 };
// 2 requests with param1
await _checker.CheckAsync("TestCompositeParamIp", context1);
@ -486,7 +486,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
await _checker.CheckAsync("TestCompositeParamIp", context2);
// 4th request with param2: IP rule blocks (3/3 from combined)
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCompositeParamIp", context2);
});
@ -516,9 +516,9 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
var param = $"triple-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// 3 successful requests
await checker.CheckAsync("TestCompositeTriple", context);
@ -526,7 +526,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
await checker.CheckAsync("TestCompositeTriple", context);
// 4th: IP rule blocks (3/3)
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestCompositeTriple", context);
});
@ -559,9 +559,9 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
var param = $"triple-nowaste-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// 3 successful requests (all rules increment to 3)
await checker.CheckAsync("TestCompositeTriple", context);
@ -571,7 +571,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
// Attempt 3 more blocked requests
for (var i = 0; i < 3; i++)
{
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestCompositeTriple", context);
});
@ -599,16 +599,16 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
using (principalAccessor.Change(claimsPrincipal))
{
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitChecker>();
var checker = scope.ServiceProvider.GetRequiredService<IOperationRateLimitingChecker>();
var param = $"triple-reset-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// Exhaust IP limit
await checker.CheckAsync("TestCompositeTriple", context);
await checker.CheckAsync("TestCompositeTriple", context);
await checker.CheckAsync("TestCompositeTriple", context);
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await checker.CheckAsync("TestCompositeTriple", context);
});
@ -633,7 +633,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
public async Task Should_Throw_When_PhoneNumber_Not_Available()
{
// No Parameter and no authenticated user
var context = new OperationRateLimitContext();
var context = new OperationRateLimitingContext();
await Assert.ThrowsAsync<AbpException>(async () =>
{
@ -644,9 +644,9 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
[Fact]
public async Task Should_Deny_First_Request_When_MaxCount_Is_Zero()
{
var context = new OperationRateLimitContext { Parameter = $"ban-{Guid.NewGuid()}" };
var context = new OperationRateLimitingContext { Parameter = $"ban-{Guid.NewGuid()}" };
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestBanPolicy", context);
});
@ -659,7 +659,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
[Fact]
public async Task Should_IsAllowed_Return_False_When_MaxCount_Is_Zero()
{
var context = new OperationRateLimitContext { Parameter = $"ban-allowed-{Guid.NewGuid()}" };
var context = new OperationRateLimitingContext { Parameter = $"ban-allowed-{Guid.NewGuid()}" };
var allowed = await _checker.IsAllowedAsync("TestBanPolicy", context);
allowed.ShouldBeFalse();
@ -668,7 +668,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
[Fact]
public async Task Should_GetStatus_Show_Not_Allowed_When_MaxCount_Is_Zero()
{
var context = new OperationRateLimitContext { Parameter = $"ban-status-{Guid.NewGuid()}" };
var context = new OperationRateLimitingContext { Parameter = $"ban-status-{Guid.NewGuid()}" };
var status = await _checker.GetStatusAsync("TestBanPolicy", context);
status.IsAllowed.ShouldBeFalse();
@ -684,14 +684,14 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
var param1 = $"op1-{Guid.NewGuid()}";
var param2 = $"op2-{Guid.NewGuid()}";
var ctx1 = new OperationRateLimitContext { Parameter = param1 };
var ctx2 = new OperationRateLimitContext { Parameter = param2 };
var ctx1 = new OperationRateLimitingContext { Parameter = param1 };
var ctx2 = new OperationRateLimitingContext { Parameter = param2 };
// Exhaust param1's quota (max=2)
await _checker.CheckAsync("TestCustomResolver", ctx1);
await _checker.CheckAsync("TestCustomResolver", ctx1);
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCustomResolver", ctx1);
});
@ -704,7 +704,7 @@ public class OperationRateLimitChecker_Tests : OperationRateLimitTestBase
[Fact]
public void Should_Throw_When_Policy_Has_Duplicate_Rules()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
Assert.Throws<AbpException>(() =>
{

38
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/OperationRateLimitFrontendIntegration_Tests.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingFrontendIntegration_Tests.cs

@ -6,19 +6,19 @@ using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Localization;
using Xunit;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTestBase
public class OperationRateLimitingFrontendIntegration_Tests : OperationRateLimitingTestBase
{
private readonly IOperationRateLimitChecker _checker;
private readonly IOperationRateLimitingChecker _checker;
private readonly IExceptionToErrorInfoConverter _errorInfoConverter;
private readonly IOperationRateLimitFormatter _formatter;
private readonly IOperationRateLimitingFormatter _formatter;
public OperationRateLimitFrontendIntegration_Tests()
public OperationRateLimitingFrontendIntegration_Tests()
{
_checker = GetRequiredService<IOperationRateLimitChecker>();
_checker = GetRequiredService<IOperationRateLimitingChecker>();
_errorInfoConverter = GetRequiredService<IExceptionToErrorInfoConverter>();
_formatter = GetRequiredService<IOperationRateLimitFormatter>();
_formatter = GetRequiredService<IOperationRateLimitingFormatter>();
}
[Fact]
@ -27,13 +27,13 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
using (CultureHelper.Use("en"))
{
var param = $"frontend-en-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestSimple", context);
});
@ -52,13 +52,13 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
using (CultureHelper.Use("zh-Hans"))
{
var param = $"frontend-zh-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestSimple", context);
});
@ -75,7 +75,7 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
public async Task ErrorInfo_Should_Include_Structured_Data_For_Frontend()
{
var param = $"frontend-data-{Guid.NewGuid()}";
var context = new OperationRateLimitContext
var context = new OperationRateLimitingContext
{
Parameter = param,
ExtraProperties =
@ -88,7 +88,7 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
await _checker.CheckAsync("TestSimple", context);
await _checker.CheckAsync("TestSimple", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestSimple", context);
});
@ -96,7 +96,7 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
var errorInfo = _errorInfoConverter.Convert(exception);
// Frontend receives error.code
errorInfo.Code.ShouldBe(AbpOperationRateLimitErrorCodes.ExceedLimit);
errorInfo.Code.ShouldBe(AbpOperationRateLimitingErrorCodes.ExceedLimit);
// Frontend receives error.data for countdown timer and UI display
exception.Data["PolicyName"].ShouldBe("TestSimple");
@ -144,7 +144,7 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
public async Task GetStatusAsync_Should_Provide_Countdown_Data_For_Frontend()
{
var param = $"frontend-status-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// Before any requests: frontend can show "3 remaining"
var status = await _checker.GetStatusAsync("TestSimple", context);
@ -180,12 +180,12 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
public async Task Custom_ErrorCode_Should_Appear_In_ErrorInfo()
{
var param = $"frontend-custom-code-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestCustomErrorCode", context);
await _checker.CheckAsync("TestCustomErrorCode", context);
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
var exception = await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestCustomErrorCode", context);
});
@ -364,7 +364,7 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
public async Task Reset_Should_Allow_Frontend_To_Resume()
{
var param = $"frontend-reset-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// Exhaust limit
await _checker.CheckAsync("TestSimple", context);
@ -389,7 +389,7 @@ public class OperationRateLimitFrontendIntegration_Tests : OperationRateLimitTes
public async Task IsAllowedAsync_Can_Be_Used_For_Frontend_PreCheck()
{
var param = $"frontend-precheck-{Guid.NewGuid()}";
var context = new OperationRateLimitContext { Parameter = param };
var context = new OperationRateLimitingContext { Parameter = param };
// Frontend precheck: button should be enabled
(await _checker.IsAllowedAsync("TestSimple", context)).ShouldBeTrue();

28
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/OperationRateLimitMultiTenant_Tests.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingMultiTenant_Tests.cs

@ -5,24 +5,24 @@ using Shouldly;
using Volo.Abp.MultiTenancy;
using Xunit;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
/// <summary>
/// Verifies per-tenant isolation for tenant-scoped partition types and
/// global (cross-tenant) sharing for ClientIp partition type.
/// </summary>
public class OperationRateLimitMultiTenant_Tests : OperationRateLimitTestBase
public class OperationRateLimitingMultiTenant_Tests : OperationRateLimitingTestBase
{
private readonly ICurrentTenant _currentTenant;
private readonly IOperationRateLimitChecker _checker;
private readonly IOperationRateLimitingChecker _checker;
private static readonly Guid TenantA = Guid.NewGuid();
private static readonly Guid TenantB = Guid.NewGuid();
public OperationRateLimitMultiTenant_Tests()
public OperationRateLimitingMultiTenant_Tests()
{
_currentTenant = GetRequiredService<ICurrentTenant>();
_checker = GetRequiredService<IOperationRateLimitChecker>();
_checker = GetRequiredService<IOperationRateLimitingChecker>();
}
[Fact]
@ -33,12 +33,12 @@ public class OperationRateLimitMultiTenant_Tests : OperationRateLimitTestBase
using (_currentTenant.Change(TenantA))
{
var ctx = new OperationRateLimitContext { Parameter = param };
var ctx = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestMultiTenantByParameter", ctx);
await _checker.CheckAsync("TestMultiTenantByParameter", ctx);
// Tenant A exhausted (max=2)
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestMultiTenantByParameter", ctx);
});
@ -46,7 +46,7 @@ public class OperationRateLimitMultiTenant_Tests : OperationRateLimitTestBase
using (_currentTenant.Change(TenantB))
{
var ctx = new OperationRateLimitContext { Parameter = param };
var ctx = new OperationRateLimitingContext { Parameter = param };
// Tenant B has its own counter and should still be allowed
await _checker.CheckAsync("TestMultiTenantByParameter", ctx);
@ -63,17 +63,17 @@ public class OperationRateLimitMultiTenant_Tests : OperationRateLimitTestBase
using (_currentTenant.Change(TenantA))
{
var ctx = new OperationRateLimitContext();
var ctx = new OperationRateLimitingContext();
await _checker.CheckAsync("TestMultiTenantByClientIp", ctx);
await _checker.CheckAsync("TestMultiTenantByClientIp", ctx);
}
using (_currentTenant.Change(TenantB))
{
var ctx = new OperationRateLimitContext();
var ctx = new OperationRateLimitingContext();
// Tenant B shares the same IP counter; should be at limit now
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestMultiTenantByClientIp", ctx);
});
@ -87,10 +87,10 @@ public class OperationRateLimitMultiTenant_Tests : OperationRateLimitTestBase
var param = $"host-vs-tenant-{Guid.NewGuid()}";
// Host context: exhaust quota
var hostCtx = new OperationRateLimitContext { Parameter = param };
var hostCtx = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestMultiTenantByParameter", hostCtx);
await _checker.CheckAsync("TestMultiTenantByParameter", hostCtx);
await Assert.ThrowsAsync<AbpOperationRateLimitException>(async () =>
await Assert.ThrowsAsync<AbpOperationRateLimitingException>(async () =>
{
await _checker.CheckAsync("TestMultiTenantByParameter", hostCtx);
});
@ -98,7 +98,7 @@ public class OperationRateLimitMultiTenant_Tests : OperationRateLimitTestBase
// Tenant A should have its own counter, unaffected by host
using (_currentTenant.Change(TenantA))
{
var tenantCtx = new OperationRateLimitContext { Parameter = param };
var tenantCtx = new OperationRateLimitingContext { Parameter = param };
await _checker.CheckAsync("TestMultiTenantByParameter", tenantCtx);
(await _checker.IsAllowedAsync("TestMultiTenantByParameter", tenantCtx)).ShouldBeTrue();
}

54
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/OperationRateLimitPolicyBuilder_Tests.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingPolicyBuilder_Tests.cs

@ -2,14 +2,14 @@ using System;
using Shouldly;
using Xunit;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitPolicyBuilder_Tests
public class OperationRateLimitingPolicyBuilder_Tests
{
[Fact]
public void Should_Build_Simple_Policy()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
options.AddPolicy("TestPolicy", policy =>
{
policy.WithFixedWindow(TimeSpan.FromHours(1), maxCount: 5)
@ -22,14 +22,14 @@ public class OperationRateLimitPolicyBuilder_Tests
policy.Rules.Count.ShouldBe(1);
policy.Rules[0].Duration.ShouldBe(TimeSpan.FromHours(1));
policy.Rules[0].MaxCount.ShouldBe(5);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.Parameter);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.Parameter);
policy.ErrorCode.ShouldBeNull();
}
[Fact]
public void Should_Build_Composite_Policy()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
options.AddPolicy("CompositePolicy", policy =>
{
policy.AddRule(rule => rule
@ -45,16 +45,16 @@ public class OperationRateLimitPolicyBuilder_Tests
policy.Name.ShouldBe("CompositePolicy");
policy.Rules.Count.ShouldBe(2);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.Parameter);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.Parameter);
policy.Rules[0].MaxCount.ShouldBe(3);
policy.Rules[1].PartitionType.ShouldBe(OperationRateLimitPartitionType.CurrentUser);
policy.Rules[1].PartitionType.ShouldBe(OperationRateLimitingPartitionType.CurrentUser);
policy.Rules[1].MaxCount.ShouldBe(10);
}
[Fact]
public void Should_Set_ErrorCode()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
options.AddPolicy("ErrorPolicy", policy =>
{
policy.WithFixedWindow(TimeSpan.FromHours(1), maxCount: 2)
@ -69,7 +69,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Build_Custom_Partition()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
options.AddPolicy("CustomPolicy", policy =>
{
policy.AddRule(rule => rule
@ -80,14 +80,14 @@ public class OperationRateLimitPolicyBuilder_Tests
var policy = options.Policies["CustomPolicy"];
policy.Rules.Count.ShouldBe(1);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.Custom);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.Custom);
policy.Rules[0].CustomPartitionKeyResolver.ShouldNotBeNull();
}
[Fact]
public void Should_Support_All_Partition_Types()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
options.AddPolicy("P1", p => p.WithFixedWindow(TimeSpan.FromHours(1), 1).PartitionByParameter());
options.AddPolicy("P2", p => p.WithFixedWindow(TimeSpan.FromHours(1), 1).PartitionByCurrentUser());
@ -96,18 +96,18 @@ public class OperationRateLimitPolicyBuilder_Tests
options.AddPolicy("P5", p => p.WithFixedWindow(TimeSpan.FromHours(1), 1).PartitionByEmail());
options.AddPolicy("P6", p => p.WithFixedWindow(TimeSpan.FromHours(1), 1).PartitionByPhoneNumber());
options.Policies["P1"].Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.Parameter);
options.Policies["P2"].Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.CurrentUser);
options.Policies["P3"].Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.CurrentTenant);
options.Policies["P4"].Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.ClientIp);
options.Policies["P5"].Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.Email);
options.Policies["P6"].Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.PhoneNumber);
options.Policies["P1"].Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.Parameter);
options.Policies["P2"].Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.CurrentUser);
options.Policies["P3"].Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.CurrentTenant);
options.Policies["P4"].Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.ClientIp);
options.Policies["P5"].Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.Email);
options.Policies["P6"].Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.PhoneNumber);
}
[Fact]
public void Should_Throw_When_Policy_Has_No_Rules()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
var exception = Assert.Throws<AbpException>(() =>
{
@ -123,7 +123,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Throw_When_WithFixedWindow_Without_PartitionBy()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
var exception = Assert.Throws<AbpException>(() =>
{
@ -140,7 +140,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Throw_When_AddRule_Without_WithFixedWindow()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
var exception = Assert.Throws<AbpException>(() =>
{
@ -159,7 +159,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Allow_MaxCount_Zero_For_Ban_Policy()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
// maxCount=0 is a valid "ban" policy - always deny
options.AddPolicy("BanPolicy", policy =>
@ -175,7 +175,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Throw_When_AddRule_Without_PartitionBy()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
var exception = Assert.Throws<AbpException>(() =>
{
@ -193,7 +193,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Throw_When_MaxCount_Is_Negative()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
var exception = Assert.Throws<AbpException>(() =>
{
@ -210,7 +210,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Allow_Same_Rule_With_Different_MultiTenancy()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
// Same Duration/MaxCount/PartitionType but different IsMultiTenant should be allowed
options.AddPolicy("MultiTenancyPolicy", policy =>
@ -234,7 +234,7 @@ public class OperationRateLimitPolicyBuilder_Tests
[Fact]
public void Should_Allow_Multiple_Custom_Partition_Rules()
{
var options = new AbpOperationRateLimitOptions();
var options = new AbpOperationRateLimitingOptions();
// Multiple custom partition rules with same Duration/MaxCount should be allowed
// because they may use different key resolvers
@ -251,7 +251,7 @@ public class OperationRateLimitPolicyBuilder_Tests
var policy = options.Policies["MultiCustomPolicy"];
policy.Rules.Count.ShouldBe(2);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitPartitionType.Custom);
policy.Rules[1].PartitionType.ShouldBe(OperationRateLimitPartitionType.Custom);
policy.Rules[0].PartitionType.ShouldBe(OperationRateLimitingPartitionType.Custom);
policy.Rules[1].PartitionType.ShouldBe(OperationRateLimitingPartitionType.Custom);
}
}

4
framework/test/Volo.Abp.OperationRateLimit.Tests/Volo/Abp/OperationRateLimit/OperationRateLimitTestBase.cs → framework/test/Volo.Abp.OperationRateLimiting.Tests/Volo/Abp/OperationRateLimiting/OperationRateLimitingTestBase.cs

@ -1,8 +1,8 @@
using Volo.Abp.Testing;
namespace Volo.Abp.OperationRateLimit;
namespace Volo.Abp.OperationRateLimiting;
public class OperationRateLimitTestBase : AbpIntegratedTest<AbpOperationRateLimitTestModule>
public class OperationRateLimitingTestBase : AbpIntegratedTest<AbpOperationRateLimitingTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
Loading…
Cancel
Save