From 4ed12e1a006980cc719dce936adfa295368a958a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 1 Jul 2018 17:36:41 +0200 Subject: [PATCH] Introduced enriched events. --- .../Actions/AlgoliaActionHandler.cs | 71 +++-------- .../Actions/AzureQueueActionHandler.cs | 8 +- .../Actions/ElasticSearchActionHandler.cs | 116 ++++-------------- .../Actions/FastlyActionHandler.cs | 23 ++-- .../Actions/MediumActionHandler.cs | 8 +- .../HandleRules/Actions/SlackActionHandler.cs | 14 +-- .../Actions/WebhookActionHandler.cs | 18 +-- .../EnrichedEvents/EnrichedContentEvent.cs | 33 +++++ .../EnrichedContentEventAction.cs | 19 +++ .../EnrichedEvents/EnrichedEvent.cs | 26 ++++ .../EnrichedEvents/EnrichedSchemaEvent.cs | 17 +++ .../HandleRules/IEventEnricher.cs | 19 +++ .../HandleRules/IRuleActionHandler.cs | 5 +- .../HandleRules/RuleActionHandler.cs | 9 +- .../HandleRules/RuleEventFormatter.cs | 77 ++++-------- .../HandleRules/RuleService.cs | 53 +++----- .../HandleRules/RuleEventFormatterTests.cs | 102 +++++++-------- .../HandleRules/RuleServiceTests.cs | 17 ++- 18 files changed, 286 insertions(+), 349 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEvent.cs create mode 100644 src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventAction.cs create mode 100644 src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs create mode 100644 src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedSchemaEvent.cs create mode 100644 src/Squidex.Domain.Apps.Core.Operations/HandleRules/IEventEnricher.cs diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs index cec57ddae..83678590c 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs @@ -10,12 +10,9 @@ using System.Threading.Tasks; using Algolia.Search; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Actions; -using Squidex.Domain.Apps.Events; -using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; #pragma warning disable SA1649 // File name must match first type name @@ -24,6 +21,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions public sealed class AlgoliaJob { public string AppId { get; set; } + public string ApiKey { get; set; } public string ContentId { get; set; } @@ -54,11 +52,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions }); } - protected override async Task<(string Description, AlgoliaJob Data)> CreateJobAsync(Envelope @event, string eventName, AlgoliaAction action) + protected override async Task<(string Description, AlgoliaJob Data)> CreateJobAsync(EnrichedEvent @event, AlgoliaAction action) { - if (@event.Payload is ContentEvent contentEvent) + if (@event is EnrichedContentEvent contentEvent) { - var contentId = contentEvent.ContentId.ToString(); + var contentId = contentEvent.Id.ToString(); var ruleDescription = string.Empty; var ruleJob = new AlgoliaJob @@ -69,56 +67,17 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions IndexName = await formatter.FormatStringAsync(action.IndexName, @event) }; - var timestamp = @event.Headers.Timestamp().ToString(); - - switch (@event.Payload) + if (contentEvent.Action == EnrichedContentEventAction.Deleted || + contentEvent.Action == EnrichedContentEventAction.Archived) + { + ruleDescription = $"Delete entry from Algolia index: {action.IndexName}"; + } + else { - case ContentCreated created: - { - ruleDescription = $"Add entry to Algolia index: {action.IndexName}"; - - ruleJob.Content = new JObject( - new JProperty("objectID", contentId), - new JProperty("id", contentId), - new JProperty("created", timestamp), - new JProperty("createdBy", created.Actor.ToString()), - new JProperty("lastModified", timestamp), - new JProperty("lastModifiedBy", created.Actor.ToString()), - new JProperty("status", Status.Draft.ToString()), - new JProperty("data", formatter.ToRouteData(created.Data))); - break; - } - - case ContentUpdated updated: - { - ruleDescription = $"Update entry in Algolia index: {action.IndexName}"; - - ruleJob.Content = new JObject( - new JProperty("objectID", contentId), - new JProperty("lastModified", timestamp), - new JProperty("lastModifiedBy", updated.Actor.ToString()), - new JProperty("data", formatter.ToRouteData(updated.Data))); - break; - } - - case ContentStatusChanged statusChanged: - { - ruleDescription = $"Update entry in Algolia index: {action.IndexName}"; - - ruleJob.Content = new JObject( - new JProperty("objectID", contentId), - new JProperty("lastModified", timestamp), - new JProperty("lastModifiedBy", statusChanged.Actor.ToString()), - new JProperty("status", statusChanged.Status.ToString())); - break; - } - - case ContentDeleted deleted: - { - ruleDescription = $"Delete entry from Algolia index: {action.IndexName}"; - - break; - } + ruleDescription = $"Add entry to Algolia index: {action.IndexName}"; + + ruleJob.Content = formatter.ToPayload(contentEvent); + ruleJob.Content["objectID"] = contentId; } return (ruleDescription, ruleJob); diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs index 2b0e50b91..fece94b2f 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs @@ -11,10 +11,9 @@ using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Queue; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Actions; -using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; #pragma warning disable SA1649 // File name must match first type name @@ -23,6 +22,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions public sealed class AzureQueueJob { public string QueueConnectionString { get; set; } + public string QueueName { get; set; } public string MessageBodyV2 { get; set; } @@ -60,9 +60,9 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions }); } - protected override async Task<(string Description, AzureQueueJob Data)> CreateJobAsync(Envelope @event, string eventName, AzureQueueAction action) + protected override async Task<(string Description, AzureQueueJob Data)> CreateJobAsync(EnrichedEvent @event, AzureQueueAction action) { - var body = formatter.ToRouteData(@event, eventName).ToString(Formatting.Indented); + var body = formatter.ToEnvelope(@event).ToString(Formatting.Indented); var queueName = await formatter.FormatStringAsync(action.Queue, @event); diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/ElasticSearchActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/ElasticSearchActionHandler.cs index 94df18789..7eb7fe61d 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/ElasticSearchActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/ElasticSearchActionHandler.cs @@ -9,12 +9,9 @@ using System; using System.Threading.Tasks; using Elasticsearch.Net; using Newtonsoft.Json.Linq; -using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Actions; -using Squidex.Domain.Apps.Events; -using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; #pragma warning disable SA1649 // File name must match first type name @@ -25,14 +22,14 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions public string Host { get; set; } public string Username { get; set; } + public string Password { get; set; } public string ContentId { get; set; } public string IndexName { get; set; } - public string IndexType { get; set; } - public string Operation { get; set; } + public string IndexType { get; set; } public JObject Content { get; set; } } @@ -63,11 +60,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions }); } - protected override async Task<(string Description, ElasticSearchJob Data)> CreateJobAsync(Envelope @event, string eventName, ElasticSearchAction action) + protected override async Task<(string Description, ElasticSearchJob Data)> CreateJobAsync(EnrichedEvent @event, ElasticSearchAction action) { - if (@event.Payload is ContentEvent contentEvent) + if (@event is EnrichedContentEvent contentEvent) { - var contentId = contentEvent.ContentId.ToString(); + var contentId = contentEvent.Id.ToString(); var ruleDescription = string.Empty; var ruleJob = new ElasticSearchJob @@ -80,59 +77,17 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions IndexType = await formatter.FormatStringAsync(action.IndexType, @event), }; - var timestamp = @event.Headers.Timestamp().ToString(); - - var actor = @event.Payload.Actor.ToString(); - - switch (@event.Payload) + if (contentEvent.Action == EnrichedContentEventAction.Deleted || + contentEvent.Action == EnrichedContentEventAction.Archived) { - case ContentCreated created: - { - ruleDescription = $"Add entry to ES index: {action.IndexName}"; - - ruleJob.Operation = "Create"; - ruleJob.Content = new JObject( - new JProperty("id", contentId), - new JProperty("created", timestamp), - new JProperty("createdBy", actor), - new JProperty("lastModified", timestamp), - new JProperty("lastModifiedBy", actor), - new JProperty("status", Status.Draft.ToString()), - new JProperty("data", formatter.ToRouteData(created.Data))); - break; - } - - case ContentUpdated updated: - { - ruleDescription = $"Update entry in ES index: {action.IndexName}"; - - ruleJob.Operation = "Update"; - ruleJob.Content = new JObject( - new JProperty("lastModified", timestamp), - new JProperty("lastModifiedBy", actor), - new JProperty("data", formatter.ToRouteData(updated.Data))); - break; - } - - case ContentStatusChanged statusChanged: - { - ruleDescription = $"Update entry in ES index: {action.IndexName}"; - - ruleJob.Operation = "Update"; - ruleJob.Content = new JObject( - new JProperty("lastModified", timestamp), - new JProperty("lastModifiedBy", actor), - new JProperty("status", statusChanged.Status.ToString())); - break; - } - - case ContentDeleted deleted: - { - ruleDescription = $"Delete entry from ES index: {action.IndexName}"; - - ruleJob.Operation = "Delete"; - break; - } + ruleDescription = $"Delete entry from Algolia index: {action.IndexName}"; + } + else + { + ruleDescription = $"Upsert to ES index: {action.IndexName}"; + + ruleJob.Content = formatter.ToPayload(contentEvent); + ruleJob.Content["objectID"] = contentId; } } @@ -141,44 +96,23 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(ElasticSearchJob job) { - if (string.IsNullOrWhiteSpace(job.Operation)) - { - return (null, new InvalidOperationException("The action cannot handle this event.")); - } - var client = clients.GetClient((new Uri(job.Host, UriKind.Absolute), job.Username, job.Password)); try { - switch (job.Operation) + if (job.Content != null) { - case "Create": - { - var doc = job.Content.ToString(); - - var response = await client.IndexAsync(job.IndexName, job.IndexType, job.ContentId, doc); - - return (response.Body, response.OriginalException); - } - - case "Update": - { - var doc = new JObject(new JProperty("doc", job.Content)).ToString(); + var doc = job.Content.ToString(); - var response = await client.UpdateAsync(job.IndexName, job.IndexType, job.ContentId, doc); + var response = await client.IndexAsync(job.IndexName, job.IndexType, job.ContentId, doc); - return (response.Body, response.OriginalException); - } - - case "Delete": - { - var response = await client.DeleteAsync(job.IndexName, job.IndexType, job.ContentId); - - return (response.Body, response.OriginalException); - } + return (response.Body, response.OriginalException); + } + else + { + var response = await client.DeleteAsync(job.IndexName, job.IndexType, job.ContentId); - default: - return (null, null); + return (response.Body, response.OriginalException); } } catch (ElasticsearchClientException ex) diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs index 8d76518cc..ef4f8c0e8 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs @@ -8,9 +8,8 @@ using System; using System.Net.Http; using System.Threading.Tasks; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Actions; -using Squidex.Domain.Apps.Events; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Http; #pragma warning disable SA1649 // File name must match first type name @@ -28,23 +27,17 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions public sealed class FastlyActionHandler : RuleActionHandler { private const string Description = "Purge key in fastly"; - private const string DescriptionIgnore = "Ignore"; - protected override Task<(string Description, FastlyJob Data)> CreateJobAsync(Envelope @event, string eventName, FastlyAction action) + protected override Task<(string Description, FastlyJob Data)> CreateJobAsync(EnrichedEvent @event, FastlyAction action) { - if (@event.Headers.Contains(CommonHeaders.AggregateId)) + var ruleJob = new FastlyJob { - var ruleJob = new FastlyJob - { - Key = @event.Headers.AggregateId().ToString(), - FastlyApiKey = action.ApiKey, - FastlyServiceID = action.ServiceId - }; - - return Task.FromResult((Description, ruleJob)); - } + Key = @event.AggregateId.ToString(), + FastlyApiKey = action.ApiKey, + FastlyServiceID = action.ServiceId + }; - return Task.FromResult((DescriptionIgnore, new FastlyJob())); + return Task.FromResult((Description, ruleJob)); } protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(FastlyJob job) diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/MediumActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/MediumActionHandler.cs index 75307c81c..62863eee5 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/MediumActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/MediumActionHandler.cs @@ -13,10 +13,9 @@ using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Actions; -using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Http; namespace Squidex.Domain.Apps.Core.HandleRules.Actions @@ -24,6 +23,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions public sealed class MediumJob { public string RequestUrl { get; set; } + public string RequestBody { get; set; } public string AccessToken { get; set; } @@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions this.formatter = formatter; } - protected override async Task<(string Description, MediumJob Data)> CreateJobAsync(Envelope @event, string eventName, MediumAction action) + protected override async Task<(string Description, MediumJob Data)> CreateJobAsync(EnrichedEvent @event, MediumAction action) { var requestUrl = !string.IsNullOrWhiteSpace(action.Author) ? @@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions return (Description, ruleJob); } - private async Task ParseTagsAsync(Envelope @event, MediumAction action) + private async Task ParseTagsAsync(EnrichedEvent @event, MediumAction action) { if (string.IsNullOrWhiteSpace(action.Tags)) { diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs index c9ec58e14..c783f8ee7 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs @@ -11,10 +11,9 @@ using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Actions; -using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Http; #pragma warning disable SA1649 // File name must match first type name @@ -50,9 +49,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions this.formatter = formatter; } - protected override async Task<(string Description, SlackJob Data)> CreateJobAsync(Envelope @event, string eventName, SlackAction action) + protected override async Task<(string Description, SlackJob Data)> CreateJobAsync(EnrichedEvent @event, SlackAction action) { - var body = await CreatePayloadAsync(@event, action.Text); + var body = + new JObject( + new JProperty("text", await formatter.FormatStringAsync(action.Text, @event))); var ruleJob = new SlackJob { @@ -63,11 +64,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions return (Description, ruleJob); } - private async Task CreatePayloadAsync(Envelope @event, string text) - { - return new JObject(new JProperty("text", await formatter.FormatStringAsync(text, @event))); - } - protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(SlackJob job) { var requestBody = job.Body; diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs index 316dc125b..d7a995810 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs @@ -11,10 +11,9 @@ using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Actions; -using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Http; #pragma warning disable SA1649 // File name must match first type name @@ -24,7 +23,9 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions public sealed class WebhookJob { public string RequestUrl { get; set; } + public string RequestSignature { get; set; } + public string RequestBodyV2 { get; set; } public JObject RequestBody { get; set; } @@ -49,16 +50,17 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Actions this.formatter = formatter; } - protected override async Task<(string Description, WebhookJob Data)> CreateJobAsync(Envelope @event, string eventName, WebhookAction action) + protected override async Task<(string Description, WebhookJob Data)> CreateJobAsync(EnrichedEvent @event, WebhookAction action) { - var body = formatter.ToRouteData(@event, eventName).ToString(Formatting.Indented); + var requestBody = formatter.ToEnvelope(@event).ToString(Formatting.Indented); + var requestUrl = await formatter.FormatStringAsync(action.Url.ToString(), @event); - var ruleDescription = $"Send event to webhook '{action.Url}'"; + var ruleDescription = $"Send event to webhook '{requestUrl}'"; var ruleJob = new WebhookJob { - RequestUrl = await formatter.FormatStringAsync(action.Url.ToString(), @event), - RequestSignature = $"{body}{action.SharedSecret}".Sha256Base64(), - RequestBodyV2 = body + RequestUrl = requestUrl, + RequestSignature = $"{requestBody}{action.SharedSecret}".Sha256Base64(), + RequestBodyV2 = requestBody }; return (ruleDescription, ruleJob); diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEvent.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEvent.cs new file mode 100644 index 000000000..730845f3b --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEvent.cs @@ -0,0 +1,33 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using NodaTime; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents +{ + public sealed class EnrichedContentEvent : EnrichedSchemaEvent + { + public EnrichedContentEventAction Action { get; set; } + + public Guid Id { get; set; } + + public Instant Created { get; set; } + + public Instant LastModified { get; set; } + + public RefToken CreatedBy { get; set; } + + public RefToken LastModifiedBy { get; set; } + + public NamedContentData Data { get; set; } + + public Status Status { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventAction.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventAction.cs new file mode 100644 index 000000000..57d107e82 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventAction.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents +{ + public enum EnrichedContentEventAction + { + Archived, + Created, + Deleted, + Published, + Restored, + Updated + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs new file mode 100644 index 000000000..d169ccdc9 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using NodaTime; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents +{ + public class EnrichedEvent + { + public Guid AggregateId { get; set; } + + public NamedId AppId { get; set; } + + public RefToken Actor { get; set; } + + public Instant Timestamp { get; set; } + + public string Name { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedSchemaEvent.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedSchemaEvent.cs new file mode 100644 index 000000000..d62e2493a --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedSchemaEvent.cs @@ -0,0 +1,17 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents +{ + public class EnrichedSchemaEvent : EnrichedEvent + { + public NamedId SchemaId { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IEventEnricher.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IEventEnricher.cs new file mode 100644 index 000000000..6d2e7961d --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IEventEnricher.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; +using Squidex.Domain.Apps.Events; +using Squidex.Infrastructure.EventSourcing; + +namespace Squidex.Domain.Apps.Core.HandleRules +{ + public interface IEventEnricher + { + Task EnrichAsync(Envelope @event); + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs index ec0fdf198..deced9228 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs @@ -8,9 +8,8 @@ using System; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules; -using Squidex.Domain.Apps.Events; -using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Core.HandleRules { @@ -18,7 +17,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules { Type ActionType { get; } - Task<(string Description, JObject Data)> CreateJobAsync(Envelope @event, string eventName, RuleAction action); + Task<(string Description, JObject Data)> CreateJobAsync(EnrichedEvent @event, RuleAction action); Task<(string Dump, Exception Exception)> ExecuteJobAsync(JObject data); } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs index ea1ead7f5..1cf0e8666 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs @@ -8,9 +8,8 @@ using System; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules; -using Squidex.Domain.Apps.Events; -using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Core.HandleRules { @@ -21,9 +20,9 @@ namespace Squidex.Domain.Apps.Core.HandleRules get { return typeof(TAction); } } - async Task<(string Description, JObject Data)> IRuleActionHandler.CreateJobAsync(Envelope @event, string eventName, RuleAction action) + async Task<(string Description, JObject Data)> IRuleActionHandler.CreateJobAsync(EnrichedEvent @event, RuleAction action) { - var (description, data) = await CreateJobAsync(@event, eventName, (TAction)action); + var (description, data) = await CreateJobAsync(@event, (TAction)action); return (description, JObject.FromObject(data)); } @@ -35,7 +34,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules return await ExecuteJobAsync(typedData); } - protected abstract Task<(string Description, TData Data)> CreateJobAsync(Envelope @event, string eventName, TAction action); + protected abstract Task<(string Description, TData Data)> CreateJobAsync(EnrichedEvent @event, TAction action); protected abstract Task<(string Dump, Exception Exception)> ExecuteJobAsync(TData job); } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs index 8adaafa90..5910dd143 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs @@ -14,11 +14,9 @@ using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Events; -using Squidex.Domain.Apps.Events.Contents; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Infrastructure; using Squidex.Infrastructure.Caching; -using Squidex.Infrastructure.EventSourcing; using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Core.HandleRules @@ -61,20 +59,20 @@ namespace Squidex.Domain.Apps.Core.HandleRules this.urlGenerator = urlGenerator; } - public virtual JToken ToRouteData(object value) + public virtual JObject ToPayload(object @event) { - return JToken.FromObject(value, serializer); + return JObject.FromObject(@event, serializer); } - public virtual JToken ToRouteData(Envelope @event, string eventName) + public virtual JObject ToEnvelope(EnrichedEvent @event) { return new JObject( - new JProperty("type", eventName), - new JProperty("payload", JToken.FromObject(@event.Payload, serializer)), - new JProperty("timestamp", @event.Headers.Timestamp().ToString())); + new JProperty("type", @event), + new JProperty("payload", ToPayload(@event)), + new JProperty("timestamp", @event.Timestamp.ToString())); } - public async virtual Task FormatStringAsync(string text, Envelope @event) + public async virtual Task FormatStringAsync(string text, EnrichedEvent @event) { if (string.IsNullOrWhiteSpace(text)) { @@ -83,57 +81,46 @@ namespace Squidex.Domain.Apps.Core.HandleRules var sb = new StringBuilder(text); - if (@event.Headers.Contains(CommonHeaders.Timestamp)) - { - var timestamp = @event.Headers.Timestamp().ToDateTimeUtc(); - - sb.Replace(TimestampDateTimePlaceholder, timestamp.ToString("yyy-MM-dd-hh-mm-ss", CultureInfo.InvariantCulture)); - sb.Replace(TimestampDatePlaceholder, timestamp.ToString("yyy-MM-dd", CultureInfo.InvariantCulture)); - } + sb.Replace(TimestampDateTimePlaceholder, @event.Timestamp.ToString("yyy-MM-dd-hh-mm-ss", CultureInfo.InvariantCulture)); + sb.Replace(TimestampDatePlaceholder, @event.Timestamp.ToString("yyy-MM-dd", CultureInfo.InvariantCulture)); - if (@event.Payload.AppId != null) + if (@event.AppId != null) { - sb.Replace(AppIdPlaceholder, @event.Payload.AppId.Id.ToString()); - sb.Replace(AppNamePlaceholder, @event.Payload.AppId.Name); + sb.Replace(AppIdPlaceholder, @event.AppId.Id.ToString()); + sb.Replace(AppNamePlaceholder, @event.AppId.Name); } - if (@event.Payload is SchemaEvent schemaEvent && schemaEvent.SchemaId != null) + if (@event is EnrichedSchemaEvent schemaEvent && schemaEvent.SchemaId != null) { sb.Replace(SchemaIdPlaceholder, schemaEvent.SchemaId.Id.ToString()); sb.Replace(SchemaNamePlaceholder, schemaEvent.SchemaId.Name); } - if (@event.Payload is ContentEvent contentEvent) + if (@event is EnrichedContentEvent contentEvent) { - sb.Replace(ContentUrlPlaceholder, urlGenerator.GenerateContentUIUrl(@event.Payload.AppId, contentEvent.SchemaId, contentEvent.ContentId)); + sb.Replace(ContentUrlPlaceholder, urlGenerator.GenerateContentUIUrl(@event.AppId, contentEvent.SchemaId, contentEvent.Id)); + sb.Replace(ContentActionPlaceholder, contentEvent.Action.ToString()); } await FormatUserInfoAsync(@event, sb); - FormatContentAction(@event, sb); - var result = sb.ToString(); - if (@event.Payload is ContentCreated contentCreated && contentCreated.Data != null) + if (@event is EnrichedContentEvent contentEvent2) { - result = ReplaceData(contentCreated.Data, result); - } - - if (@event.Payload is ContentUpdated contentUpdated && contentUpdated.Data != null) - { - result = ReplaceData(contentUpdated.Data, result); + result = ReplaceData(contentEvent2.Data, result); } return result; } - private async Task FormatUserInfoAsync(Envelope @event, StringBuilder sb) + private async Task FormatUserInfoAsync(EnrichedEvent @event, StringBuilder sb) { var text = sb.ToString(); if (text.Contains(UserEmailPlaceholder) || text.Contains(UserNamePlaceholder)) { - var actor = @event.Payload.Actor; + var actor = @event.Actor; if (actor.Type.Equals("client", StringComparison.OrdinalIgnoreCase)) { @@ -160,28 +147,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules } } - private static void FormatContentAction(Envelope @event, StringBuilder sb) - { - switch (@event.Payload) - { - case ContentCreated contentCreated: - sb.Replace(ContentActionPlaceholder, "created"); - break; - - case ContentUpdated contentUpdated: - sb.Replace(ContentActionPlaceholder, "updated"); - break; - - case ContentStatusChanged contentStatusChanged: - sb.Replace(ContentActionPlaceholder, $"set to {contentStatusChanged.Status.ToString().ToLowerInvariant()}"); - break; - - case ContentDeleted contentDeleted: - sb.Replace(ContentActionPlaceholder, "deleted"); - break; - } - } - private static string ReplaceData(NamedContentData data, string text) { text = ContentDataPlaceholder.Replace(text, match => diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs index 019399f0b..58e862cc6 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs @@ -26,17 +26,20 @@ namespace Squidex.Domain.Apps.Core.HandleRules private readonly Dictionary ruleActionHandlers; private readonly Dictionary ruleTriggerHandlers; private readonly TypeNameRegistry typeNameRegistry; + private readonly IEventEnricher eventEnricher; private readonly IClock clock; public RuleService( IEnumerable ruleTriggerHandlers, IEnumerable ruleActionHandlers, + IEventEnricher eventEnricher, IClock clock, TypeNameRegistry typeNameRegistry) { Guard.NotNull(ruleTriggerHandlers, nameof(ruleTriggerHandlers)); Guard.NotNull(ruleActionHandlers, nameof(ruleActionHandlers)); Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry)); + Guard.NotNull(eventEnricher, nameof(eventEnricher)); Guard.NotNull(clock, nameof(clock)); this.typeNameRegistry = typeNameRegistry; @@ -44,6 +47,8 @@ namespace Squidex.Domain.Apps.Core.HandleRules this.ruleTriggerHandlers = ruleTriggerHandlers.ToDictionary(x => x.TriggerType); this.ruleActionHandlers = ruleActionHandlers.ToDictionary(x => x.ActionType); + this.eventEnricher = eventEnricher; + this.clock = clock; } @@ -76,41 +81,38 @@ namespace Squidex.Domain.Apps.Core.HandleRules return null; } - var eventName = CreateEventName(appEvent); - var now = clock.GetCurrentInstant(); - var actionName = typeNameRegistry.GetName(actionType); - var actionData = await actionHandler.CreateJobAsync(appEventEnvelope, eventName, rule.Action); - var eventTime = @event.Headers.Contains(CommonHeaders.Timestamp) ? @event.Headers.Timestamp() : now; - var aggregateId = - @event.Headers.Contains(CommonHeaders.AggregateId) ? - @event.Headers.AggregateId() : - Guid.NewGuid(); + var expires = eventTime.Plus(Constants.ExpirationTime); + + if (expires < now) + { + return null; + } + + var enrichedEvent = await eventEnricher.EnrichAsync(appEventEnvelope); + + var actionName = typeNameRegistry.GetName(actionType); + var actionData = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action); var job = new RuleJob { JobId = Guid.NewGuid(), ActionName = actionName, ActionData = actionData.Data, - AggregateId = aggregateId, + AggregateId = enrichedEvent.AggregateId, AppId = appEvent.AppId.Id, Created = now, - EventName = eventName, - Expires = eventTime.Plus(Constants.ExpirationTime), + EventName = enrichedEvent.Name, + Expires = expires, Description = actionData.Description }; - if (job.Expires < now) - { - return null; - } - return job; } @@ -152,22 +154,5 @@ namespace Squidex.Domain.Apps.Core.HandleRules return (ex.ToString(), RuleResult.Failed, TimeSpan.Zero); } } - - private string CreateEventName(AppEvent appEvent) - { - var eventName = typeNameRegistry.GetName(appEvent.GetType()); - - if (appEvent is SchemaEvent schemaEvent) - { - if (eventName.StartsWith(ContentPrefix, StringComparison.Ordinal)) - { - eventName = eventName.Substring(ContentPrefix.Length); - } - - return $"{schemaEvent.SchemaId.Name.ToPascalCase()}{eventName}"; - } - - return eventName; - } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs index 7112afd42..bbe33a476 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs @@ -17,10 +17,8 @@ using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.HandleRules; -using Squidex.Domain.Apps.Events; -using Squidex.Domain.Apps.Events.Contents; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; using Squidex.Shared.Identity; using Squidex.Shared.Users; using Xunit; @@ -53,7 +51,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public void Should_serialize_object_to_json() { - var result = sut.ToRouteData(new { Value = 1 }); + var result = sut.ToPayload(new { Value = 1 }); Assert.True(result is JObject); } @@ -61,9 +59,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public void Should_create_route_data() { - var @event = new ContentCreated { AppId = appId }; + var @event = new EnrichedContentEvent { AppId = appId }; - var result = sut.ToRouteData(AsEnvelope(@event)); + var result = sut.ToPayload(@event); Assert.True(result is JObject); } @@ -71,9 +69,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public void Should_create_route_data_from_event() { - var @event = new ContentCreated { AppId = appId }; + var @event = new EnrichedContentEvent { AppId = appId, Name = "MyEventName" }; - var result = sut.ToRouteData(AsEnvelope(@event), "MyEventName"); + var result = sut.ToPayload(@event); Assert.Equal("MyEventName", result["type"]); } @@ -81,9 +79,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_replace_app_information_from_event() { - var @event = new ContentCreated { AppId = appId }; + var @event = new EnrichedContentEvent { AppId = appId }; - var result = await sut.FormatStringAsync("Name $APP_NAME has id $APP_ID", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("Name $APP_NAME has id $APP_ID", @event); Assert.Equal($"Name my-app has id {appId.Id}", result); } @@ -91,9 +89,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_replace_schema_information_from_event() { - var @event = new ContentCreated { SchemaId = schemaId }; + var @event = new EnrichedContentEvent { SchemaId = schemaId }; - var result = await sut.FormatStringAsync("Name $SCHEMA_NAME has id $SCHEMA_ID", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("Name $SCHEMA_NAME has id $SCHEMA_ID", @event); Assert.Equal($"Name my-schema has id {schemaId.Id}", result); } @@ -103,7 +101,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { var now = DateTime.UtcNow; - var envelope = AsEnvelope(new ContentCreated()).SetTimestamp(Instant.FromDateTimeUtc(now)); + var envelope = new EnrichedContentEvent { Timestamp = Instant.FromDateTimeUtc(now) }; var result = await sut.FormatStringAsync("Date: $TIMESTAMP_DATE, Full: $TIMESTAMP_DATETIME", envelope); @@ -116,9 +114,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => userResolver.FindByIdOrEmailAsync("123")) .Returns(user); - var @event = new ContentCreated { Actor = new RefToken("subject", "123") }; + var @event = new EnrichedContentEvent { Actor = new RefToken("subject", "123") }; - var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", @event); Assert.Equal($"From me (me@email.com)", result); } @@ -129,9 +127,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => userResolver.FindByIdOrEmailAsync("123")) .Returns(Task.FromResult(null)); - var @event = new ContentCreated { Actor = new RefToken("subject", "123") }; + var @event = new EnrichedContentEvent { Actor = new RefToken("subject", "123") }; - var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", @event); Assert.Equal($"From UNDEFINED (UNDEFINED)", result); } @@ -142,9 +140,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => userResolver.FindByIdOrEmailAsync("123")) .Throws(new InvalidOperationException()); - var @event = new ContentCreated { Actor = new RefToken("subject", "123") }; + var @event = new EnrichedContentEvent { Actor = new RefToken("subject", "123") }; - var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", @event); Assert.Equal($"From UNDEFINED (UNDEFINED)", result); } @@ -152,9 +150,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_format_email_and_display_name_from_client() { - var @event = new ContentCreated { Actor = new RefToken("client", "android") }; + var @event = new EnrichedContentEvent { Actor = new RefToken("client", "android") }; - var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("From $USER_NAME ($USER_EMAIL)", @event); Assert.Equal($"From client:android (client:android)", result); } @@ -167,9 +165,9 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => urlGenerator.GenerateContentUIUrl(appId, schemaId, contentId)) .Returns(url); - var @event = new ContentCreated { AppId = appId, ContentId = contentId, SchemaId = schemaId }; + var @event = new EnrichedContentEvent { AppId = appId, Id = contentId, SchemaId = schemaId }; - var result = await sut.FormatStringAsync("Go to $CONTENT_URL", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("Go to $CONTENT_URL", @event); Assert.Equal($"Go to {url}", result); } @@ -177,7 +175,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_undefined_when_field_not_found() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -186,7 +184,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .AddValue("iv", "Berlin")) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.country.iv", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.country.iv", @event); Assert.Equal("UNDEFINED", result); } @@ -194,7 +192,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_undefined_when_partition_not_found() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -203,7 +201,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .AddValue("iv", "Berlin")) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.de", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.de", @event); Assert.Equal("UNDEFINED", result); } @@ -211,7 +209,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_undefined_when_array_item_not_found() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -220,7 +218,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .AddValue("iv", new JArray())) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.de.10", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.de.10", @event); Assert.Equal("UNDEFINED", result); } @@ -228,7 +226,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_undefined_when_property_not_found() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -238,7 +236,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new JProperty("name", "Berlin")))) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.de.Name", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.de.Name", @event); Assert.Equal("UNDEFINED", result); } @@ -246,7 +244,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_plain_value_when_found() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -255,7 +253,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .AddValue("iv", "Berlin")) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", @event); Assert.Equal("Berlin", result); } @@ -263,7 +261,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_plain_value_when_found_from_update_event() { - var @event = new ContentUpdated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -272,7 +270,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .AddValue("iv", "Berlin")) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", @event); Assert.Equal("Berlin", result); } @@ -280,7 +278,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_undefined_when_null() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -289,7 +287,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .AddValue("iv", JValue.CreateNull())) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", @event); Assert.Equal("UNDEFINED", result); } @@ -297,7 +295,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_undefined_when_undefined() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -306,7 +304,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .AddValue("iv", JValue.CreateUndefined())) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", @event); Assert.Equal("UNDEFINED", result); } @@ -314,7 +312,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_string_when_object() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -324,7 +322,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new JProperty("name", "Berlin")))) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv", @event); Assert.Equal(JObject.FromObject(new { name = "Berlin" }).ToString(Formatting.Indented), result); } @@ -332,7 +330,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_plain_value_from_array_when_found() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -342,7 +340,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules "Berlin"))) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv.0", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv.0", @event); Assert.Equal("Berlin", result); } @@ -350,7 +348,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_plain_value_from_object_when_found() { - var @event = new ContentCreated + var @event = new EnrichedContentEvent { Data = new NamedContentData() @@ -360,7 +358,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new JProperty("name", "Berlin")))) }; - var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv.name", AsEnvelope(@event)); + var result = await sut.FormatStringAsync("$CONTENT_DATA.city.iv.name", @event); Assert.Equal("Berlin", result); } @@ -368,16 +366,10 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_format_content_actions_when_found() { - Assert.Equal("created", await sut.FormatStringAsync("$CONTENT_ACTION", AsEnvelope(new ContentCreated()))); - Assert.Equal("updated", await sut.FormatStringAsync("$CONTENT_ACTION", AsEnvelope(new ContentUpdated()))); - Assert.Equal("deleted", await sut.FormatStringAsync("$CONTENT_ACTION", AsEnvelope(new ContentDeleted()))); - - Assert.Equal("set to archived", await sut.FormatStringAsync("$CONTENT_ACTION", AsEnvelope(new ContentStatusChanged { Status = Status.Archived }))); - } - - private static Envelope AsEnvelope(T @event) where T : AppEvent - { - return Envelope.Create(@event).To(); + Assert.Equal("created", await sut.FormatStringAsync("$CONTENT_ACTION", new EnrichedContentEvent { Action = EnrichedContentEventAction.Created })); + Assert.Equal("updated", await sut.FormatStringAsync("$CONTENT_ACTION", new EnrichedContentEvent { Action = EnrichedContentEventAction.Updated })); + Assert.Equal("deleted", await sut.FormatStringAsync("$CONTENT_ACTION", new EnrichedContentEvent { Action = EnrichedContentEventAction.Deleted })); + Assert.Equal("archived", await sut.FormatStringAsync("$CONTENT_ACTION", new EnrichedContentEvent { Action = EnrichedContentEventAction.Archived })); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index f0bbc9d29..5c4b837b2 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -11,6 +11,7 @@ using FakeItEasy; using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Actions; using Squidex.Domain.Apps.Core.Rules.Triggers; @@ -28,6 +29,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { private readonly IRuleTriggerHandler ruleTriggerHandler = A.Fake(); private readonly IRuleActionHandler ruleActionHandler = A.Fake(); + private readonly IEventEnricher eventEnricher = A.Fake(); private readonly IClock clock = A.Fake(); private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); private readonly RuleService sut; @@ -57,13 +59,16 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules typeNameRegistry.Map(typeof(ContentCreated)); typeNameRegistry.Map(typeof(WebhookAction)); + A.CallTo(() => eventEnricher.EnrichAsync(A>.Ignored)) + .Returns(new EnrichedContentEvent()); + A.CallTo(() => ruleActionHandler.ActionType) .Returns(typeof(WebhookAction)); A.CallTo(() => ruleTriggerHandler.TriggerType) .Returns(typeof(ContentChangedTrigger)); - sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, clock, typeNameRegistry); + sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, clock, typeNameRegistry); } [Fact] @@ -128,15 +133,13 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules var actionData = new JObject(); var actionDescription = "MyDescription"; - var eventName = "MySchemaCreatedEvent"; - A.CallTo(() => clock.GetCurrentInstant()) .Returns(now); A.CallTo(() => ruleTriggerHandler.Triggers(A>.Ignored, ruleConfig.Trigger)) .Returns(true); - A.CallTo(() => ruleActionHandler.CreateJobAsync(A>.Ignored, eventName, ruleConfig.Action)) + A.CallTo(() => ruleActionHandler.CreateJobAsync(A.Ignored, ruleConfig.Action)) .Returns((actionDescription, actionData)); var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope); @@ -160,21 +163,17 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules var actionData = new JObject(); var actionDescription = "MyDescription"; - var eventName = "MySchemaCreatedEvent"; - A.CallTo(() => clock.GetCurrentInstant()) .Returns(now); A.CallTo(() => ruleTriggerHandler.Triggers(A>.Ignored, ruleConfig.Trigger)) .Returns(true); - A.CallTo(() => ruleActionHandler.CreateJobAsync(A>.Ignored, eventName, ruleConfig.Action)) + A.CallTo(() => ruleActionHandler.CreateJobAsync(A.Ignored, ruleConfig.Action)) .Returns((actionDescription, actionData)); var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope); - Assert.Equal(eventName, job.EventName); - Assert.Equal(actionData, job.ActionData); Assert.Equal(actionName, job.ActionName); Assert.Equal(actionDescription, job.Description);