* Refactors and clarifies Flowchart merge modes Improves the clarity and functionality of Flowchart merge modes by: - Renaming `None` to `Stream` for opportunistic execution. - Introducing `Merge` for waiting on activated branches only. - Enhancing `Converge` to be the strictest mode, requiring all inbound connections. - Providing more detailed descriptions for each mode, emphasizing their behavior and use cases in flow-based terminology. - Updates default merge mode to Stream This provides better control over synchronization and execution behavior in workflows. * Refactor `ActivityExtensions` to improve formatting, fix indentation, and align comments for improved readability and consistency * Update flowchart tests: replace `SetMergeMode(MergeMode.None)` with `SetMergeMode(null)` and remove unused `Elsa.Workflows` imports. * Restore None value for Flowchart MergeMode enum (#7107) * Initial plan * Add None value to MergeMode enum for backward compatibility Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> * Fix API client GetMergeMode to maintain non-nullable return type Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> * [WIP] Address feedback on flowchart merge modes refactor (#7106) * Initial plan * Fix misleading documentation for Merge mode to match actual implementation Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> * Update test/integration/Elsa.Workflows.IntegrationTests/Scenarios/JoinBehaviors/ForkDecisionJoinTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test/integration/Elsa.Workflows.IntegrationTests/Scenarios/JoinBehaviors/ForkDecisionJoinTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update test scenarios for implicit join behavior in `ForkDecisionJoinTests`. Updated file references for merge and stream join modes. --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
232 lines
11 KiB
C#
232 lines
11 KiB
C#
using System.Text.Json.Nodes;
|
|
using Elsa.Api.Client.Resources.Resilience.Models;
|
|
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
|
|
using Elsa.Api.Client.Shared.Enums;
|
|
using Elsa.Api.Client.Shared.Models;
|
|
|
|
namespace Elsa.Api.Client.Extensions;
|
|
|
|
/// <summary>
|
|
/// Provides extension methods for <see cref="JsonObject"/>.
|
|
/// </summary>
|
|
public static class ActivityExtensions
|
|
{
|
|
/// <summary>
|
|
/// Gets the type name of the specified activity.
|
|
/// </summary>
|
|
public static string GetTypeName(this JsonObject activity) => activity.GetProperty<string>("type")!;
|
|
|
|
/// <summary>
|
|
/// Gets the type name of the specified activity.
|
|
/// </summary>
|
|
public static void SetTypeName(this JsonObject activity, string value) => activity.SetProperty(JsonValue.Create(value), "type");
|
|
|
|
/// <summary>
|
|
/// Gets the version of the specified activity.
|
|
/// </summary>
|
|
public static int GetVersion(this JsonObject activity) => activity.GetProperty<int>("version");
|
|
|
|
/// <summary>
|
|
/// Gets the version of the specified activity.
|
|
/// </summary>
|
|
public static void SetVersion(this JsonObject activity, int value) => activity.SetProperty(JsonValue.Create(value), "version");
|
|
|
|
/// <summary>
|
|
/// Gets the ID of the specified activity.
|
|
/// </summary>
|
|
public static string GetId(this JsonObject activity) => activity.GetProperty<string>("id")!;
|
|
|
|
/// <summary>
|
|
/// Sets the ID of the specified activity.
|
|
/// </summary>
|
|
public static void SetId(this JsonObject activity, string value) => activity.SetProperty(JsonValue.Create(value), "id");
|
|
|
|
/// <summary>
|
|
/// Gets the node ID of the specified activity.
|
|
/// </summary>
|
|
public static string GetNodeId(this JsonObject activity) => activity.GetProperty<string>("nodeId")!;
|
|
|
|
/// <summary>
|
|
/// Sets the node ID of the specified activity.
|
|
/// </summary>
|
|
public static void SetNodeId(this JsonObject activity, string value) => activity.SetProperty(JsonValue.Create(value), "nodeId");
|
|
|
|
/// <summary>
|
|
/// Gets the name of the specified activity.
|
|
/// </summary>
|
|
public static string? GetName(this JsonObject activity) => activity.GetProperty<string>("name");
|
|
|
|
/// <summary>
|
|
/// Sets the name of the specified activity.
|
|
/// </summary>
|
|
public static void SetName(this JsonObject activity, string? value) => activity.SetProperty(JsonValue.Create(value), "name");
|
|
|
|
/// <summary>
|
|
/// Gets the designer metadata for the specified activity.
|
|
/// </summary>
|
|
public static JsonObject? GetMetadata(this JsonObject activity) => activity.GetProperty("metadata")?.AsObject();
|
|
|
|
/// <summary>
|
|
/// Sets the designer metadata for the specified activity.
|
|
/// </summary>
|
|
public static void SetDesignerMetadata(this JsonObject activity, ActivityDesignerMetadata designerMetadata) => activity.SetProperty(designerMetadata.SerializeToNode(), "metadata", "designer");
|
|
|
|
/// <summary>
|
|
/// Gets the designer metadata for the specified activity.
|
|
/// </summary>
|
|
public static ActivityDesignerMetadata GetDesignerMetadata(this JsonObject activity) => activity.GetProperty<ActivityDesignerMetadata>("metadata", "designer") ?? new ActivityDesignerMetadata();
|
|
|
|
/// <summary>
|
|
/// Gets the display text for the specified activity.
|
|
/// </summary>
|
|
public static string? GetDisplayText(this JsonObject activity) => activity.GetProperty("metadata", "displayText")?.GetValue<string>();
|
|
|
|
/// <summary>
|
|
/// Sets the display text for the specified activity.
|
|
/// </summary>
|
|
public static void SetDisplayText(this JsonObject activity, string? value)
|
|
{
|
|
activity.SetProperty(JsonValue.Create(value), "metadata", "displayText");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the description for the specified activity.
|
|
/// </summary>
|
|
public static string? GetDescription(this JsonObject activity) => activity.GetProperty("metadata", "description")?.GetValue<string>();
|
|
|
|
/// <summary>
|
|
/// Sets the description for the specified activity.
|
|
/// </summary>
|
|
public static void SetDescription(this JsonObject activity, string value) => activity.SetProperty(JsonValue.Create(value), "metadata", "description");
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the description for the specified activity should be shown.
|
|
/// </summary>
|
|
public static bool? GetShowDescription(this JsonObject activity) => activity.GetProperty<bool>("metadata", "showDescription");
|
|
|
|
/// <summary>
|
|
/// Sets a value indicating whether the description for the specified activity should be shown.
|
|
/// </summary>
|
|
public static void SetShowDescription(this JsonObject activity, bool value) => activity.SetProperty(JsonValue.Create(value), "metadata", "showDescription");
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the specified activity can trigger the workflow.
|
|
/// </summary>
|
|
public static bool? GetCanStartWorkflow(this JsonObject activity) => activity.GetProperty<bool>("customProperties", "canStartWorkflow");
|
|
|
|
/// <summary>
|
|
/// Sets a value indicating whether the specified activity can trigger the workflow.
|
|
/// </summary>
|
|
public static void SetCanStartWorkflow(this JsonObject activity, bool value) => activity.SetProperty(JsonValue.Create(value), "customProperties", "canStartWorkflow");
|
|
|
|
public static MergeMode GetMergeMode(this JsonObject activity)
|
|
{
|
|
var value = activity.GetProperty<MergeMode?>("customProperties", "mergeMode");
|
|
// Treat MergeMode.None as equivalent to null (no merge mode set), defaulting to Stream
|
|
return value == null || value == MergeMode.None ? MergeMode.Stream : value.Value;
|
|
}
|
|
|
|
public static void SetMergeMode(this JsonObject activity, MergeMode? value)
|
|
{
|
|
// Treat MergeMode.None as equivalent to null (no merge mode set)
|
|
if (value == MergeMode.None)
|
|
value = null;
|
|
activity.SetProperty(JsonValue.Create(value), "customProperties", "mergeMode");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the activities in the specified flowchart.
|
|
/// </summary>
|
|
public static IEnumerable<JsonObject> GetActivities(this JsonObject flowchart) => flowchart.GetProperty("activities")?.AsArray().AsEnumerable().Cast<JsonObject>() ?? [];
|
|
|
|
/// <summary>
|
|
/// Sets the activities in the specified flowchart.
|
|
/// </summary>
|
|
public static void SetActivities(this JsonObject flowchart, JsonArray activities) => flowchart.SetProperty(activities, "activities");
|
|
|
|
/// <summary>
|
|
/// Gets the connections in the specified flowchart.
|
|
/// </summary>
|
|
public static IEnumerable<Connection> GetConnections(this JsonObject flowchart) => flowchart.GetProperty<ICollection<Connection>>("connections") ?? new List<Connection>();
|
|
|
|
/// <summary>
|
|
/// Sets the connections in the specified flowchart.
|
|
/// </summary>
|
|
public static void SetConnections(this JsonObject flowchart, IEnumerable<Connection> connections) => flowchart.SetProperty(JsonValue.Create(connections), "connections");
|
|
|
|
/// <summary>
|
|
/// Gets the root activity in the specified activity.
|
|
/// </summary>
|
|
public static JsonObject? GetRoot(this JsonObject activity) => activity.GetProperty("root") as JsonObject;
|
|
|
|
/// <summary>
|
|
/// Sets the root activity in the specified activity.
|
|
/// </summary>
|
|
public static void SetRoot(this JsonObject container, JsonObject root) => container.SetProperty(root, "root");
|
|
|
|
/// <summary>
|
|
/// Gets the root activity in the specified activity.
|
|
/// </summary>
|
|
public static string? GetWorkflowDefinitionId(this JsonObject activity)
|
|
{
|
|
// Determine if this activity represents a workflow definition activity based on the presence of the following properties.
|
|
var workflowDefinitionIdNode = activity.GetProperty("workflowDefinitionId");
|
|
var workflowDefinitionVersionId = activity.GetProperty("workflowDefinitionVersionId");
|
|
var latestAvailablePublishedVersion = activity.GetProperty("latestAvailablePublishedVersion");
|
|
var latestAvailablePublishedVersionId = activity.GetProperty("latestAvailablePublishedVersionId");
|
|
|
|
if (workflowDefinitionIdNode == null || workflowDefinitionVersionId == null || latestAvailablePublishedVersion == null || latestAvailablePublishedVersionId == null)
|
|
return null; // Not a workflow definition activity.
|
|
|
|
if (workflowDefinitionIdNode is JsonValue value)
|
|
return value.ToString();
|
|
|
|
return null; // Not a workflow definition activity.
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether the given activity is a workflow definition activity.
|
|
/// </summary>
|
|
/// <param name="activity">The JsonObject representing the activity.</param>
|
|
/// <returns>Returns true if the activity is a workflow definition activity; otherwise, false.</returns>
|
|
public static bool GetIsWorkflowDefinitionActivity(this JsonObject activity) => activity.ContainsKey("workflowDefinitionId") && activity.ContainsKey("workflowDefinitionVersionId");
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the specified activity can trigger the workflow.
|
|
/// </summary>
|
|
public static bool? GetRunAsynchronously(this JsonObject activity) => activity.GetProperty<bool>("customProperties", "runAsynchronously");
|
|
|
|
/// <summary>
|
|
/// Sets a value indicating whether the specified activity can trigger the workflow.
|
|
/// </summary>
|
|
public static void SetRunAsynchronously(this JsonObject activity, bool value) => activity.SetProperty(JsonValue.Create(value), "customProperties", "runAsynchronously");
|
|
|
|
/// <summary>
|
|
/// Gets the commit state behavior for the specified activity.
|
|
/// </summary>
|
|
public static string? GetCommitStrategy(this JsonObject activity) => activity.TryGetProperty<string?>("customProperties", "commitStrategyName");
|
|
|
|
/// <summary>
|
|
/// Sets the commit state behavior for the specified activity.
|
|
/// </summary>
|
|
public static void SetCommitStrategy(this JsonObject activity, string? name) => activity.SetProperty(JsonValue.Create(name), "customProperties", "commitStrategyName");
|
|
|
|
|
|
/// <summary>
|
|
/// Gets the resilience strategy for the specified activity.
|
|
/// </summary>
|
|
public static ResilienceStrategyConfig? GetResilienceStrategy(this JsonObject activity)
|
|
{
|
|
var node = activity.GetProperty("customProperties", "resilienceStrategy");
|
|
return ResilienceStrategyConfig.Deserialize(node);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the resilience strategy for the specified activity.
|
|
/// </summary>
|
|
public static void SetResilienceStrategy(this JsonObject activity, ResilienceStrategyConfig? config)
|
|
{
|
|
var node = config?.SerializeToNode();
|
|
activity.SetProperty(node, "customProperties", "resilienceStrategy");
|
|
}
|
|
} |