Browse Source

Naming fixed.

pull/491/head
Sebastian 7 years ago
parent
commit
04c014c981
  1. 4
      backend/src/Squidex.Domain.Apps.Entities/Assets/AssetUsageTracker.cs
  2. 6
      backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs
  3. 12
      backend/src/Squidex.Infrastructure/UsageTracking/ApiStats.cs
  4. 26
      backend/src/Squidex.Infrastructure/UsageTracking/ApiStatsSummary.cs
  5. 34
      backend/src/Squidex.Infrastructure/UsageTracking/ApiUsageTracker.cs
  6. 2
      backend/src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs
  7. 2
      backend/src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs
  8. 6
      backend/src/Squidex.Infrastructure/UsageTracking/IApiUsageTracker.cs
  9. 2
      backend/src/Squidex.Infrastructure/UsageTracking/IUsageTracker.cs
  10. 6
      backend/src/Squidex.Web/ApiCostsAttribute.cs
  11. 2
      backend/src/Squidex.Web/IApiCostsFeature.cs
  12. 4
      backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs
  13. 2
      backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs
  14. 19
      backend/src/Squidex.Web/Pipeline/UsageMiddleware.cs
  15. 10
      backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/ApiUsageDto.cs
  16. 8
      backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/ApiUsagesDto.cs
  17. 4
      backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs
  18. 38
      backend/tests/Squidex.Infrastructure.Tests/UsageTracking/ApiUsageTrackerTests.cs
  19. 4
      backend/tests/Squidex.Web.Tests/ApiCostsAttributeTests.cs
  20. 2
      backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs
  21. 4
      backend/tests/Squidex.Web.Tests/Pipeline/CleanupHostMiddlewareTests.cs
  22. 4
      backend/tests/Squidex.Web.Tests/Pipeline/UsageMiddlewareTests.cs
  23. 6
      frontend/app/features/dashboard/pages/dashboard-page.component.ts
  24. 6
      frontend/app/shared/services/usages.service.spec.ts
  25. 8
      frontend/app/shared/services/usages.service.ts

4
backend/src/Squidex.Domain.Apps.Entities/Assets/AssetUsageTracker.cs

@ -57,9 +57,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
return enriched; return enriched;
} }
private static void AddCounters(List<AssetStats> enriched, List<(DateTime Date, Counters Counters)>? byCategory) private static void AddCounters(List<AssetStats> enriched, List<(DateTime, Counters)> details)
{ {
foreach (var (date, counters) in byCategory) foreach (var (date, counters) in details)
{ {
var totalCount = counters.GetInt64(CounterTotalCount); var totalCount = counters.GetInt64(CounterTotalCount);
var totalSize = counters.GetInt64(CounterTotalSize); var totalSize = counters.GetInt64(CounterTotalSize);

6
backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs

@ -83,18 +83,18 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
if (!target.Triggered.HasValue || target.Triggered < from) if (!target.Triggered.HasValue || target.Triggered < from)
{ {
var usage = await usageTracker.GetMonthlyWeightAsync(target.AppId.Id.ToString(), today); var costs = await usageTracker.GetMonthCostsAsync(target.AppId.Id.ToString(), today);
var limit = target.Limits; var limit = target.Limits;
if (usage > limit) if (costs > limit)
{ {
target.Triggered = today; target.Triggered = today;
var @event = new AppUsageExceeded var @event = new AppUsageExceeded
{ {
AppId = target.AppId, AppId = target.AppId,
CallsCurrent = usage, CallsCurrent = costs,
CallsLimit = limit, CallsLimit = limit,
RuleId = key RuleId = key
}; };

12
backend/src/Squidex.Infrastructure/UsageTracking/ApiStats.cs

@ -5,22 +5,28 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
namespace Squidex.Infrastructure.UsageTracking namespace Squidex.Infrastructure.UsageTracking
{ {
public sealed class ApiStats public sealed class ApiStats
{ {
public DateTime Date { get; }
public long TotalCalls { get; } public long TotalCalls { get; }
public long TotalBytes { get; } public long TotalBytes { get; }
public double AverageElapsed { get; } public double AverageElapsedMs { get; }
public ApiStats(long totalCalls, double averageElapsed, long totalBytes) public ApiStats(DateTime date, long totalCalls, double averageElapsedMs, long totalBytes)
{ {
Date = date;
TotalCalls = totalCalls; TotalCalls = totalCalls;
TotalBytes = totalBytes; TotalBytes = totalBytes;
AverageElapsed = averageElapsed; AverageElapsedMs = averageElapsedMs;
} }
} }
} }

26
backend/src/Squidex.Infrastructure/UsageTracking/ApiStatsSummary.cs

@ -0,0 +1,26 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure.UsageTracking
{
public sealed class ApiStatsSummary
{
public long TotalCalls { get; }
public long TotalBytes { get; }
public double AverageElapsedMs { get; }
public ApiStatsSummary(long totalCalls, double averageElapsedMs, long totalBytes)
{
TotalCalls = totalCalls;
TotalBytes = totalBytes;
AverageElapsedMs = averageElapsedMs;
}
}
}

34
backend/src/Squidex.Infrastructure/UsageTracking/ApiUsageTracker.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.UsageTracking
public sealed class ApiUsageTracker : IApiUsageTracker public sealed class ApiUsageTracker : IApiUsageTracker
{ {
public const string CounterTotalBytes = "TotalBytes"; public const string CounterTotalBytes = "TotalBytes";
public const string CounterTotalWeight = "TotalWeight"; public const string CounterTotalCosts = "TotalCosts";
public const string CounterTotalCalls = "TotalCalls"; public const string CounterTotalCalls = "TotalCalls";
public const string CounterTotalElapsedMs = "TotalElapsedMs"; public const string CounterTotalElapsedMs = "TotalElapsedMs";
private readonly IUsageTracker usageTracker; private readonly IUsageTracker usageTracker;
@ -24,37 +24,37 @@ namespace Squidex.Infrastructure.UsageTracking
this.usageTracker = usageTracker; this.usageTracker = usageTracker;
} }
public async Task<long> GetMonthlyWeightAsync(string key, DateTime date) public async Task<long> GetMonthCostsAsync(string key, DateTime date)
{ {
var apiKey = GetKey(key); var apiKey = GetKey(key);
var counters = await usageTracker.GetForMonthAsync(apiKey, date); var counters = await usageTracker.GetForMonthAsync(apiKey, date);
return counters.GetInt64(CounterTotalWeight); return counters.GetInt64(CounterTotalCosts);
} }
public Task TrackAsync(DateTime date, string key, string? category, double weight, long elapsed, long bytes) public Task TrackAsync(DateTime date, string key, string? category, double costs, long elapsedMs, long bytes)
{ {
var apiKey = GetKey(key); var apiKey = GetKey(key);
var counters = new Counters var counters = new Counters
{ {
[CounterTotalWeight] = weight, [CounterTotalCosts] = costs,
[CounterTotalCalls] = 1, [CounterTotalCalls] = 1,
[CounterTotalElapsedMs] = elapsed, [CounterTotalElapsedMs] = elapsedMs,
[CounterTotalBytes] = bytes [CounterTotalBytes] = bytes
}; };
return usageTracker.TrackAsync(date, apiKey, category, counters); return usageTracker.TrackAsync(date, apiKey, category, counters);
} }
public async Task<(ApiStats Summary, Dictionary<string, List<(DateTime Date, ApiStats Stats)>> Details)> QueryAsync(string key, DateTime fromDate, DateTime toDate) public async Task<(ApiStatsSummary, Dictionary<string, List<ApiStats>> Details)> QueryAsync(string key, DateTime fromDate, DateTime toDate)
{ {
var apiKey = GetKey(key); var apiKey = GetKey(key);
var queries = await usageTracker.QueryAsync(apiKey, fromDate, toDate); var queries = await usageTracker.QueryAsync(apiKey, fromDate, toDate);
var result = new Dictionary<string, List<(DateTime Date, ApiStats Stats)>>(); var details = new Dictionary<string, List<ApiStats>>();
var summaryBytes = 0L; var summaryBytes = 0L;
var summaryCalls = 0L; var summaryCalls = 0L;
@ -62,30 +62,30 @@ namespace Squidex.Infrastructure.UsageTracking
foreach (var (category, usages) in queries) foreach (var (category, usages) in queries)
{ {
var resultByCategory = new List<(DateTime Date, ApiStats)>(); var resultByCategory = new List<ApiStats>();
foreach (var usage in usages) foreach (var (date, counters) in usages)
{ {
var dateBytes = usage.Counters.GetInt64(CounterTotalBytes); var dateBytes = counters.GetInt64(CounterTotalBytes);
var dateCalls = usage.Counters.GetInt64(CounterTotalCalls); var dateCalls = counters.GetInt64(CounterTotalCalls);
var dateElapsed = usage.Counters.GetInt64(CounterTotalElapsedMs); var dateElapsed = counters.GetInt64(CounterTotalElapsedMs);
var dateElapsedAvg = CalculateAverage(dateCalls, dateElapsed); var dateElapsedAvg = CalculateAverage(dateCalls, dateElapsed);
resultByCategory.Add((usage.Date, new ApiStats(dateCalls, dateElapsedAvg, dateBytes))); resultByCategory.Add(new ApiStats(date, dateCalls, dateElapsedAvg, dateBytes));
summaryBytes += dateBytes; summaryBytes += dateBytes;
summaryCalls += dateCalls; summaryCalls += dateCalls;
summaryElapsed += dateElapsed; summaryElapsed += dateElapsed;
} }
result[category] = resultByCategory; details[category] = resultByCategory;
} }
var summaryElapsedAvg = CalculateAverage(summaryCalls, summaryElapsed); var summaryElapsedAvg = CalculateAverage(summaryCalls, summaryElapsed);
var summary = new ApiStats(summaryCalls, summaryElapsedAvg, summaryBytes); var summary = new ApiStatsSummary(summaryCalls, summaryElapsedAvg, summaryBytes);
return (summary, result); return (summary, details);
} }
private static double CalculateAverage(long calls, long elapsed) private static double CalculateAverage(long calls, long elapsed)

2
backend/src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs

@ -100,7 +100,7 @@ namespace Squidex.Infrastructure.UsageTracking
return Task.CompletedTask; return Task.CompletedTask;
} }
public async Task<Dictionary<string, List<(DateTime Date, Counters Counters)>>> QueryAsync(string key, DateTime fromDate, DateTime toDate) public async Task<Dictionary<string, List<(DateTime, Counters)>>> QueryAsync(string key, DateTime fromDate, DateTime toDate)
{ {
Guard.NotNullOrEmpty(key); Guard.NotNullOrEmpty(key);

2
backend/src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs

@ -26,7 +26,7 @@ namespace Squidex.Infrastructure.UsageTracking
this.inner = inner; this.inner = inner;
} }
public Task<Dictionary<string, List<(DateTime Date, Counters Counters)>>> QueryAsync(string key, DateTime fromDate, DateTime toDate) public Task<Dictionary<string, List<(DateTime, Counters)>>> QueryAsync(string key, DateTime fromDate, DateTime toDate)
{ {
Guard.NotNull(key); Guard.NotNull(key);

6
backend/src/Squidex.Infrastructure/UsageTracking/IApiUsageTracker.cs

@ -13,10 +13,10 @@ namespace Squidex.Infrastructure.UsageTracking
{ {
public interface IApiUsageTracker public interface IApiUsageTracker
{ {
Task TrackAsync(DateTime date, string key, string? category, double weight, long elapsed, long bytes); Task TrackAsync(DateTime date, string key, string? category, double costs, long elapsedMs, long bytes);
Task<long> GetMonthlyWeightAsync(string key, DateTime date); Task<long> GetMonthCostsAsync(string key, DateTime date);
Task<(ApiStats Summary, Dictionary<string, List<(DateTime Date, ApiStats Stats)>> Details)> QueryAsync(string key, DateTime fromDate, DateTime toDate); Task<(ApiStatsSummary, Dictionary<string, List<ApiStats>> Details)> QueryAsync(string key, DateTime fromDate, DateTime toDate);
} }
} }

2
backend/src/Squidex.Infrastructure/UsageTracking/IUsageTracker.cs

@ -19,6 +19,6 @@ namespace Squidex.Infrastructure.UsageTracking
Task<Counters> GetAsync(string key, DateTime fromDate, DateTime toDate); Task<Counters> GetAsync(string key, DateTime fromDate, DateTime toDate);
Task<Dictionary<string, List<(DateTime Date, Counters Counters)>>> QueryAsync(string key, DateTime fromDate, DateTime toDate); Task<Dictionary<string, List<(DateTime, Counters)>>> QueryAsync(string key, DateTime fromDate, DateTime toDate);
} }
} }

6
backend/src/Squidex.Web/ApiCostsAttribute.cs

@ -14,12 +14,12 @@ namespace Squidex.Web
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class ApiCostsAttribute : ServiceFilterAttribute, IApiCostsFeature public sealed class ApiCostsAttribute : ServiceFilterAttribute, IApiCostsFeature
{ {
public double Weight { get; } public double Costs { get; }
public ApiCostsAttribute(double weight) public ApiCostsAttribute(double costs)
: base(typeof(ApiCostsFilter)) : base(typeof(ApiCostsFilter))
{ {
Weight = weight; Costs = costs;
} }
} }
} }

2
backend/src/Squidex.Web/IApiCostsFeature.cs

@ -9,6 +9,6 @@ namespace Squidex.Web
{ {
public interface IApiCostsFeature public interface IApiCostsFeature
{ {
double Weight { get; } double Costs { get; }
} }
} }

4
backend/src/Squidex.Web/Pipeline/ApiCostsFilter.cs

@ -55,13 +55,13 @@ namespace Squidex.Web.Pipeline
{ {
var appId = app.Id.ToString(); var appId = app.Id.ToString();
if (FilterDefinition.Weight > 0) if (FilterDefinition.Costs > 0)
{ {
using (Profiler.Trace("CheckUsage")) using (Profiler.Trace("CheckUsage"))
{ {
var (plan, _) = appPlansProvider.GetPlanForApp(app); var (plan, _) = appPlansProvider.GetPlanForApp(app);
var usage = await usageTracker.GetMonthlyWeightAsync(appId, DateTime.Today); var usage = await usageTracker.GetMonthCostsAsync(appId, DateTime.Today);
if (plan.BlockingApiCalls >= 0 && usage > plan.BlockingApiCalls) if (plan.BlockingApiCalls >= 0 && usage > plan.BlockingApiCalls)
{ {

2
backend/src/Squidex.Web/Pipeline/RequestLogPerformanceMiddleware.cs

@ -84,7 +84,7 @@ namespace Squidex.Web.Pipeline
c.WriteProperty(nameof(clientId), clientId); c.WriteProperty(nameof(clientId), clientId);
} }
var costs = httpContext.Features.Get<IApiCostsFeature>()?.Weight ?? 0; var costs = httpContext.Features.Get<IApiCostsFeature>()?.Costs ?? 0;
c.WriteProperty(nameof(costs), costs); c.WriteProperty(nameof(costs), costs);
} }

19
backend/src/Squidex.Web/Pipeline/UsageMiddleware.cs

@ -49,11 +49,11 @@ namespace Squidex.Web.Pipeline
{ {
if (context.Response.StatusCode != StatusCodes.Status429TooManyRequests) if (context.Response.StatusCode != StatusCodes.Status429TooManyRequests)
{ {
var app = context.Features.Get<IAppFeature>()?.AppId; var appId = context.Features.Get<IAppFeature>()?.AppId;
var costs = context.Features.Get<IApiCostsFeature>()?.Weight ?? 0; var costs = context.Features.Get<IApiCostsFeature>()?.Costs ?? 0;
if (app != null) if (appId != null)
{ {
var elapsedMs = watch.Stop(); var elapsedMs = watch.Stop();
@ -62,7 +62,7 @@ namespace Squidex.Web.Pipeline
var userId = context.User.OpenIdSubject(); var userId = context.User.OpenIdSubject();
var userClient = context.User.OpenIdClientId(); var userClient = context.User.OpenIdClientId();
await log.LogAsync(app.Id, now, await log.LogAsync(appId.Id, now,
context.Request.Method, context.Request.Method,
context.Request.Path, context.Request.Path,
userId, userId,
@ -72,11 +72,16 @@ namespace Squidex.Web.Pipeline
if (costs > 0) if (costs > 0)
{ {
var bytes = usageBody.BytesWritten + (context.Request.ContentLength ?? 0); var bytes = usageBody.BytesWritten;
if (context.Request.ContentLength != null)
{
bytes += context.Request.ContentLength.Value;
}
var date = now.ToDateTimeUtc().Date; var date = now.ToDateTimeUtc().Date;
await usageTracker.TrackAsync(date, app.Id.ToString(), userClient, costs, elapsedMs, bytes); await usageTracker.TrackAsync(date, appId.Id.ToString(), userClient, costs, elapsedMs, bytes);
} }
} }
} }
@ -94,4 +99,4 @@ namespace Squidex.Web.Pipeline
return usageBody; return usageBody;
} }
} }
} }

10
backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/ApiUsageDto.cs

@ -30,18 +30,16 @@ namespace Squidex.Areas.Api.Controllers.Statistics.Models
/// <summary> /// <summary>
/// The average duration in milliseconds. /// The average duration in milliseconds.
/// </summary> /// </summary>
public double AverageMs { get; set; } public double AverageElapsedMs { get; set; }
public static ApiUsageDto FromUsage((DateTime Date, ApiStats Stats) dateStatistics) public static ApiUsageDto FromStats(ApiStats stats)
{ {
var (date, stats) = dateStatistics;
return new ApiUsageDto return new ApiUsageDto
{ {
Date = date, Date = stats.Date,
TotalBytes = stats.TotalBytes, TotalBytes = stats.TotalBytes,
TotalCalls = stats.TotalCalls, TotalCalls = stats.TotalCalls,
AverageMs = stats.AverageElapsed, AverageElapsedMs = stats.AverageElapsedMs,
}; };
} }
} }

8
backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/ApiUsagesDto.cs

@ -33,7 +33,7 @@ namespace Squidex.Areas.Api.Controllers.Statistics.Models
/// <summary> /// <summary>
/// The average duration in milliseconds. /// The average duration in milliseconds.
/// </summary> /// </summary>
public double AverageMs { get; set; } public double AverageElapsedMs { get; set; }
/// <summary> /// <summary>
/// The statistics by date and group. /// The statistics by date and group.
@ -41,15 +41,15 @@ namespace Squidex.Areas.Api.Controllers.Statistics.Models
[Required] [Required]
public Dictionary<string, ApiUsageDto[]> Details { get; set; } public Dictionary<string, ApiUsageDto[]> Details { get; set; }
public static ApiUsagesDto FromUsages(long allowedCalls, ApiStats summary, Dictionary<string, List<(DateTime Date, ApiStats Stats)>> details) public static ApiUsagesDto FromStats(long allowedCalls, ApiStatsSummary summary, Dictionary<string, List<ApiStats>> details)
{ {
return new ApiUsagesDto return new ApiUsagesDto
{ {
AllowedCalls = allowedCalls, AllowedCalls = allowedCalls,
AverageMs = summary.AverageElapsed, AverageElapsedMs = summary.AverageElapsedMs,
TotalBytes = summary.TotalBytes, TotalBytes = summary.TotalBytes,
TotalCalls = summary.TotalBytes, TotalCalls = summary.TotalBytes,
Details = details.ToDictionary(x => x.Key, x => x.Value.Select(ApiUsageDto.FromUsage).ToArray()) Details = details.ToDictionary(x => x.Key, x => x.Value.Select(ApiUsageDto.FromStats).ToArray())
}; };
} }
} }

4
backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs

@ -103,11 +103,11 @@ namespace Squidex.Areas.Api.Controllers.Statistics
return BadRequest(); return BadRequest();
} }
var usages = await usageTracker.QueryAsync(AppId.ToString(), fromDate.Date, toDate.Date); var (summary, details) = await usageTracker.QueryAsync(AppId.ToString(), fromDate.Date, toDate.Date);
var (plan, _) = appPlansProvider.GetPlanForApp(App); var (plan, _) = appPlansProvider.GetPlanForApp(App);
var response = ApiUsagesDto.FromUsages(plan.MaxApiCalls, usages.Summary, usages.Details); var response = ApiUsagesDto.FromStats(plan.MaxApiCalls, summary, details);
return Ok(response); return Ok(response);
} }

38
backend/tests/Squidex.Infrastructure.Tests/UsageTracking/ApiUsageTrackerTests.cs

@ -44,22 +44,22 @@ namespace Squidex.Infrastructure.UsageTracking
[ApiUsageTracker.CounterTotalBytes] = 1024, [ApiUsageTracker.CounterTotalBytes] = 1024,
[ApiUsageTracker.CounterTotalCalls] = 1, [ApiUsageTracker.CounterTotalCalls] = 1,
[ApiUsageTracker.CounterTotalElapsedMs] = 120, [ApiUsageTracker.CounterTotalElapsedMs] = 120,
[ApiUsageTracker.CounterTotalWeight] = 4 [ApiUsageTracker.CounterTotalCosts] = 4
}); });
} }
[Fact] [Fact]
public async Task Should_query_weight_from_tracker() public async Task Should_query_from_tracker()
{ {
var counters = new Counters var counters = new Counters
{ {
[ApiUsageTracker.CounterTotalWeight] = 4 [ApiUsageTracker.CounterTotalCosts] = 4
}; };
A.CallTo(() => usageTracker.GetForMonthAsync($"{key}_API", date)) A.CallTo(() => usageTracker.GetForMonthAsync($"{key}_API", date))
.Returns(counters); .Returns(counters);
var result = await sut.GetMonthlyWeightAsync(key, date); var result = await sut.GetMonthCostsAsync(key, date);
Assert.Equal(4, result); Assert.Equal(4, result);
} }
@ -95,27 +95,27 @@ namespace Squidex.Infrastructure.UsageTracking
var (summary, stats) = await sut.QueryAsync(key, dateFrom, dateTo); var (summary, stats) = await sut.QueryAsync(key, dateFrom, dateTo);
stats.Should().BeEquivalentTo(new Dictionary<string, List<(DateTime Date, ApiStats Counters)>> stats.Should().BeEquivalentTo(new Dictionary<string, List<ApiStats>>
{ {
["my-category"] = new List<(DateTime Date, ApiStats Counters)> ["my-category"] = new List<ApiStats>
{ {
(dateFrom.AddDays(0), new ApiStats(0, 0, 0)), new ApiStats(dateFrom.AddDays(0), 0, 0, 0),
(dateFrom.AddDays(1), new ApiStats(4, 25, 2048)), new ApiStats(dateFrom.AddDays(1), 4, 25, 2048),
(dateFrom.AddDays(2), new ApiStats(0, 0, 0)), new ApiStats(dateFrom.AddDays(2), 0, 0, 0),
(dateFrom.AddDays(3), new ApiStats(2, 30, 1024)), new ApiStats(dateFrom.AddDays(3), 2, 30, 1024),
(dateFrom.AddDays(4), new ApiStats(3, 10, 512)) new ApiStats(dateFrom.AddDays(4), 3, 10, 512)
}, },
["*"] = new List<(DateTime Date, ApiStats)> ["*"] = new List<ApiStats>
{ {
(dateFrom.AddDays(0), new ApiStats(1, 20, 128)), new ApiStats(dateFrom.AddDays(0), 1, 20, 128),
(dateFrom.AddDays(1), new ApiStats(0, 0, 0)), new ApiStats(dateFrom.AddDays(1), 0, 0, 0),
(dateFrom.AddDays(2), new ApiStats(5, 18, 16)), new ApiStats(dateFrom.AddDays(2), 5, 18, 16),
(dateFrom.AddDays(3), new ApiStats(0, 0, 0)), new ApiStats(dateFrom.AddDays(3), 0, 0, 0),
(dateFrom.AddDays(4), new ApiStats(0, 0, 0)) new ApiStats(dateFrom.AddDays(4), 0, 0, 0)
} }
}); });
summary.Should().BeEquivalentTo(new ApiStats(15, 20, 3728)); summary.Should().BeEquivalentTo(new ApiStatsSummary(15, 20, 3728));
} }
private static Counters Counters(long calls, long elapsed, long bytes) private static Counters Counters(long calls, long elapsed, long bytes)
@ -128,4 +128,4 @@ namespace Squidex.Infrastructure.UsageTracking
}; };
} }
} }
} }

4
backend/tests/Squidex.Web.Tests/ApiCostsAttributeTests.cs

@ -12,11 +12,11 @@ namespace Squidex.Web
public class ApiCostsAttributeTests public class ApiCostsAttributeTests
{ {
[Fact] [Fact]
public void Should_assign_weight() public void Should_assign_costs()
{ {
var sut = new ApiCostsAttribute(10.5); var sut = new ApiCostsAttribute(10.5);
Assert.Equal(10.5, sut.Weight); Assert.Equal(10.5, sut.Costs);
} }
} }
} }

2
backend/tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs

@ -52,7 +52,7 @@ namespace Squidex.Web.Pipeline
A.CallTo(() => appPlan.BlockingApiCalls) A.CallTo(() => appPlan.BlockingApiCalls)
.ReturnsLazily(x => apiCallsBlocking); .ReturnsLazily(x => apiCallsBlocking);
A.CallTo(() => usageTracker.GetMonthlyWeightAsync(A<string>._, DateTime.Today)) A.CallTo(() => usageTracker.GetMonthCostsAsync(A<string>._, DateTime.Today))
.ReturnsLazily(x => Task.FromResult(apiCallsCurrent)); .ReturnsLazily(x => Task.FromResult(apiCallsCurrent));
next = () => next = () =>

4
backend/tests/Squidex.Web.Tests/Pipeline/CleanupHostMiddlewareTests.cs

@ -52,7 +52,7 @@ namespace Squidex.Web.Pipeline
httpContext.Request.Scheme = "http"; httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("host", 80); httpContext.Request.Host = new HostString("host", 80);
await sut.Invoke(httpContext); await sut.InvokeAsync(httpContext);
Assert.Null(httpContext.Request.Host.Port); Assert.Null(httpContext.Request.Host.Port);
Assert.True(isNextCalled); Assert.True(isNextCalled);
@ -66,7 +66,7 @@ namespace Squidex.Web.Pipeline
httpContext.Request.Scheme = "http"; httpContext.Request.Scheme = "http";
httpContext.Request.Host = new HostString("host", 8080); httpContext.Request.Host = new HostString("host", 8080);
await sut.Invoke(httpContext); await sut.InvokeAsync(httpContext);
Assert.Equal(8080, httpContext.Request.Host.Port); Assert.Equal(8080, httpContext.Request.Host.Port);
Assert.True(isNextCalled); Assert.True(isNextCalled);

4
backend/tests/Squidex.Web.Tests/Pipeline/UsageMiddlewareTests.cs

@ -130,7 +130,7 @@ namespace Squidex.Web.Pipeline
} }
[Fact] [Fact]
public async Task Should_not_track_if_weight_is_zero() public async Task Should_not_track_if_costs_are_zero()
{ {
httpContext.Features.Set<IAppFeature>(new AppFeature(appId)); httpContext.Features.Set<IAppFeature>(new AppFeature(appId));
httpContext.Features.Set<IApiCostsFeature>(new ApiCostsAttribute(0)); httpContext.Features.Set<IApiCostsFeature>(new ApiCostsAttribute(0));
@ -146,7 +146,7 @@ namespace Squidex.Web.Pipeline
} }
[Fact] [Fact]
public async Task Should_log_request_even_if_weight_is_zero() public async Task Should_log_request_even_if_costs_are_zero()
{ {
httpContext.Features.Set<IAppFeature>(new AppFeature(appId)); httpContext.Features.Set<IAppFeature>(new AppFeature(appId));
httpContext.Features.Set<IApiCostsFeature>(new ApiCostsAttribute(0)); httpContext.Features.Set<IApiCostsFeature>(new ApiCostsAttribute(0));

6
frontend/app/features/dashboard/pages/dashboard-page.component.ts

@ -175,7 +175,7 @@ export class DashboardPageComponent extends ResourceOwner implements OnInit {
this.own( this.own(
this.appsState.selectedApp.pipe( this.appsState.selectedApp.pipe(
switchMap(app => this.usagesService.getCallsUsages(app.name, DateTime.today().addDays(-20), DateTime.today()))) switchMap(app => this.usagesService.getCallsUsages(app.name, DateTime.today().addDays(-20), DateTime.today())))
.subscribe(({ details, totalBytes, totalCalls, allowedCalls, averageMs }) => { .subscribe(({ details, totalBytes, totalCalls, allowedCalls, averageElapsedMs }) => {
const labels = createLabelsFromSet(details); const labels = createLabelsFromSet(details);
this.chartCallsCount = { this.chartCallsCount = {
@ -210,11 +210,11 @@ export class DashboardPageComponent extends ResourceOwner implements OnInit {
backgroundColor: `rgba(${COLORS[i]}, 0.6)`, backgroundColor: `rgba(${COLORS[i]}, 0.6)`,
borderColor: `rgba(${COLORS[i]}, 1)`, borderColor: `rgba(${COLORS[i]}, 1)`,
borderWidth: 1, borderWidth: 1,
data: details[k].map(x => x.averageMs) data: details[k].map(x => x.averageElapsedMs)
})) }))
}; };
this.callsPerformance = averageMs; this.callsPerformance = averageElapsedMs;
this.callsBytes = totalBytes; this.callsBytes = totalBytes;
this.callsCurrent = totalCalls; this.callsCurrent = totalCalls;
this.callsAllowed = allowedCalls; this.callsAllowed = allowedCalls;

6
frontend/app/shared/services/usages.service.spec.ts

@ -53,20 +53,20 @@ describe('UsagesService', () => {
allowedCalls: 100, allowedCalls: 100,
totalBytes: 1024, totalBytes: 1024,
totalCalls: 40, totalCalls: 40,
averageMs: 12.4, averageElapsedMs: 12.4,
details: { details: {
category1: [ category1: [
{ {
date: '2017-10-12', date: '2017-10-12',
totalBytes: 10, totalBytes: 10,
totalCalls: 130, totalCalls: 130,
averageMs: 12.3 averageElapsedMs: 12.3
}, },
{ {
date: '2017-10-13', date: '2017-10-13',
totalBytes: 13, totalBytes: 13,
totalCalls: 170, totalCalls: 170,
averageMs: 33.3 averageElapsedMs: 33.3
} }
] ]
} }

8
frontend/app/shared/services/usages.service.ts

@ -21,7 +21,7 @@ export class ApiUsagesDto {
public readonly allowedCalls: number, public readonly allowedCalls: number,
public readonly totalBytes: number, public readonly totalBytes: number,
public readonly totalCalls: number, public readonly totalCalls: number,
public readonly averageMs: number, public readonly averageElapsedMs: number,
public readonly details: { [category: string]: ReadonlyArray<ApiUsageDto> } public readonly details: { [category: string]: ReadonlyArray<ApiUsageDto> }
) { ) {
} }
@ -32,7 +32,7 @@ export class ApiUsageDto {
public readonly date: DateTime, public readonly date: DateTime,
public readonly totalBytes: number, public readonly totalBytes: number,
public readonly totalCalls: number, public readonly totalCalls: number,
public readonly averageMs: number public readonly averageElapsedMs: number
) { ) {
} }
} }
@ -95,7 +95,7 @@ export class UsagesService {
DateTime.parseISO_UTC(item.date), DateTime.parseISO_UTC(item.date),
item.totalBytes, item.totalBytes,
item.totalCalls, item.totalCalls,
item.averageMs)); item.averageElapsedMs));
} }
const usages = const usages =
@ -103,7 +103,7 @@ export class UsagesService {
body.allowedCalls, body.allowedCalls,
body.totalBytes, body.totalBytes,
body.totalBytes, body.totalBytes,
body.averageMs, body.averageElapsedMs,
details); details);
return usages; return usages;

Loading…
Cancel
Save