mirror of https://github.com/Squidex/squidex.git
Browse Source
* Add SignalR Azure management package * Add Action SignalR * Update DataTypeAttribute * Update Enum Uppercase * Add space * Update using Dispose * Move initialisation list Users/Groups in CreateJobAsync * Add Header * Test Markdown Text * Remove commande Users/Groups, dynamic by separator \n * clean code * ActionType rename to Action, technical reservation * Add contrôle List empty * Delete markdown Description * Reduce code and parameters Co-authored-by: Julien Mougneau <julien.mougneau@ydeho.fr>pull/747/head
committed by
GitHub
4 changed files with 217 additions and 0 deletions
@ -0,0 +1,76 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Text.RegularExpressions; |
||||
|
using Squidex.Domain.Apps.Core.HandleRules; |
||||
|
using Squidex.Domain.Apps.Core.Rules; |
||||
|
using Squidex.Infrastructure.Validation; |
||||
|
|
||||
|
namespace Squidex.Extensions.Actions.SignalR |
||||
|
{ |
||||
|
[RuleAction( |
||||
|
Title = "Azure SignalR", |
||||
|
IconImage = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><path d='M.011 16L0 6.248l12-1.63V16zM14 4.328L29.996 2v14H14zM30 18l-.004 14L14 29.75V18zM12 29.495L.01 27.851.009 18H12z'/></svg>", |
||||
|
IconColor = "#1566BF", |
||||
|
Display = "Send to Azure SignalR", |
||||
|
Description = "Send an message to azure SignalR.", |
||||
|
ReadMore = "https://azure.microsoft.com/fr-fr/services/signalr-service/")] |
||||
|
public sealed record SignalRAction : RuleAction |
||||
|
{ |
||||
|
[LocalizedRequired] |
||||
|
[Display(Name = "Connection", Description = "The connection string to the Signal R Azure.")] |
||||
|
[Editor(RuleFieldEditor.Text)] |
||||
|
[Formattable] |
||||
|
public string ConnectionString { get; set; } |
||||
|
|
||||
|
[LocalizedRequired] |
||||
|
[Display(Name = "Hub Name", Description = "The name of the hub.")] |
||||
|
[Editor(RuleFieldEditor.Text)] |
||||
|
[Formattable] |
||||
|
public string HubName { get; set; } |
||||
|
|
||||
|
[LocalizedRequired] |
||||
|
[Display(Name = "Action", Description = "Broadcast = send to all User, User = send to specific user(s) specified in the 'Target' field, Group = send to specific group(s) specified in the 'Target' field")] |
||||
|
public ActionTypeEnum Action { get; set; } |
||||
|
|
||||
|
[Display(Name = "Methode Name", Description = "Set the Name of the hub method received by the customer, default value 'push.")] |
||||
|
[Editor(RuleFieldEditor.Text)] |
||||
|
public string MethodName { get; set; } |
||||
|
|
||||
|
[Display(Name = "Target (Optional)", Description = "Defines a user, group or target list by an id or name. For a list, define one value per line. Not necessary with the 'Broadcast' action")] |
||||
|
[Editor(RuleFieldEditor.TextArea)] |
||||
|
[Formattable] |
||||
|
public string Target { get; set; } |
||||
|
|
||||
|
[Display(Name = "Payload (Optional)", Description = "Leave it empty to use the full event as body.")] |
||||
|
[Editor(RuleFieldEditor.TextArea)] |
||||
|
[Formattable] |
||||
|
public string Payload { get; set; } |
||||
|
|
||||
|
protected override IEnumerable<ValidationError> CustomValidate() |
||||
|
{ |
||||
|
if (!string.IsNullOrWhiteSpace(HubName) && !Regex.IsMatch(HubName, "^[a-z][a-z0-9]{2,}(\\-[a-z0-9]+)*$")) |
||||
|
{ |
||||
|
yield return new ValidationError("Hub must be valid azure hub name.", nameof(HubName)); |
||||
|
} |
||||
|
|
||||
|
if ((Action == ActionTypeEnum.User || Action == ActionTypeEnum.Group) && string.IsNullOrWhiteSpace(Target)) |
||||
|
{ |
||||
|
yield return new ValidationError("Target must be specified with 'User' or 'Group' Action.", nameof(HubName)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public enum ActionTypeEnum |
||||
|
{ |
||||
|
Broadcast, |
||||
|
User, |
||||
|
Group |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,119 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.SignalR; |
||||
|
using Microsoft.Azure.SignalR.Management; |
||||
|
using Squidex.Domain.Apps.Core.HandleRules; |
||||
|
using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; |
||||
|
|
||||
|
namespace Squidex.Extensions.Actions.SignalR |
||||
|
{ |
||||
|
public sealed class SignalRActionHandler : RuleActionHandler<SignalRAction, SignalRJob> |
||||
|
{ |
||||
|
private readonly ClientPool<(string ConnectionString, string HubName), IServiceManager> clients; |
||||
|
|
||||
|
public SignalRActionHandler(RuleEventFormatter formatter) |
||||
|
: base(formatter) |
||||
|
{ |
||||
|
clients = new ClientPool<(string ConnectionString, string HubName), IServiceManager>(key => |
||||
|
{ |
||||
|
var serviceManager = new ServiceManagerBuilder() |
||||
|
.WithOptions(option => |
||||
|
{ |
||||
|
option.ConnectionString = key.ConnectionString; |
||||
|
option.ServiceTransportType = ServiceTransportType.Transient; |
||||
|
}) |
||||
|
.Build(); |
||||
|
return serviceManager; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
protected override async Task<(string Description, SignalRJob Data)> CreateJobAsync(EnrichedEvent @event, SignalRAction action) |
||||
|
{ |
||||
|
var hubName = await FormatAsync(action.HubName, @event); |
||||
|
|
||||
|
string requestBody; |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(action.Payload)) |
||||
|
{ |
||||
|
requestBody = await FormatAsync(action.Payload, @event); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
requestBody = ToEnvelopeJson(@event); |
||||
|
} |
||||
|
|
||||
|
string[] targetArray = new string[0]; |
||||
|
string target = string.Empty; |
||||
|
target = await FormatAsync(action.Target, @event); |
||||
|
if (!string.IsNullOrEmpty(target)) |
||||
|
{ |
||||
|
targetArray = target.Split('\n'); |
||||
|
} |
||||
|
|
||||
|
var ruleDescription = $"Send SignalRJob to signalR hub '{hubName}'"; |
||||
|
|
||||
|
var ruleJob = new SignalRJob |
||||
|
{ |
||||
|
ConnectionString = action.ConnectionString, |
||||
|
HubName = hubName, |
||||
|
Action = action.Action, |
||||
|
MethodName = action.MethodName, |
||||
|
Target = target, |
||||
|
TargetArray = targetArray, |
||||
|
Payload = requestBody |
||||
|
}; |
||||
|
return (ruleDescription, ruleJob); |
||||
|
} |
||||
|
|
||||
|
protected override async Task<Result> ExecuteJobAsync(SignalRJob job, CancellationToken ct = default) |
||||
|
{ |
||||
|
var signalR = await clients.GetClientAsync((job.ConnectionString, job.HubName)); |
||||
|
|
||||
|
await using (var signalRContext = await signalR.CreateHubContextAsync(job.HubName)) |
||||
|
{ |
||||
|
var methodeName = !string.IsNullOrWhiteSpace(job.MethodName) ? job.MethodName : "push"; |
||||
|
|
||||
|
switch (job.Action) |
||||
|
{ |
||||
|
case ActionTypeEnum.Broadcast: |
||||
|
await signalRContext.Clients.All.SendAsync(methodeName, job.Payload); |
||||
|
break; |
||||
|
case ActionTypeEnum.User: |
||||
|
await signalRContext.Clients.Users(job.TargetArray).SendAsync(methodeName, job.Payload); |
||||
|
|
||||
|
break; |
||||
|
case ActionTypeEnum.Group: |
||||
|
await signalRContext.Clients.Groups(job.TargetArray).SendAsync(methodeName, job.Payload); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return Result.Complete(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class SignalRJob |
||||
|
{ |
||||
|
public string ConnectionString { get; set; } |
||||
|
|
||||
|
public string HubName { get; set; } |
||||
|
|
||||
|
public ActionTypeEnum Action { get; set; } |
||||
|
|
||||
|
public string MethodName { get; set; } |
||||
|
|
||||
|
public string Target { get; set; } |
||||
|
|
||||
|
public string[] TargetArray { get; set; } |
||||
|
|
||||
|
public string Payload { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Squidex.Infrastructure.Plugins; |
||||
|
|
||||
|
namespace Squidex.Extensions.Actions.SignalR |
||||
|
{ |
||||
|
public sealed class SignalRPlugin : IPlugin |
||||
|
{ |
||||
|
public void ConfigureServices(IServiceCollection services, IConfiguration config) |
||||
|
{ |
||||
|
services.AddRuleAction<SignalRAction, SignalRActionHandler>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue