diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs index 0e36de17a..17a289c28 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs @@ -85,6 +85,9 @@ namespace Squidex.Domain.Apps.Entities.Assets result.AssetType = asset.Type; } + // Use the concrete event to map properties that are not part of app event. + SimpleMapper.Map(assetEvent, result); + switch (@event.Payload) { case AssetCreated: diff --git a/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentTriggerHandler.cs index c1edfa6ef..a72638d6c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Comments/CommentTriggerHandler.cs @@ -42,26 +42,31 @@ namespace Squidex.Domain.Apps.Entities.Comments { var commentCreated = (CommentCreated)@event.Payload; - if (commentCreated.Mentions?.Length > 0) + if (!(commentCreated.Mentions?.Length >= 0)) { - var users = await userResolver.QueryManyAsync(commentCreated.Mentions, ct); + yield break; + } + + var users = await userResolver.QueryManyAsync(commentCreated.Mentions, ct); + + if (users.Count <= 0) + { + yield break; + } - if (users.Count > 0) + foreach (var user in users.Values) + { + var enrichedEvent = new EnrichedCommentEvent { - foreach (var user in users.Values) - { - var enrichedEvent = new EnrichedCommentEvent - { - MentionedUser = user - }; + MentionedUser = user + }; - enrichedEvent.Name = "UserMentioned"; + enrichedEvent.Name = "UserMentioned"; - SimpleMapper.Map(commentCreated, enrichedEvent); + // Use the concrete event to map properties that are not part of app event. + SimpleMapper.Map(commentCreated, enrichedEvent); - yield return enrichedEvent; - } - } + yield return enrichedEvent; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs index 3441912ba..24bedee37 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs @@ -90,6 +90,9 @@ namespace Squidex.Domain.Apps.Entities.Contents SimpleMapper.Map(content, result); } + // Use the concrete event to map properties that are not part of app event. + SimpleMapper.Map(contentEvent, result); + switch (@event.Payload) { case ContentCreated: diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/ManualTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/ManualTriggerHandler.cs index 8a0f08525..7ef0181c0 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/ManualTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/ManualTriggerHandler.cs @@ -30,7 +30,8 @@ namespace Squidex.Domain.Apps.Entities.Rules { var result = new EnrichedManualEvent(); - SimpleMapper.Map(@event.Payload, result); + // Use the concrete event to map properties that are not part of app event. + SimpleMapper.Map((RuleManuallyTriggered)@event.Payload, result); await Task.Yield(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs index 1621e98ad..fd9323130 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs @@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Rules await foreach (var job in jobs) { - if (job.Job != null && job.SkipReason == SkipReason.None) + if (job.Job != null && job.SkipReason is SkipReason.None or SkipReason.Failed) { await ruleEventRepository.EnqueueAsync(job.Job, job.EnrichmentError); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs index f1f9e3b38..2eda50aa0 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs @@ -38,7 +38,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas { var result = new EnrichedSchemaEvent(); - SimpleMapper.Map(@event.Payload, result); + // Use the concrete event to map properties that are not part of app event. + SimpleMapper.Map((SchemaEvent)@event.Payload, result); switch (@event.Payload) { diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs index 77966fb17..7e66a1204 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs @@ -19,8 +19,10 @@ using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Json; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas.Json; +using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.Collections; +using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft; using Squidex.Infrastructure.Json.Objects; @@ -185,5 +187,29 @@ namespace Squidex.Domain.Apps.Core.TestHelpers return DefaultSerializer.Deserialize(json); } + + public static TEvent CreateEvent(Action? init = null) where TEvent : IEvent, new() + { + var result = new TEvent(); + + if (result is SquidexEvent squidexEvent) + { + squidexEvent.Actor = new RefToken(RefTokenType.Client, "my-client"); + } + + if (result is AppEvent appEvent) + { + appEvent.AppId = NamedId.Of(DomainId.NewGuid(), "my-app"); + } + + if (result is SchemaEvent schemaEvent) + { + schemaEvent.SchemaId = NamedId.Of(DomainId.NewGuid(), "my-schema"); + } + + init?.Invoke(result); + + return result; + } } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs index 03f28743b..6dcf01fee 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs @@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Core.Scripting; +using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Assets; @@ -41,10 +42,10 @@ namespace Squidex.Domain.Apps.Entities.Assets public static IEnumerable TestEvents() { - yield return new object[] { new AssetCreated(), EnrichedAssetEventType.Created }; - yield return new object[] { new AssetUpdated(), EnrichedAssetEventType.Updated }; - yield return new object[] { new AssetAnnotated(), EnrichedAssetEventType.Annotated }; - yield return new object[] { new AssetDeleted(), EnrichedAssetEventType.Deleted }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedAssetEventType.Created }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedAssetEventType.Updated }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedAssetEventType.Annotated }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedAssetEventType.Deleted }; } [Fact] @@ -95,9 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Assets [MemberData(nameof(TestEvents))] public async Task Should_create_enriched_events(AssetEvent @event, EnrichedAssetEventType type) { - var ctx = Context(); - - @event.AppId = ctx.AppId; + var ctx = Context(appId: @event.AppId); var envelope = Envelope.Create(@event).SetEventStreamNumber(12); @@ -106,9 +105,12 @@ namespace Squidex.Domain.Apps.Entities.Assets var result = await sut.CreateEnrichedEventsAsync(envelope, ctx, default).ToListAsync(); - var enrichedEvent = result.Single() as EnrichedAssetEvent; + var enrichedEvent = (EnrichedAssetEvent)result.Single(); - Assert.Equal(type, enrichedEvent!.Type); + Assert.Equal(type, enrichedEvent.Type); + Assert.Equal(@event.Actor, enrichedEvent.Actor); + Assert.Equal(@event.AppId, enrichedEvent.AppId); + Assert.Equal(@event.AppId.Id, enrichedEvent.AppId.Id); } [Fact] @@ -171,13 +173,13 @@ namespace Squidex.Domain.Apps.Entities.Assets } } - private static RuleContext Context(RuleTrigger? trigger = null) + private static RuleContext Context(RuleTrigger? trigger = null, NamedId? appId = null) { trigger ??= new AssetChangedTriggerV2(); return new RuleContext { - AppId = NamedId.Of(DomainId.NewGuid(), "my-app"), + AppId = appId ?? NamedId.Of(DomainId.NewGuid(), "my-app"), Rule = new Rule(trigger, A.Fake()), RuleId = DomainId.NewGuid() }; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs index 74fce6725..030e0a870 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs @@ -20,6 +20,7 @@ using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Reflection; using Xunit; namespace Squidex.Domain.Apps.Entities.Contents @@ -46,12 +47,12 @@ namespace Squidex.Domain.Apps.Entities.Contents public static IEnumerable TestEvents() { - yield return new object[] { new ContentCreated(), EnrichedContentEventType.Created }; - yield return new object[] { new ContentUpdated(), EnrichedContentEventType.Updated }; - yield return new object[] { new ContentDeleted(), EnrichedContentEventType.Deleted }; - yield return new object[] { new ContentStatusChanged { Change = StatusChange.Change }, EnrichedContentEventType.StatusChanged }; - yield return new object[] { new ContentStatusChanged { Change = StatusChange.Published }, EnrichedContentEventType.Published }; - yield return new object[] { new ContentStatusChanged { Change = StatusChange.Unpublished }, EnrichedContentEventType.Unpublished }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedContentEventType.Created }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedContentEventType.Updated }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedContentEventType.Deleted }; + yield return new object[] { TestUtils.CreateEvent(x => x.Change = StatusChange.Change), EnrichedContentEventType.StatusChanged }; + yield return new object[] { TestUtils.CreateEvent(x => x.Change = StatusChange.Published), EnrichedContentEventType.Published }; + yield return new object[] { TestUtils.CreateEvent(x => x.Change = StatusChange.Unpublished), EnrichedContentEventType.Unpublished }; } [Fact] @@ -184,13 +185,18 @@ namespace Squidex.Domain.Apps.Entities.Contents var envelope = Envelope.Create(@event).SetEventStreamNumber(12); A.CallTo(() => contentLoader.GetAsync(ctx.AppId.Id, @event.ContentId, 12)) - .Returns(new ContentEntity { AppId = ctx.AppId, SchemaId = schemaMatch }); + .Returns(SimpleMapper.Map(@event, new ContentEntity())); var result = await sut.CreateEnrichedEventsAsync(envelope, ctx, default).ToListAsync(); - var enrichedEvent = result.Single() as EnrichedContentEvent; + var enrichedEvent = (EnrichedContentEvent)result.Single(); Assert.Equal(type, enrichedEvent!.Type); + Assert.Equal(@event.Actor, enrichedEvent.Actor); + Assert.Equal(@event.AppId, enrichedEvent.AppId); + Assert.Equal(@event.AppId.Id, enrichedEvent.AppId.Id); + Assert.Equal(@event.SchemaId, enrichedEvent.SchemaId); + Assert.Equal(@event.SchemaId.Id, enrichedEvent.SchemaId.Id); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/ManualTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/ManualTriggerHandlerTests.cs index 90ff8c68c..64e75c8d4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/ManualTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/ManualTriggerHandlerTests.cs @@ -5,8 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using FakeItEasy; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; +using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Rules; using Squidex.Infrastructure; @@ -36,11 +38,15 @@ namespace Squidex.Domain.Apps.Entities.Rules [Fact] public async Task Should_create_event_with_name() { - var @event = new RuleManuallyTriggered(); + var @event = TestUtils.CreateEvent(); var result = await sut.CreateEnrichedEventsAsync(Envelope.Create(@event), default, default).ToListAsync(); - Assert.NotEmpty(result); + var enrichedEvent = (EnrichedManualEvent)result.Single(); + + Assert.Equal(@event.Actor, enrichedEvent.Actor); + Assert.Equal(@event.AppId, enrichedEvent.AppId); + Assert.Equal(@event.AppId.Id, enrichedEvent.AppId.Id); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs index 2c3ce4ec5..bc1722471 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs @@ -134,6 +134,27 @@ namespace Squidex.Domain.Apps.Entities.Rules .MustHaveHappened(); } + [Fact] + public async Task Should_update_repository_if_enqueing_broken_job() + { + var @event = Envelope.Create(new ContentCreated { AppId = appId }); + + var rule = CreateRule(); + + var job = new RuleJob + { + Created = now + }; + + A.CallTo(() => ruleService.CreateJobsAsync(@event, MatchingContext(rule), default)) + .Returns(new List { new JobResult { Job = job, SkipReason = SkipReason.Failed } }.ToAsyncEnumerable()); + + await sut.EnqueueAsync(rule.RuleDef, rule.Id, @event); + + A.CallTo(() => ruleEventRepository.EnqueueAsync(job, (Exception?)null, default)) + .MustHaveHappened(); + } + [Fact] public async Task Should_update_repository_with_jobs_from_service() { diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs index 5a85d0ab0..4966784c8 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs @@ -11,6 +11,8 @@ using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Core.Scripting; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Schemas; @@ -38,11 +40,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas public static IEnumerable TestEvents() { - yield return new object[] { new SchemaCreated(), EnrichedSchemaEventType.Created }; - yield return new object[] { new SchemaUpdated(), EnrichedSchemaEventType.Updated }; - yield return new object[] { new SchemaDeleted(), EnrichedSchemaEventType.Deleted }; - yield return new object[] { new SchemaPublished(), EnrichedSchemaEventType.Published }; - yield return new object[] { new SchemaUnpublished(), EnrichedSchemaEventType.Unpublished }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedSchemaEventType.Created }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedSchemaEventType.Updated }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedSchemaEventType.Deleted }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedSchemaEventType.Published }; + yield return new object[] { TestUtils.CreateEvent(), EnrichedSchemaEventType.Unpublished }; } [Fact] @@ -67,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas [MemberData(nameof(TestEvents))] public async Task Should_create_enriched_events(SchemaEvent @event, EnrichedSchemaEventType type) { - var ctx = Context(); + var ctx = Context(appId: @event.AppId); var envelope = Envelope.Create(@event).SetEventStreamNumber(12); @@ -76,6 +78,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas var enrichedEvent = result.Single() as EnrichedSchemaEvent; Assert.Equal(type, enrichedEvent!.Type); + Assert.Equal(@event.Actor, enrichedEvent.Actor); + Assert.Equal(@event.AppId, enrichedEvent.AppId); + Assert.Equal(@event.AppId.Id, enrichedEvent.AppId.Id); + Assert.Equal(@event.SchemaId, enrichedEvent.SchemaId); + Assert.Equal(@event.SchemaId.Id, enrichedEvent.SchemaId.Id); } [Fact] @@ -151,13 +158,13 @@ namespace Squidex.Domain.Apps.Entities.Schemas } } - private static RuleContext Context(RuleTrigger? trigger = null) + private static RuleContext Context(RuleTrigger? trigger = null, NamedId? appId = null) { trigger ??= new SchemaChangedTrigger(); return new RuleContext { - AppId = NamedId.Of(DomainId.NewGuid(), "my-app"), + AppId = appId ?? NamedId.Of(DomainId.NewGuid(), "my-app"), Rule = new Rule(trigger, A.Fake()), RuleId = DomainId.NewGuid() };