Browse Source

Fix rule enqueue logic.

pull/1074/head
Sebastian Stehle 3 years ago
parent
commit
9485469c94
  1. 8
      backend/src/Squidex.Domain.Apps.Entities/Rules/RuleQueueWriter.cs
  2. 173
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleQueueWriterTests.cs
  3. 2
      frontend/src/app/features/assets/pages/assets-filters-page.component.html
  4. 2
      frontend/src/app/features/content/pages/contents/contents-filters-page.component.html
  5. 8
      frontend/src/app/features/rules/pages/rules/rules-page.component.html

8
backend/src/Squidex.Domain.Apps.Entities/Rules/RuleQueueWriter.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Rules;
internal sealed class RuleQueueWriter : IAsyncDisposable
public sealed class RuleQueueWriter : IAsyncDisposable
{
private readonly List<RuleEventWrite> writes = [];
private readonly IRuleEventRepository ruleEventRepository;
@ -38,10 +38,14 @@ internal sealed class RuleQueueWriter : IAsyncDisposable
{
writes.Add(new RuleEventWrite(result.Job, Error: result.EnrichmentError));
}
else
else if (result.SkipReason is SkipReason.None or SkipReason.Disabled)
{
writes.Add(new RuleEventWrite(result.Job, result.Job.Created));
}
else
{
return false;
}
if (result.Rule != null)
{

173
backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleQueueWriterTests.cs

@ -0,0 +1,173 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Elasticsearch.Net.Specification.CrossClusterReplicationApi;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents;
using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Entities.TestHelpers;
namespace Squidex.Domain.Apps.Entities.Rules;
public class RuleQueueWriterTests : GivenContext
{
private readonly IRuleEventRepository ruleEventRepository = A.Fake<IRuleEventRepository>();
private readonly IRuleUsageTracker ruleUsageTracker = A.Fake<IRuleUsageTracker>();
private readonly RuleQueueWriter sut;
public RuleQueueWriterTests()
{
sut = new RuleQueueWriter(ruleEventRepository, ruleUsageTracker, null);
}
[Fact]
public async Task Should_not_enqueue_result_without_job()
{
var result = new JobResult
{
SkipReason = SkipReason.None,
};
var writes = await EnqueueAndFlushAsync(result);
Assert.Empty(writes);
}
[Theory]
[InlineData(SkipReason.ConditionDoesNotMatch)]
[InlineData(SkipReason.ConditionPrecheckDoesNotMatch)]
[InlineData(SkipReason.FromRule)]
[InlineData(SkipReason.NoAction)]
[InlineData(SkipReason.NoTrigger)]
[InlineData(SkipReason.TooOld)]
[InlineData(SkipReason.WrongEvent)]
[InlineData(SkipReason.WrongEventForTrigger)]
public async Task Should_not_enqueue_skipped_events_without_error(SkipReason reason)
{
var result = new JobResult
{
SkipReason = reason,
EnrichedEvent = new EnrichedManualEvent(),
EnrichmentError = null,
Job = new RuleJob()
};
var writes = await EnqueueAndFlushAsync(result);
Assert.Empty(writes);
}
[Fact]
public async Task Should_enqueue_with_failed_reason()
{
var result = new JobResult
{
SkipReason = SkipReason.Failed,
EnrichedEvent = new EnrichedManualEvent(),
EnrichmentError = null,
Job = new RuleJob()
};
var writes = await EnqueueAndFlushAsync(result);
Assert.Equal(new[]
{
new RuleEventWrite(result.Job, null, null)
}, writes);
}
[Fact]
public async Task Should_enqueue_with_error()
{
var result = new JobResult
{
SkipReason = SkipReason.Failed,
EnrichedEvent = new EnrichedManualEvent(),
EnrichmentError = new InvalidOperationException(),
Job = new RuleJob()
};
var writes = await EnqueueAndFlushAsync(result);
Assert.Equal(new[]
{
new RuleEventWrite(result.Job, null, result.EnrichmentError)
}, writes);
}
[Fact]
public async Task Should_enqueue_success()
{
var result = new JobResult
{
SkipReason = SkipReason.None,
EnrichedEvent = new EnrichedManualEvent(),
EnrichmentError = null,
Job = new RuleJob()
};
var writes = await EnqueueAndFlushAsync(result);
Assert.Equal(new[]
{
new RuleEventWrite(result.Job, result.Job.Created, null)
}, writes);
}
[Fact]
public async Task Should_enqueue_disabled()
{
var result = new JobResult
{
SkipReason = SkipReason.Disabled,
EnrichedEvent = new EnrichedManualEvent(),
EnrichmentError = null,
Job = new RuleJob()
};
var writes = await EnqueueAndFlushAsync(result);
Assert.Equal(new[]
{
new RuleEventWrite(result.Job, result.Job.Created, null)
}, writes);
}
[Fact]
public async Task Should_write_batched()
{
var result = new JobResult
{
SkipReason = SkipReason.Disabled,
EnrichedEvent = new EnrichedManualEvent(),
EnrichmentError = null,
Job = new RuleJob()
};
for (var i = 0; i < 250; i++)
{
await sut.WriteAsync(result);
}
A.CallTo(() => ruleEventRepository.EnqueueAsync(A<List<RuleEventWrite>>._, default))
.MustHaveHappenedANumberOfTimesMatching(x => x == 2);
}
private async Task<RuleEventWrite[]> EnqueueAndFlushAsync(JobResult result)
{
var writes = Array.Empty<RuleEventWrite>();
A.CallTo(() => ruleEventRepository.EnqueueAsync(A<List<RuleEventWrite>>._, default))
.Invokes(x => { writes = x.GetArgument<List<RuleEventWrite>>(0)!.ToArray(); });
await sut.WriteAsync(result);
await sut.FlushAsync();
return writes;
}
}

2
frontend/src/app/features/assets/pages/assets-filters-page.component.html

@ -1,4 +1,4 @@
<sqx-layout titleText="i18n:common.filters" width="20" white="true" padding="true" overflow="true">
<sqx-layout layout="right" titleText="i18n:common.filters" width="20" white="true" padding="true" overflow="true">
<h3>{{ 'common.tags' | sqxTranslate }}</h3>
<sqx-asset-tags

2
frontend/src/app/features/content/pages/contents/contents-filters-page.component.html

@ -1,4 +1,4 @@
<sqx-layout titleText="i18n:common.filters" width="20" white="true" padding="true" overflow="true">
<sqx-layout layout="right" titleText="i18n:common.filters" width="20" white="true" padding="true" overflow="true">
<ng-container *ngIf="schemaQueries | async; let queries">
<sqx-query-list
[types]="'common.contents' | sqxTranslate"

8
frontend/src/app/features/rules/pages/rules/rules-page.component.html

@ -13,12 +13,10 @@
<ng-container>
<sqx-list-view innerWidth="54rem" [isLoading]="rulesState.isLoading | async">
<div class="alert alert-danger mb-4" *ngIf="rulesState.runningRule | async; let runningRule">
<div class="float-end">
<a (click)="cancelRun()">{{ 'common.cancel' | sqxTranslate }}</a>
</div>
<div class="alert alert-danger d-flex justify-content-between mb-4" *ngIf="rulesState.runningRule | async; let runningRule">
{{ 'rules.runningRule' | sqxTranslate: { name: runningRule.name || 'Unnamed Rule' } }}
<a class="btn" (click)="cancelRun()">{{ 'common.cancel' | sqxTranslate }}</a>
</div>
<ng-container *ngIf="supportedActions && supportedTriggers && (rulesState.isLoaded | async) && (rulesState.rules | async); let rules">

Loading…
Cancel
Save