diff --git a/src/bundles/Elsa.WorkflowServer.Web/HeartbeatWorkflow.cs b/src/bundles/Elsa.WorkflowServer.Web/HeartbeatWorkflow.cs new file mode 100644 index 000000000..456005c10 --- /dev/null +++ b/src/bundles/Elsa.WorkflowServer.Web/HeartbeatWorkflow.cs @@ -0,0 +1,24 @@ +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities; +using Elsa.Workflows.Core.Contracts; +using Timer = Elsa.Scheduling.Activities.Timer; + +namespace Elsa.WorkflowServer.Web; + +public class HeartbeatWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder builder) + { + builder.Root = new Sequence + { + Activities = + { + new Timer(TimeSpan.FromMinutes(5)) + { + CanStartWorkflow = true + }, + new WriteLine(context => $"Heartbeat workflow triggered at {DateTime.Now}") + } + }; + } +} \ No newline at end of file diff --git a/src/bundles/Elsa.WorkflowServer.Web/Program.cs b/src/bundles/Elsa.WorkflowServer.Web/Program.cs index 4463774d6..df4b2793e 100644 --- a/src/bundles/Elsa.WorkflowServer.Web/Program.cs +++ b/src/bundles/Elsa.WorkflowServer.Web/Program.cs @@ -32,6 +32,7 @@ services elsa .AddActivitiesFrom() + .AddWorkflowsFrom() .UseFluentStorageProvider() .AddTypeAlias>("ApiResponse[User]") .UseIdentity(identity => diff --git a/src/modules/Elsa.AzureServiceBus/Activities/MessageReceived.cs b/src/modules/Elsa.AzureServiceBus/Activities/MessageReceived.cs index 7ba264433..f33ff459e 100644 --- a/src/modules/Elsa.AzureServiceBus/Activities/MessageReceived.cs +++ b/src/modules/Elsa.AzureServiceBus/Activities/MessageReceived.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; using Elsa.AzureServiceBus.Models; using Elsa.Common.Contracts; using Elsa.Expressions.Models; @@ -18,8 +18,7 @@ public class MessageReceived : Trigger internal const string InputKey = "TransportMessage"; /// - [JsonConstructor] - public MessageReceived() + public MessageReceived([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } diff --git a/src/modules/Elsa.AzureServiceBus/Activities/SendMessage.cs b/src/modules/Elsa.AzureServiceBus/Activities/SendMessage.cs index 44510f3ed..8017690cf 100644 --- a/src/modules/Elsa.AzureServiceBus/Activities/SendMessage.cs +++ b/src/modules/Elsa.AzureServiceBus/Activities/SendMessage.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Azure.Messaging.ServiceBus; using Elsa.Common.Contracts; using Elsa.Common.Services; @@ -16,6 +17,11 @@ namespace Elsa.AzureServiceBus.Activities; [PublicAPI] public class SendMessage : CodeActivity { + /// + public SendMessage([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + /// /// The contents of the message to send. /// diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs index d30d0ecc6..47eedd3e6 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs @@ -1,5 +1,3 @@ -using Elsa.Common.Entities; - namespace Elsa.Dapper.Modules.Runtime.Records; /// diff --git a/src/modules/Elsa.Email/Activities/SendEmail.cs b/src/modules/Elsa.Email/Activities/SendEmail.cs index 8e5f05629..d1d58bcd5 100644 --- a/src/modules/Elsa.Email/Activities/SendEmail.cs +++ b/src/modules/Elsa.Email/Activities/SendEmail.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; -using System.Text.Json.Serialization; using Elsa.Email.Contracts; using Elsa.Email.Models; using Elsa.Email.Options; @@ -27,12 +26,6 @@ public class SendEmail : Activity { } - /// - [JsonConstructor] - public SendEmail() - { - } - /// /// The sender's email address. /// diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230806134602_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230806134602_Initial.cs index 41ceb8505..15e8beac0 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230806134602_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20230806134602_Initial.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230806134614_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230806134614_Initial.cs index 2a03d326c..30bb12bb9 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230806134614_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20230806134614_Initial.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230806134606_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230806134606_Initial.cs index 476741f5a..00b118950 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230806134606_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20230806134606_Initial.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs index 57d34468f..1423e0854 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowInboxStore.cs @@ -2,7 +2,6 @@ using System.Text.Json; using Elsa.EntityFrameworkCore.Common; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Runtime.Contracts; -using Elsa.Workflows.Runtime.Entities; using Elsa.Workflows.Runtime.Filters; using Elsa.Workflows.Runtime.Models; diff --git a/src/modules/Elsa.Http/Activities/FlowSendHttpRequest.cs b/src/modules/Elsa.Http/Activities/FlowSendHttpRequest.cs new file mode 100644 index 000000000..dd039f02e --- /dev/null +++ b/src/modules/Elsa.Http/Activities/FlowSendHttpRequest.cs @@ -0,0 +1,36 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; + +namespace Elsa.Http; + +/// +/// Send an HTTP request. +/// +[Activity("Elsa", "HTTP", "Send an HTTP request.", DisplayName = "HTTP Request (flow)", Kind = ActivityKind.Task)] +public class FlowSendHttpRequest : SendHttpRequestBase +{ + /// + public FlowSendHttpRequest([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + /// A list of expected status codes to handle. + /// + [Input(Description = "A list of expected status codes to handle.", UIHint = InputUIHints.MultiText)] + public Input> ExpectedStatusCodes { get; set; } = default!; + + /// + protected override async ValueTask HandleResponseAsync(ActivityExecutionContext context, HttpResponseMessage response) + { + var expectedStatusCodes = ExpectedStatusCodes.GetOrDefault(context) ?? new List(0); + var statusCode = (int)response.StatusCode; + var hasMatchingStatusCode = expectedStatusCodes.Contains(statusCode); + var outcome = expectedStatusCodes.Any() ? hasMatchingStatusCode ? statusCode.ToString() : "Unmatched status code" : "Done"; + + await context.CompleteActivityWithOutcomesAsync(outcome); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Http/Activities/HttpEndpoint.cs b/src/modules/Elsa.Http/Activities/HttpEndpoint.cs index 3bc9c973c..4261ac3f5 100644 --- a/src/modules/Elsa.Http/Activities/HttpEndpoint.cs +++ b/src/modules/Elsa.Http/Activities/HttpEndpoint.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Http.Contracts; @@ -23,8 +23,7 @@ public class HttpEndpoint : Trigger internal const string RequestPathInputKey = "RequestPath"; /// - [JsonConstructor] - public HttpEndpoint() + public HttpEndpoint([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } diff --git a/src/modules/Elsa.Http/Activities/SendHttpRequest.cs b/src/modules/Elsa.Http/Activities/SendHttpRequest.cs index 5e2a41699..7f9972003 100644 --- a/src/modules/Elsa.Http/Activities/SendHttpRequest.cs +++ b/src/modules/Elsa.Http/Activities/SendHttpRequest.cs @@ -1,41 +1,13 @@ -using System.Net.Http.Headers; -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; using Elsa.Extensions; -using Elsa.Http.ContentWriters; +using Elsa.Http.Models; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; using JetBrains.Annotations; -using HttpRequestHeaders = Elsa.Http.Models.HttpRequestHeaders; namespace Elsa.Http; -/// -/// Send an HTTP request. -/// -[Activity("Elsa", "HTTP", "Send an HTTP request.", DisplayName = "HTTP Request (flow)", Kind = ActivityKind.Task)] -[PublicAPI] -public class FlowSendHttpRequest : SendHttpRequestBase -{ - /// - /// A list of expected status codes to handle. - /// - [Input(Description = "A list of expected status codes to handle.", UIHint = InputUIHints.MultiText)] - public Input> ExpectedStatusCodes { get; set; } = default!; - - /// - protected override async ValueTask HandleResponseAsync(ActivityExecutionContext context, HttpResponseMessage response) - { - var expectedStatusCodes = ExpectedStatusCodes.GetOrDefault(context) ?? new List(0); - var statusCode = (int)response.StatusCode; - var hasMatchingStatusCode = expectedStatusCodes.Contains(statusCode); - var outcome = expectedStatusCodes.Any() ? hasMatchingStatusCode ? statusCode.ToString() : "Unmatched status code" : "Done"; - - await context.CompleteActivityWithOutcomesAsync(outcome); - } -} - /// /// Send an HTTP request. /// @@ -43,6 +15,11 @@ public class FlowSendHttpRequest : SendHttpRequestBase [PublicAPI] public class SendHttpRequest : SendHttpRequestBase { + /// + public SendHttpRequest([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + /// /// A list of expected status codes to handle and the corresponding activity to execute when the status code matches. /// @@ -73,182 +50,4 @@ public class SendHttpRequest : SendHttpRequestBase { await context.CompleteActivityAsync(); } -} - -/// -/// A binding between an HTTP status code and an activity. -/// -public class HttpStatusCodeCase -{ - /// - /// Creates a new instance of the class. - /// - [JsonConstructor] - public HttpStatusCodeCase() - { - } - - /// - /// Creates a new instance of the class. - /// - public HttpStatusCodeCase(int statusCode, IActivity activity) - { - StatusCode = statusCode; - Activity = activity; - } - - /// - /// The HTTP status code to match. - /// - public int StatusCode { get; set; } - - /// - /// The activity to execute when the HTTP status code matches. - /// - public IActivity? Activity { get; set; } -} - -/// -/// Base class for activities that send HTTP requests. -/// -public abstract class SendHttpRequestBase : Activity -{ - /// - /// The URL to send the request to. - /// - [Input] - public Input Url { get; set; } = default!; - - /// - /// The HTTP method to use when sending the request. - /// - [Input( - Description = "The HTTP method to use when sending the request.", - Options = new[] { "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD" }, - DefaultValue = "GET", - UIHint = InputUIHints.Dropdown - )] - public Input Method { get; set; } = new("GET"); - - /// - /// The content to send with the request. Can be a string, an object, a byte array or a stream. - /// - [Input(Description = "The content to send with the request. Can be a string, an object, a byte array or a stream.")] - public Input Content { get; set; } = default!; - - /// - /// The content type to use when sending the request. - /// - [Input( - Description = "The content type to use when sending the request.", - Options = new[] { "", "text/plain", "text/html", "application/json", "application/xml", "application/x-www-form-urlencoded" }, - UIHint = InputUIHints.Dropdown - )] - public Input ContentType { get; set; } = default!; - - /// - /// The Authorization header value to send with the request. - /// - /// Bearer {some-access-token} - [Input( - Description = "The Authorization header value to send with the request. For example: Bearer {some-access-token}", - Category = "Security" - )] - public Input Authorization { get; set; } = default!; - - /// - /// The headers to send along with the request. - /// - [Input(Description = "The headers to send along with the request.", Category = "Advanced")] - public Input RequestHeaders { get; set; } = new(new HttpRequestHeaders()); - - /// - /// The parsed content, if any. - /// - [Output(Description = "The parsed content, if any.")] - public Output ParsedContent { get; set; } = default!; - - /// - protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) - { - await TrySendAsync(context); - } - - /// - /// Handles the response. - /// - protected abstract ValueTask HandleResponseAsync(ActivityExecutionContext context, HttpResponseMessage response); - - private async Task TrySendAsync(ActivityExecutionContext context) - { - var request = PrepareRequest(context); - var httpClientFactory = context.GetRequiredService(); - var httpClient = httpClientFactory.CreateClient(nameof(SendHttpRequestBase)); - var cancellationToken = context.CancellationToken; - - try - { - var response = await httpClient.SendAsync(request, cancellationToken); - var parsedContent = await ParseContentAsync(context, response.Content); - context.Set(Result, response); - context.Set(ParsedContent, parsedContent); - - await HandleResponseAsync(context, response); - } - catch (TaskCanceledException e) - { - context.JournalData.Add("Cancelled", true); - } - } - - private async Task ParseContentAsync(ActivityExecutionContext context, HttpContent httpContent) - { - if (!HasContent(httpContent)) - return null; - - var cancellationToken = context.CancellationToken; - var targetType = ParsedContent.GetTargetType(context); - var contentStream = await httpContent.ReadAsStreamAsync(cancellationToken); - var contentType = httpContent.Headers.ContentType?.MediaType!; - - targetType ??= contentType switch - { - "application/json" => typeof(object), - _ => typeof(string) - }; - - return await context.ParseContentAsync(contentStream, contentType, targetType, cancellationToken); - } - - private static bool HasContent(HttpContent httpContent) => httpContent.Headers.ContentLength > 0; - - private HttpRequestMessage PrepareRequest(ActivityExecutionContext context) - { - var method = Method.GetOrDefault(context) ?? "GET"; - var url = Url.Get(context); - var request = new HttpRequestMessage(new HttpMethod(method), url); - var headers = context.GetHeaders(RequestHeaders); - var authorization = Authorization.GetOrDefault(context); - - if (!string.IsNullOrWhiteSpace(authorization)) - request.Headers.Authorization = AuthenticationHeaderValue.Parse(authorization); - - foreach (var header in headers) - request.Headers.Add(header.Key, header.Value.AsEnumerable()); - - var contentType = ContentType.GetOrDefault(context); - var content = Content.GetOrDefault(context); - - if (contentType != null && content != null) - { - var contentWriters = context.GetServices(); - var contentWriter = SelectContentWriter(contentType, contentWriters); - request.Content = contentWriter.CreateHttpContent(content, contentType); - } - - return request; - } - - private IHttpContentFactory SelectContentWriter(string? contentType, IEnumerable requestContentWriters) => - string.IsNullOrWhiteSpace(contentType) ? new JsonContentFactory() : requestContentWriters.First(w => w.SupportsContentType(contentType)); } \ No newline at end of file diff --git a/src/modules/Elsa.Http/Activities/SendHttpRequestBase.cs b/src/modules/Elsa.Http/Activities/SendHttpRequestBase.cs new file mode 100644 index 000000000..59c5a5df2 --- /dev/null +++ b/src/modules/Elsa.Http/Activities/SendHttpRequestBase.cs @@ -0,0 +1,159 @@ +using System.Net.Http.Headers; +using Elsa.Extensions; +using Elsa.Http.ContentWriters; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using HttpRequestHeaders = Elsa.Http.Models.HttpRequestHeaders; + +namespace Elsa.Http; + +/// +/// Base class for activities that send HTTP requests. +/// +public abstract class SendHttpRequestBase : Activity +{ + /// + protected SendHttpRequestBase(string? source = default, int? line = default) : base(source, line) + { + } + + /// + /// The URL to send the request to. + /// + [Input] + public Input Url { get; set; } = default!; + + /// + /// The HTTP method to use when sending the request. + /// + [Input( + Description = "The HTTP method to use when sending the request.", + Options = new[] { "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD" }, + DefaultValue = "GET", + UIHint = InputUIHints.Dropdown + )] + public Input Method { get; set; } = new("GET"); + + /// + /// The content to send with the request. Can be a string, an object, a byte array or a stream. + /// + [Input(Description = "The content to send with the request. Can be a string, an object, a byte array or a stream.")] + public Input Content { get; set; } = default!; + + /// + /// The content type to use when sending the request. + /// + [Input( + Description = "The content type to use when sending the request.", + Options = new[] { "", "text/plain", "text/html", "application/json", "application/xml", "application/x-www-form-urlencoded" }, + UIHint = InputUIHints.Dropdown + )] + public Input ContentType { get; set; } = default!; + + /// + /// The Authorization header value to send with the request. + /// + /// Bearer {some-access-token} + [Input( + Description = "The Authorization header value to send with the request. For example: Bearer {some-access-token}", + Category = "Security" + )] + public Input Authorization { get; set; } = default!; + + /// + /// The headers to send along with the request. + /// + [Input(Description = "The headers to send along with the request.", Category = "Advanced")] + public Input RequestHeaders { get; set; } = new(new HttpRequestHeaders()); + + /// + /// The parsed content, if any. + /// + [Output(Description = "The parsed content, if any.")] + public Output ParsedContent { get; set; } = default!; + + /// + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + { + await TrySendAsync(context); + } + + /// + /// Handles the response. + /// + protected abstract ValueTask HandleResponseAsync(ActivityExecutionContext context, HttpResponseMessage response); + + private async Task TrySendAsync(ActivityExecutionContext context) + { + var request = PrepareRequest(context); + var httpClientFactory = context.GetRequiredService(); + var httpClient = httpClientFactory.CreateClient(nameof(SendHttpRequestBase)); + var cancellationToken = context.CancellationToken; + + try + { + var response = await httpClient.SendAsync(request, cancellationToken); + var parsedContent = await ParseContentAsync(context, response.Content); + context.Set(Result, response); + context.Set(ParsedContent, parsedContent); + + await HandleResponseAsync(context, response); + } + catch (TaskCanceledException e) + { + context.JournalData.Add("Cancelled", true); + } + } + + private async Task ParseContentAsync(ActivityExecutionContext context, HttpContent httpContent) + { + if (!HasContent(httpContent)) + return null; + + var cancellationToken = context.CancellationToken; + var targetType = ParsedContent.GetTargetType(context); + var contentStream = await httpContent.ReadAsStreamAsync(cancellationToken); + var contentType = httpContent.Headers.ContentType?.MediaType!; + + targetType ??= contentType switch + { + "application/json" => typeof(object), + _ => typeof(string) + }; + + return await context.ParseContentAsync(contentStream, contentType, targetType, cancellationToken); + } + + private static bool HasContent(HttpContent httpContent) => httpContent.Headers.ContentLength > 0; + + private HttpRequestMessage PrepareRequest(ActivityExecutionContext context) + { + var method = Method.GetOrDefault(context) ?? "GET"; + var url = Url.Get(context); + var request = new HttpRequestMessage(new HttpMethod(method), url); + var headers = context.GetHeaders(RequestHeaders); + var authorization = Authorization.GetOrDefault(context); + + if (!string.IsNullOrWhiteSpace(authorization)) + request.Headers.Authorization = AuthenticationHeaderValue.Parse(authorization); + + foreach (var header in headers) + request.Headers.Add(header.Key, header.Value.AsEnumerable()); + + var contentType = ContentType.GetOrDefault(context); + var content = Content.GetOrDefault(context); + + if (contentType != null && content != null) + { + var contentWriters = context.GetServices(); + var contentWriter = SelectContentWriter(contentType, contentWriters); + request.Content = contentWriter.CreateHttpContent(content, contentType); + } + + return request; + } + + private IHttpContentFactory SelectContentWriter(string? contentType, IEnumerable requestContentWriters) => + string.IsNullOrWhiteSpace(contentType) ? new JsonContentFactory() : requestContentWriters.First(w => w.SupportsContentType(contentType)); +} \ No newline at end of file diff --git a/src/modules/Elsa.Http/Activities/WriteHttpResponse.cs b/src/modules/Elsa.Http/Activities/WriteHttpResponse.cs index c25922f5e..58ee79d93 100644 --- a/src/modules/Elsa.Http/Activities/WriteHttpResponse.cs +++ b/src/modules/Elsa.Http/Activities/WriteHttpResponse.cs @@ -1,4 +1,5 @@ using System.Net; +using System.Runtime.CompilerServices; using Elsa.Extensions; using Elsa.Http.ContentWriters; using Elsa.Http.Models; @@ -16,6 +17,11 @@ namespace Elsa.Http; [Activity("Elsa", "HTTP", "Write a response to the current HTTP response object.", DisplayName = "HTTP Response")] public class WriteHttpResponse : Activity { + /// + public WriteHttpResponse([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + /// /// The status code to return. /// diff --git a/src/modules/Elsa.Http/Models/HttpStatusCodeCase.cs b/src/modules/Elsa.Http/Models/HttpStatusCodeCase.cs new file mode 100644 index 000000000..22ce1acb0 --- /dev/null +++ b/src/modules/Elsa.Http/Models/HttpStatusCodeCase.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Serialization; +using Elsa.Workflows.Core.Contracts; + +namespace Elsa.Http.Models; + +/// +/// A binding between an HTTP status code and an activity. +/// +public class HttpStatusCodeCase +{ + /// + /// Creates a new instance of the class. + /// + [JsonConstructor] + public HttpStatusCodeCase() + { + } + + /// + /// Creates a new instance of the class. + /// + public HttpStatusCodeCase(int statusCode, IActivity activity) + { + StatusCode = statusCode; + Activity = activity; + } + + /// + /// The HTTP status code to match. + /// + public int StatusCode { get; set; } + + /// + /// The activity to execute when the HTTP status code matches. + /// + public IActivity? Activity { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.MassTransit/Activities/MessageReceived.cs b/src/modules/Elsa.MassTransit/Activities/MessageReceived.cs index 05de46990..1dbcfa74a 100644 --- a/src/modules/Elsa.MassTransit/Activities/MessageReceived.cs +++ b/src/modules/Elsa.MassTransit/Activities/MessageReceived.cs @@ -1,5 +1,5 @@ using System.ComponentModel; -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.MassTransit.Implementations; @@ -16,8 +16,7 @@ public class MessageReceived : Trigger internal const string InputKey = "Message"; /// - [JsonConstructor] - public MessageReceived() + public MessageReceived([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } diff --git a/src/modules/Elsa.MassTransit/Activities/PublishMessage.cs b/src/modules/Elsa.MassTransit/Activities/PublishMessage.cs index 6bae459ac..796125097 100644 --- a/src/modules/Elsa.MassTransit/Activities/PublishMessage.cs +++ b/src/modules/Elsa.MassTransit/Activities/PublishMessage.cs @@ -1,5 +1,5 @@ using System.ComponentModel; -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; using Elsa.Expressions.Helpers; using Elsa.Extensions; using Elsa.MassTransit.Implementations; @@ -17,8 +17,7 @@ namespace Elsa.MassTransit.Activities; public class PublishMessage : CodeActivity { /// - [JsonConstructor] - public PublishMessage() + public PublishMessage([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } diff --git a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs index 62df33798..d2a0349b5 100644 --- a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Bson.Serialization; -using MongoDB.Bson.Serialization.IdGenerators; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; using MongoDB.Driver.Core.Extensions.DiagnosticSources; diff --git a/src/modules/Elsa.Scheduling/Activities/Cron.cs b/src/modules/Elsa.Scheduling/Activities/Cron.cs index 41506f394..c01ad838a 100644 --- a/src/modules/Elsa.Scheduling/Activities/Cron.cs +++ b/src/modules/Elsa.Scheduling/Activities/Cron.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Scheduling.Bookmarks; using Elsa.Scheduling.Contracts; @@ -15,13 +14,6 @@ namespace Elsa.Scheduling.Activities; [Activity("Elsa", "Scheduling", "Trigger workflow execution at a specific interval using a CRON expression.")] public class Cron : EventGenerator { - /// - [JsonConstructor] - public Cron() - { - - } - /// public Cron([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Scheduling/Activities/Delay.cs b/src/modules/Elsa.Scheduling/Activities/Delay.cs index 7ef5df2a7..464833214 100644 --- a/src/modules/Elsa.Scheduling/Activities/Delay.cs +++ b/src/modules/Elsa.Scheduling/Activities/Delay.cs @@ -1,8 +1,5 @@ using System.Reflection; using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; using Elsa.Common.Contracts; using Elsa.Expressions.Models; using Elsa.Extensions; @@ -21,12 +18,6 @@ namespace Elsa.Scheduling.Activities; [Activity( "Elsa", "Scheduling", "Delay execution for the specified amount of time.")] public class Delay : Activity, IActivityPropertyDefaultValueProvider { - /// - [JsonConstructor] - internal Delay() - { - } - /// public Delay([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Scheduling/Activities/StartAt.cs b/src/modules/Elsa.Scheduling/Activities/StartAt.cs index d4c4854c6..202c2bf8d 100644 --- a/src/modules/Elsa.Scheduling/Activities/StartAt.cs +++ b/src/modules/Elsa.Scheduling/Activities/StartAt.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Common.Contracts; using Elsa.Expressions.Models; using Elsa.Extensions; @@ -20,12 +19,6 @@ public class StartAt : Trigger { private const string InputKey = "ExecuteAt"; - /// - [JsonConstructor] - public StartAt() - { - } - /// public StartAt([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Scheduling/Activities/Timer.cs b/src/modules/Elsa.Scheduling/Activities/Timer.cs index 9f4a12870..6aa1c380c 100644 --- a/src/modules/Elsa.Scheduling/Activities/Timer.cs +++ b/src/modules/Elsa.Scheduling/Activities/Timer.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Common.Contracts; using Elsa.Extensions; using Elsa.Scheduling.Bookmarks; @@ -15,12 +14,6 @@ namespace Elsa.Scheduling.Activities; [Activity("Elsa", "Scheduling", "Trigger workflow execution at a specific interval.")] public class Timer : EventGenerator { - /// - [JsonConstructor] - public Timer() - { - } - /// public Timer([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledCronTask.cs b/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledCronTask.cs index c7659099b..f9f2852e1 100644 --- a/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledCronTask.cs +++ b/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledCronTask.cs @@ -1,5 +1,4 @@ using Elsa.Common.Contracts; -using Elsa.Extensions; using Elsa.Mediator.Contracts; using Elsa.Scheduling.Commands; using Elsa.Scheduling.Contracts; diff --git a/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledRecurringTask.cs b/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledRecurringTask.cs index e9ba24e61..34ea5bf05 100644 --- a/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledRecurringTask.cs +++ b/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledRecurringTask.cs @@ -1,5 +1,4 @@ using Elsa.Common.Contracts; -using Elsa.Extensions; using Elsa.Mediator.Contracts; using Elsa.Scheduling.Commands; using Elsa.Scheduling.Contracts; diff --git a/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledSpecificInstantTask.cs b/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledSpecificInstantTask.cs index 3ce508a03..d1e8b8743 100644 --- a/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledSpecificInstantTask.cs +++ b/src/modules/Elsa.Scheduling/ScheduledTasks/ScheduledSpecificInstantTask.cs @@ -1,5 +1,4 @@ using Elsa.Common.Contracts; -using Elsa.Extensions; using Elsa.Mediator.Contracts; using Elsa.Scheduling.Commands; using Elsa.Scheduling.Contracts; diff --git a/src/modules/Elsa.Telnyx/Activities/AnswerCall.cs b/src/modules/Elsa.Telnyx/Activities/AnswerCall.cs index 4e247ce5a..e16850c52 100644 --- a/src/modules/Elsa.Telnyx/Activities/AnswerCall.cs +++ b/src/modules/Elsa.Telnyx/Activities/AnswerCall.cs @@ -1,57 +1,13 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; -using Elsa.Extensions; -using Elsa.Telnyx.Attributes; -using Elsa.Telnyx.Bookmarks; -using Elsa.Telnyx.Client.Models; -using Elsa.Telnyx.Client.Services; -using Elsa.Telnyx.Extensions; -using Elsa.Telnyx.Payloads.Call; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities.Flowchart.Attributes; -using Elsa.Workflows.Core.Activities.Flowchart.Models; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; -using Elsa.Workflows.Runtime.Contracts; -using JetBrains.Annotations; -using Refit; namespace Elsa.Telnyx.Activities; /// -[FlowNode("Connected", "Disconnected")] -[PublicAPI] -public class FlowAnswerCall : AnswerCallBase -{ - /// - [JsonConstructor] - public FlowAnswerCall() - { - } - - /// - public FlowAnswerCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - protected override async ValueTask HandleConnectedAsync(ActivityExecutionContext context) => await context.CompleteActivityAsync(new Outcomes("Connected")); - - /// - protected override async ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => await context.CompleteActivityAsync(new Outcomes("Disconnected")); -} - -/// -[PublicAPI] public class AnswerCall : AnswerCallBase { - /// - [JsonConstructor] - public AnswerCall() - { - } - /// public AnswerCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -74,80 +30,4 @@ public class AnswerCall : AnswerCallBase /// protected override async ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => await context.ScheduleActivityAsync(Disconnected); -} - -/// -/// Answer an incoming call. You must issue this command before executing subsequent commands on an incoming call. -/// -[Activity(Constants.Namespace, "Answer an incoming call. You must issue this command before executing subsequent commands on an incoming call.", Kind = ActivityKind.Task)] -[WebhookDriven(WebhookEventTypes.CallAnswered)] -[PublicAPI] -public abstract class AnswerCallBase : Activity, IBookmarksPersistedHandler -{ - /// - protected AnswerCallBase(string? source = default, int? line = default) : base(source, line) - { - } - - /// - /// The call control ID to answer. Leave blank when the workflow is driven by an incoming call and you wish to pick up that one. - /// - [Input(DisplayName = "Call Control ID", Description = "The call control ID of the call to answer.", Category = "Advanced")] - public Input? CallControlId { get; set; } - - /// - protected override void Execute(ActivityExecutionContext context) - { - // Create a bookmark first, then after it's persisted, we call out to Telnyx. - // This ensures that the bookmark is available in case Telnyx responds with the webhook before the runtime got a chance to persist bookmarks. - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - context.CreateBookmark(new WebhookEventBookmarkPayload(WebhookEventTypes.CallAnswered, callControlId), ResumeAsync); - } - - /// - /// Invokes Telnyx to answer the call. - /// - public async ValueTask BookmarksPersistedAsync(ActivityExecutionContext context) => await InvokeTelnyxAsync(context); - - /// - /// Invoked when the call was successfully answered. - /// - protected abstract ValueTask HandleConnectedAsync(ActivityExecutionContext context); - - /// - /// Invoked when the call was no longer active. - /// - protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); - - private async ValueTask ResumeAsync(ActivityExecutionContext context) - { - var payload = context.GetInput(); - context.Set(Result, payload); - await HandleConnectedAsync(context); - } - - /// - /// Invokes Telnyx' API to answer the call. - /// - private async ValueTask InvokeTelnyxAsync(ActivityExecutionContext context) - { - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - - var request = new AnswerCallRequest - { - ClientState = context.CreateCorrelatingClientState() - }; - - var telnyxClient = context.GetRequiredService(); - - try - { - await telnyxClient.Calls.AnswerCallAsync(callControlId, request, context.CancellationToken); - } - catch (ApiException e) - { - if (!await e.CallIsNoLongerActiveAsync()) throw; - await HandleDisconnectedAsync(context); - } - } } \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/AnswerCallBase.cs b/src/modules/Elsa.Telnyx/Activities/AnswerCallBase.cs new file mode 100644 index 000000000..40556b01f --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/AnswerCallBase.cs @@ -0,0 +1,89 @@ +using Elsa.Extensions; +using Elsa.Telnyx.Attributes; +using Elsa.Telnyx.Bookmarks; +using Elsa.Telnyx.Client.Models; +using Elsa.Telnyx.Client.Services; +using Elsa.Telnyx.Extensions; +using Elsa.Telnyx.Payloads.Call; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using Elsa.Workflows.Runtime.Contracts; +using Refit; + +namespace Elsa.Telnyx.Activities; + +/// +/// Answer an incoming call. You must issue this command before executing subsequent commands on an incoming call. +/// +[Activity(Constants.Namespace, "Answer an incoming call. You must issue this command before executing subsequent commands on an incoming call.", Kind = ActivityKind.Task)] +[WebhookDriven(WebhookEventTypes.CallAnswered)] +public abstract class AnswerCallBase : Activity, IBookmarksPersistedHandler +{ + /// + protected AnswerCallBase(string? source = default, int? line = default) : base(source, line) + { + } + + /// + /// The call control ID to answer. Leave blank when the workflow is driven by an incoming call and you wish to pick up that one. + /// + [Input(DisplayName = "Call Control ID", Description = "The call control ID of the call to answer.", Category = "Advanced")] + public Input? CallControlId { get; set; } + + /// + protected override void Execute(ActivityExecutionContext context) + { + // Create a bookmark first, then after it's persisted, we call out to Telnyx. + // This ensures that the bookmark is available in case Telnyx responds with the webhook before the runtime got a chance to persist bookmarks. + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + context.CreateBookmark(new WebhookEventBookmarkPayload(WebhookEventTypes.CallAnswered, callControlId), ResumeAsync); + } + + /// + /// Invokes Telnyx to answer the call. + /// + public async ValueTask BookmarksPersistedAsync(ActivityExecutionContext context) => await InvokeTelnyxAsync(context); + + /// + /// Invoked when the call was successfully answered. + /// + protected abstract ValueTask HandleConnectedAsync(ActivityExecutionContext context); + + /// + /// Invoked when the call was no longer active. + /// + protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); + + private async ValueTask ResumeAsync(ActivityExecutionContext context) + { + var payload = context.GetInput(); + context.Set(Result, payload); + await HandleConnectedAsync(context); + } + + /// + /// Invokes Telnyx' API to answer the call. + /// + private async ValueTask InvokeTelnyxAsync(ActivityExecutionContext context) + { + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + + var request = new AnswerCallRequest + { + ClientState = context.CreateCorrelatingClientState() + }; + + var telnyxClient = context.GetRequiredService(); + + try + { + await telnyxClient.Calls.AnswerCallAsync(callControlId, request, context.CancellationToken); + } + catch (ApiException e) + { + if (!await e.CallIsNoLongerActiveAsync()) throw; + await HandleDisconnectedAsync(context); + } + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/BridgeCalls.cs b/src/modules/Elsa.Telnyx/Activities/BridgeCalls.cs index 0d12bf358..e4999ec18 100644 --- a/src/modules/Elsa.Telnyx/Activities/BridgeCalls.cs +++ b/src/modules/Elsa.Telnyx/Activities/BridgeCalls.cs @@ -1,56 +1,15 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; -using Elsa.Extensions; -using Elsa.Telnyx.Attributes; -using Elsa.Telnyx.Bookmarks; -using Elsa.Telnyx.Client.Models; -using Elsa.Telnyx.Client.Services; -using Elsa.Telnyx.Extensions; -using Elsa.Telnyx.Payloads.Call; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; -using Elsa.Workflows.Runtime.Contracts; using JetBrains.Annotations; -using Refit; namespace Elsa.Telnyx.Activities; -/// -[FlowNode("Bridged", "Disconnected")] -[PublicAPI] -public class FlowBridgeCalls : BridgeCallsBase -{ - /// - [JsonConstructor] - public FlowBridgeCalls() - { - } - - /// - public FlowBridgeCalls([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityAsync("Disconnected"); - - /// - protected override ValueTask HandleBridgedAsync(ActivityExecutionContext context) => context.CompleteActivityAsync("Bridged"); -} - /// [PublicAPI] public class BridgeCalls : BridgeCallsBase { - /// - [JsonConstructor] - public BridgeCalls() - { - } - /// public BridgeCalls([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -73,85 +32,4 @@ public class BridgeCalls : BridgeCallsBase /// protected override async ValueTask HandleBridgedAsync(ActivityExecutionContext context) => await context.ScheduleActivityAsync(Bridged, OnCompleted); -} - -/// -/// Bridge two calls. -/// -[Activity(Constants.Namespace, "Bridge two calls.", Kind = ActivityKind.Task)] -[WebhookDriven(WebhookEventTypes.CallBridged)] -[PublicAPI] -public abstract class BridgeCallsBase : Activity, IBookmarksPersistedHandler -{ - /// - protected BridgeCallsBase(string? source = default, int? line = default) : base(source, line) - { - } - - /// - /// The source call control ID of one of the call to bridge with. Leave empty to use the ambient inbound call control Id, if there is one. - /// - [Input(DisplayName = "Call Control ID A", Description = "The source call control ID of one of the call to bridge with. Leave empty to use the ambient inbound call control Id, if there is one.")] - public Input? CallControlIdA { get; set; } - - /// - /// The destination call control ID of the call you want to bridge with. - /// - [Input(DisplayName = "Call Control ID B", Description = "The destination call control ID of the call you want to bridge with.")] - public Input? CallControlIdB { get; set; } - - /// - public async ValueTask BookmarksPersistedAsync(ActivityExecutionContext context) - { - var callControlIdA = context.GetPrimaryCallControlId(CallControlIdA) ?? throw new Exception("CallControlA is required"); - var callControlIdB = context.GetSecondaryCallControlId(CallControlIdB) ?? throw new Exception("CallControlB is required"); - var request = new BridgeCallsRequest(callControlIdB, ClientState: context.CreateCorrelatingClientState()); - var telnyxClient = context.GetRequiredService(); - - try - { - await telnyxClient.Calls.BridgeCallsAsync(callControlIdA, request, context.CancellationToken); - } - catch (ApiException e) - { - if (!await e.CallIsNoLongerActiveAsync()) throw; - - await HandleDisconnectedAsync(context); - } - } - - /// - protected override void Execute(ActivityExecutionContext context) - { - var callControlIdA = context.GetPrimaryCallControlId(CallControlIdA) ?? throw new Exception("CallControlA is required"); - var callControlIdB = context.GetSecondaryCallControlId(CallControlIdB) ?? throw new Exception("CallControlB is required"); - var bookmarkA = new WebhookEventBookmarkPayload(WebhookEventTypes.CallBridged, callControlIdA); - var bookmarkB = new WebhookEventBookmarkPayload(WebhookEventTypes.CallBridged, callControlIdB); - context.CreateBookmarks(new[] { bookmarkA, bookmarkB }, ResumeAsync); - } - - protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); - protected abstract ValueTask HandleBridgedAsync(ActivityExecutionContext context); - protected async ValueTask OnCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext) => await context.CompleteActivityAsync(); - - private async ValueTask ResumeAsync(ActivityExecutionContext context) - { - var payload = context.GetInput()!; - var callControlIdA = context.GetPrimaryCallControlId(CallControlIdA); - var callControlIdB = context.GetSecondaryCallControlId(CallControlIdB); - - if (payload.CallControlId == callControlIdA) context.SetProperty("CallBridgedPayloadA", payload); - if (payload.CallControlId == callControlIdB) context.SetProperty("CallBridgedPayloadB", payload); - - var callBridgedPayloadA = context.GetProperty("CallBridgedPayloadA"); - var callBridgedPayloadB = context.GetProperty("CallBridgedPayloadB"); - - if (callBridgedPayloadA != null && callBridgedPayloadB != null) - { - context.Set(Result, new BridgedCallsOutput(callBridgedPayloadA, callBridgedPayloadB)); - await HandleBridgedAsync(context); - } - } -} - -public record BridgedCallsOutput(CallBridgedPayload PayloadA, CallBridgedPayload PayloadB); \ No newline at end of file +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/BridgeCallsBase.cs b/src/modules/Elsa.Telnyx/Activities/BridgeCallsBase.cs new file mode 100644 index 000000000..9a4ba1063 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/BridgeCallsBase.cs @@ -0,0 +1,95 @@ +using Elsa.Extensions; +using Elsa.Telnyx.Attributes; +using Elsa.Telnyx.Bookmarks; +using Elsa.Telnyx.Client.Models; +using Elsa.Telnyx.Client.Services; +using Elsa.Telnyx.Extensions; +using Elsa.Telnyx.Models; +using Elsa.Telnyx.Payloads.Call; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using Elsa.Workflows.Runtime.Contracts; +using JetBrains.Annotations; +using Refit; + +namespace Elsa.Telnyx.Activities; + +/// +/// Bridge two calls. +/// +[Activity(Constants.Namespace, "Bridge two calls.", Kind = ActivityKind.Task)] +[WebhookDriven(WebhookEventTypes.CallBridged)] +[PublicAPI] +public abstract class BridgeCallsBase : Activity, IBookmarksPersistedHandler +{ + /// + protected BridgeCallsBase(string? source = default, int? line = default) : base(source, line) + { + } + + /// + /// The source call control ID of one of the call to bridge with. Leave empty to use the ambient inbound call control Id, if there is one. + /// + [Input(DisplayName = "Call Control ID A", Description = "The source call control ID of one of the call to bridge with. Leave empty to use the ambient inbound call control Id, if there is one.")] + public Input? CallControlIdA { get; set; } + + /// + /// The destination call control ID of the call you want to bridge with. + /// + [Input(DisplayName = "Call Control ID B", Description = "The destination call control ID of the call you want to bridge with.")] + public Input? CallControlIdB { get; set; } + + /// + public async ValueTask BookmarksPersistedAsync(ActivityExecutionContext context) + { + var callControlIdA = context.GetPrimaryCallControlId(CallControlIdA) ?? throw new Exception("CallControlA is required"); + var callControlIdB = context.GetSecondaryCallControlId(CallControlIdB) ?? throw new Exception("CallControlB is required"); + var request = new BridgeCallsRequest(callControlIdB, ClientState: context.CreateCorrelatingClientState()); + var telnyxClient = context.GetRequiredService(); + + try + { + await telnyxClient.Calls.BridgeCallsAsync(callControlIdA, request, context.CancellationToken); + } + catch (ApiException e) + { + if (!await e.CallIsNoLongerActiveAsync()) throw; + + await HandleDisconnectedAsync(context); + } + } + + /// + protected override void Execute(ActivityExecutionContext context) + { + var callControlIdA = context.GetPrimaryCallControlId(CallControlIdA) ?? throw new Exception("CallControlA is required"); + var callControlIdB = context.GetSecondaryCallControlId(CallControlIdB) ?? throw new Exception("CallControlB is required"); + var bookmarkA = new WebhookEventBookmarkPayload(WebhookEventTypes.CallBridged, callControlIdA); + var bookmarkB = new WebhookEventBookmarkPayload(WebhookEventTypes.CallBridged, callControlIdB); + context.CreateBookmarks(new[] { bookmarkA, bookmarkB }, ResumeAsync); + } + + protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); + protected abstract ValueTask HandleBridgedAsync(ActivityExecutionContext context); + protected async ValueTask OnCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext) => await context.CompleteActivityAsync(); + + private async ValueTask ResumeAsync(ActivityExecutionContext context) + { + var payload = context.GetInput()!; + var callControlIdA = context.GetPrimaryCallControlId(CallControlIdA); + var callControlIdB = context.GetSecondaryCallControlId(CallControlIdB); + + if (payload.CallControlId == callControlIdA) context.SetProperty("CallBridgedPayloadA", payload); + if (payload.CallControlId == callControlIdB) context.SetProperty("CallBridgedPayloadB", payload); + + var callBridgedPayloadA = context.GetProperty("CallBridgedPayloadA"); + var callBridgedPayloadB = context.GetProperty("CallBridgedPayloadB"); + + if (callBridgedPayloadA != null && callBridgedPayloadB != null) + { + context.Set(Result, new BridgedCallsOutput(callBridgedPayloadA, callBridgedPayloadB)); + await HandleBridgedAsync(context); + } + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/Dial.cs b/src/modules/Elsa.Telnyx/Activities/Dial.cs index 2d2e8fef9..c2ba152d1 100644 --- a/src/modules/Elsa.Telnyx/Activities/Dial.cs +++ b/src/modules/Elsa.Telnyx/Activities/Dial.cs @@ -1,4 +1,3 @@ -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Client.Models; using Elsa.Telnyx.Client.Services; @@ -8,7 +7,6 @@ using Elsa.Telnyx.Options; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; using Microsoft.Extensions.Options; namespace Elsa.Telnyx.Activities; @@ -17,15 +15,8 @@ namespace Elsa.Telnyx.Activities; /// Dial a number or SIP URI. /// [Activity(Constants.Namespace, "Dial a number or SIP URI.", Kind = ActivityKind.Task)] -[PublicAPI] public class Dial : CodeActivity { - /// - [JsonConstructor] - public Dial() - { - } - /// public Dial(string? source = default, int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/DialAndWait.cs b/src/modules/Elsa.Telnyx/Activities/DialAndWait.cs index 8babcb98f..c6b7311e5 100644 --- a/src/modules/Elsa.Telnyx/Activities/DialAndWait.cs +++ b/src/modules/Elsa.Telnyx/Activities/DialAndWait.cs @@ -1,5 +1,4 @@ -using System.Text.Json.Serialization; -using Elsa.Extensions; +using Elsa.Extensions; using Elsa.Telnyx.Attributes; using Elsa.Telnyx.Bookmarks; using Elsa.Telnyx.Client.Models; @@ -12,7 +11,6 @@ using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; using Microsoft.Extensions.Options; namespace Elsa.Telnyx.Activities; @@ -23,15 +21,8 @@ namespace Elsa.Telnyx.Activities; [Activity(Constants.Namespace, "Dial a number or SIP URI and wait for an event.", Kind = ActivityKind.Task)] [FlowNode("Answered", "Hangup")] [WebhookDriven(WebhookEventTypes.CallAnswered, WebhookEventTypes.CallHangup)] -[PublicAPI] public class DialAndWait : Activity { - /// - [JsonConstructor] - public DialAndWait() - { - } - /// public DialAndWait(string? source = default, int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/FlowAnswerCall.cs b/src/modules/Elsa.Telnyx/Activities/FlowAnswerCall.cs new file mode 100644 index 000000000..6a5ab0f0e --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/FlowAnswerCall.cs @@ -0,0 +1,23 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; +using Elsa.Workflows.Core.Activities.Flowchart.Models; + +namespace Elsa.Telnyx.Activities; + +/// +[FlowNode("Connected", "Disconnected")] +public class FlowAnswerCall : AnswerCallBase +{ + /// + public FlowAnswerCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + protected override async ValueTask HandleConnectedAsync(ActivityExecutionContext context) => await context.CompleteActivityAsync(new Outcomes("Connected")); + + /// + protected override async ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => await context.CompleteActivityAsync(new Outcomes("Disconnected")); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/FlowBridgeCalls.cs b/src/modules/Elsa.Telnyx/Activities/FlowBridgeCalls.cs new file mode 100644 index 000000000..12311e591 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/FlowBridgeCalls.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; + +namespace Elsa.Telnyx.Activities; + +/// +[FlowNode("Bridged", "Disconnected")] +public class FlowBridgeCalls : BridgeCallsBase +{ + /// + public FlowBridgeCalls([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityAsync("Disconnected"); + + /// + protected override ValueTask HandleBridgedAsync(ActivityExecutionContext context) => context.CompleteActivityAsync("Bridged"); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/FlowHangupCall.cs b/src/modules/Elsa.Telnyx/Activities/FlowHangupCall.cs new file mode 100644 index 000000000..5cdfe8711 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/FlowHangupCall.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; + +namespace Elsa.Telnyx.Activities; + +/// +[FlowNode("Done", "Disconnected")] +public class FlowHangupCall : HangupCallBase +{ + /// + public FlowHangupCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + protected override ValueTask HandleDoneAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Done"); + + /// + protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/FlowPlayAudio.cs b/src/modules/Elsa.Telnyx/Activities/FlowPlayAudio.cs new file mode 100644 index 000000000..6b5f608ea --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/FlowPlayAudio.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; + +namespace Elsa.Telnyx.Activities; + +/// +[FlowNode("Playback started", "Disconnected")] +public class FlowPlayAudio : PlayAudioBase +{ + /// + public FlowPlayAudio([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + protected override ValueTask HandlePlaybackStartedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Playback started"); + + /// + protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/FlowSpeakText.cs b/src/modules/Elsa.Telnyx/Activities/FlowSpeakText.cs new file mode 100644 index 000000000..5280941e8 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/FlowSpeakText.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; + +namespace Elsa.Telnyx.Activities; + +/// +[FlowNode("Done", "Finished speaking", "Disconnected")] +public class FlowSpeakText : SpeakTextBase +{ + /// + public FlowSpeakText([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + protected override async ValueTask HandleDisconnected(ActivityExecutionContext context) => await context.CompleteActivityWithOutcomesAsync("Disconnected", "Done"); + + /// + protected override async ValueTask HandleDone(ActivityExecutionContext context) => await context.CompleteActivityWithOutcomesAsync("Finished speaking", "Done"); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/FlowStartRecording.cs b/src/modules/Elsa.Telnyx/Activities/FlowStartRecording.cs new file mode 100644 index 000000000..b3f60a627 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/FlowStartRecording.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; + +namespace Elsa.Telnyx.Activities; + +/// +[FlowNode("Recording finished", "Disconnected")] +public class FlowStartRecording : StartRecordingBase +{ + /// + public FlowStartRecording([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); + + /// + protected override ValueTask HandleCallRecordingSavedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Recording finished"); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/FlowStopAudioPlayback.cs b/src/modules/Elsa.Telnyx/Activities/FlowStopAudioPlayback.cs new file mode 100644 index 000000000..e692766e2 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/FlowStopAudioPlayback.cs @@ -0,0 +1,22 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; + +namespace Elsa.Telnyx.Activities; + +/// +[FlowNode("Done", "Disconnected")] +public class FlowStopAudioPlayback : StopAudioPlaybackBase +{ + /// + public FlowStopAudioPlayback([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + protected override ValueTask HandleDoneAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Done"); + + /// + protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/GatherUsingAudio.cs b/src/modules/Elsa.Telnyx/Activities/GatherUsingAudio.cs index 9f4fb48e3..3d0d07d1e 100644 --- a/src/modules/Elsa.Telnyx/Activities/GatherUsingAudio.cs +++ b/src/modules/Elsa.Telnyx/Activities/GatherUsingAudio.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Attributes; using Elsa.Telnyx.Bookmarks; @@ -12,7 +11,6 @@ using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; using Elsa.Workflows.Runtime.Contracts; -using JetBrains.Annotations; using Refit; namespace Elsa.Telnyx.Activities; @@ -23,15 +21,8 @@ namespace Elsa.Telnyx.Activities; [Activity(Constants.Namespace, "Play an audio file on the call until the required DTMF signals are gathered to build interactive menus.", Kind = ActivityKind.Task)] [FlowNode("Valid input", "Invalid input", "Disconnected")] [WebhookDriven(WebhookEventTypes.CallGatherEnded)] -[PublicAPI] public class GatherUsingAudio : Activity, IBookmarksPersistedHandler { - /// - [JsonConstructor] - public GatherUsingAudio() - { - } - /// public GatherUsingAudio([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs b/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs index d8b6c89c7..44063e6f1 100644 --- a/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs +++ b/src/modules/Elsa.Telnyx/Activities/GatherUsingSpeak.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Attributes; using Elsa.Telnyx.Bookmarks; @@ -12,7 +11,6 @@ using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; using Elsa.Workflows.Runtime.Contracts; -using JetBrains.Annotations; using Refit; namespace Elsa.Telnyx.Activities; @@ -23,15 +21,8 @@ namespace Elsa.Telnyx.Activities; [Activity(Constants.Namespace, "Convert text to speech and play it on the call until the required DTMF signals are gathered to build interactive menus.", Kind = ActivityKind.Task)] [FlowNode("Valid input", "Invalid input", "Disconnected")] [WebhookDriven(WebhookEventTypes.CallGatherEnded)] -[PublicAPI] public class GatherUsingSpeak : Activity, IBookmarksPersistedHandler { - /// - [JsonConstructor] - public GatherUsingSpeak() - { - } - /// public GatherUsingSpeak([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/GetCallStatus.cs b/src/modules/Elsa.Telnyx/Activities/GetCallStatus.cs index 7f783bfe7..e047508d1 100644 --- a/src/modules/Elsa.Telnyx/Activities/GetCallStatus.cs +++ b/src/modules/Elsa.Telnyx/Activities/GetCallStatus.cs @@ -1,27 +1,18 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Client.Services; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; namespace Elsa.Telnyx.Activities; /// [FlowNode("Alive", "Dead", "Done")] [Activity(Constants.Namespace, "Get the status of a call.", Kind = ActivityKind.Task)] -[PublicAPI] public class GetCallStatus : Activity { - /// - [JsonConstructor] - public GetCallStatus() - { - } - /// public GetCallStatus([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/HangupCall.cs b/src/modules/Elsa.Telnyx/Activities/HangupCall.cs index 40d1a0d39..9789cbaa4 100644 --- a/src/modules/Elsa.Telnyx/Activities/HangupCall.cs +++ b/src/modules/Elsa.Telnyx/Activities/HangupCall.cs @@ -1,52 +1,16 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; -using Elsa.Telnyx.Client.Models; -using Elsa.Telnyx.Client.Services; -using Elsa.Telnyx.Extensions; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; using JetBrains.Annotations; -using Refit; namespace Elsa.Telnyx.Activities; -/// -[FlowNode("Done", "Disconnected")] -[PublicAPI] -public class FlowHangupCall : HangupCallBase -{ - /// - [JsonConstructor] - public FlowHangupCall() - { - } - - /// - public FlowHangupCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - protected override ValueTask HandleDoneAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Done"); - - /// - protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); -} - /// [PublicAPI] public class HangupCall : HangupCallBase { - /// - [JsonConstructor] - public HangupCall() - { - } - /// public HangupCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -67,52 +31,4 @@ public class HangupCall : HangupCallBase /// Executed when any child activity completed. /// private async ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => await context.CompleteActivityAsync(); -} - -/// -/// Hang up the call. -/// -[Activity(Constants.Namespace, "Hang up the call.", Kind = ActivityKind.Task)] -[PublicAPI] -public abstract class HangupCallBase : Activity -{ - /// - protected HangupCallBase([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - /// Unique identifier and token for controlling the call. - /// - [Input(DisplayName = "Call Control ID", Description = "Unique identifier and token for controlling the call.", Category = "Advanced")] - public Input? CallControlId { get; set; } = default!; - - /// - protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) - { - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - var request = new HangupCallRequest(ClientState: context.CreateCorrelatingClientState()); - var telnyxClient = context.GetRequiredService(); - - try - { - await telnyxClient.Calls.HangupCallAsync(callControlId, request, context.CancellationToken); - await HandleDoneAsync(context); - } - catch (ApiException e) - { - if (!await e.CallIsNoLongerActiveAsync()) throw; - await HandleDisconnectedAsync(context); - } - } - - /// - /// Executed when the call was hangup. - /// - protected abstract ValueTask HandleDoneAsync(ActivityExecutionContext context); - - /// - /// Executed when the call was no longer active. - /// - protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); } \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/HangupCallBase.cs b/src/modules/Elsa.Telnyx/Activities/HangupCallBase.cs new file mode 100644 index 000000000..544cd32f3 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/HangupCallBase.cs @@ -0,0 +1,56 @@ +using Elsa.Telnyx.Client.Models; +using Elsa.Telnyx.Client.Services; +using Elsa.Telnyx.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using Refit; + +namespace Elsa.Telnyx.Activities; + +/// +/// Hang up the call. +/// +[Activity(Constants.Namespace, "Hang up the call.", Kind = ActivityKind.Task)] +public abstract class HangupCallBase : Activity +{ + /// + protected HangupCallBase(string? source = default, int? line = default) : base(source, line) + { + } + + /// + /// Unique identifier and token for controlling the call. + /// + [Input(DisplayName = "Call Control ID", Description = "Unique identifier and token for controlling the call.", Category = "Advanced")] + public Input? CallControlId { get; set; } = default!; + + /// + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + { + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + var request = new HangupCallRequest(ClientState: context.CreateCorrelatingClientState()); + var telnyxClient = context.GetRequiredService(); + + try + { + await telnyxClient.Calls.HangupCallAsync(callControlId, request, context.CancellationToken); + await HandleDoneAsync(context); + } + catch (ApiException e) + { + if (!await e.CallIsNoLongerActiveAsync()) throw; + await HandleDisconnectedAsync(context); + } + } + + /// + /// Executed when the call was hangup. + /// + protected abstract ValueTask HandleDoneAsync(ActivityExecutionContext context); + + /// + /// Executed when the call was no longer active. + /// + protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/IncomingCall.cs b/src/modules/Elsa.Telnyx/Activities/IncomingCall.cs index f6b0f5c01..2d3a4bd62 100644 --- a/src/modules/Elsa.Telnyx/Activities/IncomingCall.cs +++ b/src/modules/Elsa.Telnyx/Activities/IncomingCall.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Telnyx.Bookmarks; @@ -10,7 +9,6 @@ using Elsa.Telnyx.Payloads.Call; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; namespace Elsa.Telnyx.Activities; @@ -22,15 +20,8 @@ namespace Elsa.Telnyx.Activities; "Telnyx", "Triggered when an inbound phone call is received for any of the specified source or destination phone numbers.", Kind = ActivityKind.Trigger)] -[PublicAPI] public class IncomingCall : Trigger { - /// - [JsonConstructor] - public IncomingCall() - { - } - /// public IncomingCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/LookupNumber.cs b/src/modules/Elsa.Telnyx/Activities/LookupNumber.cs index dccdc7145..482fca54c 100644 --- a/src/modules/Elsa.Telnyx/Activities/LookupNumber.cs +++ b/src/modules/Elsa.Telnyx/Activities/LookupNumber.cs @@ -1,12 +1,10 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Client.Models; using Elsa.Telnyx.Client.Services; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; namespace Elsa.Telnyx.Activities; @@ -14,15 +12,8 @@ namespace Elsa.Telnyx.Activities; /// Returns information about the provided phone number. /// [Activity(Constants.Namespace, "Returns information about the provided phone number.", Kind = ActivityKind.Task)] -[PublicAPI] public class LookupNumber : CodeActivity { - /// - [JsonConstructor] - public LookupNumber() - { - } - /// public LookupNumber([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/PlayAudio.cs b/src/modules/Elsa.Telnyx/Activities/PlayAudio.cs index 327a609bb..24a55ff24 100644 --- a/src/modules/Elsa.Telnyx/Activities/PlayAudio.cs +++ b/src/modules/Elsa.Telnyx/Activities/PlayAudio.cs @@ -1,55 +1,16 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; -using Elsa.Telnyx.Attributes; -using Elsa.Telnyx.Bookmarks; -using Elsa.Telnyx.Client.Models; -using Elsa.Telnyx.Client.Services; -using Elsa.Telnyx.Extensions; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; -using Elsa.Workflows.Runtime.Contracts; using JetBrains.Annotations; -using Refit; namespace Elsa.Telnyx.Activities; -/// -[FlowNode("Playback started", "Disconnected")] -[PublicAPI] -public class FlowPlayAudio : PlayAudioBase -{ - /// - [JsonConstructor] - public FlowPlayAudio() - { - } - - /// - public FlowPlayAudio([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - protected override ValueTask HandlePlaybackStartedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Playback started"); - - /// - protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); -} - /// [PublicAPI] public class PlayAudio : PlayAudioBase { - /// - [JsonConstructor] - public PlayAudio() - { - } - /// public PlayAudio([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -68,118 +29,4 @@ public class PlayAudio : PlayAudioBase protected override async ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => await context.ScheduleActivityAsync(Disconnected, OnCompletedAsync); private async ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => await context.CompleteActivityAsync(); -} - -/// -/// Play an audio file on the call. -/// -[Activity(Constants.Namespace, "Play an audio file on the call.", Kind = ActivityKind.Task)] -[FlowNode("Playback started", "Disconnected")] -[WebhookDriven(WebhookEventTypes.CallPlaybackStarted)] -[PublicAPI] -public abstract class PlayAudioBase : Activity, IBookmarksPersistedHandler -{ - /// - protected PlayAudioBase([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - /// Unique identifier and token for controlling the call. - /// - [Input( - DisplayName = "Call Control ID", - Description = "Unique identifier and token for controlling the call.", - Category = "Advanced" - )] - public Input? CallControlId { get; set; } = default!; - - /// - /// The URL of a file to be played back at the beginning of each prompt. The URL can point to either a WAV or MP3 file. - /// - [Input( - DisplayName = "Audio URL", - Description = "The URL of a file to be played back at the beginning of each prompt. The URL can point to either a WAV or MP3 file." - )] - public Input AudioUrl { get; set; } = default!; - - /// - /// The number of times the audio file should be played. If supplied, the value must be an integer between 1 and 100, or the special string 'infinity' for an endless loop. - /// - [Input( - Description = "The number of times the audio file should be played. If supplied, the value must be an integer between 1 and 100, or the special string 'infinity' for an endless loop.", - DefaultValue = "1", - Category = "Advanced" - )] - public Input Loop { get; set; } = new("1"); - - /// - /// When enabled, audio will be mixed on top of any other audio that is actively being played back. Note that `overlay: true` will only work if there is another audio file already being played on the call. - /// - [Input( - Description = "When enabled, audio will be mixed on top of any other audio that is actively being played back. Note that `overlay: true` will only work if there is another audio file already being played on the call.", - DefaultValue = false, - Category = "Advanced" - )] - public Input Overlay { get; set; } = new(false); - - /// - /// Specifies the leg or legs on which audio will be played. If supplied, the value must be either 'self', 'opposite' or 'both'. - /// - [Input( - Description = "Specifies the leg or legs on which audio will be played. If supplied, the value must be either 'self', 'opposite' or 'both'.", - UIHint = InputUIHints.Dropdown, - Options = new[] { "", "self", "opposite", "both" }, - Category = "Advanced" - )] - public Input? TargetLegs { get; set; } - - /// - /// Calls out to Telnyx to start playing an audio file. - /// - public async ValueTask BookmarksPersistedAsync(ActivityExecutionContext context) - { - var loop = Loop.Get(context); - - var request = new PlayAudioRequest( - AudioUrl.Get(context) ?? throw new Exception("AudioUrl is required."), - Overlay.Get(context), - string.IsNullOrWhiteSpace(loop) ? null : loop == "infinity" ? "infinity" : int.Parse(loop), - TargetLegs.Get(context).EmptyToNull(), - ClientState: context.CreateCorrelatingClientState() - ); - - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - var telnyxClient = context.GetRequiredService(); - - try - { - await telnyxClient.Calls.PlayAudioAsync(callControlId, request, context.CancellationToken); - } - catch (ApiException e) - { - if (!await e.CallIsNoLongerActiveAsync()) throw; - await HandleDisconnectedAsync(context); - } - } - - /// - protected override void Execute(ActivityExecutionContext context) - { - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - context.CreateBookmark(new WebhookEventBookmarkPayload(WebhookEventTypes.CallPlaybackStarted, callControlId), ResumeAsync); - } - - /// - /// Called when playback has started. - /// - protected abstract ValueTask HandlePlaybackStartedAsync(ActivityExecutionContext context); - - - /// - /// Called when the call was no longer active. - /// - protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); - - private async ValueTask ResumeAsync(ActivityExecutionContext context) => await HandlePlaybackStartedAsync(context); } \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/PlayAudioBase.cs b/src/modules/Elsa.Telnyx/Activities/PlayAudioBase.cs new file mode 100644 index 000000000..56e49f5da --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/PlayAudioBase.cs @@ -0,0 +1,128 @@ +using System.Runtime.CompilerServices; +using Elsa.Extensions; +using Elsa.Telnyx.Attributes; +using Elsa.Telnyx.Bookmarks; +using Elsa.Telnyx.Client.Models; +using Elsa.Telnyx.Client.Services; +using Elsa.Telnyx.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities.Flowchart.Attributes; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using Elsa.Workflows.Runtime.Contracts; +using Refit; + +namespace Elsa.Telnyx.Activities; + +/// +/// Play an audio file on the call. +/// +[Activity(Constants.Namespace, "Play an audio file on the call.", Kind = ActivityKind.Task)] +[FlowNode("Playback started", "Disconnected")] +[WebhookDriven(WebhookEventTypes.CallPlaybackStarted)] +public abstract class PlayAudioBase : Activity, IBookmarksPersistedHandler +{ + /// + protected PlayAudioBase([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + + /// + /// Unique identifier and token for controlling the call. + /// + [Input( + DisplayName = "Call Control ID", + Description = "Unique identifier and token for controlling the call.", + Category = "Advanced" + )] + public Input? CallControlId { get; set; } = default!; + + /// + /// The URL of a file to be played back at the beginning of each prompt. The URL can point to either a WAV or MP3 file. + /// + [Input( + DisplayName = "Audio URL", + Description = "The URL of a file to be played back at the beginning of each prompt. The URL can point to either a WAV or MP3 file." + )] + public Input AudioUrl { get; set; } = default!; + + /// + /// The number of times the audio file should be played. If supplied, the value must be an integer between 1 and 100, or the special string 'infinity' for an endless loop. + /// + [Input( + Description = "The number of times the audio file should be played. If supplied, the value must be an integer between 1 and 100, or the special string 'infinity' for an endless loop.", + DefaultValue = "1", + Category = "Advanced" + )] + public Input Loop { get; set; } = new("1"); + + /// + /// When enabled, audio will be mixed on top of any other audio that is actively being played back. Note that `overlay: true` will only work if there is another audio file already being played on the call. + /// + [Input( + Description = "When enabled, audio will be mixed on top of any other audio that is actively being played back. Note that `overlay: true` will only work if there is another audio file already being played on the call.", + DefaultValue = false, + Category = "Advanced" + )] + public Input Overlay { get; set; } = new(false); + + /// + /// Specifies the leg or legs on which audio will be played. If supplied, the value must be either 'self', 'opposite' or 'both'. + /// + [Input( + Description = "Specifies the leg or legs on which audio will be played. If supplied, the value must be either 'self', 'opposite' or 'both'.", + UIHint = InputUIHints.Dropdown, + Options = new[] { "", "self", "opposite", "both" }, + Category = "Advanced" + )] + public Input? TargetLegs { get; set; } + + /// + /// Calls out to Telnyx to start playing an audio file. + /// + public async ValueTask BookmarksPersistedAsync(ActivityExecutionContext context) + { + var loop = Loop.Get(context); + + var request = new PlayAudioRequest( + AudioUrl.Get(context) ?? throw new Exception("AudioUrl is required."), + Overlay.Get(context), + string.IsNullOrWhiteSpace(loop) ? null : loop == "infinity" ? "infinity" : int.Parse(loop), + TargetLegs.Get(context).EmptyToNull(), + ClientState: context.CreateCorrelatingClientState() + ); + + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + var telnyxClient = context.GetRequiredService(); + + try + { + await telnyxClient.Calls.PlayAudioAsync(callControlId, request, context.CancellationToken); + } + catch (ApiException e) + { + if (!await e.CallIsNoLongerActiveAsync()) throw; + await HandleDisconnectedAsync(context); + } + } + + /// + protected override void Execute(ActivityExecutionContext context) + { + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + context.CreateBookmark(new WebhookEventBookmarkPayload(WebhookEventTypes.CallPlaybackStarted, callControlId), ResumeAsync); + } + + /// + /// Called when playback has started. + /// + protected abstract ValueTask HandlePlaybackStartedAsync(ActivityExecutionContext context); + + + /// + /// Called when the call was no longer active. + /// + protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); + + private async ValueTask ResumeAsync(ActivityExecutionContext context) => await HandlePlaybackStartedAsync(context); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/SpeakText.cs b/src/modules/Elsa.Telnyx/Activities/SpeakText.cs index 65f478299..f12a99031 100644 --- a/src/modules/Elsa.Telnyx/Activities/SpeakText.cs +++ b/src/modules/Elsa.Telnyx/Activities/SpeakText.cs @@ -1,166 +1,15 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; -using Elsa.Extensions; -using Elsa.Telnyx.Client.Models; -using Elsa.Telnyx.Client.Services; -using Elsa.Telnyx.Extensions; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; using JetBrains.Annotations; -using Refit; namespace Elsa.Telnyx.Activities; -/// -/// Convert text to speech and play it back on the call. -/// -[Activity(Constants.Namespace, "Convert text to speech and play it back on the call.", Kind = ActivityKind.Task)] -[PublicAPI] -public abstract class SpeakTextBase : Activity -{ - /// - protected SpeakTextBase([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - /// Unique identifier and token for controlling the call. - /// - [Input( - DisplayName = "Call Control ID", - Description = "Unique identifier and token for controlling the call.", - Category = "Advanced" - )] - public Input CallControlId { get; set; } = default!; - - /// - /// The language you want spoken. - /// - [Input( - Description = "The language you want spoken.", - UIHint = InputUIHints.Dropdown, - Options = new[] { "en-US", "en-AU", "nl-NL", "es-ES", "ru-RU" }, - DefaultValue = "en-US" - )] - public Input Language { get; set; } = new("en-US"); - - /// - /// The gender of the voice used to speak back the text. - /// - [Input( - Description = "The gender of the voice used to speak back the text.", - UIHint = InputUIHints.Dropdown, - Options = new[] { "female", "male" }, - DefaultValue = "female" - )] - public Input Voice { get; set; } = new("female"); - - /// - /// The text or SSML to be converted into speech. There is a 5,000 character limit. - /// - [Input( - Description = "The text or SSML to be converted into speech. There is a 5,000 character limit.", - UIHint = InputUIHints.MultiLine - )] - public Input Payload { get; set; } = default!; - - /// - /// The type of the provided payload. The payload can either be plain text, or Speech Synthesis Markup Language (SSML). - /// - [Input( - Description = "The type of the provided payload. The payload can either be plain text, or Speech Synthesis Markup Language (SSML).", - UIHint = InputUIHints.Dropdown, - Options = new[] { "", "text", "ssml" } - )] - public Input? PayloadType { get; set; } - - /// - /// This parameter impacts speech quality, language options and payload types. When using `basic`, only the `en-US` language and payload type `text` are allowed. - /// - [Input( - Description = "This parameter impacts speech quality, language options and payload types. When using `basic`, only the `en-US` language and payload type `text` are allowed.", - UIHint = InputUIHints.Dropdown, - Options = new[] { "", "basic", "premium" }, - Category = "Advanced" - )] - public Input ServiceLevel { get; set; } = default!; - - /// - protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) - { - var request = new SpeakTextRequest( - Language.Get(context), - Voice.Get(context), - Payload.Get(context), - PayloadType.GetOrDefault(context).EmptyToNull(), - ServiceLevel.GetOrDefault(context).EmptyToNull(), - ClientState: context.CreateCorrelatingClientState() - ); - - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - var telnyxClient = context.GetRequiredService(); - - try - { - await telnyxClient.Calls.SpeakTextAsync(callControlId, request, context.CancellationToken); - await HandleDone(context); - } - catch (ApiException e) - { - if (!await e.CallIsNoLongerActiveAsync()) throw; - await HandleDisconnected(context); - } - } - - /// - /// Called when the call was no longer active. - /// - protected abstract ValueTask HandleDisconnected(ActivityExecutionContext context); - - /// - /// Called when speaking has finished. - /// - protected abstract ValueTask HandleDone(ActivityExecutionContext context); - - private async ValueTask ResumeAsync(ActivityExecutionContext context) => await HandleDone(context); -} - -/// -[FlowNode("Done", "Finished speaking", "Disconnected")] -[PublicAPI] -public class FlowSpeakText : SpeakTextBase -{ - /// - [JsonConstructor] - public FlowSpeakText() - { - } - - /// - public FlowSpeakText([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - protected override async ValueTask HandleDisconnected(ActivityExecutionContext context) => await context.CompleteActivityWithOutcomesAsync("Disconnected", "Done"); - - /// - protected override async ValueTask HandleDone(ActivityExecutionContext context) => await context.CompleteActivityWithOutcomesAsync("Finished speaking", "Done"); -} - /// [PublicAPI] public class SpeakText : SpeakTextBase { - /// - [JsonConstructor] - public SpeakText() - { - } - /// public SpeakText([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -171,7 +20,7 @@ public class SpeakText : SpeakTextBase /// [Port] public IActivity? Disconnected { get; set; } - + /// /// The to execute when speaking has finished. /// diff --git a/src/modules/Elsa.Telnyx/Activities/SpeakTextBase.cs b/src/modules/Elsa.Telnyx/Activities/SpeakTextBase.cs new file mode 100644 index 000000000..657479f0f --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/SpeakTextBase.cs @@ -0,0 +1,123 @@ +using Elsa.Extensions; +using Elsa.Telnyx.Client.Models; +using Elsa.Telnyx.Client.Services; +using Elsa.Telnyx.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using Refit; + +namespace Elsa.Telnyx.Activities; + +/// +/// Convert text to speech and play it back on the call. +/// +[Activity(Constants.Namespace, "Convert text to speech and play it back on the call.", Kind = ActivityKind.Task)] +public abstract class SpeakTextBase : Activity +{ + /// + protected SpeakTextBase(string? source = default, int? line = default) : base(source, line) + { + } + + /// + /// Unique identifier and token for controlling the call. + /// + [Input( + DisplayName = "Call Control ID", + Description = "Unique identifier and token for controlling the call.", + Category = "Advanced" + )] + public Input CallControlId { get; set; } = default!; + + /// + /// The language you want spoken. + /// + [Input( + Description = "The language you want spoken.", + UIHint = InputUIHints.Dropdown, + Options = new[] { "en-US", "en-AU", "nl-NL", "es-ES", "ru-RU" }, + DefaultValue = "en-US" + )] + public Input Language { get; set; } = new("en-US"); + + /// + /// The gender of the voice used to speak back the text. + /// + [Input( + Description = "The gender of the voice used to speak back the text.", + UIHint = InputUIHints.Dropdown, + Options = new[] { "female", "male" }, + DefaultValue = "female" + )] + public Input Voice { get; set; } = new("female"); + + /// + /// The text or SSML to be converted into speech. There is a 5,000 character limit. + /// + [Input( + Description = "The text or SSML to be converted into speech. There is a 5,000 character limit.", + UIHint = InputUIHints.MultiLine + )] + public Input Payload { get; set; } = default!; + + /// + /// The type of the provided payload. The payload can either be plain text, or Speech Synthesis Markup Language (SSML). + /// + [Input( + Description = "The type of the provided payload. The payload can either be plain text, or Speech Synthesis Markup Language (SSML).", + UIHint = InputUIHints.Dropdown, + Options = new[] { "", "text", "ssml" } + )] + public Input? PayloadType { get; set; } + + /// + /// This parameter impacts speech quality, language options and payload types. When using `basic`, only the `en-US` language and payload type `text` are allowed. + /// + [Input( + Description = "This parameter impacts speech quality, language options and payload types. When using `basic`, only the `en-US` language and payload type `text` are allowed.", + UIHint = InputUIHints.Dropdown, + Options = new[] { "", "basic", "premium" }, + Category = "Advanced" + )] + public Input ServiceLevel { get; set; } = default!; + + /// + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + { + var request = new SpeakTextRequest( + Language.Get(context), + Voice.Get(context), + Payload.Get(context), + PayloadType.GetOrDefault(context).EmptyToNull(), + ServiceLevel.GetOrDefault(context).EmptyToNull(), + ClientState: context.CreateCorrelatingClientState() + ); + + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + var telnyxClient = context.GetRequiredService(); + + try + { + await telnyxClient.Calls.SpeakTextAsync(callControlId, request, context.CancellationToken); + await HandleDone(context); + } + catch (ApiException e) + { + if (!await e.CallIsNoLongerActiveAsync()) throw; + await HandleDisconnected(context); + } + } + + /// + /// Called when the call was no longer active. + /// + protected abstract ValueTask HandleDisconnected(ActivityExecutionContext context); + + /// + /// Called when speaking has finished. + /// + protected abstract ValueTask HandleDone(ActivityExecutionContext context); + + private async ValueTask ResumeAsync(ActivityExecutionContext context) => await HandleDone(context); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/StartRecording.cs b/src/modules/Elsa.Telnyx/Activities/StartRecording.cs index 0fb019274..d353dc094 100644 --- a/src/modules/Elsa.Telnyx/Activities/StartRecording.cs +++ b/src/modules/Elsa.Telnyx/Activities/StartRecording.cs @@ -1,55 +1,16 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; -using Elsa.Telnyx.Attributes; -using Elsa.Telnyx.Bookmarks; -using Elsa.Telnyx.Client.Models; -using Elsa.Telnyx.Client.Services; -using Elsa.Telnyx.Extensions; -using Elsa.Telnyx.Payloads.Call; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; using JetBrains.Annotations; -using Refit; namespace Elsa.Telnyx.Activities; -/// -[FlowNode("Recording finished", "Disconnected")] -[PublicAPI] -public class FlowStartRecording : StartRecordingBase -{ - /// - [JsonConstructor] - public FlowStartRecording() - { - } - - /// - public FlowStartRecording([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); - - /// - protected override ValueTask HandleCallRecordingSavedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Recording finished"); -} - /// [PublicAPI] public class StartRecording : StartRecordingBase { - /// - [JsonConstructor] - public StartRecording() - { - } - /// public StartRecording([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -72,100 +33,4 @@ public class StartRecording : StartRecordingBase protected override async ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => await context.ScheduleActivityAsync(Disconnected, OnCompletedAsync); private async ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => await context.CompleteActivityAsync(); -} - -/// -/// Start recording the call. -/// -[Activity(Constants.Namespace, "Start recording the call.", Kind = ActivityKind.Task)] -[WebhookDriven(WebhookEventTypes.CallRecordingSaved)] -[PublicAPI] -public abstract class StartRecordingBase : Activity -{ - /// - protected StartRecordingBase(string? source = default, int? line = default) : base(source, line) - { - } - - /// - /// Unique identifier and token for controlling the call. - /// - [Input( - DisplayName = "Call Control ID", - Description = "Unique identifier and token for controlling the call.", - Category = "Advanced" - )] - public Input CallControlId { get; set; } = default!; - - /// - /// When 'dual', final audio file will be stereo recorded with the first leg on channel A, and the rest on channel B. - /// - [Input( - Description = "When 'dual', final audio file will be stereo recorded with the first leg on channel A, and the rest on channel B.", - UIHint = InputUIHints.Dropdown, - Options = new[] { "single", "dual" }, - DefaultValue = "single" - )] - public Input Channels { get; set; } = new("single"); - - /// - /// The audio file format used when storing the call recording. Can be either 'mp3' or 'wav'. - /// - [Input( - Description = "The audio file format used when storing the call recording. Can be either 'mp3' or 'wav'.", - UIHint = InputUIHints.Dropdown, - Options = new[] { "wav", "mp3" }, - DefaultValue = "wav" - )] - public Input Format { get; set; } = new("wav"); - - /// - /// If enabled, a beep sound will be played at the start of a recording. - /// - [Input(Description = "If enabled, a beep sound will be played at the start of a recording.")] - public Input? PlayBeep { get; set; } - - /// - protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) - { - var request = new StartRecordingRequest( - Channels.GetOrDefault(context) ?? "single", - Format.GetOrDefault(context) ?? "wav", - PlayBeep.GetOrDefault(context), - ClientState: context.CreateCorrelatingClientState() - ); - - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - var telnyxClient = context.GetRequiredService(); - - try - { - await telnyxClient.Calls.StartRecordingAsync(callControlId, request, context.CancellationToken); - - context.CreateBookmark(new WebhookEventBookmarkPayload(WebhookEventTypes.CallRecordingSaved, callControlId), ResumeAsync); - } - catch (ApiException e) - { - if (!await e.CallIsNoLongerActiveAsync()) throw; - await HandleDisconnectedAsync(context); - } - } - - /// - /// Called when the recording was saved. - /// - protected abstract ValueTask HandleCallRecordingSavedAsync(ActivityExecutionContext context); - - - /// - /// Called when the call was no longer active. - /// - protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); - - private async ValueTask ResumeAsync(ActivityExecutionContext context) - { - var payload = context.GetInput(); - context.Set(Result, payload); - await HandleCallRecordingSavedAsync(context); - } } \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/StartRecordingBase.cs b/src/modules/Elsa.Telnyx/Activities/StartRecordingBase.cs new file mode 100644 index 000000000..f7e8f86c2 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/StartRecordingBase.cs @@ -0,0 +1,108 @@ +using Elsa.Extensions; +using Elsa.Telnyx.Attributes; +using Elsa.Telnyx.Bookmarks; +using Elsa.Telnyx.Client.Models; +using Elsa.Telnyx.Client.Services; +using Elsa.Telnyx.Extensions; +using Elsa.Telnyx.Payloads.Call; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using Refit; + +namespace Elsa.Telnyx.Activities; + +/// +/// Start recording the call. +/// +[Activity(Constants.Namespace, "Start recording the call.", Kind = ActivityKind.Task)] +[WebhookDriven(WebhookEventTypes.CallRecordingSaved)] +public abstract class StartRecordingBase : Activity +{ + /// + protected StartRecordingBase(string? source = default, int? line = default) : base(source, line) + { + } + + /// + /// Unique identifier and token for controlling the call. + /// + [Input( + DisplayName = "Call Control ID", + Description = "Unique identifier and token for controlling the call.", + Category = "Advanced" + )] + public Input CallControlId { get; set; } = default!; + + /// + /// When 'dual', final audio file will be stereo recorded with the first leg on channel A, and the rest on channel B. + /// + [Input( + Description = "When 'dual', final audio file will be stereo recorded with the first leg on channel A, and the rest on channel B.", + UIHint = InputUIHints.Dropdown, + Options = new[] { "single", "dual" }, + DefaultValue = "single" + )] + public Input Channels { get; set; } = new("single"); + + /// + /// The audio file format used when storing the call recording. Can be either 'mp3' or 'wav'. + /// + [Input( + Description = "The audio file format used when storing the call recording. Can be either 'mp3' or 'wav'.", + UIHint = InputUIHints.Dropdown, + Options = new[] { "wav", "mp3" }, + DefaultValue = "wav" + )] + public Input Format { get; set; } = new("wav"); + + /// + /// If enabled, a beep sound will be played at the start of a recording. + /// + [Input(Description = "If enabled, a beep sound will be played at the start of a recording.")] + public Input? PlayBeep { get; set; } + + /// + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + { + var request = new StartRecordingRequest( + Channels.GetOrDefault(context) ?? "single", + Format.GetOrDefault(context) ?? "wav", + PlayBeep.GetOrDefault(context), + ClientState: context.CreateCorrelatingClientState() + ); + + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + var telnyxClient = context.GetRequiredService(); + + try + { + await telnyxClient.Calls.StartRecordingAsync(callControlId, request, context.CancellationToken); + + context.CreateBookmark(new WebhookEventBookmarkPayload(WebhookEventTypes.CallRecordingSaved, callControlId), ResumeAsync); + } + catch (ApiException e) + { + if (!await e.CallIsNoLongerActiveAsync()) throw; + await HandleDisconnectedAsync(context); + } + } + + /// + /// Called when the recording was saved. + /// + protected abstract ValueTask HandleCallRecordingSavedAsync(ActivityExecutionContext context); + + + /// + /// Called when the call was no longer active. + /// + protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); + + private async ValueTask ResumeAsync(ActivityExecutionContext context) + { + var payload = context.GetInput(); + context.Set(Result, payload); + await HandleCallRecordingSavedAsync(context); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/StopAudioPlayback.cs b/src/modules/Elsa.Telnyx/Activities/StopAudioPlayback.cs index 25d6eebbf..53660c712 100644 --- a/src/modules/Elsa.Telnyx/Activities/StopAudioPlayback.cs +++ b/src/modules/Elsa.Telnyx/Activities/StopAudioPlayback.cs @@ -1,52 +1,14 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; -using Elsa.Telnyx.Client.Models; -using Elsa.Telnyx.Client.Services; -using Elsa.Telnyx.Extensions; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; -using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; -using Refit; namespace Elsa.Telnyx.Activities; /// -[FlowNode("Done", "Disconnected")] -[PublicAPI] -public class FlowStopAudioPlayback : StopAudioPlaybackBase -{ - /// - [JsonConstructor] - public FlowStopAudioPlayback() - { - } - - /// - public FlowStopAudioPlayback([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - - /// - protected override ValueTask HandleDoneAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Done"); - - /// - protected override ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => context.CompleteActivityWithOutcomesAsync("Disconnected"); -} - -/// -[PublicAPI] public class StopAudioPlayback : StopAudioPlaybackBase { - /// - [JsonConstructor] - public StopAudioPlayback() - { - } - /// public StopAudioPlayback([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -64,65 +26,4 @@ public class StopAudioPlayback : StopAudioPlaybackBase protected override async ValueTask HandleDisconnectedAsync(ActivityExecutionContext context) => await context.ScheduleActivityAsync(Disconnected, OnCompletedAsync); private async ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => await context.CompleteActivityAsync(); -} - -/// -/// Stop audio playback. -/// -[Activity(Constants.Namespace, Description = "Stop audio playback.", Kind = ActivityKind.Task)] -[PublicAPI] -public abstract class StopAudioPlaybackBase : Activity -{ - /// - protected StopAudioPlaybackBase(string? source = default, int? line = default) : base(source, line) - { - } - - /// - /// Unique identifier and token for controlling the call. - /// - [Input( - DisplayName = "Call Control ID", - Description = "Unique identifier and token for controlling the call.", - Category = "Advanced" - )] - public Input CallControlId { get; set; } = default!; - - /// - /// Use 'current' to stop only the current audio or 'all' to stop all audios in the queue. - /// - [Input( - Description = "Use 'current' to stop only the current audio or 'all' to stop all audios in the queue.", - DefaultValue = "all", - Category = "Advanced" - )] - public Input Stop { get; set; } = new("all"); - - /// - protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) - { - var request = new StopAudioPlaybackRequest(Stop.Get(context), context.CreateCorrelatingClientState()); - var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); - var telnyxClient = context.GetRequiredService(); - - try - { - await telnyxClient.Calls.StopAudioPlaybackAsync(callControlId, request, context.CancellationToken); - await HandleDoneAsync(context); - } - catch (ApiException e) - { - if (!await e.CallIsNoLongerActiveAsync()) throw; - await HandleDisconnectedAsync(context); - } - } - - /// - /// Called when audio playback is stopping. - /// - protected abstract ValueTask HandleDoneAsync(ActivityExecutionContext context); - /// - /// Called when the call was no longer active. - /// - protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); } \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/StopAudioPlaybackBase.cs b/src/modules/Elsa.Telnyx/Activities/StopAudioPlaybackBase.cs new file mode 100644 index 000000000..3f9419471 --- /dev/null +++ b/src/modules/Elsa.Telnyx/Activities/StopAudioPlaybackBase.cs @@ -0,0 +1,70 @@ +using Elsa.Extensions; +using Elsa.Telnyx.Client.Models; +using Elsa.Telnyx.Client.Services; +using Elsa.Telnyx.Extensions; +using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Attributes; +using Elsa.Workflows.Core.Models; +using Refit; + +namespace Elsa.Telnyx.Activities; + +/// +/// Stop audio playback. +/// +[Activity(Constants.Namespace, Description = "Stop audio playback.", Kind = ActivityKind.Task)] +public abstract class StopAudioPlaybackBase : Activity +{ + /// + protected StopAudioPlaybackBase(string? source = default, int? line = default) : base(source, line) + { + } + + /// + /// Unique identifier and token for controlling the call. + /// + [Input( + DisplayName = "Call Control ID", + Description = "Unique identifier and token for controlling the call.", + Category = "Advanced" + )] + public Input CallControlId { get; set; } = default!; + + /// + /// Use 'current' to stop only the current audio or 'all' to stop all audios in the queue. + /// + [Input( + Description = "Use 'current' to stop only the current audio or 'all' to stop all audios in the queue.", + DefaultValue = "all", + Category = "Advanced" + )] + public Input Stop { get; set; } = new("all"); + + /// + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + { + var request = new StopAudioPlaybackRequest(Stop.Get(context), context.CreateCorrelatingClientState()); + var callControlId = context.GetPrimaryCallControlId(CallControlId) ?? throw new Exception("CallControlId is required."); + var telnyxClient = context.GetRequiredService(); + + try + { + await telnyxClient.Calls.StopAudioPlaybackAsync(callControlId, request, context.CancellationToken); + await HandleDoneAsync(context); + } + catch (ApiException e) + { + if (!await e.CallIsNoLongerActiveAsync()) throw; + await HandleDisconnectedAsync(context); + } + } + + /// + /// Called when audio playback is stopping. + /// + protected abstract ValueTask HandleDoneAsync(ActivityExecutionContext context); + /// + /// Called when the call was no longer active. + /// + protected abstract ValueTask HandleDisconnectedAsync(ActivityExecutionContext context); +} \ No newline at end of file diff --git a/src/modules/Elsa.Telnyx/Activities/StopRecording.cs b/src/modules/Elsa.Telnyx/Activities/StopRecording.cs index 054c75baa..f4514aad8 100644 --- a/src/modules/Elsa.Telnyx/Activities/StopRecording.cs +++ b/src/modules/Elsa.Telnyx/Activities/StopRecording.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Client.Models; using Elsa.Telnyx.Client.Services; @@ -8,7 +7,6 @@ using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; using Refit; namespace Elsa.Telnyx.Activities; @@ -18,15 +16,8 @@ namespace Elsa.Telnyx.Activities; /// [Activity(Constants.Namespace, "Stop recording the call.", Kind = ActivityKind.Task)] [FlowNode("Recording stopped", "Disconnected")] -[PublicAPI] public class StopRecording : Activity { - /// - [JsonConstructor] - public StopRecording() - { - } - /// public StopRecording([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/TransferCall.cs b/src/modules/Elsa.Telnyx/Activities/TransferCall.cs index 84bb1ee6b..56674ffd6 100644 --- a/src/modules/Elsa.Telnyx/Activities/TransferCall.cs +++ b/src/modules/Elsa.Telnyx/Activities/TransferCall.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Client.Models; using Elsa.Telnyx.Client.Services; @@ -9,7 +8,6 @@ using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities.Flowchart.Attributes; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; -using JetBrains.Annotations; using Refit; namespace Elsa.Telnyx.Activities; @@ -19,15 +17,8 @@ namespace Elsa.Telnyx.Activities; /// [Activity(Constants.Namespace, "Transfer a call to a new destination.", Kind = ActivityKind.Task)] [FlowNode("Transferred", "Hangup", "Disconnected")] -[PublicAPI] public class TransferCall : Activity { - /// - [JsonConstructor] - public TransferCall() - { - } - /// public TransferCall([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Activities/WebhookEvent.cs b/src/modules/Elsa.Telnyx/Activities/WebhookEvent.cs index 655f80a3d..ed5e1842d 100644 --- a/src/modules/Elsa.Telnyx/Activities/WebhookEvent.cs +++ b/src/modules/Elsa.Telnyx/Activities/WebhookEvent.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Telnyx.Bookmarks; using Elsa.Telnyx.Helpers; @@ -20,12 +19,6 @@ namespace Elsa.Telnyx.Activities; [Browsable(false)] public class WebhookEvent : Activity { - /// - [JsonConstructor] - public WebhookEvent() - { - } - /// public WebhookEvent([CallerFilePath]string? source = default, [CallerLineNumber]int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Telnyx/Models/BridgedCallsOutput.cs b/src/modules/Elsa.Telnyx/Models/BridgedCallsOutput.cs new file mode 100644 index 000000000..abf46912a --- /dev/null +++ b/src/modules/Elsa.Telnyx/Models/BridgedCallsOutput.cs @@ -0,0 +1,11 @@ +using Elsa.Telnyx.Activities; +using Elsa.Telnyx.Payloads.Call; + +namespace Elsa.Telnyx.Models; + +/// +/// Contains output of the activity. +/// +/// The payload from leg A. +/// The payload from leg B. +public record BridgedCallsOutput(CallBridgedPayload PayloadA, CallBridgedPayload PayloadB); \ No newline at end of file diff --git a/src/modules/Elsa.WorkflowContexts/Activities/SetWorkflowContextParameter.cs b/src/modules/Elsa.WorkflowContexts/Activities/SetWorkflowContextParameter.cs index 890ce2564..74207f0c8 100644 --- a/src/modules/Elsa.WorkflowContexts/Activities/SetWorkflowContextParameter.cs +++ b/src/modules/Elsa.WorkflowContexts/Activities/SetWorkflowContextParameter.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.WorkflowContexts.Contracts; @@ -17,12 +16,6 @@ namespace Elsa.WorkflowContexts.Activities; [PublicAPI] public class SetWorkflowContextParameter : CodeActivity { - /// - [JsonConstructor] - public SetWorkflowContextParameter() - { - } - /// public SetWorkflowContextParameter([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Api/RealTime/Extensions/ApplicationBuilderExtensions.cs b/src/modules/Elsa.Workflows.Api/RealTime/Extensions/ApplicationBuilderExtensions.cs index fc8fa7228..a7cc91f5c 100644 --- a/src/modules/Elsa.Workflows.Api/RealTime/Extensions/ApplicationBuilderExtensions.cs +++ b/src/modules/Elsa.Workflows.Api/RealTime/Extensions/ApplicationBuilderExtensions.cs @@ -1,4 +1,3 @@ -using Elsa.Workflows.Api.Middleware; using Elsa.Workflows.Api.RealTime.Hubs; using Microsoft.AspNetCore.Builder; diff --git a/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs b/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs index 61199aede..835a955f7 100644 --- a/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs +++ b/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs @@ -1,5 +1,4 @@ using Elsa.Workflows.Api.RealTime.Contracts; -using Elsa.Workflows.Api.RealTime.Messages; using Elsa.Workflows.Runtime.Contracts; using JetBrains.Annotations; using Microsoft.AspNetCore.SignalR; diff --git a/src/modules/Elsa.Workflows.Core/Abstractions/Activity.cs b/src/modules/Elsa.Workflows.Core/Abstractions/Activity.cs index c93d6cd32..296974e12 100644 --- a/src/modules/Elsa.Workflows.Core/Abstractions/Activity.cs +++ b/src/modules/Elsa.Workflows.Core/Abstractions/Activity.cs @@ -5,6 +5,7 @@ using Elsa.Workflows.Core.Behaviors; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Helpers; using Elsa.Workflows.Core.Models; +using JetBrains.Annotations; namespace Elsa.Workflows.Core; @@ -12,6 +13,7 @@ namespace Elsa.Workflows.Core; /// Base class for custom activities. /// [DebuggerDisplay("{Type} - {Id}")] +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public abstract class Activity : IActivity, ISignalHandler { private readonly ICollection _signalHandlers = new List(); diff --git a/src/modules/Elsa.Workflows.Core/Abstractions/WorkflowBase.cs b/src/modules/Elsa.Workflows.Core/Abstractions/WorkflowBase.cs index 44831d5fd..4594b8ea7 100644 --- a/src/modules/Elsa.Workflows.Core/Abstractions/WorkflowBase.cs +++ b/src/modules/Elsa.Workflows.Core/Abstractions/WorkflowBase.cs @@ -1,11 +1,13 @@ using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; +using JetBrains.Annotations; -namespace Elsa.Workflows.Core.Abstractions; +namespace Elsa.Workflows.Core; /// /// A base class for implementing workflow definitions using the pipelineBuilder API. /// +[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)] public abstract class WorkflowBase : IWorkflow { /// diff --git a/src/modules/Elsa.Workflows.Core/Activities/Break.cs b/src/modules/Elsa.Workflows.Core/Activities/Break.cs index 29ed66a51..dd24f610b 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Break.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Break.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Signals; @@ -14,17 +13,12 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class Break : CodeActivity { - /// - [JsonConstructor] - public Break() - { - } - /// public Break([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } - + + /// protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) { await context.SendSignalAsync(new BreakSignal()); diff --git a/src/modules/Elsa.Workflows.Core/Activities/Complete.cs b/src/modules/Elsa.Workflows.Core/Activities/Complete.cs index e2e0fcc14..910ecea4b 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Complete.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Complete.cs @@ -1,6 +1,5 @@ using System.Runtime.CompilerServices; using System.Text.Json; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Core.Activities.Flowchart.Models; @@ -18,12 +17,6 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class Complete : Activity { - /// - [JsonConstructor] - public Complete() - { - } - /// public Complete([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Correlate.cs b/src/modules/Elsa.Workflows.Core/Activities/Correlate.cs index 532f80574..4b5fcbd2d 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Correlate.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Correlate.cs @@ -1,5 +1,5 @@ using System.ComponentModel; -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; using JetBrains.Annotations; @@ -14,8 +14,7 @@ namespace Elsa.Workflows.Core.Activities; public class Correlate : CodeActivity { /// - [JsonConstructor] - public Correlate() + public Correlate([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } @@ -24,8 +23,9 @@ public class Correlate : CodeActivity /// [Description("An expression that evaluates to the value to store as the correlation id")] public Input CorrelationId { get; set; } = default!; - - protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + + /// + protected override void Execute(ActivityExecutionContext context) { var correlationId = context.Get(CorrelationId); context.WorkflowExecutionContext.CorrelationId = correlationId; diff --git a/src/modules/Elsa.Workflows.Core/Activities/End.cs b/src/modules/Elsa.Workflows.Core/Activities/End.cs new file mode 100644 index 000000000..85bdabfe3 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Activities/End.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using Elsa.Workflows.Core.Attributes; +using JetBrains.Annotations; + +namespace Elsa.Workflows.Core.Activities; + +/// +/// Marks the end of a flowchart, causing the flowchart to complete. +/// +[Activity("Elsa", "Flow", "A milestone activity that marks the start of a flowchart.", Kind = ActivityKind.Action)] +[PublicAPI] +public class End : CodeActivity +{ + /// + public End([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Activities/Fault.cs b/src/modules/Elsa.Workflows.Core/Activities/Fault.cs index 82110bd50..3736365bb 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Fault.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Fault.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; @@ -14,12 +13,6 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class Fault : Activity { - /// - [JsonConstructor] - public Fault() - { - } - /// public Fault([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Finish.cs b/src/modules/Elsa.Workflows.Core/Activities/Finish.cs index 1621daf4d..68a2f0eec 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Finish.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Finish.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Workflows.Core.Attributes; using JetBrains.Annotations; @@ -12,12 +11,6 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class Finish : Activity { - /// - [JsonConstructor] - public Finish() - { - } - /// public Finish([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowDecision.cs b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowDecision.cs index 19d002ba6..b7b4fb45d 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowDecision.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowDecision.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Core.Activities.Flowchart.Attributes; @@ -18,12 +17,6 @@ namespace Elsa.Workflows.Core.Activities.Flowchart.Activities; [PublicAPI] public class FlowDecision : Activity { - /// - [JsonConstructor] - public FlowDecision() - { - } - /// public FlowDecision([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowJoin.cs b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowJoin.cs index 67db994d5..5df52b97c 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowJoin.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowJoin.cs @@ -6,7 +6,6 @@ using Elsa.Workflows.Core.Activities.Flowchart.Models; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; using JetBrains.Annotations; -using Newtonsoft.Json; namespace Elsa.Workflows.Core.Activities.Flowchart.Activities; @@ -17,12 +16,6 @@ namespace Elsa.Workflows.Core.Activities.Flowchart.Activities; [PublicAPI] public class FlowJoin : Activity, IJoinNode { - /// - [JsonConstructor] - public FlowJoin() - { - } - /// public FlowJoin([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowNode.cs b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowNode.cs index e35ae2134..6d3cf05e1 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowNode.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowNode.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; @@ -14,11 +13,6 @@ namespace Elsa.Workflows.Core.Activities.Flowchart.Activities; [PublicAPI] public class FlowNode : Activity { - /// - [JsonConstructor] - public FlowNode() - { - } /// public FlowNode([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) diff --git a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowSwitch.cs b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowSwitch.cs index f9f48b079..f2003ea64 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowSwitch.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/FlowSwitch.cs @@ -20,12 +20,6 @@ namespace Elsa.Workflows.Core.Activities.Flowchart.Activities; [PublicAPI] public class FlowSwitch : Activity { - /// - [JsonConstructor] - public FlowSwitch() - { - } - /// public FlowSwitch([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/Flowchart.cs b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/Flowchart.cs index 6a8dc3d7c..88f450879 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/Flowchart.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities/Flowchart.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Activities.Flowchart.Contracts; using Elsa.Workflows.Core.Activities.Flowchart.Extensions; @@ -22,12 +21,6 @@ public class Flowchart : Container { internal const string ScopeProperty = "Scope"; - /// - [JsonConstructor] - public Flowchart() : this(default, default) - { - } - /// public Flowchart([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -92,47 +85,55 @@ public class Flowchart : Container scope.RegisterActivityExecution(completedActivity); - if (children.Any()) + // If the completed activity is an End activity, complete the flowchart. + if (completedActivity is End) { - scope.AddActivities(children); - - // Schedule each child, but only if all of its left inbound activities have already executed. - foreach (var activity in children) + await flowchartActivityExecutionContext.CompleteActivityAsync(); + } + else + { + if (children.Any()) { - var inboundActivities = Connections.LeftInboundActivities(activity).ToList(); + scope.AddActivities(children); - // If the completed activity is not part of the left inbound path, always allow its children to be scheduled. - if (!inboundActivities.Contains(completedActivity)) + // Schedule each child, but only if all of its left inbound activities have already executed. + foreach (var activity in children) { - await flowchartActivityExecutionContext.ScheduleActivityAsync(activity); - continue; - } + var inboundActivities = Connections.LeftInboundActivities(activity).ToList(); - // If the activity is anything but a join activity, only schedule it if all of its left-inbound activities have executed, effectively implementing a "wait all" join. - if (activity is not IJoinNode) - { - var executionCount = scope.GetExecutionCount(activity); - var haveInboundActivitiesExecuted = inboundActivities.All(x => scope.GetExecutionCount(x) > executionCount); - - if (haveInboundActivitiesExecuted) + // If the completed activity is not part of the left inbound path, always allow its children to be scheduled. + if (!inboundActivities.Contains(completedActivity)) + { await flowchartActivityExecutionContext.ScheduleActivityAsync(activity); - } - else - { - await flowchartActivityExecutionContext.ScheduleActivityAsync(activity); + continue; + } + + // If the activity is anything but a join activity, only schedule it if all of its left-inbound activities have executed, effectively implementing a "wait all" join. + if (activity is not IJoinNode) + { + var executionCount = scope.GetExecutionCount(activity); + var haveInboundActivitiesExecuted = inboundActivities.All(x => scope.GetExecutionCount(x) > executionCount); + + if (haveInboundActivitiesExecuted) + await flowchartActivityExecutionContext.ScheduleActivityAsync(activity); + } + else + { + await flowchartActivityExecutionContext.ScheduleActivityAsync(activity); + } } } - } - if (!children.Any()) - { - var workflowExecutionContext = context.ReceiverActivityExecutionContext.WorkflowExecutionContext; + if (!children.Any()) + { + var workflowExecutionContext = context.ReceiverActivityExecutionContext.WorkflowExecutionContext; - // If there is no pending work, complete the flowchart activity. - var hasPendingWork = HasPendingWork(workflowExecutionContext); + // If there is no pending work, complete the flowchart activity. + var hasPendingWork = HasPendingWork(workflowExecutionContext); - if (!hasPendingWork) - await flowchartActivityExecutionContext.CompleteActivityAsync(); + if (!hasPendingWork) + await flowchartActivityExecutionContext.CompleteActivityAsync(); + } } flowchartActivityExecutionContext.SetProperty(ScopeProperty, scope); @@ -149,7 +150,7 @@ public class Flowchart : Container var activityExecutionContexts = workflowExecutionContext.ActivityExecutionContexts.Where(x => activityIds.Contains(x.Activity.Id)).ToList(); var hasPendingWork = workflowExecutionContext.Scheduler.List().Any(x => activityNodeIds.Contains(x.ActivityId)); var hasRunningActivityInstances = activityExecutionContexts.Any(x => x.Status == ActivityStatus.Running); - + return hasRunningActivityInstances || hasPendingWork; } @@ -158,18 +159,18 @@ public class Flowchart : Container // If there's a trigger that triggered this workflow, use that. var triggerActivityId = context.WorkflowExecutionContext.TriggerActivityId; var triggerActivity = triggerActivityId != null ? Activities.FirstOrDefault(x => x.Id == triggerActivityId) : default; - - if(triggerActivity != null) + + if (triggerActivity != null) return triggerActivity; - + // If an explicit Start activity was provided, use that. - if(Start != null) + if (Start != null) return Start; - + // If there is a Start activity on the flowchart, use that. var startActivity = Activities.FirstOrDefault(x => x is Start); - - if(startActivity != null) + + if (startActivity != null) return startActivity; // If there is a single activity that has no inbound connections, use that. @@ -177,7 +178,7 @@ public class Flowchart : Container if (root != null) return root; - + // If no start activity found, return the first activity. return Activities.FirstOrDefault(); } @@ -190,7 +191,7 @@ public class Flowchart : Container let inboundConnections = Connections.Any(x => x.Target.Activity == activity) where !inboundConnections select activity; - + var rootActivity = query.FirstOrDefault(); return rootActivity; } diff --git a/src/modules/Elsa.Workflows.Core/Activities/For.cs b/src/modules/Elsa.Workflows.Core/Activities/For.cs index ffda12044..5701f8c64 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/For.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/For.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Behaviors; @@ -18,12 +17,6 @@ public class For : Activity { private const string CurrentStepProperty = "CurrentStep"; - /// - [JsonConstructor] - public For() : this(default, default) - { - } - /// public For([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/ForEach.cs b/src/modules/Elsa.Workflows.Core/Activities/ForEach.cs index 1cbfd2ce4..ac31950dc 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/ForEach.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/ForEach.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; @@ -19,12 +18,6 @@ public class ForEach : Activity { private const string CurrentIndexProperty = "CurrentIndex"; - /// - [JsonConstructor] - public ForEach() : this(default, default) - { - } - /// public ForEach([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) { @@ -98,8 +91,7 @@ public class ForEach : Activity public class ForEach : ForEach { /// - [JsonConstructor] - public ForEach() + public ForEach([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } diff --git a/src/modules/Elsa.Workflows.Core/Activities/Fork.cs b/src/modules/Elsa.Workflows.Core/Activities/Fork.cs index 09680905f..4f0eca438 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Fork.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Fork.cs @@ -1,7 +1,6 @@ using System.Collections.Immutable; using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; @@ -17,12 +16,6 @@ namespace Elsa.Workflows.Core.Activities; [Browsable(false)] public class Fork : Activity { - /// - [JsonConstructor] - public Fork() - { - } - /// public Fork([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/If.cs b/src/modules/Elsa.Workflows.Core/Activities/If.cs index ba99be26e..54e702840 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/If.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/If.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; @@ -16,12 +15,6 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class If : Activity { - /// - [JsonConstructor] - public If() - { - } - /// public If([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Inline.cs b/src/modules/Elsa.Workflows.Core/Activities/Inline.cs index 85895b5ab..191d9b54f 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Inline.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Inline.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Workflows.Core.Attributes; using JetBrains.Annotations; @@ -17,12 +16,6 @@ public class Inline : CodeActivity { private readonly Func _activity = default!; - /// - [JsonConstructor] - public Inline() - { - } - /// public Inline([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/NotFoundActivity.cs b/src/modules/Elsa.Workflows.Core/Activities/NotFoundActivity.cs index 61c11bc07..42cc12550 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/NotFoundActivity.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/NotFoundActivity.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.Runtime.CompilerServices; using Elsa.Workflows.Core.Attributes; using JetBrains.Annotations; @@ -13,12 +14,12 @@ namespace Elsa.Workflows.Core.Activities; public class NotFoundActivity : CodeActivity { /// - public NotFoundActivity() + public NotFoundActivity([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } /// - public NotFoundActivity(string missingTypeName) + public NotFoundActivity(string missingTypeName, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) { MissingTypeName = missingTypeName; } diff --git a/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs b/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs index 41ed38bd0..46cee0833 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Parallel.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; @@ -18,12 +17,6 @@ public class Parallel : Container { private const string ScheduledChildrenProperty = "ScheduledChildren"; - /// - [JsonConstructor] - public Parallel() - { - } - /// public Parallel([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/ParallelForEach.cs b/src/modules/Elsa.Workflows.Core/Activities/ParallelForEach.cs index 1186ef885..d657a0dc1 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/ParallelForEach.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/ParallelForEach.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; @@ -19,12 +18,6 @@ public class ParallelForEach : CodeActivity { private const string CollectedCountProperty = nameof(CollectedCountProperty); - /// - [JsonConstructor] - public ParallelForEach() - { - } - /// public ParallelForEach([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/ReadLine.cs b/src/modules/Elsa.Workflows.Core/Activities/ReadLine.cs index 9c808c5b3..dba92c573 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/ReadLine.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/ReadLine.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; @@ -16,12 +15,6 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class ReadLine : CodeActivity { - /// - [JsonConstructor] - public ReadLine() - { - } - /// public ReadLine([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Sequence.cs b/src/modules/Elsa.Workflows.Core/Activities/Sequence.cs index 2487a5271..9e86d790f 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Sequence.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Sequence.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Contracts; @@ -19,13 +18,7 @@ namespace Elsa.Workflows.Core.Activities; public class Sequence : Container { private const string CurrentIndexProperty = "CurrentIndex"; - - /// - [JsonConstructor] - public Sequence() : this(default(string?), default) - { - } - + /// public Sequence(params IActivity[] activities) : this(default(string?), default) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/SetName.cs b/src/modules/Elsa.Workflows.Core/Activities/SetName.cs index 229a46a77..f2cd2fb51 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/SetName.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/SetName.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; using JetBrains.Annotations; @@ -18,12 +17,6 @@ public class SetName : CodeActivity /// public const string WorkflowInstanceNameKey = "WorkflowInstanceName"; - /// - [JsonConstructor] - public SetName() - { - } - /// public SetName([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs b/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs index 713e14ad5..fdfd78b89 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs @@ -1,6 +1,5 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; @@ -18,12 +17,6 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class SetVariable : CodeActivity { - /// - [JsonConstructor] - public SetVariable() - { - } - /// public SetVariable([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { @@ -87,12 +80,6 @@ public class SetVariable : CodeActivity [PublicAPI] public class SetVariable : CodeActivity { - /// - [JsonConstructor] - public SetVariable() - { - } - /// public SetVariable([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Start.cs b/src/modules/Elsa.Workflows.Core/Activities/Start.cs index 3fbaa416f..4e0df953b 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Start.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Start.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Workflows.Core.Attributes; using JetBrains.Annotations; @@ -8,16 +7,10 @@ namespace Elsa.Workflows.Core.Activities; /// /// Marks the start of a flowchart. /// -[Activity("Elsa", "Flow", "A milestone activity with no behavior other than marking the start of a flowchart.", Kind = ActivityKind.Action)] +[Activity("Elsa", "Flow", "A milestone activity that marks the start of a flowchart.", Kind = ActivityKind.Action)] [PublicAPI] public class Start : CodeActivity { - /// - [JsonConstructor] - public Start() - { - } - /// public Start([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/Switch.cs b/src/modules/Elsa.Workflows.Core/Activities/Switch.cs index 56d9c9048..a655a2e87 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Switch.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Switch.cs @@ -19,12 +19,6 @@ namespace Elsa.Workflows.Core.Activities; [PublicAPI] public class Switch : Activity { - /// - [JsonConstructor] - public Switch() - { - } - /// public Switch([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Core/Activities/While.cs b/src/modules/Elsa.Workflows.Core/Activities/While.cs index 9356ba1bf..4967f8b15 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/While.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/While.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Core.Attributes; @@ -26,17 +25,15 @@ public class While : Activity }; /// - [JsonConstructor] - public While() : this(default, default, default) + public While([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { + Behaviors.Add(this); } /// - public While(IActivity? body = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + public While(IActivity? body = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) { Body = body; - Behaviors.Add(this); - Behaviors.Remove(); } /// diff --git a/src/modules/Elsa.Workflows.Core/Activities/Workflow.cs b/src/modules/Elsa.Workflows.Core/Activities/Workflow.cs index 2396890af..b15c83287 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Workflow.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Workflow.cs @@ -17,7 +17,7 @@ namespace Elsa.Workflows.Core.Activities; public class Workflow : Composite, ICloneable { /// - /// Constructor. + /// Initializes a new instance of the class. /// public Workflow( WorkflowIdentity identity, @@ -47,7 +47,7 @@ public class Workflow : Composite, ICloneable } /// - /// Constructor. + /// Initializes a new instance of the class. /// public Workflow(IActivity root) : this() { diff --git a/src/modules/Elsa.Workflows.Core/Activities/WriteLine.cs b/src/modules/Elsa.Workflows.Core/Activities/WriteLine.cs index 373bae576..9704802f8 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/WriteLine.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/WriteLine.cs @@ -7,6 +7,7 @@ using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; using Elsa.Workflows.Core.Models; using Elsa.Workflows.Core.Services; +using JetBrains.Annotations; namespace Elsa.Workflows.Core.Activities; @@ -14,16 +15,12 @@ namespace Elsa.Workflows.Core.Activities; /// Write a line of text to the console. /// [Activity("Elsa", "Console", "Write a line of text to the console.")] +[PublicAPI] public class WriteLine : CodeActivity { - /// - internal WriteLine([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) - { - } - /// [JsonConstructor] - public WriteLine() : this(default, default) + private WriteLine(string? source = default, int? line = default) : base(source, line) { } diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflow.cs b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflow.cs index bbdebfe57..f466115d1 100644 --- a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflow.cs +++ b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflow.cs @@ -1,5 +1,3 @@ -using Elsa.Workflows.Core.Abstractions; - namespace Elsa.Workflows.Core.Contracts; /// diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs index c087b96c4..5abfe539e 100644 --- a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs +++ b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs @@ -1,4 +1,3 @@ -using Elsa.Workflows.Core.Abstractions; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Models; using Elsa.Workflows.Core.State; diff --git a/src/modules/Elsa.Workflows.Core/Activities/ForkJoinMode.cs b/src/modules/Elsa.Workflows.Core/Enums/ForkJoinMode.cs similarity index 84% rename from src/modules/Elsa.Workflows.Core/Activities/ForkJoinMode.cs rename to src/modules/Elsa.Workflows.Core/Enums/ForkJoinMode.cs index baad92c68..584a442d9 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/ForkJoinMode.cs +++ b/src/modules/Elsa.Workflows.Core/Enums/ForkJoinMode.cs @@ -1,4 +1,6 @@ -namespace Elsa.Workflows.Core.Activities; +using Elsa.Workflows.Core.Activities; + +namespace Elsa.Workflows.Core; /// /// Controls when a completes. diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ActivityPropertyExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/ActivityPropertyExtensions.cs index 983ed0f92..195da6705 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/ActivityPropertyExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/ActivityPropertyExtensions.cs @@ -53,7 +53,7 @@ public static class ActivityPropertyExtensions /// public static void SetSource(this IActivity activity, string? sourceFile, int? lineNumber) { - if (sourceFile == null || lineNumber != null) + if (sourceFile == null || lineNumber == null) return; var source = $"{Path.GetFileName(sourceFile)}:{lineNumber}"; diff --git a/src/modules/Elsa.Workflows.Core/Serialization/ActivityConstructorContractResolver.cs b/src/modules/Elsa.Workflows.Core/Serialization/ActivityConstructorContractResolver.cs new file mode 100644 index 000000000..87812db8e --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Serialization/ActivityConstructorContractResolver.cs @@ -0,0 +1,66 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; + +namespace Elsa.Workflows.Core.Serialization; + +/// +/// A custom JSON type info resolver that allows private constructors to be used when deserializing JSON. +/// +public class ActivityConstructorContractResolver : DefaultJsonTypeInfoResolver +{ + /// + public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) + { + var jsonTypeInfo = base.GetTypeInfo(type, options); + + if (jsonTypeInfo is not { Kind: JsonTypeInfoKind.Object, CreateObject: null }) + return jsonTypeInfo; + + var defaultConstructor = GetDefaultConstructor(jsonTypeInfo.Type); + if (defaultConstructor != null) + { + jsonTypeInfo.CreateObject = defaultConstructor; + } + + return jsonTypeInfo; + } + + private static Func? GetDefaultConstructor([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) + { + foreach (var constructor in type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) + { + // If we have a default constructor, use that one. + if (!constructor.GetParameters().Any()) + return () => constructor.Invoke(default, Array.Empty())!; + + // Else, find a constructor with the following signature: (string?, int?). + // Check for a constructor with the following signature: + // ctor(string, int) where string is decorated with [CallerFilePath] and int is decorated with [CallerLineNumber] + var parameters = constructor.GetParameters(); + + // Check parameter count + if (parameters.Length != 2) continue; + + // Does the constructor have a [JsonConstructor] attribute? + var isJsonConstructor = constructor.GetCustomAttribute() != null; + + // Check first parameter type and attribute + if (parameters[0].ParameterType != typeof(string) || + parameters[0].DefaultValue != null || + (parameters[0].GetCustomAttribute() == null && !isJsonConstructor)) continue; + + // Check second parameter type and attribute + if (parameters[1].ParameterType != typeof(int?) || + parameters[1].DefaultValue != null || + (parameters[1].GetCustomAttribute() == null && !isJsonConstructor)) continue; + + return () => constructor.Invoke(new object[] { null!, 0 }); + } + + return null; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Serialization/PrivateConstructorContractResolver.cs b/src/modules/Elsa.Workflows.Core/Serialization/PrivateConstructorContractResolver.cs deleted file mode 100644 index a90c60b81..000000000 --- a/src/modules/Elsa.Workflows.Core/Serialization/PrivateConstructorContractResolver.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; - -namespace Elsa.Workflows.Core.Serialization; - -/// -/// A custom JSON type info resolver that allows private constructors to be used when deserializing JSON. -/// -public class PrivateConstructorContractResolver : DefaultJsonTypeInfoResolver -{ - /// - public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) - { - var jsonTypeInfo = base.GetTypeInfo(type, options); - - if (jsonTypeInfo is not { Kind: JsonTypeInfoKind.Object, CreateObject: null }) - return jsonTypeInfo; - - if (HasPrivateJsonConstructor(jsonTypeInfo.Type)) - { - // The type doesn't have public constructors - jsonTypeInfo.CreateObject = () => Activator.CreateInstance(jsonTypeInfo.Type, true)!; - } - - return jsonTypeInfo; - } - - private static bool HasPrivateJsonConstructor(Type type) => type - .GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance) - .Any(x => (x.IsPrivate || x.IsAssembly || x.IsFamily) && !x.GetParameters().Any() && x.GetCustomAttribute() != null); -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/ApiSerializer.cs b/src/modules/Elsa.Workflows.Core/Services/ApiSerializer.cs index 840dbc476..3fe2442be 100644 --- a/src/modules/Elsa.Workflows.Core/Services/ApiSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Services/ApiSerializer.cs @@ -49,7 +49,7 @@ public class ApiSerializer : IApiSerializer options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; options.PropertyNameCaseInsensitive = true; options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; - options.TypeInfoResolver = new PrivateConstructorContractResolver(); + options.TypeInfoResolver = new ActivityConstructorContractResolver(); options.Converters.Add(Create()); options.Converters.Add(Create()); options.Converters.Add(JsonMetadataServices.TimeSpanConverter); diff --git a/src/modules/Elsa.Workflows.Core/Services/JsonActivitySerializer.cs b/src/modules/Elsa.Workflows.Core/Services/JsonActivitySerializer.cs index 385d976cd..d6cec8734 100644 --- a/src/modules/Elsa.Workflows.Core/Services/JsonActivitySerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Services/JsonActivitySerializer.cs @@ -54,7 +54,7 @@ public class JsonActivitySerializer : IActivitySerializer { var options = new JsonSerializerOptions { - TypeInfoResolver = new PrivateConstructorContractResolver(), + TypeInfoResolver = new ActivityConstructorContractResolver(), PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs index 8cc977732..ace7bbd39 100644 --- a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs +++ b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs @@ -1,7 +1,6 @@ using Elsa.Common.Contracts; using Elsa.Extensions; using Elsa.Mediator.Contracts; -using Elsa.Workflows.Core.Abstractions; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Models; diff --git a/src/modules/Elsa.Workflows.Management/Activities/RunJavaScript/RunJavaScript.cs b/src/modules/Elsa.Workflows.Management/Activities/RunJavaScript/RunJavaScript.cs index d13653698..e1cd237f9 100644 --- a/src/modules/Elsa.Workflows.Management/Activities/RunJavaScript/RunJavaScript.cs +++ b/src/modules/Elsa.Workflows.Management/Activities/RunJavaScript/RunJavaScript.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Serialization; +using System.Runtime.CompilerServices; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; @@ -15,13 +15,12 @@ namespace Elsa.JavaScript.Activities; public class RunJavaScript : CodeActivity { /// - [JsonConstructor] - public RunJavaScript() + public RunJavaScript([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } /// - public RunJavaScript(string script) + public RunJavaScript(string script, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) { Script = new Input(script); } @@ -52,7 +51,7 @@ public class RunJavaScript : CodeActivity var result = await javaScriptEvaluator.EvaluateAsync(script, typeof(object), context.ExpressionExecutionContext, cancellationToken: context.CancellationToken); // Set the result as output, if any. - if(result is not null) + if (result is not null) context.Set(Result, result); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs b/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs index 627bb3a47..1c8b00a7c 100644 --- a/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs +++ b/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Elsa.Extensions; using Elsa.Workflows.Core; using Elsa.Workflows.Core.Attributes; @@ -13,6 +14,11 @@ namespace Elsa.Workflows.Management.Activities.SetOutput; [PublicAPI] public class SetOutput : CodeActivity { + /// + public SetOutput([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + /// /// The name of the output to assign. /// diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs index 363eea750..166ba8146 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionPublisher.cs @@ -1,7 +1,6 @@ using Elsa.Common.Contracts; using Elsa.Common.Entities; using Elsa.Common.Models; -using Elsa.Extensions; using Elsa.Mediator.Contracts; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceManager.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceManager.cs index d0dcbd654..3940e22f3 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceManager.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceManager.cs @@ -1,4 +1,3 @@ -using Elsa.Extensions; using Elsa.Mediator.Contracts; using Elsa.Workflows.Management.Contracts; using Elsa.Workflows.Management.Filters; diff --git a/src/modules/Elsa.Workflows.Runtime/Activities/DispatchWorkflow.cs b/src/modules/Elsa.Workflows.Runtime/Activities/DispatchWorkflow.cs index 022392641..4fffd6685 100644 --- a/src/modules/Elsa.Workflows.Runtime/Activities/DispatchWorkflow.cs +++ b/src/modules/Elsa.Workflows.Runtime/Activities/DispatchWorkflow.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Elsa.Common.Models; using Elsa.Extensions; using Elsa.Workflows.Core; @@ -18,6 +19,11 @@ namespace Elsa.Workflows.Runtime.Activities; [PublicAPI] public class DispatchWorkflow : Activity, IBookmarksPersistedHandler { + /// + public DispatchWorkflow([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + { + } + /// /// The definition ID of the workflow to dispatch. /// diff --git a/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs b/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs index 0434e1162..082dc7bb0 100644 --- a/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs +++ b/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Core; @@ -18,12 +17,6 @@ namespace Elsa.Workflows.Runtime.Activities; [PublicAPI] public class Event : Trigger { - /// - [JsonConstructor] - public Event() - { - } - /// internal Event([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Runtime/Activities/PublishEvent.cs b/src/modules/Elsa.Workflows.Runtime/Activities/PublishEvent.cs index e2f2ecf5f..545605b2d 100644 --- a/src/modules/Elsa.Workflows.Runtime/Activities/PublishEvent.cs +++ b/src/modules/Elsa.Workflows.Runtime/Activities/PublishEvent.cs @@ -4,7 +4,6 @@ using Elsa.Workflows.Core.Models; using Elsa.Workflows.Runtime.Contracts; using JetBrains.Annotations; using System.Runtime.CompilerServices; -using System.Text.Json.Serialization; using Elsa.Workflows.Core; namespace Elsa.Workflows.Runtime.Activities; @@ -16,12 +15,6 @@ namespace Elsa.Workflows.Runtime.Activities; [PublicAPI] public class PublishEvent : Activity { - /// - [JsonConstructor] - public PublishEvent() - { - } - /// public PublishEvent([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { diff --git a/src/modules/Elsa.Workflows.Runtime/Activities/RunTask.cs b/src/modules/Elsa.Workflows.Runtime/Activities/RunTask.cs index 3f7e8a35a..a0078298c 100644 --- a/src/modules/Elsa.Workflows.Runtime/Activities/RunTask.cs +++ b/src/modules/Elsa.Workflows.Runtime/Activities/RunTask.cs @@ -43,10 +43,10 @@ public class RunTask : Activity, IBookmarksPersistedHandler /// [JsonConstructor] - public RunTask() + private RunTask(string? source = default, int? line = default) : base(source, line) { } - + /// public RunTask(MemoryBlockReference output, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(output, source, line) { diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.AzureServiceBusActivities/Pages/Index.cshtml b/src/samples/aspnet/Elsa.Samples.AspNet.AzureServiceBusActivities/Pages/Index.cshtml index d76ef7cc9..743ebb3a6 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.AzureServiceBusActivities/Pages/Index.cshtml +++ b/src/samples/aspnet/Elsa.Samples.AspNet.AzureServiceBusActivities/Pages/Index.cshtml @@ -1,6 +1,5 @@ @page @using Elsa.Workflows.Designer -@using Microsoft.AspNetCore.Mvc.TagHelpers @{ var serverUrl = Url.Content("elsa/api"); } diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.ElasticsearchStorage/Pages/Index.cshtml b/src/samples/aspnet/Elsa.Samples.AspNet.ElasticsearchStorage/Pages/Index.cshtml index d76ef7cc9..743ebb3a6 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.ElasticsearchStorage/Pages/Index.cshtml +++ b/src/samples/aspnet/Elsa.Samples.AspNet.ElasticsearchStorage/Pages/Index.cshtml @@ -1,6 +1,5 @@ @page @using Elsa.Workflows.Designer -@using Microsoft.AspNetCore.Mvc.TagHelpers @{ var serverUrl = Url.Content("elsa/api"); } diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.HelloWorld/HelloWorldHttpWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.HelloWorld/HelloWorldHttpWorkflow.cs index 4b9fd993a..04ba62bfc 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.HelloWorld/HelloWorldHttpWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.HelloWorld/HelloWorldHttpWorkflow.cs @@ -1,6 +1,6 @@ using System.Net; using Elsa.Http; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/SubmissionWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/SubmissionWorkflow.cs index 63b6cbb4c..384cc015c 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/SubmissionWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/SubmissionWorkflow.cs @@ -1,7 +1,7 @@ using System.Net.Mime; using Elsa.Extensions; using Elsa.Http; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/TypedSubmissionWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/TypedSubmissionWorkflow.cs index abeb969dd..844391d73 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/TypedSubmissionWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.HttpEndpoints/Workflows/TypedSubmissionWorkflow.cs @@ -2,7 +2,7 @@ using System.Net.Mime; using Elsa.Http; using Elsa.Samples.AspNet.HttpEndpoints.Activities; using Elsa.Samples.AspNet.HttpEndpoints.Models; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.MassTransitActivities/Pages/Index.cshtml b/src/samples/aspnet/Elsa.Samples.AspNet.MassTransitActivities/Pages/Index.cshtml index d76ef7cc9..743ebb3a6 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.MassTransitActivities/Pages/Index.cshtml +++ b/src/samples/aspnet/Elsa.Samples.AspNet.MassTransitActivities/Pages/Index.cshtml @@ -1,6 +1,5 @@ @page @using Elsa.Workflows.Designer -@using Microsoft.AspNetCore.Mvc.TagHelpers @{ var serverUrl = Url.Content("elsa/api"); } diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.MyBackendApi/Workflows/HelloWorldHttpWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.MyBackendApi/Workflows/HelloWorldHttpWorkflow.cs index 5e1861f43..830093d46 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.MyBackendApi/Workflows/HelloWorldHttpWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.MyBackendApi/Workflows/HelloWorldHttpWorkflow.cs @@ -1,5 +1,5 @@ using Elsa.Http; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Home/Index.cshtml b/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Home/Index.cshtml index 3881a961d..f67020193 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Home/Index.cshtml +++ b/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Home/Index.cshtml @@ -1,5 +1,4 @@ -@using Microsoft.AspNetCore.Mvc.TagHelpers -@model Elsa.Samples.AspNet.Onboarding.Web.Views.Home.IndexViewModel +@model Elsa.Samples.AspNet.Onboarding.Web.Views.Home.IndexViewModel @{ ViewData["Title"] = "Home Page"; } diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Shared/_Layout.cshtml b/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Shared/_Layout.cshtml index 1a151c76f..64630617e 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Shared/_Layout.cshtml +++ b/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.Web/Views/Shared/_Layout.cshtml @@ -1,5 +1,4 @@ -@using Microsoft.AspNetCore.Mvc.TagHelpers - + diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.WorkflowServer/Workflows/OnboardingWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.WorkflowServer/Workflows/OnboardingWorkflow.cs index 4c87f7e6a..6bd16a787 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.WorkflowServer/Workflows/OnboardingWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.Onboarding.WorkflowServer/Workflows/OnboardingWorkflow.cs @@ -1,6 +1,6 @@ using Elsa.Extensions; using Elsa.Samples.AspNet.Onboarding.WorkflowServer.Models; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Runtime.Activities; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Workflows/HeartbeatWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Workflows/HeartbeatWorkflow.cs index de925c299..2b814f26e 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Workflows/HeartbeatWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.QuartzIntegration/Workflows/HeartbeatWorkflow.cs @@ -1,8 +1,9 @@ using Elsa.Common.Contracts; using Elsa.Scheduling.Activities; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; +using Elsa.Workflows.Core.Models; using JetBrains.Annotations; namespace Elsa.Samples.AspNet.QuartzIntegration.Workflows; @@ -16,7 +17,7 @@ public class HeartbeatWorkflow : WorkflowBase { _systemClock = systemClock; } - + protected override void Build(IWorkflowBuilder builder) { builder.Root = new Sequence @@ -28,10 +29,7 @@ public class HeartbeatWorkflow : WorkflowBase CronExpression = new("*/1 * * * * ?"), CanStartWorkflow = true }, - new WriteLine - { - Text = new($"Heartbeat workflow triggered at {_systemClock.UtcNow.LocalDateTime}") - } + new WriteLine(new Input($"Heartbeat workflow triggered at {_systemClock.UtcNow.LocalDateTime}")) } }; } diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.RunTaskIntegration/Workflows/HungryWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.RunTaskIntegration/Workflows/HungryWorkflow.cs index 51e07f5e0..36e260087 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.RunTaskIntegration/Workflows/HungryWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.RunTaskIntegration/Workflows/HungryWorkflow.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using Elsa.Http; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.Webhooks.WorkflowServer/Workflows/HungryWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.Webhooks.WorkflowServer/Workflows/HungryWorkflow.cs index 06d1c2704..6274e5c54 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.Webhooks.WorkflowServer/Workflows/HungryWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.Webhooks.WorkflowServer/Workflows/HungryWorkflow.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using Elsa.Http; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Models; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Workflows/CustomerCommunicationsWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Workflows/CustomerCommunicationsWorkflow.cs index adb0a8e12..d06b525a3 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Workflows/CustomerCommunicationsWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowContexts/Workflows/CustomerCommunicationsWorkflow.cs @@ -4,7 +4,7 @@ using Elsa.Samples.AspNet.WorkflowContexts.Providers; using Elsa.Samples.AspNet.WorkflowContexts.Extensions; using Elsa.Scheduling.Activities; using Elsa.WorkflowContexts.Activities; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowDesigner/Pages/Index.cshtml b/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowDesigner/Pages/Index.cshtml index 16c1b978f..260393a07 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowDesigner/Pages/Index.cshtml +++ b/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowDesigner/Pages/Index.cshtml @@ -1,5 +1,4 @@ @page -@using Microsoft.AspNetCore.Mvc.TagHelpers @{ var serverUrl = "https://localhost:7248/elsa/api"; } diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowSinks/Workflows/OrderWorkflow.cs b/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowSinks/Workflows/OrderWorkflow.cs index 0ee92add9..256569a20 100644 --- a/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowSinks/Workflows/OrderWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.AspNet.WorkflowSinks/Workflows/OrderWorkflow.cs @@ -1,5 +1,5 @@ using Elsa.Extensions; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/LastResultWorkflow.cs b/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/LastResultWorkflow.cs index 3ab68d77a..ea89853d7 100644 --- a/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/LastResultWorkflow.cs +++ b/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/LastResultWorkflow.cs @@ -1,5 +1,5 @@ using Elsa.Extensions; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/TargetActivityOutputWorkflow.cs b/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/TargetActivityOutputWorkflow.cs index dedb67667..2e7bc1873 100644 --- a/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/TargetActivityOutputWorkflow.cs +++ b/src/samples/console/Elsa.Samples.ConsoleApp.ActivityOutput/Workflows/TargetActivityOutputWorkflow.cs @@ -1,5 +1,5 @@ using Elsa.Extensions; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/console/Elsa.Samples.ConsoleApp.OutboundHttpRequests/Workflows/GetUsersWorkflow.cs b/src/samples/console/Elsa.Samples.ConsoleApp.OutboundHttpRequests/Workflows/GetUsersWorkflow.cs index 0d2823158..b3f919fc4 100644 --- a/src/samples/console/Elsa.Samples.ConsoleApp.OutboundHttpRequests/Workflows/GetUsersWorkflow.cs +++ b/src/samples/console/Elsa.Samples.ConsoleApp.OutboundHttpRequests/Workflows/GetUsersWorkflow.cs @@ -1,6 +1,6 @@ using System.Dynamic; using Elsa.Http; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Microsoft.AspNetCore.Http; diff --git a/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddInputsWorkflow.cs b/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddInputsWorkflow.cs index 4e86a0c17..8dc603b31 100644 --- a/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddInputsWorkflow.cs +++ b/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddInputsWorkflow.cs @@ -1,5 +1,5 @@ using Elsa.Extensions; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddWorkflow.cs b/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddWorkflow.cs index 3145fc8ae..fe1f58492 100644 --- a/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddWorkflow.cs +++ b/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/AddWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/SumInputsWorkflow.cs b/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/SumInputsWorkflow.cs index 0257ede74..ce748b1e0 100644 --- a/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/SumInputsWorkflow.cs +++ b/src/samples/console/Elsa.Samples.ConsoleApp.WorkflowFunctions/Workflows/SumInputsWorkflow.cs @@ -1,5 +1,5 @@ using Elsa.Extensions; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Models; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForEachWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForEachWorkflow.cs index 0185957d1..96001fc92 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForEachWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForEachWorkflow.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForWorkflow.cs index 26823906f..1912e3f2e 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakForWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileForkWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileForkWorkflow.cs index 5d94b1669..f0fac1348 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileForkWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileForkWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileWorkflow.cs index f3e13c6e0..2d92acb0c 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Break/BreakWhileWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishForkedWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishForkedWorkflow.cs index 43e4d0906..3f5c74817 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishForkedWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishForkedWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Runtime.Activities; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishSequentialWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishSequentialWorkflow.cs index 81772ce23..563dc6519 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishSequentialWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Finish/FinishSequentialWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/test/integration/Elsa.IntegrationTests/Activities/ForEach/ForEachWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/ForEach/ForEachWorkflow.cs index 82b0d5a05..306eba7a4 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/ForEach/ForEachWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/ForEach/ForEachWorkflow.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Activities/ForEach/NestedForEachWithBreakWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/ForEach/NestedForEachWithBreakWorkflow.cs index c4b010a4d..a368dbb0c 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/ForEach/NestedForEachWithBreakWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/ForEach/NestedForEachWithBreakWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Fork/BasicForkWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Fork/BasicForkWorkflow.cs index dbc12708a..71f2994fe 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Fork/BasicForkWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Fork/BasicForkWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Fork/JoinAnyForkWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Fork/JoinAnyForkWorkflow.cs index 722c1f031..27d52ee58 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Fork/JoinAnyForkWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Fork/JoinAnyForkWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Runtime.Activities; diff --git a/test/integration/Elsa.IntegrationTests/Activities/If/ComplexIfWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/If/ComplexIfWorkflow.cs index 3688156eb..846856b79 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/If/ComplexIfWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/If/ComplexIfWorkflow.cs @@ -1,5 +1,5 @@ using System; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/test/integration/Elsa.IntegrationTests/Activities/If/IfThenWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/If/IfThenWorkflow.cs index 7b6808954..477f33ac0 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/If/IfThenWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/If/IfThenWorkflow.cs @@ -1,5 +1,5 @@ using System; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Sequence/NestedSequentialWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Sequence/NestedSequentialWorkflow.cs index ddc42dc6c..54aedb315 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Sequence/NestedSequentialWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Sequence/NestedSequentialWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/test/integration/Elsa.IntegrationTests/Activities/Sequence/SequentialWorkflow.cs b/test/integration/Elsa.IntegrationTests/Activities/Sequence/SequentialWorkflow.cs index ed35730ad..11be9b895 100644 --- a/test/integration/Elsa.IntegrationTests/Activities/Sequence/SequentialWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Activities/Sequence/SequentialWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/ActivityNotificationsMiddleware/Workflows.cs b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityNotificationsMiddleware/Workflows.cs index 976751b4e..4f284e565 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/ActivityNotificationsMiddleware/Workflows.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/ActivityNotificationsMiddleware/Workflows.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/Blocking/Workflows.cs b/test/integration/Elsa.IntegrationTests/Scenarios/Blocking/Workflows.cs index 6a8a800be..9a2f14368 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/Blocking/Workflows.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/Blocking/Workflows.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Runtime.Activities; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/BlockingAndBreaking/Workflows.cs b/test/integration/Elsa.IntegrationTests/Scenarios/BlockingAndBreaking/Workflows.cs index 0c9f2ef54..cc87197c7 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/BlockingAndBreaking/Workflows.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/BlockingAndBreaking/Workflows.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/Composites/Workflows.cs b/test/integration/Elsa.IntegrationTests/Scenarios/Composites/Workflows.cs index 9bfd3d64f..77fbc7040 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/Composites/Workflows.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/Composites/Workflows.cs @@ -1,7 +1,6 @@ using Elsa.Extensions; using Elsa.JavaScript.Activities; using Elsa.Workflows.Core; -using Elsa.Workflows.Core.Abstractions; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs b/test/integration/Elsa.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs index d5c8f9562..3be52f04f 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/CompositesPassingData/Workflows.cs @@ -1,5 +1,5 @@ using Elsa.Extensions; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/BraidedWorkflow.cs b/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/BraidedWorkflow.cs index 41ab81ef9..7f97fea16 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/BraidedWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/BraidedWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Activities.Flowchart.Activities; using Elsa.Workflows.Core.Activities.Flowchart.Models; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/ImplicitLoopWorkflow.cs b/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/ImplicitLoopWorkflow.cs index b85c57eb1..fef7c570f 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/ImplicitLoopWorkflow.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/ImplicitJoins/Workflows/ImplicitLoopWorkflow.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Activities.Flowchart.Activities; using Elsa.Workflows.Core.Activities.Flowchart.Models; diff --git a/test/integration/Elsa.IntegrationTests/Scenarios/SetGetVariables/Workflows.cs b/test/integration/Elsa.IntegrationTests/Scenarios/SetGetVariables/Workflows.cs index 2ce0e5aa3..893dd5912 100644 --- a/test/integration/Elsa.IntegrationTests/Scenarios/SetGetVariables/Workflows.cs +++ b/test/integration/Elsa.IntegrationTests/Scenarios/SetGetVariables/Workflows.cs @@ -1,5 +1,5 @@ using Elsa.Extensions; -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Contracts; using Elsa.Workflows.Core.Memory; diff --git a/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs index 6f9dd03db..383330c0e 100644 --- a/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs +++ b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs @@ -1,4 +1,4 @@ -using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Contracts; namespace Elsa.IntegrationTests.Serialization.VariableTypes;