Browse Source

Started with scripting.

pull/341/head
Sebastian Stehle 7 years ago
parent
commit
522d1192f5
  1. 4
      extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs
  2. 4
      src/Squidex.Domain.Apps.Core.Model/Rules/IRuleTriggerVisitor.cs
  3. 10
      src/Squidex.Domain.Apps.Core.Model/Rules/Json/JsonRule.cs
  4. 4
      src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs
  5. 12
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTriggerV2.cs
  6. 16
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs
  7. 6
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerV2.cs
  8. 21
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedAssetEvent.cs
  9. 18
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEvent.cs
  10. 31
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEntityEvent.cs
  11. 5
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs
  12. 2
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedSchemaEvent.cs
  13. 5
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs
  14. 10
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs
  15. 7
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs
  16. 29
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs
  17. 78
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs
  18. 4
      src/Squidex.Domain.Apps.Core.Operations/Scripting/IScriptEngine.cs
  19. 53
      src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs
  20. 33
      src/Squidex.Domain.Apps.Core.Operations/Scripting/JintUser.cs
  21. 4
      src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs
  22. 2
      src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerGrain.cs
  23. 1
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs
  24. 4
      src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs
  25. 6
      src/Squidex.Infrastructure/IMigrated.cs
  26. 8
      src/Squidex.Infrastructure/Tasks/PartitionedActionBlock.cs
  27. 4
      src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs
  28. 2
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/AssetChangedRuleTriggerDto.cs
  29. 4
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedRuleTriggerDto.cs
  30. 6
      tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs
  31. 12
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs
  32. 92
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs
  33. 193
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs
  34. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintUserTests.cs
  35. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs
  36. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs
  37. 6
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs
  38. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleGrainTests.cs
  39. 2
      tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs
  40. 3
      tools/Migrate_01/OldEvents/AppClientChanged.cs
  41. 3
      tools/Migrate_01/OldEvents/AppClientUpdated.cs
  42. 3
      tools/Migrate_01/OldEvents/AppContributorAssigned.cs
  43. 3
      tools/Migrate_01/OldEvents/ContentArchived.cs
  44. 3
      tools/Migrate_01/OldEvents/ContentPublished.cs
  45. 3
      tools/Migrate_01/OldEvents/ContentRestored.cs
  46. 3
      tools/Migrate_01/OldEvents/ContentUnpublished.cs
  47. 67
      tools/Migrate_01/OldTriggers/AssetChangedTrigger.cs
  48. 45
      tools/Migrate_01/OldTriggers/ContentChangedTrigger.cs
  49. 83
      tools/Migrate_01/OldTriggers/ContentChangedTriggerSchema.cs

4
extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs

@ -30,9 +30,11 @@ namespace Squidex.Extensions.Actions.Fastly
protected override (string Description, FastlyJob Data) CreateJob(EnrichedEvent @event, FastlyAction action)
{
var id = @event is EnrichedEntityEvent entityEvent ? entityEvent.Id.ToString() : string.Empty;
var ruleJob = new FastlyJob
{
Key = @event.AggregateId.ToString(),
Key = id,
FastlyApiKey = action.ApiKey,
FastlyServiceID = action.ServiceId
};

4
src/Squidex.Domain.Apps.Core.Model/Rules/IRuleTriggerVisitor.cs

@ -11,8 +11,8 @@ namespace Squidex.Domain.Apps.Core.Rules
{
public interface IRuleTriggerVisitor<out T>
{
T Visit(AssetChangedTrigger trigger);
T Visit(AssetChangedTriggerV2 trigger);
T Visit(ContentChangedTrigger trigger);
T Visit(ContentChangedTriggerV2 trigger);
}
}

10
src/Squidex.Domain.Apps.Core.Model/Rules/Json/JsonRule.cs

@ -6,6 +6,7 @@
// ==========================================================================
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Core.Rules.Json
@ -32,7 +33,14 @@ namespace Squidex.Domain.Apps.Core.Rules.Json
public Rule ToRule()
{
var rule = new Rule(Trigger, Action);
var trigger = Trigger;
if (trigger is IMigrated<RuleTrigger> migrated)
{
trigger = migrated.Migrate();
}
var rule = new Rule(trigger, Action);
if (!IsEnabled)
{

4
src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs

@ -16,8 +16,6 @@ namespace Squidex.Domain.Apps.Core.Rules
public Guid AppId { get; set; }
public Guid AggregateId { get; set; }
public string EventName { get; set; }
public string ActionName { get; set; }
@ -26,6 +24,8 @@ namespace Squidex.Domain.Apps.Core.Rules
public string Description { get; set; }
public long ExecutionPartition { get; set; }
public Instant Created { get; set; }
public Instant Expires { get; set; }

12
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTrigger.cs → src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTriggerV2.cs

@ -9,16 +9,10 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.Triggers
{
[TypeName(nameof(AssetChangedTrigger))]
public sealed class AssetChangedTrigger : RuleTrigger
[TypeName(nameof(AssetChangedTriggerV2))]
public sealed class AssetChangedTriggerV2 : RuleTrigger
{
public bool SendCreate { get; set; }
public bool SendUpdate { get; set; }
public bool SendRename { get; set; }
public bool SendDelete { get; set; }
public string Condition { get; set; }
public override T Accept<T>(IRuleTriggerVisitor<T> visitor)
{

16
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchema.cs → src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs

@ -9,22 +9,10 @@ using System;
namespace Squidex.Domain.Apps.Core.Rules.Triggers
{
public sealed class ContentChangedTriggerSchema : Freezable
public sealed class ContentChangedTriggerSchemaV2 : Freezable
{
public Guid SchemaId { get; set; }
public bool SendCreate { get; set; }
public bool SendUpdate { get; set; }
public bool SendDelete { get; set; }
public bool SendPublish { get; set; }
public bool SendUnpublish { get; set; }
public bool SendArchived { get; set; }
public bool SendRestore { get; set; }
public string Condition { get; set; }
}
}

6
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs → src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerV2.cs

@ -10,10 +10,10 @@ using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Rules.Triggers
{
[TypeName(nameof(ContentChangedTrigger))]
public sealed class ContentChangedTrigger : RuleTrigger
[TypeName(nameof(ContentChangedTriggerV2))]
public sealed class ContentChangedTriggerV2 : RuleTrigger
{
public ReadOnlyCollection<ContentChangedTriggerSchema> Schemas { get; set; }
public ReadOnlyCollection<ContentChangedTriggerSchemaV2> Schemas { get; set; }
public bool HandleAll { get; set; }

21
src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedAssetEvent.cs

@ -5,26 +5,12 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents
{
public sealed class EnrichedAssetEvent : EnrichedEvent
public sealed class EnrichedAssetEvent : EnrichedEntityEvent
{
public EnrichedAssetEventType Type { 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 string MimeType { get; set; }
public string FileName { get; set; }
@ -38,10 +24,5 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents
public int? PixelWidth { get; set; }
public int? PixelHeight { get; set; }
public override Guid AggregateId
{
get { return Id; }
}
}
}

18
src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedContentEvent.cs

@ -5,10 +5,7 @@
// 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
{
@ -16,23 +13,8 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents
{
public EnrichedContentEventType Type { 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; }
public override Guid AggregateId
{
get { return Id; }
}
}
}

31
src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEntityEvent.cs

@ -0,0 +1,31 @@
// ==========================================================================
// 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 abstract class EnrichedEntityEvent : EnrichedEvent
{
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 override void CalculatePartition()
{
Partition = Id.GetHashCode();
}
}
}

5
src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs

@ -25,10 +25,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents
public long Version { get; set; }
[IgnoreDataMember]
public abstract Guid AggregateId { get; }
public long Partition { get; set; }
[IgnoreDataMember]
public IUser User { get; set; }
public abstract void CalculatePartition();
}
}

2
src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedSchemaEvent.cs

@ -10,7 +10,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents
{
public abstract class EnrichedSchemaEvent : EnrichedEvent
public abstract class EnrichedSchemaEvent : EnrichedEntityEvent
{
public NamedId<Guid> SchemaId { get; set; }
}

5
src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleTriggerHandler.cs

@ -6,9 +6,8 @@
// ==========================================================================
using System;
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
{
@ -16,6 +15,6 @@ namespace Squidex.Domain.Apps.Core.HandleRules
{
Type TriggerType { get; }
bool Triggers(Envelope<AppEvent> @event, RuleTrigger trigger);
bool Triggers(EnrichedEvent @event, RuleTrigger trigger);
}
}

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

@ -85,7 +85,9 @@ namespace Squidex.Domain.Apps.Core.HandleRules
var appEventEnvelope = @event.To<AppEvent>();
if (!triggerHandler.Triggers(appEventEnvelope, rule.Trigger))
var enrichedEvent = await eventEnricher.EnrichAsync(appEventEnvelope);
if (!triggerHandler.Triggers(enrichedEvent, rule.Trigger))
{
return null;
}
@ -104,22 +106,22 @@ namespace Squidex.Domain.Apps.Core.HandleRules
return null;
}
var enrichedEvent = await eventEnricher.EnrichAsync(appEventEnvelope);
var actionName = typeNameRegistry.GetName(actionType);
var actionData = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action);
var json = jsonSerializer.Serialize(actionData.Data);
enrichedEvent.CalculatePartition();
var job = new RuleJob
{
JobId = Guid.NewGuid(),
ActionName = actionName,
ActionData = json,
AggregateId = enrichedEvent.AggregateId,
AppId = appEvent.AppId.Id,
Created = now,
EventName = enrichedEvent.Name,
ExecutionPartition = enrichedEvent.Partition,
Expires = expires,
Description = actionData.Description
};

7
src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTriggerHandler.cs

@ -6,9 +6,8 @@
// ==========================================================================
using System;
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
{
@ -19,11 +18,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules
get { return typeof(T); }
}
bool IRuleTriggerHandler.Triggers(Envelope<AppEvent> @event, RuleTrigger trigger)
bool IRuleTriggerHandler.Triggers(EnrichedEvent @event, RuleTrigger trigger)
{
return Triggers(@event, (T)trigger);
}
protected abstract bool Triggers(Envelope<AppEvent> @event, T trigger);
protected abstract bool Triggers(EnrichedEvent @event, T trigger);
}
}

29
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/AssetChangedTriggerHandler.cs

@ -5,27 +5,32 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
{
public sealed class AssetChangedTriggerHandler : RuleTriggerHandler<AssetChangedTrigger>
public sealed class AssetChangedTriggerHandler : RuleTriggerHandler<AssetChangedTriggerV2>
{
protected override bool Triggers(Envelope<AppEvent> @event, AssetChangedTrigger trigger)
private readonly IScriptEngine scriptEngine;
public AssetChangedTriggerHandler(IScriptEngine scriptEngine)
{
Guard.NotNull(scriptEngine, nameof(scriptEngine));
this.scriptEngine = scriptEngine;
}
protected override bool Triggers(EnrichedEvent @event, AssetChangedTriggerV2 trigger)
{
return @event.Payload is AssetEvent assetEvent && MatchsType(trigger, assetEvent);
return @event is EnrichedAssetEvent assetEvent && MatchsType(trigger, assetEvent);
}
private static bool MatchsType(AssetChangedTrigger trigger, AssetEvent @event)
private bool MatchsType(AssetChangedTriggerV2 trigger, EnrichedAssetEvent assetEvent)
{
return
trigger.SendCreate && @event is AssetCreated ||
trigger.SendUpdate && @event is AssetUpdated ||
trigger.SendDelete && @event is AssetDeleted ||
trigger.SendRename && @event is AssetRenamed;
return string.IsNullOrWhiteSpace(trigger.Condition) || scriptEngine.Evaluate("event", assetEvent, trigger.Condition);
}
}
}

78
src/Squidex.Domain.Apps.Core.Operations/HandleRules/Triggers/ContentChangedTriggerHandler.cs

@ -5,32 +5,36 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
{
public sealed class ContentChangedTriggerHandler : RuleTriggerHandler<ContentChangedTrigger>
public sealed class ContentChangedTriggerHandler : RuleTriggerHandler<ContentChangedTriggerV2>
{
protected override bool Triggers(Envelope<AppEvent> @event, ContentChangedTrigger trigger)
private readonly IScriptEngine scriptEngine;
public ContentChangedTriggerHandler(IScriptEngine scriptEngine)
{
if (trigger.HandleAll &&
@event.Payload is ContentEvent &&
!(@event.Payload is ContentChangesPublished) &&
!(@event.Payload is ContentChangesDiscarded) &&
!(@event.Payload is ContentUpdateProposed))
Guard.NotNull(scriptEngine, nameof(scriptEngine));
this.scriptEngine = scriptEngine;
}
protected override bool Triggers(EnrichedEvent @event, ContentChangedTriggerV2 trigger)
{
if (trigger.HandleAll)
{
return true;
}
if (trigger.Schemas != null && @event.Payload is SchemaEvent schemaEvent)
if (trigger.Schemas != null && @event is EnrichedSchemaEvent schemaEvent)
{
foreach (var schema in trigger.Schemas)
{
if (MatchsSchema(schema, schemaEvent) && MatchsType(schema, schemaEvent))
if (MatchsSchema(schema, schemaEvent) && MatchsCondition(schema, schemaEvent))
{
return true;
}
@ -40,56 +44,14 @@ namespace Squidex.Domain.Apps.Core.HandleRules.Triggers
return false;
}
private static bool MatchsSchema(ContentChangedTriggerSchema schema, SchemaEvent @event)
private bool MatchsSchema(ContentChangedTriggerSchemaV2 schema, EnrichedSchemaEvent @event)
{
return @event.SchemaId.Id == schema.SchemaId;
}
private static bool MatchsType(ContentChangedTriggerSchema schema, SchemaEvent @event)
{
return
IsArchived(schema, @event) ||
IsCreate(schema, @event) ||
IsDelete(schema, @event) ||
IsPublished(schema, @event) ||
IsRestored(schema, @event) ||
IsUpdate(schema, @event) ||
IsUnpublished(schema, @event);
}
private static bool IsPublished(ContentChangedTriggerSchema schema, SchemaEvent @event)
{
return schema.SendPublish && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Published;
}
private static bool IsRestored(ContentChangedTriggerSchema schema, SchemaEvent @event)
{
return schema.SendRestore && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Restored;
}
private static bool IsArchived(ContentChangedTriggerSchema schema, SchemaEvent @event)
{
return schema.SendArchived && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Archived;
}
private static bool IsUnpublished(ContentChangedTriggerSchema schema, SchemaEvent @event)
{
return schema.SendUnpublish && @event is ContentStatusChanged statusChanged && statusChanged.Change == StatusChange.Unpublished;
}
private static bool IsCreate(ContentChangedTriggerSchema schema, SchemaEvent @event)
{
return schema.SendCreate && @event is ContentCreated;
}
private static bool IsUpdate(ContentChangedTriggerSchema schema, SchemaEvent @event)
{
return schema.SendUpdate && @event is ContentUpdated || schema.SendUpdate && @event is ContentChangesPublished;
}
private static bool IsDelete(ContentChangedTriggerSchema schema, SchemaEvent @event)
private bool MatchsCondition(ContentChangedTriggerSchemaV2 schema, EnrichedSchemaEvent @event)
{
return schema.SendDelete && @event is ContentDeleted;
return string.IsNullOrWhiteSpace(schema.Condition) || scriptEngine.Evaluate("event", @event, schema.Condition);
}
}
}

4
src/Squidex.Domain.Apps.Core.Operations/Scripting/IScriptEngine.cs

@ -16,5 +16,9 @@ namespace Squidex.Domain.Apps.Core.Scripting
NamedContentData ExecuteAndTransform(ScriptContext context, string script);
NamedContentData Transform(ScriptContext context, string script);
bool Evaluate(string name, object context, string script);
string Interpolate(string name, object context, string script);
}
}

53
src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs

@ -6,6 +6,9 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security.Claims;
using Jint;
using Jint.Native;
using Jint.Native.Object;
@ -22,6 +25,14 @@ namespace Squidex.Domain.Apps.Core.Scripting
{
public TimeSpan Timeout { get; set; } = TimeSpan.FromMilliseconds(200);
static JintScriptEngine()
{
var typeMappers = (Dictionary<Type, Func<Engine, object, JsValue>>)typeof(Engine).GetField("TypeMappers", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
typeMappers.Add(typeof(NamedContentData), (engine, data) => new ContentDataObject(engine, (NamedContentData)data));
typeMappers.Add(typeof(ClaimsPrincipal), (engine, data) => JintUser.Create(engine, (ClaimsPrincipal)data));
}
public void Execute(ScriptContext context, string script)
{
Guard.NotNull(context, nameof(context));
@ -143,7 +154,7 @@ namespace Squidex.Domain.Apps.Core.Scripting
if (context.User != null)
{
contextInstance.FastAddProperty("user", new JintUser(engine, context.User), false, true, false);
contextInstance.FastAddProperty("user", JintUser.Create(engine, context.User), false, true, false);
}
if (!string.IsNullOrWhiteSpace(context.Operation))
@ -152,7 +163,7 @@ namespace Squidex.Domain.Apps.Core.Scripting
}
engine.SetValue("ctx", contextInstance);
engine.SetValue("context", contextInstance);
engine.SetValue("slugify", new ClrFunctionInstance(engine, Slugify));
return engine;
@ -197,5 +208,43 @@ namespace Squidex.Domain.Apps.Core.Scripting
throw new ValidationException("Script rejected the operation.", errors);
}));
}
public bool Evaluate(string name, object context, string script)
{
try
{
var result =
new Engine(options => options.TimeoutInterval(Timeout).Strict())
.SetValue(name, context)
.Execute(script)
.GetCompletionValue()
.ToObject();
return (bool)result;
}
catch
{
return false;
}
}
public string Interpolate(string name, object context, string script)
{
try
{
var result =
new Engine(options => options.TimeoutInterval(Timeout).Strict())
.SetValue(name, context)
.Execute(script)
.GetCompletionValue()
.ToObject();
return (string)result;
}
catch
{
return string.Empty;
}
}
}
}

33
src/Squidex.Domain.Apps.Core.Operations/Scripting/JintUser.cs

@ -8,18 +8,16 @@
using System.Linq;
using System.Security.Claims;
using Jint;
using Jint.Native;
using Jint.Native.Object;
using Jint.Runtime.Interop;
using Squidex.Infrastructure.Security;
namespace Squidex.Domain.Apps.Core.Scripting
{
public sealed class JintUser : ObjectInstance
public static class JintUser
{
private static readonly char[] ClaimSeparators = { '/', '.', ':' };
public JintUser(Engine engine, ClaimsPrincipal principal)
: base(engine)
public static ObjectWrapper Create(Engine engine, ClaimsPrincipal principal)
{
var id = principal.OpenIdSubject();
@ -30,22 +28,19 @@ namespace Squidex.Domain.Apps.Core.Scripting
id = principal.OpenIdClientId();
}
FastAddProperty("id", id, false, true, false);
FastAddProperty("isClient", isClient, false, true, false);
var claims =
principal.Claims.GroupBy(x => x.Type)
.ToDictionary(
x => x.Key.Split(ClaimSeparators).Last(),
x => x.Select(y => y.Value).ToArray());
FastAddProperty("email", principal.OpenIdEmail(), false, true, false);
var claimsInstance = new ObjectInstance(engine);
foreach (var group in principal.Claims.GroupBy(x => x.Type))
return new ObjectWrapper(engine, new
{
var propertyName = group.Key.Split(ClaimSeparators).Last();
var propertyValue = engine.Array.Construct(group.Select(x => new JsValue(x.Value)).ToArray());
claimsInstance.FastAddProperty(propertyName, propertyValue, false, true, false);
}
FastAddProperty("claims", claimsInstance, false, true, false);
Id = id,
IsClient = isClient,
Email = principal.OpenIdEmail(),
Claims = claims,
});
}
}
}

4
src/Squidex.Domain.Apps.Entities/Rules/Guards/RuleTriggerValidator.cs

@ -35,12 +35,12 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
return action.Accept(visitor);
}
public Task<IEnumerable<ValidationError>> Visit(AssetChangedTrigger trigger)
public Task<IEnumerable<ValidationError>> Visit(AssetChangedTriggerV2 trigger)
{
return Task.FromResult(Enumerable.Empty<ValidationError>());
}
public async Task<IEnumerable<ValidationError>> Visit(ContentChangedTrigger trigger)
public async Task<IEnumerable<ValidationError>> Visit(ContentChangedTriggerV2 trigger)
{
if (trigger.Schemas != null)
{

2
src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerGrain.cs

@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
this.log = log;
requestBlock =
new PartitionedActionBlock<IRuleEventEntity>(HandleAsync, x => x.Job.AggregateId.GetHashCode(),
new PartitionedActionBlock<IRuleEventEntity>(HandleAsync, x => x.Job.ExecutionPartition,
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 32, BoundedCapacity = 32 });
}

1
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

4
src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs

@ -30,7 +30,7 @@ namespace Squidex.Infrastructure.EventSourcing
var payloadType = typeNameRegistry.GetType(eventData.Type);
var payloadObj = serializer.Deserialize<IEvent>(eventData.Payload, payloadType, stringConverter);
if (payloadObj is IMigratedEvent migratedEvent)
if (payloadObj is IMigrated<IEvent> migratedEvent)
{
payloadObj = migratedEvent.Migrate();
}
@ -44,7 +44,7 @@ namespace Squidex.Infrastructure.EventSourcing
{
var eventPayload = envelope.Payload;
if (migrate && eventPayload is IMigratedEvent migratedEvent)
if (migrate && eventPayload is IMigrated<IEvent> migratedEvent)
{
eventPayload = migratedEvent.Migrate();
}

6
src/Squidex.Infrastructure/EventSourcing/IMigratedEvent.cs → src/Squidex.Infrastructure/IMigrated.cs

@ -5,10 +5,10 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure.EventSourcing
namespace Squidex.Infrastructure
{
public interface IMigratedEvent
public interface IMigrated<T>
{
IEvent Migrate();
T Migrate();
}
}

8
src/Squidex.Infrastructure/Tasks/PartitionedActionBlock.cs

@ -23,22 +23,22 @@ namespace Squidex.Infrastructure.Tasks
get { return Task.WhenAll(workers.Select(x => x.Completion)); }
}
public PartitionedActionBlock(Action<TInput> action, Func<TInput, int> partitioner)
public PartitionedActionBlock(Action<TInput> action, Func<TInput, long> partitioner)
: this (action?.ToAsync(), partitioner, new ExecutionDataflowBlockOptions())
{
}
public PartitionedActionBlock(Func<TInput, Task> action, Func<TInput, int> partitioner)
public PartitionedActionBlock(Func<TInput, Task> action, Func<TInput, long> partitioner)
: this(action, partitioner, new ExecutionDataflowBlockOptions())
{
}
public PartitionedActionBlock(Action<TInput> action, Func<TInput, int> partitioner, ExecutionDataflowBlockOptions dataflowBlockOptions)
public PartitionedActionBlock(Action<TInput> action, Func<TInput, long> partitioner, ExecutionDataflowBlockOptions dataflowBlockOptions)
: this(action?.ToAsync(), partitioner, dataflowBlockOptions)
{
}
public PartitionedActionBlock(Func<TInput, Task> action, Func<TInput, int> partitioner, ExecutionDataflowBlockOptions dataflowBlockOptions)
public PartitionedActionBlock(Func<TInput, Task> action, Func<TInput, long> partitioner, ExecutionDataflowBlockOptions dataflowBlockOptions)
{
Guard.NotNull(action, nameof(action));
Guard.NotNull(partitioner, nameof(partitioner));

4
src/Squidex/Areas/Api/Controllers/Rules/Models/Converters/RuleTriggerDtoFactory.cs

@ -26,12 +26,12 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Converters
return properties.Accept(Instance);
}
public RuleTriggerDto Visit(AssetChangedTrigger trigger)
public RuleTriggerDto Visit(AssetChangedTriggerV2 trigger)
{
return SimpleMapper.Map(trigger, new AssetChangedRuleTriggerDto());
}
public RuleTriggerDto Visit(ContentChangedTrigger trigger)
public RuleTriggerDto Visit(ContentChangedTriggerV2 trigger)
{
var schemas = trigger.Schemas.Select(x => SimpleMapper.Map(x, new ContentChangedRuleTriggerSchemaDto())).ToArray();

2
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/AssetChangedRuleTriggerDto.cs

@ -35,7 +35,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
public override RuleTrigger ToTrigger()
{
return SimpleMapper.Map(this, new AssetChangedTrigger());
return SimpleMapper.Map(this, new AssetChangedTriggerV2());
}
}
}

4
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedRuleTriggerDto.cs

@ -29,9 +29,9 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
public override RuleTrigger ToTrigger()
{
var schemas = Schemas.Select(x => SimpleMapper.Map(x, new ContentChangedTriggerSchema())).ToReadOnlyCollection();
var schemas = Schemas.Select(x => SimpleMapper.Map(x, new ContentChangedTriggerSchemaV2())).ToReadOnlyCollection();
return new ContentChangedTrigger { HandleAll = HandleAll, Schemas = schemas };
return new ContentChangedTriggerV2 { HandleAll = HandleAll, Schemas = schemas };
}
}
}

6
tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs

@ -26,7 +26,7 @@ namespace Squidex.Domain.Apps.Core.Model.Rules
.Select(x => new[] { x })
.ToList();
private readonly Rule rule_0 = new Rule(new ContentChangedTrigger(), new TestAction1());
private readonly Rule rule_0 = new Rule(new ContentChangedTriggerV2(), new TestAction1());
public sealed class OtherTrigger : RuleTrigger
{
@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Core.Model.Rules
[Fact]
public void Should_create_with_trigger_and_action()
{
var ruleTrigger = new ContentChangedTrigger();
var ruleTrigger = new ContentChangedTriggerV2();
var ruleAction = new TestAction1();
var newRule = new Rule(ruleTrigger, ruleAction);
@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Core.Model.Rules
[Fact]
public void Should_replace_trigger_when_updating()
{
var newTrigger = new ContentChangedTrigger();
var newTrigger = new ContentChangedTriggerV2();
var rule_1 = rule_0.Update(newTrigger);

12
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs

@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
.Returns(typeof(ValidData));
A.CallTo(() => ruleTriggerHandler.TriggerType)
.Returns(typeof(ContentChangedTrigger));
.Returns(typeof(ContentChangedTriggerV2));
sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, TestUtils.DefaultSerializer, clock, typeNameRegistry);
}
@ -118,7 +118,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
[Fact]
public async Task Should_not_create_job_if_no_action_handler_registered()
{
var ruleConfig = new Rule(new ContentChangedTrigger(), new InvalidAction());
var ruleConfig = new Rule(new ContentChangedTriggerV2(), new InvalidAction());
var ruleEnvelope = Envelope.Create(new ContentCreated());
var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope);
@ -132,7 +132,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
var ruleConfig = ValidRule();
var ruleEnvelope = Envelope.Create(new ContentCreated());
A.CallTo(() => ruleTriggerHandler.Triggers(A<Envelope<AppEvent>>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Triggers(A<EnrichedEvent>.Ignored, ruleConfig.Trigger))
.Returns(false);
var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope);
@ -155,7 +155,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => clock.GetCurrentInstant())
.Returns(now);
A.CallTo(() => ruleTriggerHandler.Triggers(A<Envelope<AppEvent>>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Triggers(A<EnrichedEvent>.Ignored, ruleConfig.Trigger))
.Returns(true);
A.CallTo(() => ruleActionHandler.CreateJobAsync(A<EnrichedEvent>.Ignored, ruleConfig.Action))
@ -181,7 +181,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => clock.GetCurrentInstant())
.Returns(now);
A.CallTo(() => ruleTriggerHandler.Triggers(A<Envelope<AppEvent>>.Ignored, ruleConfig.Trigger))
A.CallTo(() => ruleTriggerHandler.Triggers(A<EnrichedEvent>.Ignored, ruleConfig.Trigger))
.Returns(true);
A.CallTo(() => ruleActionHandler.CreateJobAsync(A<EnrichedEvent>.Ignored, ruleConfig.Action))
@ -260,7 +260,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
private static Rule ValidRule()
{
return new Rule(new ContentChangedTrigger(), new ValidAction());
return new Rule(new ContentChangedTriggerV2(), new ValidAction());
}
}
}

92
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/AssetChangedTriggerTests.cs

@ -5,56 +5,80 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using FakeItEasy;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.HandleRules.Triggers;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Domain.Apps.Core.Scripting;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
{
public class AssetChangedTriggerTests
{
private readonly IRuleTriggerHandler sut = new AssetChangedTriggerHandler();
private readonly IScriptEngine scriptEngine = A.Fake<IScriptEngine>();
private readonly IRuleTriggerHandler sut;
public static IEnumerable<object[]> TestData
public AssetChangedTriggerTests()
{
get
{
return new[]
{
new object[] { 0, 1, 1, 1, 1, new RuleCreated() },
new object[] { 1, 1, 0, 0, 0, new AssetCreated() },
new object[] { 0, 0, 0, 0, 0, new AssetCreated() },
new object[] { 1, 0, 1, 0, 0, new AssetUpdated() },
new object[] { 0, 0, 0, 0, 0, new AssetUpdated() },
new object[] { 1, 0, 0, 1, 0, new AssetRenamed() },
new object[] { 0, 0, 0, 0, 0, new AssetRenamed() },
new object[] { 1, 0, 0, 0, 1, new AssetDeleted() },
new object[] { 0, 0, 0, 0, 0, new AssetDeleted() }
};
}
sut = new AssetChangedTriggerHandler(scriptEngine);
}
[Theory]
[MemberData(nameof(TestData))]
public void Should_return_result_depending_on_event(int expected, int sendCreate, int sendUpdate, int sendRename, int sendDelete, AppEvent @event)
[Fact]
public void Should_trigger_when_condition_is_null()
{
var trigger = new AssetChangedTrigger
{
SendCreate = sendCreate == 1,
SendUpdate = sendUpdate == 1,
SendRename = sendRename == 1,
SendDelete = sendDelete == 1
};
var trigger = new AssetChangedTriggerV2();
var result = sut.Triggers(new Envelope<AppEvent>(@event), trigger);
var result = sut.Triggers(new EnrichedAssetEvent(), trigger);
Assert.Equal(expected == 1, result);
Assert.True(result);
A.CallTo(() => scriptEngine.Evaluate(A<string>.Ignored, A<object>.Ignored, A<string>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public void Should_trigger_when_condition_is_empty()
{
var trigger = new AssetChangedTriggerV2 { Condition = string.Empty };
var result = sut.Triggers(new EnrichedAssetEvent(), trigger);
Assert.True(result);
A.CallTo(() => scriptEngine.Evaluate(A<string>.Ignored, A<object>.Ignored, A<string>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public void Should_trigger_when_condition_matchs()
{
var trigger = new AssetChangedTriggerV2 { Condition = "true" };
var @event = new EnrichedAssetEvent();
A.CallTo(() => scriptEngine.Evaluate("event", @event, trigger.Condition))
.Returns(true);
var result = sut.Triggers(@event, trigger);
Assert.True(result);
}
[Fact]
public void Should_not_trigger_when_condition_does_not_matchs()
{
var trigger = new AssetChangedTriggerV2 { Condition = "false" };
var @event = new EnrichedAssetEvent();
A.CallTo(() => scriptEngine.Evaluate("event", @event, trigger.Condition))
.Returns(false);
var result = sut.Triggers(@event, trigger);
Assert.False(result);
}
}
}

193
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs

@ -7,17 +7,14 @@
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Contents;
using System.Collections.ObjectModel;
using FakeItEasy;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.HandleRules.Triggers;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.EventSourcing;
using Xunit;
#pragma warning disable SA1401 // Fields must be private
@ -26,82 +23,164 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
{
public class ContentChangedTriggerTests
{
private readonly IRuleTriggerHandler sut = new ContentChangedTriggerHandler();
private readonly IScriptEngine scriptEngine = A.Fake<IScriptEngine>();
private readonly IRuleTriggerHandler sut;
private static readonly NamedId<Guid> SchemaMatch = NamedId.Of(Guid.NewGuid(), "my-schema1");
private static readonly NamedId<Guid> SchemaNonMatch = NamedId.Of(Guid.NewGuid(), "my-schema2");
public static IEnumerable<object[]> TestData = new[]
public ContentChangedTriggerTests()
{
new object[] { 0, 1, 1, 1, 1, 0, 0, 0, new RuleCreated() },
new object[] { 0, 1, 1, 1, 1, 0, 0, 0, new ContentCreated { SchemaId = SchemaNonMatch } },
new object[] { 1, 1, 0, 0, 0, 0, 0, 0, new ContentCreated { SchemaId = SchemaMatch } },
new object[] { 0, 0, 0, 0, 0, 0, 0, 0, new ContentCreated { SchemaId = SchemaMatch } },
new object[] { 1, 0, 1, 0, 0, 0, 0, 0, new ContentUpdated { SchemaId = SchemaMatch } },
new object[] { 0, 0, 0, 0, 0, 0, 0, 0, new ContentUpdated { SchemaId = SchemaMatch } },
new object[] { 1, 0, 0, 1, 0, 0, 0, 0, new ContentDeleted { SchemaId = SchemaMatch } },
new object[] { 0, 0, 0, 0, 0, 0, 0, 0, new ContentDeleted { SchemaId = SchemaMatch } },
new object[] { 1, 1, 1, 1, 0, 0, 1, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Archived } },
new object[] { 0, 1, 1, 1, 0, 0, 0, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Archived } },
new object[] { 1, 0, 0, 0, 0, 0, 0, 1, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Restored } },
new object[] { 0, 0, 0, 0, 0, 0, 0, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Restored } },
new object[] { 1, 0, 0, 0, 1, 0, 0, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Published } },
new object[] { 0, 0, 0, 0, 0, 0, 0, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Published } },
new object[] { 1, 1, 1, 1, 0, 1, 0, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Unpublished } },
new object[] { 0, 1, 1, 1, 0, 0, 0, 0, new ContentStatusChanged { SchemaId = SchemaMatch, Change = StatusChange.Unpublished } },
new object[] { 0, 1, 1, 1, 1, 0, 0, 0, new SchemaCreated { SchemaId = SchemaNonMatch } }
};
sut = new ContentChangedTriggerHandler(scriptEngine);
}
[Fact]
public void Should_return_false_when_trigger_contains_no_schemas()
public void Should_not_trigger_when_o_contains_no_schemas()
{
var trigger = new ContentChangedTrigger();
var trigger = new ContentChangedTriggerV2();
var @event = new EnrichedContentEvent { SchemaId = SchemaMatch };
var result = sut.Triggers(new Envelope<AppEvent>(new ContentCreated()), trigger);
var result = sut.Triggers(@event, trigger);
Assert.False(result);
A.CallTo(() => scriptEngine.Evaluate(A<string>.Ignored, A<object>.Ignored, A<string>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public void Should_return_true_when_cathing_all_events()
public void Should_trigger_when_cathing_all_events()
{
var trigger = new ContentChangedTrigger { HandleAll = true };
var trigger = new ContentChangedTriggerV2 { HandleAll = true };
var @event = new EnrichedContentEvent { SchemaId = SchemaMatch };
var result = sut.Triggers(new Envelope<AppEvent>(new ContentCreated()), trigger);
var result = sut.Triggers(@event, trigger);
Assert.True(result);
A.CallTo(() => scriptEngine.Evaluate(A<string>.Ignored, A<object>.Ignored, A<string>.Ignored))
.MustNotHaveHappened();
}
[Theory]
[MemberData(nameof(TestData))]
public void Should_return_result_depending_on_event(int expected,
int sendCreate,
int sendUpdate,
int sendDelete,
int sendPublish,
int sendUnpublish,
int sendArchive,
int sendRestore,
AppEvent @event)
[Fact]
public void Should_trigger_when_condition_is_null()
{
var trigger = new ContentChangedTrigger
var trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchema
Schemas = new ReadOnlyCollection<ContentChangedTriggerSchemaV2>(new List<ContentChangedTriggerSchemaV2>
{
new ContentChangedTriggerSchemaV2
{
SendCreate = sendCreate == 1,
SendUpdate = sendUpdate == 1,
SendDelete = sendDelete == 1,
SendPublish = sendPublish == 1,
SendUnpublish = sendUnpublish == 1,
SendArchived = sendArchive == 1,
SendRestore = sendRestore == 1,
SchemaId = SchemaMatch.Id
})
}
})
};
var result = sut.Triggers(new Envelope<AppEvent>(@event), trigger);
var @event = new EnrichedContentEvent { SchemaId = SchemaMatch };
var result = sut.Triggers(@event, trigger);
Assert.True(result);
A.CallTo(() => scriptEngine.Evaluate(A<string>.Ignored, A<object>.Ignored, A<string>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public void Should_not_trigger_when_schema_id_does_not_match()
{
var trigger = new ContentChangedTriggerV2
{
Schemas = new ReadOnlyCollection<ContentChangedTriggerSchemaV2>(new List<ContentChangedTriggerSchemaV2>
{
new ContentChangedTriggerSchemaV2
{
SchemaId = SchemaNonMatch.Id
}
})
};
var @event = new EnrichedContentEvent { SchemaId = SchemaMatch };
var result = sut.Triggers(@event, trigger);
Assert.False(result);
A.CallTo(() => scriptEngine.Evaluate(A<string>.Ignored, A<object>.Ignored, A<string>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public void Should_trigger_when_condition_is_empty()
{
var trigger = new ContentChangedTriggerV2
{
Schemas = new ReadOnlyCollection<ContentChangedTriggerSchemaV2>(new List<ContentChangedTriggerSchemaV2>
{
new ContentChangedTriggerSchemaV2
{
SchemaId = SchemaMatch.Id, Condition = string.Empty
}
})
};
Assert.Equal(expected == 1, result);
var @event = new EnrichedContentEvent { SchemaId = SchemaMatch };
var result = sut.Triggers(@event, trigger);
Assert.True(result);
A.CallTo(() => scriptEngine.Evaluate(A<string>.Ignored, A<object>.Ignored, A<string>.Ignored))
.MustNotHaveHappened();
}
[Fact]
public void Should_trigger_when_condition_matchs()
{
var trigger = new ContentChangedTriggerV2
{
Schemas = new ReadOnlyCollection<ContentChangedTriggerSchemaV2>(new List<ContentChangedTriggerSchemaV2>
{
new ContentChangedTriggerSchemaV2
{
SchemaId = SchemaMatch.Id, Condition = "true"
}
})
};
var @event = new EnrichedContentEvent { SchemaId = SchemaMatch };
A.CallTo(() => scriptEngine.Evaluate("event", @event, "true"))
.Returns(true);
var result = sut.Triggers(@event, trigger);
Assert.True(result);
}
[Fact]
public void Should_not_trigger_when_condition_does_not_matchs()
{
var trigger = new ContentChangedTriggerV2
{
Schemas = new ReadOnlyCollection<ContentChangedTriggerSchemaV2>(new List<ContentChangedTriggerSchemaV2>
{
new ContentChangedTriggerSchemaV2
{
SchemaId = SchemaMatch.Id, Condition = "false"
}
})
};
var @event = new EnrichedContentEvent { SchemaId = SchemaMatch };
A.CallTo(() => scriptEngine.Evaluate("event", @event, "false"))
.Returns(false);
var result = sut.Triggers(@event, trigger);
Assert.False(result);
}
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintUserTests.cs

@ -88,7 +88,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting
{
var engine = new Engine();
engine.SetValue("user", new JintUser(engine, new ClaimsPrincipal(new[] { identity })));
engine.SetValue("user", JintUser.Create(engine, new ClaimsPrincipal(new[] { identity })));
return engine.Execute(script).GetCompletionValue().ToObject();
}

14
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs

@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
public class GuardRuleTests
{
private readonly Uri validUrl = new Uri("https://squidex.io");
private readonly Rule rule_0 = new Rule(new ContentChangedTrigger(), new TestAction());
private readonly Rule rule_0 = new Rule(new ContentChangedTriggerV2(), new TestAction());
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app");
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
@ -60,9 +60,9 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
var command = CreateCommand(new CreateRule
{
Trigger = new ContentChangedTrigger
Trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchemaV2>()
},
Action = null
});
@ -76,9 +76,9 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
var command = CreateCommand(new CreateRule
{
Trigger = new ContentChangedTrigger
Trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchemaV2>()
},
Action = new TestAction
{
@ -103,9 +103,9 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
var command = new UpdateRule
{
Trigger = new ContentChangedTrigger
Trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchemaV2>()
},
Action = new TestAction
{

14
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs

@ -26,10 +26,10 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
A.CallTo(() => appProvider.GetSchemaAsync(appId, A<Guid>.Ignored, false))
.Returns(Task.FromResult<ISchemaEntity>(null));
var trigger = new ContentChangedTrigger
var trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchema()
new ContentChangedTriggerSchemaV2()
)
};
@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
[Fact]
public async Task Should_not_add_error_if_schemas_is_null()
{
var trigger = new ContentChangedTrigger();
var trigger = new ContentChangedTriggerV2();
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
@ -51,9 +51,9 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
[Fact]
public async Task Should_not_add_error_if_schemas_is_empty()
{
var trigger = new ContentChangedTrigger
var trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchemaV2>()
};
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
@ -67,10 +67,10 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
A.CallTo(() => appProvider.GetSchemaAsync(appId, A<Guid>.Ignored, false))
.Returns(A.Fake<ISchemaEntity>());
var trigger = new ContentChangedTrigger
var trigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchema()
new ContentChangedTriggerSchemaV2()
)
};

6
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs

@ -70,9 +70,9 @@ namespace Squidex.Domain.Apps.Entities.Rules
{
var @event = Envelope.Create(new ContentCreated { AppId = appId });
var rule1 = new Rule(new ContentChangedTrigger(), new TestAction { Url = new Uri("https://squidex.io") });
var rule2 = new Rule(new ContentChangedTrigger(), new TestAction { Url = new Uri("https://squidex.io") });
var rule3 = new Rule(new ContentChangedTrigger(), new TestAction { Url = new Uri("https://squidex.io") });
var rule1 = new Rule(new ContentChangedTriggerV2(), new TestAction { Url = new Uri("https://squidex.io") });
var rule2 = new Rule(new ContentChangedTriggerV2(), new TestAction { Url = new Uri("https://squidex.io") });
var rule3 = new Rule(new ContentChangedTriggerV2(), new TestAction { Url = new Uri("https://squidex.io") });
var job1 = new RuleJob { Created = now };
var job2 = new RuleJob { Created = now };

8
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleGrainTests.cs

@ -189,9 +189,9 @@ namespace Squidex.Domain.Apps.Entities.Rules
private static CreateRule MakeCreateCommand()
{
var newTrigger = new ContentChangedTrigger
var newTrigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchemaV2>()
};
var newAction = new TestAction
@ -204,9 +204,9 @@ namespace Squidex.Domain.Apps.Entities.Rules
private static UpdateRule MakeUpdateCommand()
{
var newTrigger = new ContentChangedTrigger
var newTrigger = new ContentChangedTriggerV2
{
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchemaV2>()
};
var newAction = new TestAction

2
tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs

@ -15,7 +15,7 @@ namespace Squidex.Infrastructure.EventSourcing
{
public class DefaultEventDataFormatterTests
{
public sealed class MyOldEvent : IEvent, IMigratedEvent
public sealed class MyOldEvent : IEvent, IMigrated<IEvent>
{
public string MyProperty { get; set; }

3
tools/Migrate_01/OldEvents/AppClientChanged.cs

@ -7,6 +7,7 @@
using System;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
@ -14,7 +15,7 @@ namespace Migrate_01.OldEvents
{
[EventType(nameof(AppClientChanged))]
[Obsolete]
public sealed class AppClientChanged : AppEvent, IMigratedEvent
public sealed class AppClientChanged : AppEvent, IMigrated<IEvent>
{
public string Id { get; set; }

3
tools/Migrate_01/OldEvents/AppClientUpdated.cs

@ -8,6 +8,7 @@
using System;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using AppClientUpdatedV2 = Squidex.Domain.Apps.Events.Apps.AppClientUpdated;
@ -16,7 +17,7 @@ namespace Migrate_01.OldEvents
{
[EventType(nameof(AppClientUpdated))]
[Obsolete]
public sealed class AppClientUpdated : AppEvent, IMigratedEvent
public sealed class AppClientUpdated : AppEvent, IMigrated<IEvent>
{
public string Id { get; set; }

3
tools/Migrate_01/OldEvents/AppContributorAssigned.cs

@ -8,6 +8,7 @@
using System;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
using AppContributorAssignedV2 = Squidex.Domain.Apps.Events.Apps.AppContributorAssigned;
@ -16,7 +17,7 @@ namespace Migrate_01.OldEvents
{
[EventType(nameof(AppContributorAssigned))]
[Obsolete]
public sealed class AppContributorAssigned : AppEvent, IMigratedEvent
public sealed class AppContributorAssigned : AppEvent, IMigrated<IEvent>
{
public string ContributorId { get; set; }

3
tools/Migrate_01/OldEvents/ContentArchived.cs

@ -8,6 +8,7 @@
using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
@ -15,7 +16,7 @@ namespace Migrate_01.OldEvents
{
[EventType(nameof(ContentArchived))]
[Obsolete]
public sealed class ContentArchived : ContentEvent, IMigratedEvent
public sealed class ContentArchived : ContentEvent, IMigrated<IEvent>
{
public IEvent Migrate()
{

3
tools/Migrate_01/OldEvents/ContentPublished.cs

@ -8,6 +8,7 @@
using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
@ -15,7 +16,7 @@ namespace Migrate_01.OldEvents
{
[EventType(nameof(ContentPublished))]
[Obsolete]
public sealed class ContentPublished : ContentEvent, IMigratedEvent
public sealed class ContentPublished : ContentEvent, IMigrated<IEvent>
{
public IEvent Migrate()
{

3
tools/Migrate_01/OldEvents/ContentRestored.cs

@ -8,6 +8,7 @@
using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
@ -15,7 +16,7 @@ namespace Migrate_01.OldEvents
{
[EventType(nameof(ContentRestored))]
[Obsolete]
public sealed class ContentRestored : ContentEvent, IMigratedEvent
public sealed class ContentRestored : ContentEvent, IMigrated<IEvent>
{
public IEvent Migrate()
{

3
tools/Migrate_01/OldEvents/ContentUnpublished.cs

@ -8,6 +8,7 @@
using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
@ -15,7 +16,7 @@ namespace Migrate_01.OldEvents
{
[EventType(nameof(ContentUnpublished))]
[Obsolete]
public sealed class ContentUnpublished : ContentEvent, IMigratedEvent
public sealed class ContentUnpublished : ContentEvent, IMigrated<IEvent>
{
public IEvent Migrate()
{

67
tools/Migrate_01/OldTriggers/AssetChangedTrigger.cs

@ -0,0 +1,67 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Infrastructure;
namespace Migrate_01.OldTriggers
{
[TypeName(nameof(AssetChangedTrigger))]
public sealed class AssetChangedTrigger : RuleTrigger, IMigrated<RuleTrigger>
{
public bool SendCreate { get; set; }
public bool SendUpdate { get; set; }
public bool SendRename { get; set; }
public bool SendDelete { get; set; }
public override T Accept<T>(IRuleTriggerVisitor<T> visitor)
{
throw new NotSupportedException();
}
public RuleTrigger Migrate()
{
var conditions = new List<string>();
if (SendCreate)
{
conditions.Add($"event.type == '{EnrichedAssetEventType.Created}'");
}
if (SendUpdate)
{
conditions.Add($"event.type == '{EnrichedAssetEventType.Updated}'");
}
if (SendRename)
{
conditions.Add($"event.type == '{EnrichedAssetEventType.Renamed}'");
}
if (SendDelete)
{
conditions.Add($"event.type == '{EnrichedAssetEventType.Deleted}'");
}
var condition = "false";
if (conditions.Count > 0)
{
condition = string.Join(" || ", conditions);
}
return new AssetChangedTriggerV2 { Condition = condition };
}
}
}

45
tools/Migrate_01/OldTriggers/ContentChangedTrigger.cs

@ -0,0 +1,45 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.ObjectModel;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Infrastructure;
namespace Migrate_01.OldTriggers
{
[TypeName(nameof(ContentChangedTrigger))]
public sealed class ContentChangedTrigger : RuleTrigger, IMigrated<RuleTrigger>
{
public ReadOnlyCollection<ContentChangedTriggerSchema> Schemas { get; set; }
public bool HandleAll { get; set; }
public override T Accept<T>(IRuleTriggerVisitor<T> visitor)
{
throw new NotSupportedException();
}
public override void Freeze()
{
base.Freeze();
if (Schemas != null)
{
foreach (var schema in Schemas)
{
schema.Freeze();
}
}
}
public RuleTrigger Migrate()
{
throw new NotImplementedException();
}
}
}

83
tools/Migrate_01/OldTriggers/ContentChangedTriggerSchema.cs

@ -0,0 +1,83 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Domain.Apps.Core.Rules.Triggers;
namespace Migrate_01.OldTriggers
{
public sealed class ContentChangedTriggerSchema : Freezable
{
public Guid SchemaId { get; set; }
public bool SendCreate { get; set; }
public bool SendUpdate { get; set; }
public bool SendDelete { get; set; }
public bool SendPublish { get; set; }
public bool SendUnpublish { get; set; }
public bool SendArchived { get; set; }
public bool SendRestore { get; set; }
public ContentChangedTriggerSchemaV2 Migrate()
{
var conditions = new List<string>();
if (SendCreate)
{
conditions.Add($"event.type == '{EnrichedContentEventType.Created}'");
}
if (SendUpdate)
{
conditions.Add($"event.type == '{EnrichedContentEventType.Updated}'");
}
if (SendPublish)
{
conditions.Add($"event.type == '{EnrichedContentEventType.Published}'");
}
if (SendUnpublish)
{
conditions.Add($"event.type == '{EnrichedContentEventType.Unpublished}'");
}
if (SendArchived)
{
conditions.Add($"event.type == '{EnrichedContentEventType.Archived}'");
}
if (SendRestore)
{
conditions.Add($"event.type == '{EnrichedContentEventType.Restored}'");
}
if (SendDelete)
{
conditions.Add($"event.type == '{EnrichedAssetEventType.Deleted}'");
}
var condition = "false";
if (conditions.Count > 0)
{
condition = string.Join(" || ", conditions);
}
return new ContentChangedTriggerSchemaV2 { SchemaId = SchemaId, Condition = condition };
}
}
}
Loading…
Cancel
Save