2024-11-28 11:19:38 +00:00
|
|
|
using System.Text.Json;
|
|
|
|
|
using System.Text.Json.Serialization;
|
|
|
|
|
using Elsa.Expressions.Contracts;
|
|
|
|
|
using Elsa.Expressions.Models;
|
2023-10-10 10:34:32 +00:00
|
|
|
using Elsa.Extensions;
|
2024-04-19 19:57:55 +00:00
|
|
|
using Elsa.Workflows.Activities;
|
2024-10-25 17:41:10 +00:00
|
|
|
using Elsa.Workflows.LogPersistence;
|
2024-11-28 11:19:38 +00:00
|
|
|
using Elsa.Workflows.LogPersistence.Strategies;
|
2024-03-30 20:22:35 +00:00
|
|
|
using Elsa.Workflows.Management.Options;
|
Add a more generic UIHandler to customize how inputAttributes can be handle by UI (#4688)
* add a more generic UIHandler to customize how inputAttributes can be handle by the ui
* Add IPropertyUIHandlerResolver and update PropertyUIHandlerResolver
Introduced a new interface, IPropertyUIHandlerResolver, to resolve UI options for a property. Refactored PropertyUIHandlerResolver to implement this interface and removed the unnecessary partial class structure. Also, cleaned up some unnecessary usings in various files for better code organization.
* Refactor variable name and description in InputDescriptor
The 'uISpecifications' variable in the InputDescriptor model is renamed to 'uiSpecifications' for better readability. Additionally, the associated comment was revised to explain that the dictionary is used by the UI.
* "Refactor codebase for improved organization and cleaner architecture"
The codebase has been significantly refactored, moving several classes to more appropriate namespaces for improved organization and cleaner architecture. This includes shifting UI hint handlers, activities, and memory-related components, amongst others. The changes should improve code readability and maintainability, but as this is a broad refactoring effort, thorough regression testing is advised.
* Add CheckList UIHint with associated handler and provider
This update introduces a new UIHint called CheckList to the Elsa.Workflows.Core. This includes the necessary handler and provider classes. The handler is registered in the WorkflowsFeature.cs, and the CheckList UIHint key has been added to the InputUIHints.cs. Various associated files have been created in both the Elsa.Api.Client and Elsa.Workflows.Core project to support this new UIHint.
---------
Co-authored-by: Jérémie DEVILLARD <jdevillard@users.noreply.github.com>
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
2023-12-26 17:56:29 +00:00
|
|
|
using Elsa.Workflows.Models;
|
2023-10-10 10:34:32 +00:00
|
|
|
using Elsa.Workflows.Runtime.Entities;
|
2024-11-28 11:19:38 +00:00
|
|
|
using Elsa.Workflows.Serialization.Converters;
|
Add a more generic UIHandler to customize how inputAttributes can be handle by UI (#4688)
* add a more generic UIHandler to customize how inputAttributes can be handle by the ui
* Add IPropertyUIHandlerResolver and update PropertyUIHandlerResolver
Introduced a new interface, IPropertyUIHandlerResolver, to resolve UI options for a property. Refactored PropertyUIHandlerResolver to implement this interface and removed the unnecessary partial class structure. Also, cleaned up some unnecessary usings in various files for better code organization.
* Refactor variable name and description in InputDescriptor
The 'uISpecifications' variable in the InputDescriptor model is renamed to 'uiSpecifications' for better readability. Additionally, the associated comment was revised to explain that the dictionary is used by the UI.
* "Refactor codebase for improved organization and cleaner architecture"
The codebase has been significantly refactored, moving several classes to more appropriate namespaces for improved organization and cleaner architecture. This includes shifting UI hint handlers, activities, and memory-related components, amongst others. The changes should improve code readability and maintainability, but as this is a broad refactoring effort, thorough regression testing is advised.
* Add CheckList UIHint with associated handler and provider
This update introduces a new UIHint called CheckList to the Elsa.Workflows.Core. This includes the necessary handler and provider classes. The handler is registered in the WorkflowsFeature.cs, and the CheckList UIHint key has been added to the InputUIHints.cs. Various associated files have been created in both the Elsa.Api.Client and Elsa.Workflows.Core project to support this new UIHint.
---------
Co-authored-by: Jérémie DEVILLARD <jdevillard@users.noreply.github.com>
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
2023-12-26 17:56:29 +00:00
|
|
|
using Elsa.Workflows.State;
|
2024-03-30 20:22:35 +00:00
|
|
|
using Humanizer;
|
2024-11-28 18:23:13 +00:00
|
|
|
using Microsoft.Extensions.Logging;
|
2024-03-30 20:22:35 +00:00
|
|
|
using Microsoft.Extensions.Options;
|
2023-10-10 10:34:32 +00:00
|
|
|
|
Add Bookmark Queue and Restore Background Activity Execution (#5758)
* Update package versions and add PrivateAssets attributes
Updated multiple package versions to the latest releases and added the `PrivateAssets="All"` attribute to several dependencies to improve project isolation. This ensures that the specified packages will not be propagated as transitive dependencies.
* Fix incorrect serializer and generator references.
Replaced `_payloadSerializer` and `_identityGenerator` with `payloadSerializer` and `identityGenerator` respectively. This resolves potential null reference issues and ensures the correct instances are used during workflow and definition processing.
* Refactor background activity scheduling logic
Moved the state commit outside of the deferred task to ensure workflow state is saved before scheduling activities. This change ensures the workflow instance is updated promptly, preventing potential concurrency issues.
* Add bookmark queue management system
Introduced a comprehensive bookmark queue system to manage and process bookmarks efficiently. This includes entities, stores, filters, processors, and workers for both in-memory and distributed environments. Additionally, added notifications to signal bookmark queue workers and updated related contracts.
* Add state commit handler with various implementations
Introduced ICommitStateHandler for committing workflow state. Provided NoopCommitStateHandler and StoreCommitStateHandler implementations. Updated namespaces and dependencies across the project to incorporate these changes.
* Fix order in CommitAsync method for proper task execution
Reorder `ExecuteDeferredTasksAsync` after `SaveAsync` in `CommitAsync` method to ensure that deferred tasks execute correctly after the state is saved, addressing potential issues with task execution dependencies.
* Remove unused and deprecated middleware and annotations
Deleted unused classes `ExecuteDeferredActivityTasks` and `ScheduleBackgroundActivitiesMiddleware`. Removed unnecessary summary tags and unused usings across multiple files to clean up the codebase.
* Remove unnecessary initial migration files
Deleted initial migration files for alterations, management, and identity contexts. These files are no longer needed and their removal helps keep the repository clean and maintainable.
* Add delay in TriggerBookmarkQueueWorker loop
Introduced a 10-second delay within the while loop of TriggerBookmarkQueueWorker. This change aims to alleviate potential tight loop issues, ensuring better performance and resource management.
* Enable all database providers in migration script
Uncomment the providers array and add previously commented database providers (SqlServer, Sqlite, PostgreSql, Oracle). This ensures compatibility with multiple database systems during the migration process.
* Remove unused Microsoft.Extensions.DependencyInjection import
The import statement for `Microsoft.Extensions.DependencyInjection` was not being utilized and has been removed. This cleanup helps in maintaining cleaner and more readable code without unnecessary imports.
* Add Dapper persistence support for bookmark queue
Introduced a new Dapper-based bookmark queue store and related classes for handling bookmark queue items. Various migrations were added to support the new `BookmarkQueueItems` table with tenant-specific columns. Refactored existing EF Core and in-memory bookmark queue item stores to use a unified interface.
* Add MongoBookmarkQueueStore implementation
Added a MongoDB-based BookmarkQueueStore to handle CRUD operations for bookmark queue items. Updated the workflow runtime persistence feature to include and utilize this new store for managing bookmark queue items.
* Add migration helper for altering columns and update keys
Introduced MigrationHelper to simplify altering DateTime and Boolean columns. Updated keys in KeyValueStore and KeyValueFilter classes to use 'Id' instead of 'Key'. Revised migration scripts to utilize the new helper methods for modifying column types.
* Prevent BookmarkQueueWorker.Stop from cancelling when not running
Previously, the Stop method would always cancel the token source regardless of the worker's state. By checking if the worker is running before cancelling, we avoid unnecessary operations and potential errors related to an already cancelled token source.
* Update database schema and bookmark handling logic
Refactored various database migration scripts to allow nullable `WorkflowInstanceId` fields and added new fields such as `ActivityInstanceId` and `CorrelationId`. Enhanced bookmark queue and bound workflow handling logic to support these new fields, ensuring precise and efficient workflow execution and queuing.
* Refactor BookmarkHash to StimulusHash and introduce ActivityTypeName
Renamed BookmarkHash to StimulusHash across multiple components and added ActivityTypeName to enhance traceability. Updated indices, filters, and entities for this change, ensuring consistent naming and improved functionality.
* Remove unnecessary timeouts in AzureServiceBusTests.
Eliminated redundant timeout parameters in _signalManager.WaitAsync calls to streamline test execution and reduce potential waiting time. This change ensures more efficient and accurate testing synchronization.
* Rename and refactor BookmarkQueueWorkerSignaler
Renamed IBookmarkQueueWorkerSignaler to IBookmarkQueueSignaler across the project for clarity. Updated related classes and methods to reflect this change. Added bookmark queue signaling to ensure new items are processed, and renamed BookmarkQueueStore to EFBookmarkQueueStore for consistency.
* Refactor EF Core stores and add migration field
Refactored EF Core store classes to simplify field usage and constructors. Added "CorrelationId" field and created corresponding index in SQLite and PostgreSQL migration files to support new functionality.
* Update workflow context ID and optimize Task handling
Added `ParentInstanceId` to `BulkDispatchWorkflowsStimulus` for context propagation. Also, reset `TaskCompletionSource` in `BookmarkQueueSignaler` to reduce memory usage and ensure proper task lifecycle management.
* Update async method signature and fix variable usage
Modified `AddAsync` to include the `OnSaveAsync` parameter. Corrected the variable used for `parentInstanceId` and utilized `ActivityTypeNameHelper` for generating type names.
* Reset migrations to 3.2
* Generate 3.3 migrations
2024-07-15 20:37:14 +00:00
|
|
|
namespace Elsa.Workflows.Runtime;
|
2023-10-10 10:34:32 +00:00
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2024-10-25 17:41:10 +00:00
|
|
|
public class DefaultActivityExecutionMapper : IActivityExecutionMapper
|
2024-04-12 13:00:00 +00:00
|
|
|
{
|
2024-11-28 11:19:38 +00:00
|
|
|
private readonly JsonSerializerOptions _logPersistenceConfigSerializerOptions;
|
2024-10-25 17:41:10 +00:00
|
|
|
private readonly IOptions<ManagementOptions> _options;
|
2024-11-28 11:19:38 +00:00
|
|
|
private readonly IExpressionEvaluator _expressionEvaluator;
|
2024-11-28 18:23:13 +00:00
|
|
|
private readonly ILogger<DefaultActivityExecutionMapper> _logger;
|
2024-10-25 17:41:10 +00:00
|
|
|
private readonly IDictionary<string, ILogPersistenceStrategy> _logPersistenceStrategies;
|
|
|
|
|
|
2024-11-28 11:19:38 +00:00
|
|
|
public DefaultActivityExecutionMapper(
|
|
|
|
|
IOptions<ManagementOptions> options,
|
|
|
|
|
ILogPersistenceStrategyService logPersistenceStrategyService,
|
|
|
|
|
IExpressionEvaluator expressionEvaluator,
|
2024-11-28 18:23:13 +00:00
|
|
|
IExpressionDescriptorRegistry expressionDescriptorRegistry,
|
|
|
|
|
ILogger<DefaultActivityExecutionMapper> logger)
|
2024-10-25 17:41:10 +00:00
|
|
|
{
|
|
|
|
|
_options = options;
|
2024-11-28 11:19:38 +00:00
|
|
|
_expressionEvaluator = expressionEvaluator;
|
2024-11-28 18:23:13 +00:00
|
|
|
_logger = logger;
|
2024-10-25 17:41:10 +00:00
|
|
|
_logPersistenceStrategies = logPersistenceStrategyService.ListStrategies().ToDictionary(x => x.GetType().GetSimpleAssemblyQualifiedName(), x => x);
|
2024-11-28 11:19:38 +00:00
|
|
|
|
|
|
|
|
_logPersistenceConfigSerializerOptions = new JsonSerializerOptions
|
|
|
|
|
{
|
|
|
|
|
PropertyNameCaseInsensitive = true
|
|
|
|
|
}.WithConverters(
|
|
|
|
|
new ExpressionJsonConverterFactory(expressionDescriptorRegistry),
|
|
|
|
|
new JsonStringEnumConverter(),
|
|
|
|
|
new ExpandoObjectConverterFactory());
|
2024-10-25 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private const string LegacyLogPersistenceModeKey = "logPersistenceMode";
|
2024-11-28 11:19:38 +00:00
|
|
|
private const string LogPersistenceConfigKey = "logPersistenceConfig";
|
2024-04-12 13:00:00 +00:00
|
|
|
|
2023-10-10 10:34:32 +00:00
|
|
|
/// <inheritdoc />
|
2024-10-25 17:41:10 +00:00
|
|
|
public async Task<ActivityExecutionRecord> MapAsync(ActivityExecutionContext source)
|
2024-04-12 13:00:00 +00:00
|
|
|
{
|
2024-10-25 17:41:10 +00:00
|
|
|
/* The following legacy JSON structure is expected to be found in the custom properties of the workflow and activity:
|
2024-04-12 13:00:00 +00:00
|
|
|
* {
|
|
|
|
|
* "logPersistenceMode": {
|
|
|
|
|
* "default": "default",
|
|
|
|
|
* "inputs": { k : v },
|
|
|
|
|
* "outputs": { k: v }
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*/
|
|
|
|
|
|
2024-10-25 17:41:10 +00:00
|
|
|
/* The following JSON structure is expected to be found in the custom properties of the workflow and activity:
|
|
|
|
|
* {
|
2024-11-28 11:19:38 +00:00
|
|
|
* "logPersistenceConfig": {
|
|
|
|
|
* "default": { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." },
|
|
|
|
|
* "inputs": { "input1" : { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } },
|
|
|
|
|
* "outputs": { "output1" : { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } }
|
2024-10-25 17:41:10 +00:00
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*/
|
2024-04-12 13:00:00 +00:00
|
|
|
|
2024-10-25 17:41:10 +00:00
|
|
|
var cancellationToken = source.WorkflowExecutionContext.CancellationToken;
|
|
|
|
|
var workflow = (Workflow?)source.GetAncestors().FirstOrDefault(x => x.Activity is Workflow)?.Activity ?? source.WorkflowExecutionContext.Workflow;
|
2024-11-28 18:23:13 +00:00
|
|
|
var rootActivityExecutionContext = source.WorkflowExecutionContext.ActivityExecutionContexts.First(x => x.ParentActivityExecutionContext == null);
|
|
|
|
|
var workflowPersistenceProperty = await GetDefaultPersistenceModeAsync(rootActivityExecutionContext.ExpressionExecutionContext, workflow.CustomProperties, () => _options.Value.LogPersistenceMode, cancellationToken);
|
2024-11-28 11:19:38 +00:00
|
|
|
var activityPersistencePropertyDefault = await GetDefaultPersistenceModeAsync(source.ExpressionExecutionContext, source.Activity.CustomProperties, () => workflowPersistenceProperty, cancellationToken);
|
2024-10-25 17:41:10 +00:00
|
|
|
var legacyActivityPersistenceProperties = source.Activity.CustomProperties.GetValueOrDefault<IDictionary<string, object?>>(LegacyLogPersistenceModeKey, () => new Dictionary<string, object?>());
|
2024-11-28 11:19:38 +00:00
|
|
|
var activityPersistenceProperties = source.Activity.CustomProperties.GetValueOrDefault<IDictionary<string, object?>>(LogPersistenceConfigKey, () => new Dictionary<string, object?>());
|
2024-10-25 17:41:10 +00:00
|
|
|
var payload = GetPayload(source);
|
|
|
|
|
var outputs = GetOutputs(source);
|
|
|
|
|
|
|
|
|
|
outputs = await StorePropertyUsingPersistenceMode(
|
2024-11-28 11:19:38 +00:00
|
|
|
source.ExpressionExecutionContext,
|
2024-10-25 17:41:10 +00:00
|
|
|
outputs,
|
|
|
|
|
legacyActivityPersistenceProperties!.GetValueOrDefault("outputs", () => new Dictionary<string, object>())!,
|
|
|
|
|
activityPersistenceProperties!.GetValueOrDefault("outputs", () => new Dictionary<string, object>())!,
|
|
|
|
|
activityPersistencePropertyDefault,
|
|
|
|
|
cancellationToken);
|
|
|
|
|
|
|
|
|
|
var inputs = await StorePropertyUsingPersistenceMode(
|
2024-11-28 11:19:38 +00:00
|
|
|
source.ExpressionExecutionContext,
|
|
|
|
|
source.ActivityState,
|
2024-10-25 17:41:10 +00:00
|
|
|
legacyActivityPersistenceProperties!.GetValueOrDefault("inputs", () => new Dictionary<string, object>())!,
|
|
|
|
|
activityPersistenceProperties!.GetValueOrDefault("inputs", () => new Dictionary<string, object>())!,
|
|
|
|
|
activityPersistencePropertyDefault,
|
|
|
|
|
cancellationToken);
|
2024-04-12 13:00:00 +00:00
|
|
|
|
2023-10-13 08:34:21 +00:00
|
|
|
return new ActivityExecutionRecord
|
|
|
|
|
{
|
|
|
|
|
Id = source.Id,
|
|
|
|
|
ActivityId = source.Activity.Id,
|
2023-10-25 20:46:06 +00:00
|
|
|
ActivityNodeId = source.NodeId,
|
2023-10-13 08:34:21 +00:00
|
|
|
WorkflowInstanceId = source.WorkflowExecutionContext.Id,
|
|
|
|
|
ActivityType = source.Activity.Type,
|
|
|
|
|
ActivityName = source.Activity.Name,
|
2024-10-02 07:11:35 +00:00
|
|
|
ActivityState = inputs,
|
2023-10-13 08:34:21 +00:00
|
|
|
Outputs = outputs,
|
2024-03-08 07:54:08 +00:00
|
|
|
Properties = source.Properties,
|
2023-10-13 08:34:21 +00:00
|
|
|
Payload = payload,
|
|
|
|
|
Exception = ExceptionState.FromException(source.Exception),
|
|
|
|
|
ActivityTypeVersion = source.Activity.Version,
|
|
|
|
|
StartedAt = source.StartedAt,
|
|
|
|
|
HasBookmarks = source.Bookmarks.Any(),
|
|
|
|
|
Status = GetAggregateStatus(source),
|
|
|
|
|
CompletedAt = source.CompletedAt
|
|
|
|
|
};
|
2024-04-12 13:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
2024-11-28 11:19:38 +00:00
|
|
|
private async Task<LogPersistenceMode> GetDefaultPersistenceModeAsync(ExpressionExecutionContext expressionExecutionContext, IDictionary<string, object> customProperties, Func<LogPersistenceMode> defaultFactory, CancellationToken cancellationToken)
|
2024-04-12 13:00:00 +00:00
|
|
|
{
|
2024-10-25 17:41:10 +00:00
|
|
|
var legacyProperties = customProperties.GetValueOrDefault<IDictionary<string, object?>>(LegacyLogPersistenceModeKey, () => new Dictionary<string, object?>());
|
2024-11-28 11:19:38 +00:00
|
|
|
var properties = customProperties.GetValueOrDefault<IDictionary<string, object>>(LogPersistenceConfigKey, () => new Dictionary<string, object>());
|
|
|
|
|
var defaultPersistenceConfigObject = properties?.TryGetValue("default", out var defaultPersistenceConfigObjectValue) == true ? defaultPersistenceConfigObjectValue : null;
|
2024-10-25 17:41:10 +00:00
|
|
|
|
2024-11-28 11:19:38 +00:00
|
|
|
if (defaultPersistenceConfigObject == null)
|
2024-10-25 17:41:10 +00:00
|
|
|
{
|
|
|
|
|
var legacyPersistencePropertyDefault = legacyProperties!.GetValueOrDefault("default", defaultFactory);
|
|
|
|
|
|
|
|
|
|
if (legacyPersistencePropertyDefault == LogPersistenceMode.Inherit)
|
|
|
|
|
return defaultFactory();
|
|
|
|
|
return legacyPersistencePropertyDefault;
|
|
|
|
|
}
|
2024-04-12 13:00:00 +00:00
|
|
|
|
2024-11-28 11:19:38 +00:00
|
|
|
var defaultPersistenceConfig = Convert(defaultPersistenceConfigObject);
|
|
|
|
|
return await EvaluateLogPersistenceConfigAsync(defaultPersistenceConfig, expressionExecutionContext, defaultFactory, cancellationToken);
|
2024-04-12 13:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-25 17:41:10 +00:00
|
|
|
private async Task<Dictionary<string, object?>> StorePropertyUsingPersistenceMode(
|
2024-11-28 11:19:38 +00:00
|
|
|
ExpressionExecutionContext expressionExecutionContext,
|
2024-10-25 17:41:10 +00:00
|
|
|
IDictionary<string, object?> state,
|
2024-11-28 11:19:38 +00:00
|
|
|
IDictionary<string, object> obsoletePersistenceModeConfiguration,
|
2024-10-25 17:41:10 +00:00
|
|
|
IDictionary<string, object> persistenceStrategyConfiguration,
|
|
|
|
|
LogPersistenceMode defaultLogPersistenceMode,
|
|
|
|
|
CancellationToken cancellationToken)
|
2024-04-12 13:00:00 +00:00
|
|
|
{
|
|
|
|
|
var result = new Dictionary<string, object?>();
|
|
|
|
|
|
2024-10-02 07:11:35 +00:00
|
|
|
foreach (var value in state)
|
2024-04-12 13:00:00 +00:00
|
|
|
{
|
2024-10-25 17:41:10 +00:00
|
|
|
var propKey = value.Key.Camelize();
|
2024-11-28 11:19:38 +00:00
|
|
|
var logPersistenceConfigObject = persistenceStrategyConfiguration.GetValueOrDefault(propKey, () => null);
|
|
|
|
|
var logPersistenceConfig = Convert(logPersistenceConfigObject);
|
2024-10-25 17:41:10 +00:00
|
|
|
var mode = defaultLogPersistenceMode;
|
|
|
|
|
|
2024-11-28 11:19:38 +00:00
|
|
|
if (logPersistenceConfig != null)
|
2024-10-25 17:41:10 +00:00
|
|
|
{
|
2024-11-28 11:19:38 +00:00
|
|
|
mode = await EvaluateLogPersistenceConfigAsync(logPersistenceConfig, expressionExecutionContext, () => defaultLogPersistenceMode, cancellationToken);
|
2024-10-25 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-11-28 11:19:38 +00:00
|
|
|
mode = obsoletePersistenceModeConfiguration.GetValueOrDefault(propKey, () => defaultLogPersistenceMode);
|
2024-10-25 17:41:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mode == LogPersistenceMode.Include || mode == LogPersistenceMode.Inherit && defaultLogPersistenceMode is LogPersistenceMode.Include or LogPersistenceMode.Inherit)
|
2024-10-02 07:11:35 +00:00
|
|
|
result.Add(value.Key, value.Value);
|
2024-04-12 13:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-28 11:19:38 +00:00
|
|
|
private LogPersistenceConfiguration? Convert(object? value)
|
|
|
|
|
{
|
|
|
|
|
if (value == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
if (value is LogPersistenceConfiguration c)
|
|
|
|
|
return c;
|
|
|
|
|
|
|
|
|
|
var json = JsonSerializer.Serialize(value);
|
|
|
|
|
var config = JsonSerializer.Deserialize<LogPersistenceConfiguration>(json, _logPersistenceConfigSerializerOptions);
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<LogPersistenceMode> EvaluateLogPersistenceConfigAsync(LogPersistenceConfiguration? config, ExpressionExecutionContext executionContext, Func<LogPersistenceMode> defaultMode, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
if (config == null)
|
|
|
|
|
return defaultMode();
|
|
|
|
|
|
|
|
|
|
if (config.EvaluationMode == LogPersistenceEvaluationMode.Strategy)
|
|
|
|
|
{
|
|
|
|
|
var strategyTypeName = config.StrategyType ?? typeof(Inherit).GetSimpleAssemblyQualifiedName();
|
|
|
|
|
var strategy = _logPersistenceStrategies.TryGetValue(strategyTypeName, out var v) ? v : null;
|
|
|
|
|
|
|
|
|
|
if (strategy == null)
|
|
|
|
|
return defaultMode();
|
|
|
|
|
|
|
|
|
|
var strategyContext = new LogPersistenceStrategyContext(cancellationToken);
|
2024-11-28 18:41:01 +00:00
|
|
|
var logMode = await strategy.GetPersistenceModeAsync(strategyContext);
|
|
|
|
|
return logMode == LogPersistenceMode.Inherit ? defaultMode() : logMode;
|
2024-11-28 11:19:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config.Expression == null)
|
|
|
|
|
return defaultMode();
|
|
|
|
|
|
|
|
|
|
var expression = config.Expression;
|
2024-11-28 18:23:13 +00:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return await _expressionEvaluator.EvaluateAsync<LogPersistenceMode>(expression, executionContext);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning(e, "Error evaluating log persistence expression");
|
|
|
|
|
return defaultMode();
|
|
|
|
|
}
|
2024-11-28 11:19:38 +00:00
|
|
|
}
|
|
|
|
|
|
2024-10-25 17:41:10 +00:00
|
|
|
private static ActivityStatus GetAggregateStatus(ActivityExecutionContext context)
|
2023-10-10 10:34:32 +00:00
|
|
|
{
|
|
|
|
|
// If any child activity is faulted, the aggregate status is faulted.
|
|
|
|
|
var descendantContexts = context.GetDescendants().ToList();
|
2023-10-13 08:34:21 +00:00
|
|
|
|
2023-10-10 10:34:32 +00:00
|
|
|
if (descendantContexts.Any(x => x.Status == ActivityStatus.Faulted))
|
|
|
|
|
return ActivityStatus.Faulted;
|
|
|
|
|
|
|
|
|
|
return context.Status;
|
|
|
|
|
}
|
2024-11-28 11:19:38 +00:00
|
|
|
|
2024-10-25 17:41:10 +00:00
|
|
|
private static IDictionary<string, object> GetPayload(ActivityExecutionContext source)
|
|
|
|
|
{
|
|
|
|
|
var outcomes = source.JournalData.TryGetValue("Outcomes", out var resultValue) ? resultValue as string[] : default;
|
|
|
|
|
var payload = new Dictionary<string, object>();
|
|
|
|
|
|
|
|
|
|
if (outcomes != null)
|
|
|
|
|
payload.Add("Outcomes", outcomes);
|
2024-11-28 11:19:38 +00:00
|
|
|
|
2024-10-25 17:41:10 +00:00
|
|
|
return payload;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static IDictionary<string, object?> GetOutputs(ActivityExecutionContext source)
|
|
|
|
|
{
|
|
|
|
|
var activity = source.Activity;
|
|
|
|
|
var expressionExecutionContext = source.ExpressionExecutionContext;
|
|
|
|
|
var activityDescriptor = source.ActivityDescriptor;
|
|
|
|
|
var outputDescriptors = activityDescriptor.Outputs;
|
|
|
|
|
|
|
|
|
|
var outputs = outputDescriptors.ToDictionary(x => x.Name, x =>
|
|
|
|
|
{
|
|
|
|
|
if (x.IsSerializable == false)
|
|
|
|
|
return "(not serializable)";
|
|
|
|
|
|
|
|
|
|
var cachedValue = activity.GetOutput(expressionExecutionContext, x.Name);
|
|
|
|
|
|
|
|
|
|
if (cachedValue != default)
|
|
|
|
|
return cachedValue;
|
|
|
|
|
|
|
|
|
|
if (x.ValueGetter(activity) is Output output && source.TryGet(output.MemoryBlockReference(), out var outputValue))
|
|
|
|
|
return outputValue;
|
|
|
|
|
|
|
|
|
|
return default;
|
|
|
|
|
});
|
2024-11-28 11:19:38 +00:00
|
|
|
|
2024-10-25 17:41:10 +00:00
|
|
|
return outputs;
|
|
|
|
|
}
|
2023-10-10 10:34:32 +00:00
|
|
|
}
|