mirror of https://github.com/Squidex/squidex.git
Browse Source
* Make everything async ready. * Testing the extension interface and extracting predefined rules. * Resolve reference. * Do not check for data. * Timestamp transform.pull/531/head
committed by
GitHub
25 changed files with 798 additions and 437 deletions
@ -0,0 +1,25 @@ |
|||||
|
// ==========================================================================
|
||||
|
// 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.Rules.EnrichedEvents; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.HandleRules |
||||
|
{ |
||||
|
public interface IRuleEventFormatter |
||||
|
{ |
||||
|
(bool Match, string?, int ReplacedLength) Format(EnrichedEvent @event, string text) |
||||
|
{ |
||||
|
return default; |
||||
|
} |
||||
|
|
||||
|
(bool Match, ValueTask<string?>) Format(EnrichedEvent @event, object value, string[] path) |
||||
|
{ |
||||
|
return default; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,197 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Shared.Users; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.HandleRules |
||||
|
{ |
||||
|
public sealed class PredefinedPatternsFormatter : IRuleEventFormatter |
||||
|
{ |
||||
|
private readonly List<(string Pattern, Func<EnrichedEvent, string?> Replacer)> patterns = new List<(string Pattern, Func<EnrichedEvent, string?> Replacer)>(); |
||||
|
private readonly IUrlGenerator urlGenerator; |
||||
|
|
||||
|
public PredefinedPatternsFormatter(IUrlGenerator urlGenerator) |
||||
|
{ |
||||
|
Guard.NotNull(urlGenerator, nameof(urlGenerator)); |
||||
|
|
||||
|
this.urlGenerator = urlGenerator; |
||||
|
|
||||
|
AddPattern("APP_ID", AppId); |
||||
|
AddPattern("APP_NAME", AppName); |
||||
|
AddPattern("ASSET_CONTENT_URL", AssetContentUrl); |
||||
|
AddPattern("CONTENT_ACTION", ContentAction); |
||||
|
AddPattern("CONTENT_URL", ContentUrl); |
||||
|
AddPattern("MENTIONED_ID", MentionedId); |
||||
|
AddPattern("MENTIONED_NAME", MentionedName); |
||||
|
AddPattern("MENTIONED_EMAIL", MentionedEmail); |
||||
|
AddPattern("SCHEMA_ID", SchemaId); |
||||
|
AddPattern("SCHEMA_NAME", SchemaName); |
||||
|
AddPattern("TIMESTAMP_DATETIME", TimestampTime); |
||||
|
AddPattern("TIMESTAMP_DATE", TimestampDate); |
||||
|
AddPattern("USER_ID", UserId); |
||||
|
AddPattern("USER_NAME", UserName); |
||||
|
AddPattern("USER_EMAIL", UserEmail); |
||||
|
} |
||||
|
|
||||
|
private void AddPattern(string placeholder, Func<EnrichedEvent, string?> generator) |
||||
|
{ |
||||
|
patterns.Add((placeholder, generator)); |
||||
|
} |
||||
|
|
||||
|
public (bool Match, string?, int ReplacedLength) Format(EnrichedEvent @event, string text) |
||||
|
{ |
||||
|
for (var j = 0; j < patterns.Count; j++) |
||||
|
{ |
||||
|
var (pattern, replacer) = patterns[j]; |
||||
|
|
||||
|
if (text.StartsWith(pattern, StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var result = replacer(@event); |
||||
|
|
||||
|
return (true, result, pattern.Length); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return default; |
||||
|
} |
||||
|
|
||||
|
private static string TimestampDate(EnrichedEvent @event) |
||||
|
{ |
||||
|
return @event.Timestamp.ToDateTimeUtc().ToString("yyy-MM-dd", CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
|
||||
|
private static string TimestampTime(EnrichedEvent @event) |
||||
|
{ |
||||
|
return @event.Timestamp.ToDateTimeUtc().ToString("yyy-MM-dd-hh-mm-ss", CultureInfo.InvariantCulture); |
||||
|
} |
||||
|
|
||||
|
private static string AppId(EnrichedEvent @event) |
||||
|
{ |
||||
|
return @event.AppId.Id.ToString(); |
||||
|
} |
||||
|
|
||||
|
private static string AppName(EnrichedEvent @event) |
||||
|
{ |
||||
|
return @event.AppId.Name; |
||||
|
} |
||||
|
|
||||
|
private static string? SchemaId(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedSchemaEventBase schemaEvent) |
||||
|
{ |
||||
|
return schemaEvent.SchemaId.Id.ToString(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? SchemaName(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedSchemaEventBase schemaEvent) |
||||
|
{ |
||||
|
return schemaEvent.SchemaId.Name; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? ContentAction(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedContentEvent contentEvent) |
||||
|
{ |
||||
|
return contentEvent.Type.ToString(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private string? AssetContentUrl(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedAssetEvent assetEvent) |
||||
|
{ |
||||
|
return urlGenerator.AssetContent(assetEvent.Id); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private string? ContentUrl(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedContentEvent contentEvent) |
||||
|
{ |
||||
|
return urlGenerator.ContentUI(contentEvent.AppId, contentEvent.SchemaId, contentEvent.Id); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? UserName(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedUserEventBase userEvent) |
||||
|
{ |
||||
|
return userEvent.User?.DisplayName(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? UserId(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedUserEventBase userEvent) |
||||
|
{ |
||||
|
return userEvent.User?.Id; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? UserEmail(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedUserEventBase userEvent) |
||||
|
{ |
||||
|
return userEvent.User?.Email; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? MentionedName(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedCommentEvent commentEvent) |
||||
|
{ |
||||
|
return commentEvent.MentionedUser.DisplayName(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? MentionedId(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedCommentEvent commentEvent) |
||||
|
{ |
||||
|
return commentEvent.MentionedUser.Id; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private static string? MentionedEmail(EnrichedEvent @event) |
||||
|
{ |
||||
|
if (@event is EnrichedCommentEvent commentEvent) |
||||
|
{ |
||||
|
return commentEvent.MentionedUser.Email; |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
using Squidex.Infrastructure.Json.Objects; |
||||
|
using Squidex.Shared.Identity; |
||||
|
using Squidex.Shared.Users; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.HandleRules |
||||
|
{ |
||||
|
public static class RuleVariable |
||||
|
{ |
||||
|
public static (object? Result, string[] Remaining) GetValue(object @event, string[] path) |
||||
|
{ |
||||
|
object? current = @event; |
||||
|
|
||||
|
var i = 0; |
||||
|
|
||||
|
for (; i < path.Length; i++) |
||||
|
{ |
||||
|
var segment = path[i]; |
||||
|
|
||||
|
if (current is NamedContentData data) |
||||
|
{ |
||||
|
if (!data.TryGetValue(segment, out var temp) || temp == null) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
current = temp; |
||||
|
} |
||||
|
else if (current is ContentFieldData field) |
||||
|
{ |
||||
|
if (!field.TryGetValue(segment, out var temp) || temp == null) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
current = temp; |
||||
|
} |
||||
|
else if (current is IJsonValue json) |
||||
|
{ |
||||
|
if (!json.TryGet(segment, out var temp) || temp == null || temp.Type == JsonValueType.Null) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
current = temp; |
||||
|
} |
||||
|
else if (current != null) |
||||
|
{ |
||||
|
if (current is IUser user) |
||||
|
{ |
||||
|
var type = segment; |
||||
|
|
||||
|
if (string.Equals(type, "Name", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
type = SquidexClaimTypes.DisplayName; |
||||
|
} |
||||
|
|
||||
|
var claim = user.Claims.FirstOrDefault(x => string.Equals(x.Type, type, StringComparison.OrdinalIgnoreCase)); |
||||
|
|
||||
|
if (claim != null) |
||||
|
{ |
||||
|
current = claim.Value; |
||||
|
continue; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const BindingFlags bindingFlags = |
||||
|
BindingFlags.FlattenHierarchy | |
||||
|
BindingFlags.Public | |
||||
|
BindingFlags.Instance; |
||||
|
|
||||
|
var properties = current.GetType().GetProperties(bindingFlags); |
||||
|
var property = properties.FirstOrDefault(x => x.CanRead && string.Equals(x.Name, segment, StringComparison.OrdinalIgnoreCase)); |
||||
|
|
||||
|
if (property == null) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
current = property.GetValue(current); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return (current, path.Skip(i).ToArray()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue