elsa-core/src/clients/Elsa.Api.Client/Extensions/ActivityExtensions.cs

216 lines
10 KiB
C#
Raw Normal View History

using System.Text.Json.Nodes;
Add IResilienceStrategy Abstraction with Category Matching and Expression-Based Configuration (#6637) * Add resilience module with core interfaces and services Introduced a new `Elsa.Resilience` module and its core components to support resilient services and activities. This includes resilience strategies, providers, and attributes, along with integration into the existing HTTP module for enhanced fault tolerance. Added solution and project references for proper dependency management. * Add resilience strategy framework with HTTP strategy support Introduced a resilience strategy architecture, including a configurable `HttpResilienceStrategy` with retry capabilities, strategy serialization, and integration with existing modules. Enhanced ResilienceFeature to support registration of strategy types and updated application configuration to enable resilience strategies. This change ensures more robust and fault-tolerant HTTP request handling. * Add JSON serialization support for resilience configuration Introduced `ConfigurationExtensions` to enable JSON serialization of configuration sections. Updated resilience strategies to utilize the new extension methods and adjusted JSON serialization logic to support polymorphism with `$type` discriminator. Minor modifications were made to support deserialization and property mutability. * Add resilience strategy support to workflows and API clients Introduced resilience strategy configuration, serialization, and execution support across workflows and API clients. Added new APIs, models, and services to enhance fault tolerance capabilities for activities and HTTP interactions. * Refactor resilience services for improved modularity. Replaced `IResilienceService` with new modular interfaces (`IResilienceStrategyCatalog`, `IResilienceStrategyConfigEvaluator`, `IResilientActivityInvoker`) and corresponding implementations. Enhanced maintainability by simplifying components and responsibilities, ensuring better separation of concerns. * Rename methods in ResilienceStrategyCatalog for clarity Updated method names in `ResilienceStrategyCatalog` and its interfaces for better readability and alignment with naming conventions. Replaced `GetAllStrategiesAsync` with `ListAsync` and `GetStrategyAsync` with `GetAsync` across the codebase. * Refactor resilience handling in HTTP activities. Replaced `ResilienceCategory` property with `ResilienceCategoryAttribute` for a cleaner implementation. Updated `IResilientActivity` to simplify its interface and adjusted related modifications accordingly. Introduced `IResilientActivityInvoker` to enhance resilience strategy execution. * Add support for additional resilience and scripting features Extended resilience strategy handling with serialization support, added `HttpResilienceStrategy` type in JavaScript handler, and refined object conversion logic for interfaces. Minor adjustments to `Expression` class properties for consistency. * Remove `UseResilience` call from Program.cs This call was redundant and no longer necessary for the application. Its removal simplifies the code and ensures only required middleware is used. * Remove commented-out JSON converter code in serializer setup Cleaned up unused and commented-out converter initialization code in `ResilienceStrategySerializer`. This improves readability and removes unnecessary clutter from the file. * Fix typo in method names from 'Resiliency' to 'Resilience' Renamed methods to maintain consistency in naming conventions across the codebase. This change ensures clarity and alignment with established terminology. * Refactor namespace for ConfigurationResilienceStrategySource Updated the namespace of ConfigurationResilienceStrategySource to "StrategySources" for better alignment with naming conventions and structure. Removed an unused namespace reference in ResilienceFeature for cleanup. * Mark EnableResiliency as obsolete in SendHttpRequestBase. The EnableResiliency property is now marked with the [Obsolete] attribute. Developers are encouraged to use the common Resilience Strategy setting instead for managing HTTP request resiliency. This change ensures better consistency and alignment with the broader resilience strategy. * Restrict ResilienceCategoryAttribute to class targets only Removed support for using ResilienceCategoryAttribute on properties. This change enforces a stricter and more focused usage of the attribute, ensuring it applies only to class-level declarations. * Add documentation for IResilientActivityInvoker interface Include summaries and parameter descriptions for the `InvokeAsync` method. This improves code clarity and helps developers understand the functionality and usage of the resilient activity invocation process. * Fix logical operator precedence in type comparison check Parentheses were added to ensure correct evaluation of conditions when checking type compatibility. This prevents potential logical errors when determining the target type in object conversions. * Add support for resilience source identification Introduce the `ResilienceSourceNameAttribute` to allow naming of resilience sources. Updated `ResilienceStrategyCatalog` to utilize the attribute for prefixing strategy IDs, improving source identification and traceability. Applied the attribute to `ConfigurationResilienceStrategySource` as an example. * Revert "Add support for resilience source identification" This reverts commit 19b4e7121d6330b5de4f692b78da4c1e4a2d1f67. * Reapply "Add support for resilience source identification" This reverts commit 8bcba9d040c4eb247077aec6d90dc02817adcbd5. * Revert "Reapply "Add support for resilience source identification"" This reverts commit ee04d35e7930956c752dda3ed150ca34a535e66c.
2025-05-12 08:09:04 +00:00
using Elsa.Api.Client.Resources.ResilienceStrategies.Models;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
using Elsa.Api.Client.Shared.Models;
2023-06-19 13:32:26 +00:00
namespace Elsa.Api.Client.Extensions;
/// <summary>
/// Provides extension methods for <see cref="JsonObject"/>.
2023-06-19 13:32:26 +00:00
/// </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();
2023-06-19 13:32:26 +00:00
/// <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");
2023-06-19 13:32:26 +00:00
/// <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");
/// <summary>
/// Gets the activities in the specified flowchart.
/// </summary>
public static IEnumerable<JsonObject> GetActivities(this JsonObject flowchart) => flowchart.GetProperty("activities")?.AsArray().AsEnumerable().Cast<JsonObject>() ?? Array.Empty<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");
Add IResilienceStrategy Abstraction with Category Matching and Expression-Based Configuration (#6637) * Add resilience module with core interfaces and services Introduced a new `Elsa.Resilience` module and its core components to support resilient services and activities. This includes resilience strategies, providers, and attributes, along with integration into the existing HTTP module for enhanced fault tolerance. Added solution and project references for proper dependency management. * Add resilience strategy framework with HTTP strategy support Introduced a resilience strategy architecture, including a configurable `HttpResilienceStrategy` with retry capabilities, strategy serialization, and integration with existing modules. Enhanced ResilienceFeature to support registration of strategy types and updated application configuration to enable resilience strategies. This change ensures more robust and fault-tolerant HTTP request handling. * Add JSON serialization support for resilience configuration Introduced `ConfigurationExtensions` to enable JSON serialization of configuration sections. Updated resilience strategies to utilize the new extension methods and adjusted JSON serialization logic to support polymorphism with `$type` discriminator. Minor modifications were made to support deserialization and property mutability. * Add resilience strategy support to workflows and API clients Introduced resilience strategy configuration, serialization, and execution support across workflows and API clients. Added new APIs, models, and services to enhance fault tolerance capabilities for activities and HTTP interactions. * Refactor resilience services for improved modularity. Replaced `IResilienceService` with new modular interfaces (`IResilienceStrategyCatalog`, `IResilienceStrategyConfigEvaluator`, `IResilientActivityInvoker`) and corresponding implementations. Enhanced maintainability by simplifying components and responsibilities, ensuring better separation of concerns. * Rename methods in ResilienceStrategyCatalog for clarity Updated method names in `ResilienceStrategyCatalog` and its interfaces for better readability and alignment with naming conventions. Replaced `GetAllStrategiesAsync` with `ListAsync` and `GetStrategyAsync` with `GetAsync` across the codebase. * Refactor resilience handling in HTTP activities. Replaced `ResilienceCategory` property with `ResilienceCategoryAttribute` for a cleaner implementation. Updated `IResilientActivity` to simplify its interface and adjusted related modifications accordingly. Introduced `IResilientActivityInvoker` to enhance resilience strategy execution. * Add support for additional resilience and scripting features Extended resilience strategy handling with serialization support, added `HttpResilienceStrategy` type in JavaScript handler, and refined object conversion logic for interfaces. Minor adjustments to `Expression` class properties for consistency. * Remove `UseResilience` call from Program.cs This call was redundant and no longer necessary for the application. Its removal simplifies the code and ensures only required middleware is used. * Remove commented-out JSON converter code in serializer setup Cleaned up unused and commented-out converter initialization code in `ResilienceStrategySerializer`. This improves readability and removes unnecessary clutter from the file. * Fix typo in method names from 'Resiliency' to 'Resilience' Renamed methods to maintain consistency in naming conventions across the codebase. This change ensures clarity and alignment with established terminology. * Refactor namespace for ConfigurationResilienceStrategySource Updated the namespace of ConfigurationResilienceStrategySource to "StrategySources" for better alignment with naming conventions and structure. Removed an unused namespace reference in ResilienceFeature for cleanup. * Mark EnableResiliency as obsolete in SendHttpRequestBase. The EnableResiliency property is now marked with the [Obsolete] attribute. Developers are encouraged to use the common Resilience Strategy setting instead for managing HTTP request resiliency. This change ensures better consistency and alignment with the broader resilience strategy. * Restrict ResilienceCategoryAttribute to class targets only Removed support for using ResilienceCategoryAttribute on properties. This change enforces a stricter and more focused usage of the attribute, ensuring it applies only to class-level declarations. * Add documentation for IResilientActivityInvoker interface Include summaries and parameter descriptions for the `InvokeAsync` method. This improves code clarity and helps developers understand the functionality and usage of the resilient activity invocation process. * Fix logical operator precedence in type comparison check Parentheses were added to ensure correct evaluation of conditions when checking type compatibility. This prevents potential logical errors when determining the target type in object conversions. * Add support for resilience source identification Introduce the `ResilienceSourceNameAttribute` to allow naming of resilience sources. Updated `ResilienceStrategyCatalog` to utilize the attribute for prefixing strategy IDs, improving source identification and traceability. Applied the attribute to `ConfigurationResilienceStrategySource` as an example. * Revert "Add support for resilience source identification" This reverts commit 19b4e7121d6330b5de4f692b78da4c1e4a2d1f67. * Reapply "Add support for resilience source identification" This reverts commit 8bcba9d040c4eb247077aec6d90dc02817adcbd5. * Revert "Reapply "Add support for resilience source identification"" This reverts commit ee04d35e7930956c752dda3ed150ca34a535e66c.
2025-05-12 08:09:04 +00:00
/// <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");
}
2023-06-19 13:32:26 +00:00
}