mirror of https://github.com/Squidex/squidex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
5.4 KiB
159 lines
5.4 KiB
// ==========================================================================
|
|
// RuleService.cs
|
|
// Squidex Headless CMS
|
|
// ==========================================================================
|
|
// Copyright (c) Squidex Group
|
|
// All rights reserved.
|
|
// ==========================================================================
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json.Linq;
|
|
using NodaTime;
|
|
using Squidex.Domain.Apps.Core.Rules;
|
|
using Squidex.Domain.Apps.Events;
|
|
using Squidex.Infrastructure;
|
|
using Squidex.Infrastructure.CQRS.Events;
|
|
|
|
namespace Squidex.Domain.Apps.Core.HandleRules
|
|
{
|
|
public sealed class RuleService
|
|
{
|
|
private const string ContentPrefix = "Content";
|
|
private static readonly Duration ExpirationTime = Duration.FromDays(2);
|
|
private readonly Dictionary<Type, IRuleActionHandler> ruleActionHandlers;
|
|
private readonly Dictionary<Type, IRuleTriggerHandler> ruleTriggerHandlers;
|
|
private readonly TypeNameRegistry typeNameRegistry;
|
|
private readonly IClock clock;
|
|
|
|
public RuleService(
|
|
IEnumerable<IRuleTriggerHandler> ruleTriggerHandlers,
|
|
IEnumerable<IRuleActionHandler> ruleActionHandlers,
|
|
IClock clock,
|
|
TypeNameRegistry typeNameRegistry)
|
|
{
|
|
Guard.NotNull(ruleTriggerHandlers, nameof(ruleTriggerHandlers));
|
|
Guard.NotNull(ruleActionHandlers, nameof(ruleActionHandlers));
|
|
Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry));
|
|
Guard.NotNull(clock, nameof(clock));
|
|
|
|
this.typeNameRegistry = typeNameRegistry;
|
|
|
|
this.ruleTriggerHandlers = ruleTriggerHandlers.ToDictionary(x => x.TriggerType);
|
|
this.ruleActionHandlers = ruleActionHandlers.ToDictionary(x => x.ActionType);
|
|
|
|
this.clock = clock;
|
|
}
|
|
|
|
public RuleJob CreateJob(Rule rule, Envelope<IEvent> @event)
|
|
{
|
|
Guard.NotNull(rule, nameof(rule));
|
|
Guard.NotNull(@event, nameof(@event));
|
|
|
|
if (!(@event.Payload is AppEvent appEvent))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var actionType = rule.Action.GetType();
|
|
|
|
if (!ruleTriggerHandlers.TryGetValue(rule.Trigger.GetType(), out var triggerHandler))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (!ruleActionHandlers.TryGetValue(actionType, out var actionHandler))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var appEventEnvelope = @event.To<AppEvent>();
|
|
|
|
if (!triggerHandler.Triggers(appEventEnvelope, rule.Trigger))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var eventName = CreateEventName(appEvent);
|
|
|
|
var now = clock.GetCurrentInstant();
|
|
|
|
var actionName = typeNameRegistry.GetName(actionType);
|
|
var actionData = actionHandler.CreateJob(appEventEnvelope, eventName, rule.Action);
|
|
|
|
var job = new RuleJob
|
|
{
|
|
Id = Guid.NewGuid(),
|
|
ActionName = actionName,
|
|
ActionData = actionData.Data,
|
|
AppId = appEvent.AppId.Id,
|
|
Created = now,
|
|
EventName = eventName,
|
|
Expires = now.Plus(ExpirationTime),
|
|
Description = actionData.Description
|
|
};
|
|
|
|
return job;
|
|
}
|
|
|
|
public async Task<(string Dump, RuleResult Result, TimeSpan Elapsed)> InvokeAsync(string actionName, RuleJobData job)
|
|
{
|
|
try
|
|
{
|
|
var actionType = typeNameRegistry.GetType(actionName);
|
|
var actionWatch = Stopwatch.StartNew();
|
|
|
|
var result = await ruleActionHandlers[actionType].ExecuteJobAsync(job);
|
|
|
|
actionWatch.Stop();
|
|
|
|
var dumpBuilder = new StringBuilder(result.Dump);
|
|
|
|
dumpBuilder.AppendLine();
|
|
dumpBuilder.AppendFormat("Elapesed {0}.", actionWatch.Elapsed);
|
|
dumpBuilder.AppendLine();
|
|
|
|
if (result.Exception is TimeoutException || result.Exception is OperationCanceledException)
|
|
{
|
|
dumpBuilder.AppendLine();
|
|
dumpBuilder.AppendLine("Action timed out.");
|
|
|
|
return (dumpBuilder.ToString(), RuleResult.Timeout, actionWatch.Elapsed);
|
|
}
|
|
else if (result.Exception != null)
|
|
{
|
|
return (dumpBuilder.ToString(), RuleResult.Failed, actionWatch.Elapsed);
|
|
}
|
|
else
|
|
{
|
|
return (dumpBuilder.ToString(), RuleResult.Success, actionWatch.Elapsed);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|