Signaling fixes and enhancements
This commit is contained in:
parent
37c07be05c
commit
09f11c026e
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
// ReSharper disable ExplicitCallerInfoArgument
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Activities.Signaling
|
||||
{
|
||||
public static class SendSignalBuilderExtensions
|
||||
{
|
||||
public static IActivityBuilder SendSignal(this IBuilder builder, Action<ISetupActivity<SendSignal>> setup, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.Then(setup, null, lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder SendSignal(this IBuilder builder, Func<ActivityExecutionContext, string> signal, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendSignal(activity => activity.WithSignal(signal), lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder SendSignal(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<string>> signal, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendSignal(activity => activity.WithSignal(signal!), lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder SendSignal(this IBuilder builder, Func<string> signal, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendSignal(activity => activity.WithSignal(signal!), lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder SendSignal(this IBuilder builder, Func<ValueTask<string>> signal, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendSignal(activity => activity.WithSignal(signal!), lineNumber, sourceFile);
|
||||
|
||||
public static IActivityBuilder SendSignal(this IBuilder builder, string signal, [CallerLineNumber] int lineNumber = default, [CallerFilePath] string? sourceFile = default) =>
|
||||
builder.SendSignal(activity => activity.WithSignal(signal!), lineNumber, sourceFile);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Activities.Signaling
|
||||
{
|
||||
public static class SendSignalExtensions
|
||||
{
|
||||
public static ISetupActivity<SendSignal> WithSignal(this ISetupActivity<SendSignal> activity, Func<ActivityExecutionContext, ValueTask<string?>> value) => activity.Set(x => x.Signal, value);
|
||||
public static ISetupActivity<SendSignal> WithSignal(this ISetupActivity<SendSignal> activity, Func<ActivityExecutionContext, string?> value) => activity.Set(x => x.Signal, value);
|
||||
public static ISetupActivity<SendSignal> WithSignal(this ISetupActivity<SendSignal> activity, Func<ValueTask<string?>> value) => activity.Set(x => x.Signal, value);
|
||||
public static ISetupActivity<SendSignal> WithSignal(this ISetupActivity<SendSignal> activity, Func<string?> value) => activity.Set(x => x.Signal, value);
|
||||
public static ISetupActivity<SendSignal> WithSignal(this ISetupActivity<SendSignal> activity, string? value) => activity.Set(x => x.Signal, value);
|
||||
|
||||
public static ISetupActivity<SendSignal> WithCorrelationId(this ISetupActivity<SendSignal> activity, Func<ActivityExecutionContext, ValueTask<string?>> value) => activity.Set(x => x.CorrelationId, value);
|
||||
public static ISetupActivity<SendSignal> WithCorrelationId(this ISetupActivity<SendSignal> activity, Func<ActivityExecutionContext, string?> value) => activity.Set(x => x.CorrelationId, value);
|
||||
public static ISetupActivity<SendSignal> WithCorrelationId(this ISetupActivity<SendSignal> activity, Func<ValueTask<string?>> value) => activity.Set(x => x.CorrelationId, value);
|
||||
public static ISetupActivity<SendSignal> WithCorrelationId(this ISetupActivity<SendSignal> activity, Func<string?> value) => activity.Set(x => x.CorrelationId, value);
|
||||
public static ISetupActivity<SendSignal> WithCorrelationId(this ISetupActivity<SendSignal> activity, string? value) => activity.Set(x => x.CorrelationId, value);
|
||||
|
||||
public static ISetupActivity<SendSignal> WithInput(this ISetupActivity<SendSignal> activity, Func<ActivityExecutionContext, ValueTask<object?>> value) => activity.Set(x => x.Input, value);
|
||||
public static ISetupActivity<SendSignal> WithInput(this ISetupActivity<SendSignal> activity, Func<ActivityExecutionContext, object?> value) => activity.Set(x => x.Input, value);
|
||||
public static ISetupActivity<SendSignal> WithInput(this ISetupActivity<SendSignal> activity, Func<ValueTask<object?>> value) => activity.Set(x => x.Input, value);
|
||||
public static ISetupActivity<SendSignal> WithInput(this ISetupActivity<SendSignal> activity, Func<object?> value) => activity.Set(x => x.Input, value);
|
||||
public static ISetupActivity<SendSignal> WithInput(this ISetupActivity<SendSignal> activity, object? value) => activity.Set(x => x.Input, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,14 +14,18 @@ namespace Elsa.Activities.Signaling
|
|||
|
||||
public class SignalReceivedBookmarkProvider : BookmarkProvider<SignalReceivedBookmark, SignalReceived>
|
||||
{
|
||||
public override async ValueTask<IEnumerable<IBookmark>> GetBookmarksAsync(BookmarkProviderContext<SignalReceived> context, CancellationToken cancellationToken) =>
|
||||
new[]
|
||||
public override async ValueTask<IEnumerable<IBookmark>> GetBookmarksAsync(BookmarkProviderContext<SignalReceived> context, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowInstanceId = context.Mode == BookmarkIndexingMode.WorkflowBlueprint ? default : context.ActivityExecutionContext.WorkflowInstance.Id;
|
||||
|
||||
return new[]
|
||||
{
|
||||
new SignalReceivedBookmark
|
||||
{
|
||||
Signal = (await context.Activity.GetPropertyValueAsync(x => x.Signal, cancellationToken))!,
|
||||
WorkflowInstanceId = context.ActivityExecutionContext.WorkflowInstance.Id
|
||||
WorkflowInstanceId = workflowInstanceId
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ export const SignalReceivedIcon = props =>
|
|||
(
|
||||
`<span class="${`rounded-lg inline-flex p-3 bg-rose-50 text-rose-700 ring-4 ring-white`}">
|
||||
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke="none" d="M0 0h24v24H0z"/> <polyline points="21 12 17 12 14 20 10 4 7 12 3 12"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
||||
</svg>
|
||||
</span>`
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
import {ElsaPlugin} from "../services/elsa-plugin";
|
||||
import {eventBus} from '../services/event-bus';
|
||||
import {ActivityDesignDisplayContext, EventTypes, SyntaxNames} from "../models";
|
||||
import {h} from "@stencil/core";
|
||||
|
||||
export class SendSignalPlugin implements ElsaPlugin {
|
||||
constructor() {
|
||||
eventBus.on(EventTypes.ActivityDesignDisplaying, this.onActivityDisplaying);
|
||||
}
|
||||
|
||||
onActivityDisplaying(context: ActivityDesignDisplayContext) {
|
||||
const activityModel = context.activityModel;
|
||||
|
||||
if (activityModel.type !== 'SendSignal')
|
||||
return;
|
||||
|
||||
const props = activityModel.properties || [];
|
||||
const signalName = props.find(x => x.name == 'Signal') || { name: 'Signal', expressions: { 'Literal': '', syntax: SyntaxNames.Literal } };
|
||||
const syntax = signalName.syntax || SyntaxNames.Literal;
|
||||
context.bodyDisplay = `<p>${signalName.expressions[syntax]}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import {ElsaPlugin} from "../services/elsa-plugin";
|
||||
import {eventBus} from '../services/event-bus';
|
||||
import {ActivityDesignDisplayContext, EventTypes, SyntaxNames} from "../models";
|
||||
import {h} from "@stencil/core";
|
||||
|
||||
export class SignalReceivedPlugin implements ElsaPlugin {
|
||||
constructor() {
|
||||
eventBus.on(EventTypes.ActivityDesignDisplaying, this.onActivityDisplaying);
|
||||
}
|
||||
|
||||
onActivityDisplaying(context: ActivityDesignDisplayContext) {
|
||||
const activityModel = context.activityModel;
|
||||
|
||||
if (activityModel.type !== 'SignalReceived')
|
||||
return;
|
||||
|
||||
const props = activityModel.properties || [];
|
||||
const signalName = props.find(x => x.name == 'Signal') || { name: 'Signal', expressions: { 'Literal': '', syntax: SyntaxNames.Literal } };
|
||||
const syntax = signalName.syntax || SyntaxNames.Literal;
|
||||
context.bodyDisplay = `<p>${signalName.expressions[syntax]}</p>`;
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@ import {SwitchPlugin} from "../plugins/switch-plugin";
|
|||
import {WhilePlugin} from "../plugins/while-plugin";
|
||||
import {StartAtPlugin} from "../plugins/start-at-plugin";
|
||||
import {CronPlugin} from "../plugins/cron-plugin";
|
||||
import {SignalReceivedPlugin} from "../plugins/signal-received-plugin";
|
||||
import {SendSignalPlugin} from "../plugins/send-signal-plugin";
|
||||
|
||||
export class PluginManager {
|
||||
|
||||
|
|
@ -29,6 +31,8 @@ export class PluginManager {
|
|||
new TimerPlugin(),
|
||||
new StartAtPlugin(),
|
||||
new CronPlugin(),
|
||||
new SignalReceivedPlugin(),
|
||||
new SendSignalPlugin(),
|
||||
new WriteLinePlugin(),
|
||||
new RunJavascriptPlugin(),
|
||||
new SendEmailPlugin()
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Signaling;
|
||||
using Elsa.Activities.Signaling.Models;
|
||||
using Elsa.Activities.Signaling.Services;
|
||||
using Elsa.Dispatch;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Samples.SignalingConsole
|
||||
|
|
@ -24,41 +17,29 @@ namespace Elsa.Samples.SignalingConsole
|
|||
var services = new ServiceCollection()
|
||||
.AddElsa(options => options
|
||||
.AddConsoleActivities()
|
||||
.AddWorkflow<TrafficLightWorkflow>())
|
||||
.AddWorkflowsFrom<SignalReceiverWorkflow>())
|
||||
.BuildServiceProvider();
|
||||
|
||||
// Run startup actions (not needed when registering Elsa with a Host).
|
||||
// Run startup actions (will index triggers such as SignalReceived from workflows).
|
||||
var startupRunner = services.GetRequiredService<IStartupRunner>();
|
||||
await startupRunner.StartupAsync();
|
||||
|
||||
|
||||
Console.WriteLine("Press enter to send a signal from a workflow:");
|
||||
Console.ReadLine();
|
||||
|
||||
// Get a workflow runner.
|
||||
var workflowRunner = services.GetRequiredService<IBuildsAndStartsWorkflow>();
|
||||
|
||||
// Define a couple of cars so we can correlate workflows with them.
|
||||
var cars = new[] { "Car 1", "Car 2" };
|
||||
|
||||
var workflowInstances = new List<WorkflowInstance>();
|
||||
|
||||
// Execute a workflow for each car.
|
||||
foreach (var car in cars)
|
||||
{
|
||||
var workflowInstance = await workflowRunner.BuildAndStartWorkflowAsync<TrafficLightWorkflow>(correlationId: car);
|
||||
workflowInstances.Add(workflowInstance);
|
||||
}
|
||||
|
||||
Console.WriteLine("Hit enter to signal green light for Car 2.");
|
||||
|
||||
// This workflow will send a signal, which will be handled by the `SignalReceiverWorkflow`.
|
||||
await workflowRunner.BuildAndStartWorkflowAsync<SignalSenderWorkflow>();
|
||||
|
||||
// We can also trigger a signal manually:
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Press enter again to send a signal manually:");
|
||||
Console.ReadLine();
|
||||
|
||||
// The workflows are now suspended at the red light.
|
||||
// Trigger a green light signal for the first car.
|
||||
var signal = "Green";
|
||||
var correlationId = "Car 2";
|
||||
var car2Workflow = workflowInstances.First(x => x.CorrelationId == correlationId);
|
||||
|
||||
var signaler = services.GetRequiredService<ISignaler>();
|
||||
|
||||
await signaler.TriggerSignalAsync(signal, workflowInstanceId: car2Workflow.Id);
|
||||
|
||||
// Notice that only the workflow correlated to the second car executed.
|
||||
await signaler.TriggerSignalAsync("Demo Signal");
|
||||
|
||||
// Keep the application alive for the workflow scheduler to have enough time to resume the workflow.
|
||||
Console.ReadLine();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using Elsa.Activities.Console;
|
||||
using Elsa.Activities.ControlFlow;
|
||||
using Elsa.Builders;
|
||||
|
||||
namespace Elsa.Samples.SignalingConsole
|
||||
{
|
||||
public class SignalReceiverWorkflow : IWorkflow
|
||||
{
|
||||
public void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder
|
||||
.SignalReceived("Demo Signal")
|
||||
.WriteLine(context => $"Signal received!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Elsa.Activities.Console;
|
||||
using Elsa.Activities.Signaling;
|
||||
using Elsa.Builders;
|
||||
|
||||
namespace Elsa.Samples.SignalingConsole
|
||||
{
|
||||
public class SignalSenderWorkflow : IWorkflow
|
||||
{
|
||||
public void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder
|
||||
.WriteLine(context => $"Sending signal from workflow!")
|
||||
.SendSignal("Demo Signal")
|
||||
.WriteLine(context => $"Signal sent from workflow!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
using Elsa.Activities.Console;
|
||||
using Elsa.Activities.ControlFlow;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Samples.SignalingConsole
|
||||
{
|
||||
public class TrafficLightWorkflow : IWorkflow
|
||||
{
|
||||
public void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
builder
|
||||
.WriteLine(context => $"{GetCarName(context)} is approaching red traffic light...")
|
||||
.SignalReceived("Green")
|
||||
.WriteLine(context => $"Light turned green for {GetCarName(context)}. Hit that power pedal!");
|
||||
}
|
||||
|
||||
private string GetCarName(ActivityExecutionContext context) => context.WorkflowExecutionContext.CorrelationId;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue