public ApiCostsFilterTests() { actionContext = new ActionExecutingContext( new ActionContext(httpContext, new RouteData(), new ActionDescriptor()), new List <IFilterMetadata>(), new Dictionary <string, object>(), null); A.CallTo(() => appPlansProvider.GetPlan(null)) .Returns(appPlan); A.CallTo(() => appPlansProvider.GetPlanForApp(appEntity)) .Returns((appPlan, "free")); A.CallTo(() => appPlan.BlockingApiCalls) .ReturnsLazily(x => apiCallsBlocking); A.CallTo(() => usageTracker.GetMonthCostsAsync(A <string> ._, DateTime.Today)) .ReturnsLazily(x => Task.FromResult(apiCallsCurrent)); next = () => { isNextCalled = true; return(Task.FromResult <ActionExecutedContext?>(null)); }; sut = new ApiCostsFilter(appPlansProvider, usageTracker); }
public async Task CheckUsagesAsync() { var today = DateTime.Today; foreach (var(key, target) in state.Value.Targets) { var from = GetFromDate(today, target.NumDays); if (!target.Triggered.HasValue || target.Triggered < from) { var costs = await usageTracker.GetMonthCostsAsync(target.AppId.Id.ToString(), today); var limit = target.Limits; if (costs > limit) { target.Triggered = today; var @event = new AppUsageExceeded { AppId = target.AppId, CallsCurrent = costs, CallsLimit = limit, RuleId = key }; await state.WriteEventAsync(Envelope.Create <IEvent>(@event)); } } } await state.WriteAsync(); }
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { context.HttpContext.Features.Set <IApiCostsFeature>(FilterDefinition); var app = context.HttpContext.Context().App; if (app != null) { var appId = app.Id.ToString(); if (FilterDefinition.Costs > 0) { using (Profiler.Trace("CheckUsage")) { var(plan, _) = appPlansProvider.GetPlanForApp(app); var usage = await usageTracker.GetMonthCostsAsync(appId, DateTime.Today); if (plan.BlockingApiCalls >= 0 && usage > plan.BlockingApiCalls) { context.Result = new StatusCodeResult(429); return; } } } context.HttpContext.Response.Headers.Add("X-Costs", FilterDefinition.Costs.ToString()); } await next(); }
public virtual async Task <bool> IsBlockedAsync(IAppEntity app, DateTime today) { Guard.NotNull(app, nameof(app)); var isLocked = false; var(plan, _) = appPlansProvider.GetPlanForApp(app); if (plan.MaxApiCalls > 0 || plan.BlockingApiCalls > 0) { var appId = app.Id; var usage = await apiUsageTracker.GetMonthCostsAsync(appId.ToString(), today); if (IsAboutToBeLocked(today, plan.MaxApiCalls, usage) && !HasNotifiedBefore(app.Id)) { var users = app.Contributors.Where(x => x.Value == Role.Owner).Select(x => x.Key).ToArray(); var notification = new UsageNotification { AppId = appId, AppName = app.Name, Usage = usage, UsageLimit = plan.MaxApiCalls, Users = users }; usageLimitNotifier.NotifyAsync(notification).Forget(); TrackNotified(appId); } isLocked = plan.BlockingApiCalls > 0 && usage > plan.BlockingApiCalls; } return(isLocked); }
public UsageGateTests() { appEntity = Mocks.App(appId); A.CallTo(() => grainFactory.GetGrain <IUsageNotifierGrain>(SingleGrain.Id, null)) .Returns(usageNotifierGrain); A.CallTo(() => appPlansProvider.GetPlan(null)) .Returns(appPlan); A.CallTo(() => appPlansProvider.GetPlanForApp(appEntity)) .Returns((appPlan, "free")); A.CallTo(() => appPlan.MaxApiCalls) .ReturnsLazily(x => apiCallsMax); A.CallTo(() => appPlan.BlockingApiCalls) .ReturnsLazily(x => apiCallsBlocking); A.CallTo(() => usageTracker.GetMonthCostsAsync(appId.Id.ToString(), today)) .ReturnsLazily(x => Task.FromResult(apiCallsCurrent)); sut = new UsageGate(appPlansProvider, usageTracker, grainFactory); }