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