Implement ring group transfer for prioritized hunt strategy

This commit is contained in:
Sipke Schoorstra 2021-03-24 20:38:07 +01:00
parent ae27cbb301
commit 4807ace13c
36 changed files with 408 additions and 92 deletions

View file

@ -1,8 +1,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Client.Models;
using Elsa.Activities.Telnyx.Client.Services;
using Elsa.Activities.Telnyx.Extensions;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
@ -52,7 +51,7 @@ namespace Elsa.Activities.Telnyx.Activities
private async ValueTask AnswerCallAsync(ActivityExecutionContext context)
{
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
var callControlId = context.GetCallControlId(CallControlId);
var request = new AnswerCallRequest(BillingGroupId, ClientState, CommandId, WebhookUrl, WebhookUrlMethod);
await _telnyxClient.Calls.AnswerCallAsync(callControlId, request, context.CancellationToken);
}

View file

@ -28,6 +28,13 @@ namespace Elsa.Activities.Telnyx.Activities
set => SetState(value);
}
[ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call.", Category = PropertyCategories.Advanced)]
public string? CallControlId
{
get => GetState<string>();
set => SetState(value);
}
[ActivityProperty]
public RingGroupStrategy Strategy
{
@ -69,7 +76,8 @@ namespace Elsa.Activities.Telnyx.Activities
builder
.ForEach(() => Extensions, iterate => iterate
.Then<ResolveExtension>(a => a.WithExtension(context => context.GetInput<string>()))
.Then<Dial>(a => a
.Then<TransferCall>(a => a
.WithCallControlAppId(() => CallControlId)
.WithTo(context => context.GetInput<string>())
.WithTimeoutSecs(() => (int) RingTime.TotalSeconds)
.WithFrom(() => From)
@ -107,7 +115,8 @@ namespace Elsa.Activities.Telnyx.Activities
.When("Dial Everyone")
.ParallelForEach(() => Extensions, iterate => iterate
.Then<ResolveExtension>(a => a.WithExtension(context => context.GetInput<string>()))
.Then<Dial>(a => a
.Then<TransferCall>(a => a
.WithCallControlAppId(() => CallControlId)
.WithTo(context => context.GetInput<string>())
.WithTimeoutSecs(() => (int) RingTime.TotalSeconds)
.WithFrom(() => From)

View file

@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Client.Models;
using Elsa.Activities.Telnyx.Client.Services;
using Elsa.Activities.Telnyx.Options;
using Elsa.Activities.Telnyx.Extensions;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Builders;
@ -25,19 +24,17 @@ namespace Elsa.Activities.Telnyx.Activities
public class Dial : Activity
{
private readonly ITelnyxClient _telnyxClient;
private readonly TelnyxOptions _telnyxOptions;
public Dial(ITelnyxClient telnyxClient, TelnyxOptions telnyxOptions)
public Dial(ITelnyxClient telnyxClient)
{
_telnyxClient = telnyxClient;
_telnyxOptions = telnyxOptions;
}
[ActivityProperty(Label = "Connection ID", Hint = "The ID of the Call Control App (formerly ID of the connection) to be used when dialing the destination.", Category = PropertyCategories.Advanced)]
public string CallControlAppId { get; set; } = default!;
[ActivityProperty(Label = "Call Control ID", Hint = "The ID of the Call Control App (formerly ID of the connection) to be used when dialing the destination.", Category = PropertyCategories.Advanced)]
public string CallControlId { get; set; } = default!;
[ActivityProperty(Label = "To", Hint = "The DID or SIP URI to dial out and bridge to the given call.")]
public string To { get; set; }
public string To { get; set; } = default!;
[ActivityProperty(Label = "From",
Hint = "The 'from' number to be used as the caller id presented to the destination ('To' number). The number should be in +E164 format. This attribute will default to the 'From' number of the original call if omitted.")]
@ -90,10 +87,10 @@ namespace Elsa.Activities.Telnyx.Activities
private async ValueTask DialAsync(ActivityExecutionContext context)
{
var connectionId = CallControlAppId is not null and not "" ? CallControlAppId : _telnyxOptions.CallControlAppId ?? throw new Exception("No Call Control ID specified and no default value configured");
var callControlId = context.GetCallControlId(CallControlId);
var request = new DialRequest(
connectionId,
callControlId,
To,
From,
FromDisplayName,
@ -123,11 +120,11 @@ namespace Elsa.Activities.Telnyx.Activities
public static class DialExtensions
{
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CallControlAppId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CallControlAppId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CallControlAppId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<string?> value) => setup.Set(x => x.CallControlAppId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, string? value) => setup.Set(x => x.CallControlAppId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, Func<string?> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<Dial> WithCallControlAppId(this ISetupActivity<Dial> setup, string? value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<Dial> WithTo(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.To, value);
public static ISetupActivity<Dial> WithTo(this ISetupActivity<Dial> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.To, value);

View file

@ -1,8 +1,8 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Client.Models;
using Elsa.Activities.Telnyx.Client.Services;
using Elsa.Activities.Telnyx.Extensions;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
@ -86,11 +86,11 @@ namespace Elsa.Activities.Telnyx.Activities
EmptyToNull(ValidDigits)
);
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
var callControlId = context.GetCallControlId(CallControlId);
try
{
await _telnyxClient.Calls.GatherUsingAudiAsync(callControlId, request, context.CancellationToken);
await _telnyxClient.Calls.GatherUsingAudioAsync(callControlId, request, context.CancellationToken);
}
catch (ApiException e)
{

View file

@ -1,8 +1,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Client.Models;
using Elsa.Activities.Telnyx.Client.Services;
using Elsa.Activities.Telnyx.Extensions;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
@ -45,7 +44,7 @@ namespace Elsa.Activities.Telnyx.Activities
private async ValueTask HangupCallAsync(ActivityExecutionContext context)
{
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
var callControlId = context.GetCallControlId(CallControlId);
var request = new HangupCallRequest(ClientState, CommandId);
try

View file

@ -1,5 +1,4 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Services;
using Elsa.ActivityResults;

View file

@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Client.Models;
using Elsa.Activities.Telnyx.Client.Services;
using Elsa.Activities.Telnyx.Extensions;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Builders;
using Elsa.Design;
using Elsa.Exceptions;
using Elsa.Services;
@ -33,7 +34,7 @@ namespace Elsa.Activities.Telnyx.Activities
public string CallControlId { get; set; } = default!;
[ActivityProperty(Label = "To", Hint = "The DID or SIP URI to dial out and bridge to the given call.")]
public string To { get; set; }
public string To { get; set; } = default!;
[ActivityProperty(Label = "From",
Hint = "The 'from' number to be used as the caller id presented to the destination ('To' number). The number should be in +E164 format. This attribute will default to the 'From' number of the original call if omitted.")]
@ -111,7 +112,7 @@ namespace Elsa.Activities.Telnyx.Activities
WebhookUrlMethod
);
var callControlId = CallControlId is not null and not "" ? CallControlId : context.CorrelationId ?? throw new InvalidOperationException("Cannot answer call without a call control ID");
var callControlId = context.GetCallControlId(CallControlId);
try
{
@ -123,4 +124,97 @@ namespace Elsa.Activities.Telnyx.Activities
}
}
}
public static class TransferCallExtensions
{
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<TransferCall> WithCallControlAppId(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.CallControlId, value);
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.To, value);
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.To, value);
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.To, value);
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.To, value);
public static ISetupActivity<TransferCall> WithTo(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.To, value);
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.From, value);
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.From, value);
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.From, value);
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.From, value);
public static ISetupActivity<TransferCall> WithFrom(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.From, value);
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.FromDisplayName, value);
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.FromDisplayName, value);
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.FromDisplayName, value);
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.FromDisplayName, value);
public static ISetupActivity<TransferCall> WithFromDisplayName(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.FromDisplayName, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.AnsweringMachineDetection, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.AnsweringMachineDetection, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.AnsweringMachineDetection, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.AnsweringMachineDetection, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetection(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.AnsweringMachineDetection, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<AnsweringMachineConfig?>> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, AnsweringMachineConfig?> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<ValueTask<AnsweringMachineConfig?>> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, Func<AnsweringMachineConfig?> value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
public static ISetupActivity<TransferCall> WithAnsweringMachineDetectionConfig(this ISetupActivity<TransferCall> setup, AnsweringMachineConfig? value) => setup.Set(x => x.AnsweringMachineDetectionConfig, value);
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.CommandId, value);
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.CommandId, value);
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.CommandId, value);
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.CommandId, value);
public static ISetupActivity<TransferCall> WithCommandId(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.CommandId, value);
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.ClientState, value);
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.ClientState, value);
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.ClientState, value);
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.ClientState, value);
public static ISetupActivity<TransferCall> WithClientState(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.ClientState, value);
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<IList<Header>?>> value) => setup.Set(x => x.CustomHeaders, value);
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, IList<Header>?> value) => setup.Set(x => x.CustomHeaders, value);
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<ValueTask<IList<Header>?>> value) => setup.Set(x => x.CustomHeaders, value);
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, Func<IList<Header>?> value) => setup.Set(x => x.CustomHeaders, value);
public static ISetupActivity<TransferCall> WithCustomHeaders(this ISetupActivity<TransferCall> setup, IList<Header>? value) => setup.Set(x => x.CustomHeaders, value);
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.SipAuthUsername, value);
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.SipAuthUsername, value);
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.SipAuthUsername, value);
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.SipAuthUsername, value);
public static ISetupActivity<TransferCall> WithSipAuthUsername(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.SipAuthUsername, value);
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.SipAuthPassword, value);
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.SipAuthPassword, value);
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.SipAuthPassword, value);
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.SipAuthPassword, value);
public static ISetupActivity<TransferCall> WithSipAuthPassword(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.SipAuthPassword, value);
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<int?>> value) => setup.Set(x => x.TimeLimitSecs, value);
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, int?> value) => setup.Set(x => x.TimeLimitSecs, value);
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<ValueTask<int?>> value) => setup.Set(x => x.TimeLimitSecs, value);
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, Func<int?> value) => setup.Set(x => x.TimeLimitSecs, value);
public static ISetupActivity<TransferCall> WithTimeLimitSecs(this ISetupActivity<TransferCall> setup, int? value) => setup.Set(x => x.TimeLimitSecs, value);
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<int?>> value) => setup.Set(x => x.TimeoutSecs, value);
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, int?> value) => setup.Set(x => x.TimeoutSecs, value);
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<ValueTask<int?>> value) => setup.Set(x => x.TimeoutSecs, value);
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, Func<int?> value) => setup.Set(x => x.TimeoutSecs, value);
public static ISetupActivity<TransferCall> WithTimeoutSecs(this ISetupActivity<TransferCall> setup, int? value) => setup.Set(x => x.TimeoutSecs, value);
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.WebhookUrl, value);
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.WebhookUrl, value);
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.WebhookUrl, value);
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.WebhookUrl, value);
public static ISetupActivity<TransferCall> WithWebhookUrl(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.WebhookUrl, value);
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, ValueTask<string?>> value) => setup.Set(x => x.WebhookUrlMethod, value);
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<ActivityExecutionContext, string?> value) => setup.Set(x => x.WebhookUrlMethod, value);
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<ValueTask<string?>> value) => setup.Set(x => x.WebhookUrlMethod, value);
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, Func<string?> value) => setup.Set(x => x.WebhookUrlMethod, value);
public static ISetupActivity<TransferCall> WithWebhookUrlMethod(this ISetupActivity<TransferCall> setup, string? value) => setup.Set(x => x.WebhookUrlMethod, value);
}
}

View file

@ -7,18 +7,28 @@ using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Webhooks.Attributes;
using Elsa.Activities.Telnyx.Webhooks.Models;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Call;
using Elsa.Activities.Telnyx.Webhooks.Services;
using Elsa.ActivityProviders;
using Elsa.ActivityResults;
using Elsa.Metadata;
using Elsa.Services.Models;
using Microsoft.AspNetCore.Authorization.Policy;
namespace Elsa.Activities.Telnyx.ActivityTypes
{
public class NotificationActivityTypeProvider : IActivityTypeProvider
internal class NotificationActivityTypeProvider : IActivityTypeProvider
{
private readonly IWebhookFilterService _webhookFilterService;
public NotificationActivityTypeProvider(IWebhookFilterService webhookFilterService)
{
_webhookFilterService = webhookFilterService;
}
public const string NotificationAttribute = "TelnyxNotification";
public const string EventTypeAttribute = "EventType";
public ValueTask<IEnumerable<ActivityType>> GetActivityTypesAsync(CancellationToken cancellationToken = default)
{
var activityTypes = GetActivityTypes();
@ -27,20 +37,31 @@ namespace Elsa.Activities.Telnyx.ActivityTypes
private IEnumerable<ActivityType> GetActivityTypes()
{
var types = GetType().Assembly.GetAllWithBaseClass<Payload>().Where(x => x.GetCustomAttribute<PayloadAttribute>() != null);
return types.Select(CreateWebhookActivityType);
var payloadTypes = GetType().Assembly.GetAllWithBaseClass<Payload>().Where(x => x.GetCustomAttribute<WebhookAttribute>() != null).ToList();
var activityTypes = payloadTypes.Select(CreateWebhookActivityType).ToList();
// Add variations on the same webhooks. The webhook filters will conditionally select the appropriate one.
var hangupWebhookAttribute = payloadTypes.First(x => x == typeof(CallHangupPayload)).GetCustomAttribute<WebhookAttribute>()!;
activityTypes.Add(CreateWebhookActivityType(new WebhookAttribute(hangupWebhookAttribute.EventType, "OriginatorCallHangup", "Originator Call Hangup", "Triggered when an incoming call was hangup by the originator.")));
return activityTypes;
}
private static ActivityType CreateWebhookActivityType(Type payloadType)
{
var payloadAttribute = payloadType.GetCustomAttribute<PayloadAttribute>();
var webhookAttribute = payloadType.GetCustomAttribute<WebhookAttribute>();
if (payloadAttribute == null)
throw new InvalidOperationException($"Make sure that the payload type is annotated with the ${nameof(PayloadAttribute)} attribute");
if (webhookAttribute == null)
throw new InvalidOperationException($"Make sure that the payload type is annotated with the ${nameof(WebhookAttribute)} attribute");
var typeName = payloadAttribute.ActivityType;
var displayName = payloadAttribute.DisplayName;
var description = payloadAttribute.Description;
return CreateWebhookActivityType(webhookAttribute);
}
private static ActivityType CreateWebhookActivityType(WebhookAttribute webhookAttribute)
{
var typeName = webhookAttribute.ActivityType;
var displayName = webhookAttribute.DisplayName;
var description = webhookAttribute.Description;
return new ActivityType
{
@ -59,18 +80,24 @@ namespace Elsa.Activities.Telnyx.ActivityTypes
Attributes = new Dictionary<string, object>
{
[NotificationAttribute] = true,
[EventTypeAttribute] = payloadAttribute.EventType
[EventTypeAttribute] = webhookAttribute.EventType
},
CanExecuteAsync = _ => new ValueTask<bool>(true),
ExecuteAsync = context => context.WorkflowExecutionContext.IsFirstPass ? ExecuteInternal(context) : new ValueTask<IActivityExecutionResult>(new SuspendResult()),
ResumeAsync = ExecuteInternal,
};
}
private static ValueTask<IActivityExecutionResult> ExecuteInternal(ActivityExecutionContext context)
{
var webhook = (TelnyxWebhook) context.Input!;
context.WorkflowExecutionContext.CorrelationId ??= (webhook.Data.Payload as ICorrelationId)?.CorrelationId;
var webhook = (TelnyxWebhook) context.Input!;
if (webhook.Data.Payload is CallPayload callPayload)
{
context.WorkflowExecutionContext.CorrelationId ??= callPayload.CallSessionId;
context.SetVariable("CallControlId", callPayload.CallControlId);
}
return new(new CombinedResult(new OutputResult(context.Input), new DoneResult()));
}
}

View file

@ -20,6 +20,6 @@ namespace Elsa.Activities.Telnyx.Client.Services
Task HangupCallAsync(string callControlId, [Body]HangupCallRequest request, CancellationToken cancellationToken = default);
[Post("/v2/calls/{callControlId}/actions/gather_using_audio")]
Task GatherUsingAudiAsync(string callControlId, [Body]GatherUsingAudioRequest request, CancellationToken cancellationToken = default);
Task GatherUsingAudioAsync(string callControlId, [Body]GatherUsingAudioRequest request, CancellationToken cancellationToken = default);
}
}

View file

@ -0,0 +1,31 @@
using System;
using Elsa.Activities.Telnyx.Options;
using Elsa.Services.Models;
namespace Elsa.Activities.Telnyx.Extensions
{
public static class ActivityExecutionContextExtensions
{
/// <summary>
/// Returns the default call control app ID if the specified value is null or empty.
/// </summary>
public static string GetCallControlId(this ActivityExecutionContext context, string? callControlId)
{
if (!string.IsNullOrWhiteSpace(callControlId))
return callControlId;
callControlId = context.GetVariable<string>("CallControlId");
if (!string.IsNullOrWhiteSpace(callControlId))
return callControlId;
var options = context.GetService<TelnyxOptions>();
callControlId = options.CallControlAppId;
if (!string.IsNullOrWhiteSpace(callControlId))
return callControlId;
throw new Exception("No Call Control ID specified and no default value configured");
}
}
}

View file

@ -9,9 +9,9 @@ using Elsa.Activities.Telnyx.ActivityTypes;
using Elsa.Activities.Telnyx.Bookmarks;
using Elsa.Activities.Telnyx.Client.Services;
using Elsa.Activities.Telnyx.Options;
using Elsa.Activities.Telnyx.Services;
using Elsa.Activities.Telnyx.Webhooks.Consumers;
using Elsa.Activities.Telnyx.Webhooks.Events;
using Elsa.Activities.Telnyx.Webhooks.Filters;
using Elsa.Activities.Telnyx.Webhooks.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
@ -46,6 +46,9 @@ namespace Elsa.Activities.Telnyx.Extensions
.AddBookmarkProvider<NotificationBookmarkProvider>()
.AddNotificationHandlers(typeof(TriggerWorkflows))
.AddScoped<IWebhookHandler, WebhookHandler>()
.AddSingleton<IWebhookFilterService, WebhookFilterService>()
.AddSingleton<IWebhookFilter, AttributeBasedWebhookFilter>()
.AddSingleton<IWebhookFilter, HangupWebhookFilter>()
.AddScoped(telnyxOptions.ExtensionProviderFactory);
// Telnyx API Client.

View file

@ -3,9 +3,9 @@
namespace Elsa.Activities.Telnyx.Webhooks.Attributes
{
[AttributeUsage(AttributeTargets.Class)]
public class PayloadAttribute : Attribute
public class WebhookAttribute : Attribute
{
public PayloadAttribute(string eventType, string activityType, string displayName, string description)
public WebhookAttribute(string eventType, string activityType, string displayName, string description)
{
EventType = eventType;
ActivityType = activityType;

View file

@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Bookmarks;
using Elsa.Activities.Telnyx.Webhooks.Attributes;
using Elsa.Activities.Telnyx.Webhooks.Events;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Call;
using Elsa.Activities.Telnyx.Webhooks.Services;
using Elsa.DistributedLock;
using Elsa.Models;
using Elsa.Persistence;
@ -24,14 +28,22 @@ namespace Elsa.Activities.Telnyx.Webhooks.Consumers
private readonly IWorkflowInstanceStore _workflowInstanceStore;
private readonly IDistributedLockProvider _distributedLockProvider;
private readonly ICommandSender _commandSender;
private readonly IWebhookFilterService _webhookFilterService;
private readonly ILogger<TriggerWorkflows> _logger;
public TriggerWorkflows(IWorkflowRunner workflowRunner, IWorkflowInstanceStore workflowInstanceStore, IDistributedLockProvider distributedLockProvider, ICommandSender commandSender, ILogger<TriggerWorkflows> logger)
public TriggerWorkflows(
IWorkflowRunner workflowRunner,
IWorkflowInstanceStore workflowInstanceStore,
IDistributedLockProvider distributedLockProvider,
ICommandSender commandSender,
IWebhookFilterService webhookFilterService,
ILogger<TriggerWorkflows> logger)
{
_workflowRunner = workflowRunner;
_workflowInstanceStore = workflowInstanceStore;
_distributedLockProvider = distributedLockProvider;
_commandSender = commandSender;
_webhookFilterService = webhookFilterService;
_logger = logger;
}
@ -40,18 +52,17 @@ namespace Elsa.Activities.Telnyx.Webhooks.Consumers
var webhook = message.Webhook;
var eventType = webhook.Data.EventType;
var payload = message.Webhook.Data.Payload;
var payloadAttribute = payload.GetType().GetCustomAttribute<PayloadAttribute>();
var activityType = _webhookFilterService.GetActivityTypeName(payload);
if (payloadAttribute == null)
if (activityType == null)
{
_logger.LogWarning("The received payload type '{PayloadTypeName}' does not have a PayloadAttribute containing metadata, which means it's an unsupported event", payload.GetType().Name);
_logger.LogWarning("The received event '{EventType}' is an unsupported event", webhook.Data.EventType);
return;
}
var activityType = payloadAttribute.ActivityType;
var correlationId = (payload as ICorrelationId)?.CorrelationId;
var correlationId = GetCorrelationId(payload);
var lockKey = $"telnyx:trigger-workflows:correlation-{correlationId}";
if (!await _distributedLockProvider.AcquireLockAsync(lockKey))
{
_logger.LogDebug("Lock {LockKey} already taken", lockKey);
@ -75,5 +86,13 @@ namespace Elsa.Activities.Telnyx.Webhooks.Consumers
await _distributedLockProvider.ReleaseLockAsync(lockKey);
}
}
private string GetCorrelationId(Payload payload)
{
if (payload is CallPayload callPayload)
return callPayload.CallSessionId;
throw new NotSupportedException($"The received payload type {payload.GetType().Name} is not supported yet.");
}
}
}

View file

@ -19,7 +19,7 @@ namespace Elsa.Activities.Telnyx.Webhooks.Converters
var query =
from payloadType in payloadTypes
let payloadAttribute = payloadType.GetCustomAttribute<PayloadAttribute>()
let payloadAttribute = payloadType.GetCustomAttribute<WebhookAttribute>()
where payloadAttribute != null
select (payloadType, payloadAttribute);

View file

@ -0,0 +1,14 @@
using System.Reflection;
using Elsa.Activities.Telnyx.Webhooks.Attributes;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
using Elsa.Activities.Telnyx.Webhooks.Services;
namespace Elsa.Activities.Telnyx.Webhooks.Filters
{
public class AttributeBasedWebhookFilter : IWebhookFilter
{
public int Priority => 0;
public bool CanHandlePayload(Payload payload) => payload.GetType().GetCustomAttribute<WebhookAttribute>() != null;
public string GetActivityTypeName(Payload payload) => payload.GetType().GetCustomAttribute<WebhookAttribute>()!.ActivityType;
}
}

View file

@ -0,0 +1,26 @@
using System.Reflection;
using Elsa.Activities.Telnyx.Webhooks.Attributes;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Call;
using Elsa.Activities.Telnyx.Webhooks.Services;
namespace Elsa.Activities.Telnyx.Webhooks.Filters
{
public class HangupWebhookFilter : IWebhookFilter
{
public int Priority => 1;
public bool CanHandlePayload(Payload payload) => payload is CallHangupPayload;
public string GetActivityTypeName(Payload payload)
{
var hangupPayload = (CallHangupPayload) payload;
var attribute = payload.GetType().GetCustomAttribute<WebhookAttribute>()!;
if (hangupPayload.HangupCause != "originator_cancel")
return attribute.ActivityType;
return "OriginatorCallHangup";
}
}
}

View file

@ -1,7 +0,0 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract
{
public interface ICorrelationId
{
string CorrelationId { get; }
}
}

View file

@ -2,7 +2,7 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
[Payload(EventType, ActivityTypeName, "Call Answered", "Triggered when an incoming call is answered.")]
[Webhook(EventType, ActivityTypeName, "Call Answered", "Triggered when an incoming call is answered.")]
public sealed record CallAnsweredPayload : CallPayload
{
public const string EventType = "call.answered";

View file

@ -2,7 +2,7 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
[Payload(EventType, ActivityTypeName, "Call DTMF Received", "Triggered when DTMF input is received.")]
[Webhook(EventType, ActivityTypeName, "Call DTMF Received", "Triggered when DTMF input is received.")]
public sealed record CallDtmfReceivedPayload : CallPayload
{
public const string EventType = "call.dtmf.received";

View file

@ -2,7 +2,7 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
[Payload(EventType, ActivityTypeName, "Call Gather Ended", "Triggered when an call gather has ended.")]
[Webhook(EventType, ActivityTypeName, "Call Gather Ended", "Triggered when an call gather has ended.")]
public sealed record CallGatherEndedPayload : CallPayload
{
public const string EventType = "call.gather.ended";

View file

@ -3,7 +3,7 @@ using NodaTime;
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
[Payload(EventType, ActivityTypeName, "Call Hangup", "Triggered when an incoming call was hangup.")]
[Webhook(EventType, ActivityTypeName, "Call Hangup", "Triggered when an incoming call was hangup.")]
public sealed record CallHangupPayload : CallPayload
{
public const string EventType = "call.hangup";

View file

@ -2,7 +2,7 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
[Payload(EventType, ActivityTypeName, "Call Initiated", "Triggered when an incoming call is received.")]
[Webhook(EventType, ActivityTypeName, "Call Initiated", "Triggered when an incoming call is received.")]
public sealed record CallInitiatedPayload : CallPayload
{
public const string EventType = "call.initiated";

View file

@ -2,12 +2,11 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
public abstract record CallPayload : Payload, ICorrelationId
public abstract record CallPayload : Payload
{
public string CallControlId { get; init; } = default!;
public string CallLegId { get; init; } = default!;
public string CallSessionId { get; init; } = default!;
public string ConnectionId { get; init; } = default!;
string ICorrelationId.CorrelationId => CallControlId;
}
}

View file

@ -2,7 +2,7 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
[Payload(EventType, ActivityTypeName, "Call Playback Ended", "Triggered when an audio playback has ended.")]
[Webhook(EventType, ActivityTypeName, "Call Playback Ended", "Triggered when an audio playback has ended.")]
public sealed record CallPlaybackEndedPayload : CallPlayback
{
public const string EventType = "call.playback.ended";

View file

@ -2,7 +2,7 @@
namespace Elsa.Activities.Telnyx.Webhooks.Payloads.Call
{
[Payload(EventType, ActivityTypeName, "Call Playback Started", "Triggered when an audio playback has started.")]
[Webhook(EventType, ActivityTypeName, "Call Playback Started", "Triggered when an audio playback has started.")]
public sealed record CallPlaybackStartedPayload : CallPlayback
{
public const string EventType = "call.playback.started";

View file

@ -0,0 +1,12 @@
using Elsa.Activities.Telnyx.Webhooks.Attributes;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
namespace Elsa.Activities.Telnyx.Webhooks.Services
{
internal interface IWebhookFilter
{
int Priority { get; }
bool CanHandlePayload(Payload payload);
string GetActivityTypeName(Payload payload);
}
}

View file

@ -0,0 +1,9 @@
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
namespace Elsa.Activities.Telnyx.Webhooks.Services
{
internal interface IWebhookFilterService
{
string? GetActivityTypeName(Payload payload);
}
}

View file

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using Elsa.Activities.Telnyx.Webhooks.Attributes;
using Microsoft.AspNetCore.Http;
namespace Elsa.Activities.Telnyx.Webhooks.Services

View file

@ -0,0 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using Elsa.Activities.Telnyx.Webhooks.Attributes;
using Elsa.Activities.Telnyx.Webhooks.Payloads.Abstract;
namespace Elsa.Activities.Telnyx.Webhooks.Services
{
internal class WebhookFilterService : IWebhookFilterService
{
private readonly IEnumerable<IWebhookFilter> _filters;
public WebhookFilterService(IEnumerable<IWebhookFilter> filters)
{
_filters = filters.OrderByDescending(x => x.Priority).ToList();
}
public string? GetActivityTypeName(Payload payload)
{
var filter = _filters.FirstOrDefault(x => x.CanHandlePayload(payload));
return filter?.GetActivityTypeName(payload);
}
}
}

View file

@ -43,7 +43,7 @@ namespace Elsa.Activities.ControlFlow
var collection = Items.ToList();
var currentIndex = CurrentIndex ?? 0;
if (currentIndex < collection.Count - 1)
if (currentIndex < collection.Count)
{
var currentValue = collection[currentIndex];
var scope = context.CreateScope();

View file

@ -33,7 +33,7 @@ namespace Elsa.Activities.ControlFlow
Action<IOutcomeBuilder> iterate,
[CallerLineNumber] int lineNumber = default,
[CallerFilePath] string? sourceFile = default) =>
builder.ForEach(activity => activity.Set(x => x.Items, () => items().Cast<object>()), iterate, lineNumber, sourceFile);
builder.ForEach(activity => activity.Set(x => x.Items, () => items().Select(x => (object)x!).ToList()), iterate, lineNumber, sourceFile);
public static IActivityBuilder ForEach<T>(
this IBuilder builder,

View file

@ -16,6 +16,17 @@ namespace Elsa.Builders
private readonly Func<ICompositeActivityBuilder> _compositeActivityBuilderFactory;
private readonly IGetsStartActivitiesForCompositeActivityBlueprint _startingActivitiesProvider;
internal CompositeActivityBuilder(
IServiceProvider serviceProvider,
IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider,
Type activityType,
string activityTypeName) : this(serviceProvider, startingActivitiesProvider)
{
ActivityType = activityType;
ActivityTypeName = activityTypeName;
WorkflowBuilder = this;
}
public CompositeActivityBuilder(IServiceProvider serviceProvider, IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider)
{
_startingActivitiesProvider = startingActivitiesProvider ?? throw new ArgumentNullException(nameof(startingActivitiesProvider));
@ -106,7 +117,7 @@ namespace Elsa.Builders
[CallerFilePath] string? sourceFile = default)
where T : class, IActivity
{
var activityBuilder = new ActivityBuilder( typeof(T), activityTypeName, this, propertyValueProviders, lineNumber, sourceFile);
var activityBuilder = new ActivityBuilder(typeof(T), activityTypeName, this, propertyValueProviders, lineNumber, sourceFile);
return Add(activityBuilder, branch);
}
@ -119,12 +130,18 @@ namespace Elsa.Builders
return activityBuilder;
}
public override IActivityBuilder Then<T>(string activityTypeName, Action<ISetupActivity<T>>? setup = null, Action<IActivityBuilder>? branch = null, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
StartWith(activityTypeName,setup, branch, lineNumber, sourceFile);
public override IActivityBuilder Then<T>(
string activityTypeName,
Action<ISetupActivity<T>>? setup = null,
Action<IActivityBuilder>? branch = null,
[CallerLineNumber] int lineNumber = default,
[CallerFilePath] string? sourceFile = default) =>
StartWith(activityTypeName, setup, branch, lineNumber, sourceFile);
public override IActivityBuilder Then(IActivityBuilder targetActivity) => Add(targetActivity);
public override IActivityBuilder Then<T>(string activityTypeName, Action<IActivityBuilder>? branch = null, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) => StartWith<T>(activityTypeName, branch, lineNumber, sourceFile);
public override IActivityBuilder Then<T>(string activityTypeName, Action<IActivityBuilder>? branch = null, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
StartWith<T>(activityTypeName, branch, lineNumber, sourceFile);
public IConnectionBuilder Connect(
IActivityBuilder source,
@ -147,7 +164,13 @@ namespace Elsa.Builders
{
var compositeActivityBlueprint = new CompositeActivityBlueprint
{
Id = ActivityId
Id = ActivityId,
Name = Name,
Type = ActivityTypeName,
PersistOutput = PersistOutputEnabled,
PersistWorkflow = PersistWorkflowEnabled,
LoadWorkflowContext = LoadWorkflowContextEnabled,
SaveWorkflowContext = SaveWorkflowContextEnabled
};
var activityBuilders = ActivityBuilders.ToList();

View file

@ -20,6 +20,9 @@ namespace Elsa.Expressions
if (returnType == typeof(string))
return Task.FromResult<object?>(expression);
if (string.IsNullOrWhiteSpace(expression))
return Task.FromResult((object?) null);
var converter = TypeDescriptor.GetConverter(returnType);
var value = converter.CanConvertFrom(typeof(string)) ? converter.ConvertFrom(expression) : default;
return Task.FromResult(value)!;

View file

@ -1,10 +1,14 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Elsa.ActivityProviders;
using Elsa.Builders;
using Elsa.Models;
using Elsa.Services.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetBox.Extensions;
@ -13,14 +17,18 @@ namespace Elsa.Services
public class WorkflowBlueprintMaterializer : IWorkflowBlueprintMaterializer
{
private readonly IActivityTypeService _activityTypeService;
private readonly IGetsStartActivitiesForCompositeActivityBlueprint _startingActivitiesProvider;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private readonly IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider;
public WorkflowBlueprintMaterializer(IActivityTypeService activityTypeService,
ILogger<WorkflowBlueprintMaterializer> logger,
IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider)
public WorkflowBlueprintMaterializer(
IActivityTypeService activityTypeService,
IGetsStartActivitiesForCompositeActivityBlueprint startingActivitiesProvider,
IServiceProvider serviceProvider,
ILogger<WorkflowBlueprintMaterializer> logger)
{
this.startingActivitiesProvider = startingActivitiesProvider ?? throw new System.ArgumentNullException(nameof(startingActivitiesProvider));
_startingActivitiesProvider = startingActivitiesProvider;
_serviceProvider = serviceProvider;
_activityTypeService = activityTypeService;
_logger = logger;
}
@ -105,7 +113,8 @@ namespace Elsa.Services
private async Task<IEnumerable<IActivityBlueprint>> CreateBlueprintsAsync(ActivityDefinition activityDefinition, CancellationToken cancellationToken)
{
var list = new List<IActivityBlueprint>();
var activityType = await _activityTypeService.GetActivityTypeAsync(activityDefinition.Type, cancellationToken);
if (activityDefinition is CompositeActivityDefinition compositeActivityDefinition)
{
var manyActivityBlueprints = await Task.WhenAll(compositeActivityDefinition.Activities.Select(async x => await CreateBlueprintsAsync(x, cancellationToken)));
@ -130,7 +139,33 @@ namespace Elsa.Services
list.Add(compositeActivityBlueprint);
// Connect the composite activity to its starting activities.
var startActivities = startingActivitiesProvider.GetStartActivities(compositeActivityBlueprint).ToList();
var startActivities = _startingActivitiesProvider.GetStartActivities(compositeActivityBlueprint).ToList();
compositeActivityBlueprint.Connections.AddRange(startActivities.Select(x => new Connection(compositeActivityBlueprint, x, CompositeActivity.Enter)));
}
else if (typeof(CompositeActivity).IsAssignableFrom(activityType.Type))
{
var compositeActivity = (CompositeActivity) ActivatorUtilities.CreateInstance(_serviceProvider, activityType.Type);
var compositeActivityBuilder = new CompositeActivityBuilder(_serviceProvider, _startingActivitiesProvider, activityType.Type, activityType.TypeName)
{
ActivityId = activityDefinition.ActivityId,
Name = activityDefinition.Name,
DisplayName = activityDefinition.DisplayName,
PersistOutputEnabled = activityDefinition.PersistOutput,
PersistWorkflowEnabled = activityDefinition.PersistWorkflow,
LoadWorkflowContextEnabled = activityDefinition.LoadWorkflowContext,
SaveWorkflowContextEnabled = activityDefinition.SaveWorkflowContext,
Description = activityDefinition.Description
};
compositeActivity.Build(compositeActivityBuilder);
var compositeActivityBlueprint = compositeActivityBuilder.Build($"{activityDefinition.ActivityId}:activity");
list.Add(compositeActivityBlueprint);
list.AddRange(compositeActivityBlueprint.Activities);
// Connect the composite activity to its starting activities.
var startActivities = _startingActivitiesProvider.GetStartActivities(compositeActivityBlueprint).ToList();
compositeActivityBlueprint.Connections.AddRange(startActivities.Select(x => new Connection(compositeActivityBlueprint, x, CompositeActivity.Enter)));
}
else

View file

@ -39,7 +39,7 @@ namespace ElsaDashboard.Samples.Monolith
.AddQuartzTemporalActivities()
.AddTelnyx(Configuration.GetSection("Telnyx").Bind)
.AddJavaScriptActivities()
.AddWorkflowsFrom<Startup>()
//.AddWorkflowsFrom<Startup>()
);
services

View file

@ -22,6 +22,7 @@
},
"Telnyx": {
"ApiUrl": "",
"ApiKey": ""
"ApiKey": "",
"CallControlAppId": ""
}
}