Browse Source

Define day range.

pull/342/head
Sebastian Stehle 7 years ago
parent
commit
4bf3badffd
  1. 2
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/UsageTrigger.cs
  2. 4
      src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/IUsageTrackerGrain.cs
  3. 14
      src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerCommandMiddleware.cs
  4. 32
      src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs
  5. 12
      src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs
  6. 18
      src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs
  7. 2
      src/Squidex.Infrastructure/UsageTracking/IUsageTracker.cs
  8. 5
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/UsageRuleTriggerDto.cs
  9. 24
      tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs
  10. 19
      tests/Squidex.Infrastructure.Tests/UsageTracking/CachingUsageTrackerTests.cs

2
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/UsageTrigger.cs

@ -16,6 +16,8 @@ namespace Squidex.Domain.Apps.Core.Rules.Triggers
public int Limit { get; set; }
public int? NumDays { get; set; }
public override T Accept<T>(IRuleTriggerVisitor<T> visitor)
{
return visitor.Visit(this);

4
src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/IUsageTrackerGrain.cs

@ -15,10 +15,10 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
{
public interface IUsageTrackerGrain : IGrainWithStringKey, IBackgroundGrain
{
Task AddTargetAsync(Guid ruleId, NamedId<Guid> appId, int limits);
Task AddTargetAsync(Guid ruleId, NamedId<Guid> appId, int limits, int? numDays);
Task RemoveTargetAsync(Guid ruleId);
Task UpdateTargetAsync(Guid ruleId, int limits);
Task UpdateTargetAsync(Guid ruleId, int limits, int? numDays);
}
}

14
src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerCommandMiddleware.cs

@ -36,21 +36,23 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
break;
case CreateRule createRule:
{
if (createRule.Trigger is UsageTrigger createdTrigger)
if (createRule.Trigger is UsageTrigger usage)
{
await usageTrackerGrain.AddTargetAsync(createRule.RuleId, createRule.AppId, createdTrigger.Limit);
await usageTrackerGrain.AddTargetAsync(createRule.RuleId, createRule.AppId, usage.Limit, usage.NumDays);
}
break;
}
case UpdateRule ruleUpdated:
if (ruleUpdated.Trigger is UsageTrigger updatedTrigger)
{
await usageTrackerGrain.UpdateTargetAsync(ruleUpdated.RuleId, updatedTrigger.Limit);
}
if (ruleUpdated.Trigger is UsageTrigger usage)
{
await usageTrackerGrain.UpdateTargetAsync(ruleUpdated.RuleId, usage.Limit, usage.NumDays);
}
break;
break;
}
}
await next();

32
src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerGrain.cs

@ -24,12 +24,15 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
[Reentrant]
public sealed class UsageTrackerGrain : GrainOfString<UsageTrackerGrain.GrainState>, IRemindable, IUsageTrackerGrain
{
private const int MaxDays = 30;
private readonly IUsageTracker usageTracker;
public sealed class Target
{
public int Limits { get; set; }
public int? NumDays { get; set; }
public DateTime? Triggered { get; set; }
public NamedId<Guid> AppId { get; set; }
@ -75,11 +78,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
foreach (var kvp in State.Targets)
{
var appId = kvp.Value.AppId;
var target = kvp.Value;
var (from, to) = GetDateRange(today, target.NumDays);
if (!kvp.Value.Triggered.HasValue || !IsSameMonth(today, kvp.Value.Triggered.Value))
if (!target.Triggered.HasValue || target.Triggered < from)
{
var usage = await usageTracker.GetMonthlyCallsAsync(appId.Id.ToString(), today);
var usage = await usageTracker.GetMonthlyCallsAsync(target.AppId.Id.ToString(), today);
var limit = kvp.Value.Limits;
@ -89,7 +94,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
var @event = new AppUsageExceeded
{
AppId = appId,
AppId = target.AppId,
CallsCurrent = usage,
CallsLimit = limit,
RuleId = kvp.Key
@ -103,21 +108,28 @@ namespace Squidex.Domain.Apps.Entities.Rules.UsageTracking
await WriteStateAsync();
}
private static bool IsSameMonth(DateTime lhs, DateTime rhs)
private (DateTime, DateTime) GetDateRange(DateTime today, int? numDays)
{
return lhs.Year == rhs.Year && lhs.Month == rhs.Month;
if (numDays > 0 && numDays < MaxDays)
{
return (today.AddDays(-numDays.Value).AddDays(1), today);
}
else
{
return (new DateTime(today.Year, today.Month, 1), today);
}
}
public Task AddTargetAsync(Guid ruleId, NamedId<Guid> appId, int limits)
public Task AddTargetAsync(Guid ruleId, NamedId<Guid> appId, int limits, int? numDays)
{
UpdateTarget(ruleId, t => { t.Limits = limits; t.AppId = appId; });
UpdateTarget(ruleId, t => { t.Limits = limits; t.AppId = appId; t.NumDays = numDays; });
return WriteStateAsync();
}
public Task UpdateTargetAsync(Guid ruleId, int limits)
public Task UpdateTargetAsync(Guid ruleId, int limits, int? numDays)
{
UpdateTarget(ruleId, t => t.Limits = limits);
UpdateTarget(ruleId, t => { t.Limits = limits; t.NumDays = numDays; });
return WriteStateAsync();
}

12
src/Squidex.Infrastructure/UsageTracking/BackgroundUsageTracker.cs

@ -167,16 +167,18 @@ namespace Squidex.Infrastructure.UsageTracking
return result;
}
public async Task<long> GetMonthlyCallsAsync(string key, DateTime date)
public Task<long> GetMonthlyCallsAsync(string key, DateTime date)
{
return GetPreviousCallsAsync(key, new DateTime(date.Year, date.Month, 1), date);
}
public async Task<long> GetPreviousCallsAsync(string key, DateTime fromDate, DateTime toDate)
{
key = GetKey(key);
ThrowIfDisposed();
var dateFrom = new DateTime(date.Year, date.Month, 1);
var dateTo = dateFrom.AddMonths(1).AddDays(-1);
var originalUsages = await usageRepository.QueryAsync(key, dateFrom, dateTo);
var originalUsages = await usageRepository.QueryAsync(key, fromDate, toDate);
return originalUsages.Sum(x => (long)x.Counters.Get(CounterTotalCalls));
}

18
src/Squidex.Infrastructure/UsageTracking/CachingUsageTracker.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.UsageTracking
{
public sealed class CachingUsageTracker : CachingProviderBase, IUsageTracker
{
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10);
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(5);
private readonly IUsageTracker inner;
public CachingUsageTracker(IUsageTracker inner, IMemoryCache cache)
@ -43,7 +43,7 @@ namespace Squidex.Infrastructure.UsageTracking
{
Guard.NotNull(key, nameof(key));
var cacheKey = string.Concat(key, date);
var cacheKey = string.Join("$", "Usage", nameof(GetMonthlyCallsAsync), key, date);
return Cache.GetOrCreateAsync(cacheKey, entry =>
{
@ -52,5 +52,19 @@ namespace Squidex.Infrastructure.UsageTracking
return inner.GetMonthlyCallsAsync(key, date);
});
}
public Task<long> GetPreviousCallsAsync(string key, DateTime fromDate, DateTime toDate)
{
Guard.NotNull(key, nameof(key));
var cacheKey = string.Join("$", "Usage", nameof(GetPreviousCallsAsync), key, fromDate, toDate);
return Cache.GetOrCreateAsync(cacheKey, entry =>
{
entry.AbsoluteExpirationRelativeToNow = CacheDuration;
return inner.GetPreviousCallsAsync(key, fromDate, toDate);
});
}
}
}

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

@ -17,6 +17,8 @@ namespace Squidex.Infrastructure.UsageTracking
Task<long> GetMonthlyCallsAsync(string key, DateTime date);
Task<long> GetPreviousCallsAsync(string key, DateTime fromDate, DateTime toDate);
Task<IReadOnlyDictionary<string, IReadOnlyList<DateUsage>>> QueryAsync(string key, DateTime fromDate, DateTime toDate);
}
}

5
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/UsageRuleTriggerDto.cs

@ -18,6 +18,11 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
/// </summary>
public int Limit { get; set; }
/// <summary>
/// The number of days to check or null for the current month.
/// </summary>
public int? NumDays { get; set; }
public override RuleTrigger ToTrigger()
{
return SimpleMapper.Map(this, new UsageTrigger());

24
tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs

@ -64,7 +64,7 @@ namespace Squidex.Infrastructure.UsageTracking
new StoredUsage("category1", date.AddDays(7), Counters(17, 22))
};
A.CallTo(() => usageStore.QueryAsync($"{key}_API", new DateTime(2016, 1, 1), new DateTime(2016, 1, 31)))
A.CallTo(() => usageStore.QueryAsync($"{key}_API", new DateTime(2016, 1, 1), new DateTime(2016, 1, 15)))
.Returns(originalData);
var result = await sut.GetMonthlyCallsAsync(key, date);
@ -72,6 +72,28 @@ namespace Squidex.Infrastructure.UsageTracking
Assert.Equal(55, result);
}
[Fact]
public async Task Should_sum_up_when_getting_last_calls_calls()
{
var f = DateTime.Today;
var t = DateTime.Today.AddDays(10);
IReadOnlyList<StoredUsage> originalData = new List<StoredUsage>
{
new StoredUsage("category1", f.AddDays(1), Counters(10, 15)),
new StoredUsage("category1", f.AddDays(3), Counters(13, 18)),
new StoredUsage("category1", f.AddDays(5), Counters(15, 20)),
new StoredUsage("category1", f.AddDays(7), Counters(17, 22))
};
A.CallTo(() => usageStore.QueryAsync($"{key}_API", f, t))
.Returns(originalData);
var result = await sut.GetPreviousCallsAsync(key, f, t);
Assert.Equal(55, result);
}
[Fact]
public async Task Should_fill_missing_days()
{

19
tests/Squidex.Infrastructure.Tests/UsageTracking/CachingUsageTrackerTests.cs

@ -59,5 +59,24 @@ namespace Squidex.Infrastructure.UsageTracking
A.CallTo(() => inner.GetMonthlyCallsAsync(key, DateTime.Today))
.MustHaveHappened(Repeated.Exactly.Once);
}
[Fact]
public async Task Should_cache_days_usage()
{
var f = DateTime.Today;
var t = DateTime.Today.AddDays(10);
A.CallTo(() => inner.GetPreviousCallsAsync(key, f, t))
.Returns(120);
var result1 = await sut.GetPreviousCallsAsync(key, f, t);
var result2 = await sut.GetPreviousCallsAsync(key, f, t);
Assert.Equal(120, result1);
Assert.Equal(120, result2);
A.CallTo(() => inner.GetPreviousCallsAsync(key, f, t))
.MustHaveHappened(Repeated.Exactly.Once);
}
}
}

Loading…
Cancel
Save