Browse Source

Introduced enriched events.

pull/306/head
Sebastian 8 years ago
parent
commit
4ed12e1a00
  1. 71
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AlgoliaActionHandler.cs
  2. 8
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/AzureQueueActionHandler.cs
  3. 116
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/ElasticSearchActionHandler.cs
  4. 23
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/FastlyActionHandler.cs
  5. 8
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/MediumActionHandler.cs
  6. 14
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/SlackActionHandler.cs
  7. 18
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
  8. 33
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEvent.cs
  9. 19
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEventAction.cs
  10. 26
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs
  11. 17
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedSchemaEvent.cs
  12. 19
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/IEventEnricher.cs
  13. 5
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs
  14. 9
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs
  15. 77
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs
  16. 53
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs
  17. 102
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs
  18. 17
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs

71
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<AppEvent> @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);

8
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<AppEvent> @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);

116
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<AppEvent> @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<StringResponse>(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<StringResponse>(job.IndexName, job.IndexType, job.ContentId, doc);
var response = await client.IndexAsync<StringResponse>(job.IndexName, job.IndexType, job.ContentId, doc);
return (response.Body, response.OriginalException);
}
case "Delete":
{
var response = await client.DeleteAsync<StringResponse>(job.IndexName, job.IndexType, job.ContentId);
return (response.Body, response.OriginalException);
}
return (response.Body, response.OriginalException);
}
else
{
var response = await client.DeleteAsync<StringResponse>(job.IndexName, job.IndexType, job.ContentId);
default:
return (null, null);
return (response.Body, response.OriginalException);
}
}
catch (ElasticsearchClientException ex)

23
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<FastlyAction, FastlyJob>
{
private const string Description = "Purge key in fastly";
private const string DescriptionIgnore = "Ignore";
protected override Task<(string Description, FastlyJob Data)> CreateJobAsync(Envelope<AppEvent> @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)

8
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<AppEvent> @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<JArray> ParseTagsAsync(Envelope<AppEvent> @event, MediumAction action)
private async Task<JArray> ParseTagsAsync(EnrichedEvent @event, MediumAction action)
{
if (string.IsNullOrWhiteSpace(action.Tags))
{

14
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<AppEvent> @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<JObject> CreatePayloadAsync(Envelope<AppEvent> @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;

18
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<AppEvent> @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);

33
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; }
}
}

19
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
}
}

26
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<Guid> AppId { get; set; }
public RefToken Actor { get; set; }
public Instant Timestamp { get; set; }
public string Name { get; set; }
}
}

17
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<Guid> SchemaId { get; set; }
}
}

19
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<EnrichedEvent> EnrichAsync(Envelope<AppEvent> @event);
}
}

5
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<AppEvent> @event, string eventName, RuleAction action);
Task<(string Description, JObject Data)> CreateJobAsync(EnrichedEvent @event, RuleAction action);
Task<(string Dump, Exception Exception)> ExecuteJobAsync(JObject data);
}

9
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<AppEvent> @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<AppEvent> @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);
}

77
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<AppEvent> @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<string> FormatStringAsync(string text, Envelope<AppEvent> @event)
public async virtual Task<string> 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<AppEvent> @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<AppEvent> @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 =>

53
src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs

@ -26,17 +26,20 @@ namespace Squidex.Domain.Apps.Core.HandleRules
private readonly Dictionary<Type, IRuleActionHandler> ruleActionHandlers;
private readonly Dictionary<Type, IRuleTriggerHandler> ruleTriggerHandlers;
private readonly TypeNameRegistry typeNameRegistry;
private readonly IEventEnricher eventEnricher;
private readonly IClock clock;
public RuleService(
IEnumerable<IRuleTriggerHandler> ruleTriggerHandlers,
IEnumerable<IRuleActionHandler> 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;
}
}
}

102
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<IUser>(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<AppEvent> AsEnvelope<T>(T @event) where T : AppEvent
{
return Envelope.Create<AppEvent>(@event).To<AppEvent>();
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 }));
}
}
}

17
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<IRuleTriggerHandler>();
private readonly IRuleActionHandler ruleActionHandler = A.Fake<IRuleActionHandler>();
private readonly IEventEnricher eventEnricher = A.Fake<IEventEnricher>();
private readonly IClock clock = A.Fake<IClock>();
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<Envelope<AppEvent>>.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<Envelope<AppEvent>>.Ignored, ruleConfig.Trigger))
.Returns(true);
A.CallTo(() => ruleActionHandler.CreateJobAsync(A<Envelope<AppEvent>>.Ignored, eventName, ruleConfig.Action))
A.CallTo(() => ruleActionHandler.CreateJobAsync(A<EnrichedEvent>.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<Envelope<AppEvent>>.Ignored, ruleConfig.Trigger))
.Returns(true);
A.CallTo(() => ruleActionHandler.CreateJobAsync(A<Envelope<AppEvent>>.Ignored, eventName, ruleConfig.Action))
A.CallTo(() => ruleActionHandler.CreateJobAsync(A<EnrichedEvent>.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);

Loading…
Cancel
Save