diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsConverter.cs similarity index 93% rename from backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs rename to backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsConverter.cs index a5c8d14b4..03acd02c3 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsConverter.cs @@ -12,7 +12,7 @@ using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Domain.Apps.Core.Contents.Json { - public sealed class WorkflowConverter : JsonClassConverter + public sealed class WorkflowsConverter : JsonClassConverter { protected override void WriteValue(JsonWriter writer, Workflows value, JsonSerializer serializer) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EventEnricher.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EventEnricher.cs index fde694065..fbddaf977 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EventEnricher.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EventEnricher.cs @@ -42,7 +42,10 @@ namespace Squidex.Domain.Apps.Core.HandleRules userEvent.Actor = squidexEvent.Actor; } - userEvent.User = await FindUserAsync(userEvent.Actor); + if (userEvent.Actor != null) + { + userEvent.User = await FindUserAsync(userEvent.Actor); + } } enrichedEvent.AppId = @event.Payload.AppId; diff --git a/backend/src/Squidex/Config/Domain/SerializationServices.cs b/backend/src/Squidex/Config/Domain/SerializationServices.cs index 9d74defdf..0543e944c 100644 --- a/backend/src/Squidex/Config/Domain/SerializationServices.cs +++ b/backend/src/Squidex/Config/Domain/SerializationServices.cs @@ -52,7 +52,7 @@ namespace Squidex.Config.Domain new SchemaConverter(), new StatusConverter(), new StringEnumConverter(), - new WorkflowConverter(), + new WorkflowsConverter(), new WorkflowStepConverter()); settings.NullValueHandling = NullValueHandling.Ignore; diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs index 1864b0a5a..5ece0290d 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using System.Collections.Generic; using System.Linq; using FluentAssertions; using Squidex.Domain.Apps.Core.Contents; @@ -18,6 +20,26 @@ namespace Squidex.Domain.Apps.Core.Model.Contents { [Fact] public void Should_serialize_and_deserialize() + { + var workflow = new Workflow( + Status.Draft, new Dictionary + { + [Status.Draft] = new WorkflowStep( + new Dictionary + { + [Status.Published] = WorkflowTransition.When("Expression", "Role1", "Role2") + }, + "#00ff00", + NoUpdate.When("Expression", "Role1", "Role2")) + }, new List { Guid.NewGuid() }, "MyName"); + + var serialized = workflow.SerializeAndDeserialize(); + + serialized.Should().BeEquivalentTo(workflow); + } + + [Fact] + public void Should_serialize_and_deserialize_default() { var workflow = Workflow.Default; diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs index 6d2e796ae..a774bb1ae 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Collections.Generic; using FluentAssertions; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.TestHelpers; @@ -19,21 +18,11 @@ namespace Squidex.Domain.Apps.Core.Model.Contents [Fact] public void Should_serialize_and_deserialize() { - var workflow = new Workflow( - Status.Draft, new Dictionary - { - [Status.Draft] = new WorkflowStep( - new Dictionary - { - [Status.Published] = WorkflowTransition.When("Expression", "Role1", "Role2") - }, - "#00ff00", - NoUpdate.When("Expression", "Role1", "Role2")) - }, new List { Guid.NewGuid() }, "MyName"); + var workflows = Workflows.Empty.Add(Guid.NewGuid(), "my-workflow"); - var serialized = workflow.SerializeAndDeserialize(); + var serialized = workflows.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(workflow); + serialized.Should().BeEquivalentTo(workflows); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/EventEnricherTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/EventEnricherTests.cs new file mode 100644 index 000000000..ba4d8e119 --- /dev/null +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/EventEnricherTests.cs @@ -0,0 +1,153 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using FakeItEasy; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using NodaTime; +using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Contents; +using Squidex.Infrastructure; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Shared.Users; +using Xunit; + +namespace Squidex.Domain.Apps.Core.Operations.HandleRules +{ + public class EventEnricherTests + { + private readonly IUserResolver userResolver = A.Fake(); + private readonly EventEnricher sut; + + public EventEnricherTests() + { + var cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + + sut = new EventEnricher(cache, userResolver); + } + + [Fact] + public async Task Should_enrich_with_timestamp() + { + var timestamp = SystemClock.Instance.GetCurrentInstant().WithoutMs(); + + var @event = + Envelope.Create(new ContentCreated()) + .SetTimestamp(timestamp); + + var enrichedEvent = new EnrichedContentEvent(); + + await sut.EnrichAsync(enrichedEvent, @event); + + Assert.Equal(timestamp, enrichedEvent.Timestamp); + } + + [Fact] + public async Task Should_enrich_with_appId() + { + var appId = NamedId.Of(Guid.NewGuid(), "my-app"); + + var @event = + Envelope.Create(new ContentCreated + { + AppId = appId + }); + + var enrichedEvent = new EnrichedContentEvent(); + + await sut.EnrichAsync(enrichedEvent, @event); + + Assert.Equal(appId, enrichedEvent.AppId); + } + + [Fact] + public async Task Should_not_enrich_with_user_if_token_is_null() + { + RefToken actor = null!; + + var @event = + Envelope.Create(new ContentCreated + { + Actor = actor + }); + + var enrichedEvent = new EnrichedContentEvent(); + + await sut.EnrichAsync(enrichedEvent, @event); + + Assert.Null(enrichedEvent.User); + + A.CallTo(() => userResolver.FindByIdAsync(A._)) + .MustNotHaveHappened(); + } + + [Fact] + public async Task Should_enrich_with_user() + { + var actor = new RefToken(RefTokenType.Client, "me"); + + var user = A.Dummy(); + + A.CallTo(() => userResolver.FindByIdAsync(actor.Identifier)) + .Returns(user); + + var @event = + Envelope.Create(new ContentCreated + { + Actor = actor + }); + + var enrichedEvent = new EnrichedContentEvent(); + + await sut.EnrichAsync(enrichedEvent, @event); + + Assert.Equal(user, enrichedEvent.User); + + A.CallTo(() => userResolver.FindByIdAsync(A._)) + .MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task Should_enrich_with_user_and_cache() + { + var actor = new RefToken(RefTokenType.Client, "me"); + + var user = A.Dummy(); + + A.CallTo(() => userResolver.FindByIdAsync(actor.Identifier)) + .Returns(user); + + var @event1 = + Envelope.Create(new ContentCreated + { + Actor = actor + }); + + var @event2 = + Envelope.Create(new ContentCreated + { + Actor = actor + }); + + var enrichedEvent1 = new EnrichedContentEvent(); + var enrichedEvent2 = new EnrichedContentEvent(); + + await sut.EnrichAsync(enrichedEvent1, @event1); + await sut.EnrichAsync(enrichedEvent2, @event2); + + Assert.Equal(user, enrichedEvent1.User); + Assert.Equal(user, enrichedEvent2.User); + + A.CallTo(() => userResolver.FindByIdAsync(A._)) + .MustHaveHappenedOnceExactly(); + } + } +} 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 39e254264..c1a506196 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs @@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Core.TestHelpers new SchemaConverter(), new StatusConverter(), new StringEnumConverter(), - new WorkflowConverter(), + new WorkflowsConverter(), new WorkflowStepConverter()), TypeNameHandling = typeNameHandling