| | | 1 | | using Intentum.Core.Intents; |
| | | 2 | | using Intentum.Runtime.Engine; |
| | | 3 | | using Intentum.Runtime.Localization; |
| | | 4 | | using Intentum.Runtime.Policy; |
| | | 5 | | using Intentum.Runtime.RateLimiting; |
| | | 6 | | |
| | | 7 | | namespace Intentum.Runtime; |
| | | 8 | | |
| | | 9 | | public static class RuntimeExtensions |
| | | 10 | | { |
| | | 11 | | [Obsolete("Use DecideWithRateLimitAsync for async scenarios. This method will be removed in v2.0.")] |
| | | 12 | | [System.Diagnostics.CodeAnalysis.SuppressMessage("Intentionality", "S1133", Justification = "Deprecated until v2.0 f |
| | | 13 | | public static PolicyDecision Decide( |
| | | 14 | | this Intent intent, |
| | | 15 | | IntentPolicy policy) |
| | 1 | 16 | | { |
| | 1 | 17 | | ArgumentNullException.ThrowIfNull(intent); |
| | 1 | 18 | | ArgumentNullException.ThrowIfNull(policy); |
| | 1 | 19 | | return IntentPolicyEngine.Evaluate(intent, policy); |
| | 1 | 20 | | } |
| | | 21 | | |
| | | 22 | | /// <summary> |
| | | 23 | | /// Decides using context-aware policy (intent + system load, region, etc.). |
| | | 24 | | /// </summary> |
| | | 25 | | public static PolicyDecision Decide( |
| | | 26 | | this Intent intent, |
| | | 27 | | PolicyContext context, |
| | | 28 | | ContextAwareIntentPolicy policy) |
| | 0 | 29 | | { |
| | 0 | 30 | | ArgumentNullException.ThrowIfNull(intent); |
| | 0 | 31 | | ArgumentNullException.ThrowIfNull(context); |
| | 0 | 32 | | ArgumentNullException.ThrowIfNull(policy); |
| | 0 | 33 | | return ContextAwarePolicyEngine.Evaluate(intent, context, policy); |
| | 0 | 34 | | } |
| | | 35 | | |
| | | 36 | | /// <summary> |
| | | 37 | | /// Decides using an A/B policy variant set (selector picks which policy applies to this intent). |
| | | 38 | | /// </summary> |
| | | 39 | | public static PolicyDecision Decide( |
| | | 40 | | this Intent intent, |
| | | 41 | | PolicyVariantSet variantSet) |
| | 3 | 42 | | { |
| | 3 | 43 | | ArgumentNullException.ThrowIfNull(intent); |
| | 3 | 44 | | ArgumentNullException.ThrowIfNull(variantSet); |
| | 3 | 45 | | return variantSet.Decide(intent); |
| | 3 | 46 | | } |
| | | 47 | | |
| | | 48 | | /// <summary> |
| | | 49 | | /// Decides policy and, when decision is RateLimit, checks the rate limiter. |
| | | 50 | | /// Returns the policy decision; use <paramref name="rateLimitResult"/> when decision is RateLimit. |
| | | 51 | | /// </summary> |
| | | 52 | | public static PolicyDecision DecideWithRateLimit( |
| | | 53 | | this Intent intent, |
| | | 54 | | IntentPolicy policy, |
| | | 55 | | IRateLimiter rateLimiter, |
| | | 56 | | RateLimitOptions options, |
| | | 57 | | out RateLimitResult? rateLimitResult, |
| | | 58 | | CancellationToken cancellationToken = default) |
| | 2 | 59 | | { |
| | 2 | 60 | | ArgumentNullException.ThrowIfNull(intent); |
| | 2 | 61 | | ArgumentNullException.ThrowIfNull(policy); |
| | 2 | 62 | | ArgumentNullException.ThrowIfNull(rateLimiter); |
| | 2 | 63 | | ArgumentNullException.ThrowIfNull(options); |
| | | 64 | | |
| | 2 | 65 | | var decision = IntentPolicyEngine.Evaluate(intent, policy); |
| | 2 | 66 | | rateLimitResult = null; |
| | 2 | 67 | | if (decision != PolicyDecision.RateLimit) |
| | 1 | 68 | | return decision; |
| | 1 | 69 | | var result = rateLimiter.TryAcquireAsync(options.Key, options.Limit, options.Window, cancellationToken).AsTask() |
| | 1 | 70 | | rateLimitResult = result; |
| | 1 | 71 | | return decision; |
| | 2 | 72 | | } |
| | | 73 | | |
| | | 74 | | /// <summary> |
| | | 75 | | /// Async version: decides policy and, when decision is RateLimit, checks the rate limiter. |
| | | 76 | | /// </summary> |
| | | 77 | | public static async ValueTask<(PolicyDecision Decision, RateLimitResult? RateLimitResult)> DecideWithRateLimitAsync( |
| | | 78 | | this Intent intent, |
| | | 79 | | IntentPolicy policy, |
| | | 80 | | IRateLimiter rateLimiter, |
| | | 81 | | RateLimitOptions options, |
| | | 82 | | CancellationToken cancellationToken = default) |
| | 2 | 83 | | { |
| | 2 | 84 | | ArgumentNullException.ThrowIfNull(intent); |
| | 2 | 85 | | ArgumentNullException.ThrowIfNull(policy); |
| | 2 | 86 | | ArgumentNullException.ThrowIfNull(rateLimiter); |
| | 2 | 87 | | ArgumentNullException.ThrowIfNull(options); |
| | | 88 | | |
| | 2 | 89 | | var decision = IntentPolicyEngine.Evaluate(intent, policy); |
| | 2 | 90 | | if (decision != PolicyDecision.RateLimit) |
| | 1 | 91 | | return (decision, null); |
| | 1 | 92 | | var result = await rateLimiter.TryAcquireAsync(options.Key, options.Limit, options.Window, cancellationToken); |
| | 1 | 93 | | return (decision, result); |
| | 2 | 94 | | } |
| | | 95 | | |
| | | 96 | | public static string ToLocalizedString( |
| | | 97 | | this PolicyDecision decision, |
| | | 98 | | IIntentumLocalizer localizer) |
| | 10 | 99 | | { |
| | 10 | 100 | | ArgumentNullException.ThrowIfNull(localizer); |
| | | 101 | | |
| | 10 | 102 | | var key = decision switch |
| | 10 | 103 | | { |
| | 1 | 104 | | PolicyDecision.Allow => LocalizationKeys.DecisionAllow, |
| | 1 | 105 | | PolicyDecision.Observe => LocalizationKeys.DecisionObserve, |
| | 1 | 106 | | PolicyDecision.Warn => LocalizationKeys.DecisionWarn, |
| | 1 | 107 | | PolicyDecision.Block => LocalizationKeys.DecisionBlock, |
| | 2 | 108 | | PolicyDecision.Escalate => LocalizationKeys.DecisionEscalate, |
| | 2 | 109 | | PolicyDecision.RequireAuth => LocalizationKeys.DecisionRequireAuth, |
| | 2 | 110 | | PolicyDecision.RateLimit => LocalizationKeys.DecisionRateLimit, |
| | 0 | 111 | | _ => decision.ToString() |
| | 10 | 112 | | }; |
| | | 113 | | |
| | 10 | 114 | | return localizer.Get(key); |
| | 10 | 115 | | } |
| | | 116 | | } |