diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 8d8bdeb4..8c622106 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -4,6 +4,7 @@ on: push: branches: - 'main' + - 'develop/*' - 'bug/*' - 'issue/*' - 'patch/*' @@ -12,7 +13,7 @@ on: release: types: [ prereleased, published ] env: - base_version: '3.6.0' + base_version: '3.7.0' feedz_feed_source: 'https://f.feedz.io/elsa-workflows/elsa-3/nuget/index.json' nuget_feed_source: 'https://api.nuget.org/v3/index.json' @@ -30,7 +31,7 @@ jobs: PACKAGE_PREFIX=$(echo $BRANCH_NAME | rev | cut -d/ -f1 | rev | tr '_' '-') # If the branch name is main, use the preview version. Otherwise, use the branch name as the version prefix. - if [[ "${BRANCH_NAME}" == "main" || "${BRANCH_NAME}" =~ ^rc/ ]]; then + if [[ "${BRANCH_NAME}" == "main" || "${BRANCH_NAME}" =~ ^develop/ ]]; then PACKAGE_PREFIX="preview" fi diff --git a/Directory.Build.props b/Directory.Build.props index ffe061f8..fd0bc391 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - true + false Elsa Workflows Community @@ -41,8 +41,8 @@ - 3.6.0-preview.3056 - 3.6.0-preview.1087 + 3.7.0-preview.3055 + 3.6.0-preview.1029 9.0.6 diff --git a/Directory.Packages.props b/Directory.Packages.props index 320d550f..e55ae999 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -38,6 +38,7 @@ + @@ -45,7 +46,7 @@ - + diff --git a/src/modules/agents/Elsa.Agents.Activities/Activities/AgentActivity.cs b/src/modules/agents/Elsa.Agents.Activities/Activities/AgentActivity.cs index 89f097ae..9dce26cd 100644 --- a/src/modules/agents/Elsa.Agents.Activities/Activities/AgentActivity.cs +++ b/src/modules/agents/Elsa.Agents.Activities/Activities/AgentActivity.cs @@ -47,7 +47,11 @@ protected override async ValueTask ExecuteAsync(ActivityExecutionContext context var agentInvoker = context.GetRequiredService(); var result = await agentInvoker.InvokeAgentAsync(AgentName, functionInput, context.CancellationToken); - var json = result.FunctionResult.GetValue(); + var json = result.ChatMessageContent.Content?.Trim(); + + if (string.IsNullOrWhiteSpace(json)) + throw new InvalidOperationException("The message content is empty or null."); + var outputType = context.ActivityDescriptor.Outputs.Single().Type; // If the target type is object, we want the JSON to be deserialized into an ExpandoObject for dynamic field access. diff --git a/src/modules/agents/Elsa.Agents.Activities/Elsa.Agents.Activities.csproj b/src/modules/agents/Elsa.Agents.Activities/Elsa.Agents.Activities.csproj index d108f98c..09997e26 100644 --- a/src/modules/agents/Elsa.Agents.Activities/Elsa.Agents.Activities.csproj +++ b/src/modules/agents/Elsa.Agents.Activities/Elsa.Agents.Activities.csproj @@ -1,4 +1,4 @@ - + Provides Agent activities diff --git a/src/modules/agents/Elsa.Agents.Core/Contracts/IkernelFactory.cs b/src/modules/agents/Elsa.Agents.Core/Contracts/IkernelFactory.cs new file mode 100644 index 00000000..7b14d61d --- /dev/null +++ b/src/modules/agents/Elsa.Agents.Core/Contracts/IkernelFactory.cs @@ -0,0 +1,8 @@ +using Microsoft.SemanticKernel; +namespace Elsa.Agents; + +public interface IKernelFactory +{ + Kernel CreateKernel(KernelConfig kernelConfig, AgentConfig agentConfig); + Kernel CreateKernel(KernelConfig kernelConfig, string agentName); +} \ No newline at end of file diff --git a/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj b/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj index 50a7f65e..ad2e3965 100644 --- a/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj +++ b/src/modules/agents/Elsa.Agents.Core/Elsa.Agents.Core.csproj @@ -11,6 +11,7 @@ + diff --git a/src/modules/agents/Elsa.Agents.Core/Extensions/AgentConfigExtensions.cs b/src/modules/agents/Elsa.Agents.Core/Extensions/AgentConfigExtensions.cs index 2a4cdc6f..5afbbab3 100644 --- a/src/modules/agents/Elsa.Agents.Core/Extensions/AgentConfigExtensions.cs +++ b/src/modules/agents/Elsa.Agents.Core/Extensions/AgentConfigExtensions.cs @@ -20,6 +20,7 @@ public static OpenAIPromptExecutionSettings ToOpenAIPromptExecutionSettings(this ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, ResponseFormat = agentConfig.ExecutionSettings.ResponseFormat, ChatSystemPrompt = agentConfig.PromptTemplate, + ServiceId = "default" }; } diff --git a/src/modules/agents/Elsa.Agents.Core/Extensions/FunctionResultExtensions.cs b/src/modules/agents/Elsa.Agents.Core/Extensions/FunctionResultExtensions.cs index ba02314f..90b66954 100644 --- a/src/modules/agents/Elsa.Agents.Core/Extensions/FunctionResultExtensions.cs +++ b/src/modules/agents/Elsa.Agents.Core/Extensions/FunctionResultExtensions.cs @@ -8,18 +8,24 @@ public static class FunctionResultExtensions public static async Task AsJsonElementAsync(this Task resultTask) { var result = await resultTask; - return result.FunctionResult.AsJsonElement(); + return result.ChatMessageContent.AsJsonElement(); } - - public static async Task AsJsonElementAsync(this Task resultTask) - { - var result = await resultTask; - return result.AsJsonElement(); - } - - public static JsonElement AsJsonElement(this FunctionResult result) + + public static JsonElement AsJsonElement(this ChatMessageContent result) { - var response = result.GetValue()!; - return JsonSerializer.Deserialize(response); + var content = result.Content?.Trim(); + + if (string.IsNullOrWhiteSpace(content)) + throw new InvalidOperationException("The message content is empty."); + + try + { + return JsonSerializer.Deserialize(content!); + } + catch (JsonException ex) + { + throw new InvalidOperationException($"Error deserializing the message content as JSON:\n{content}", ex); + } + } } \ No newline at end of file diff --git a/src/modules/agents/Elsa.Agents.Core/Models/InvokeAgentResult.cs b/src/modules/agents/Elsa.Agents.Core/Models/InvokeAgentResult.cs index ee2732b9..d7b032ba 100644 --- a/src/modules/agents/Elsa.Agents.Core/Models/InvokeAgentResult.cs +++ b/src/modules/agents/Elsa.Agents.Core/Models/InvokeAgentResult.cs @@ -3,12 +3,8 @@ namespace Elsa.Agents; -public record InvokeAgentResult(AgentConfig Function, FunctionResult FunctionResult) +public record InvokeAgentResult(AgentConfig Function, ChatMessageContent ChatMessageContent) { - public object? ParseResult() - { - var targetType = Type.GetType(Function.OutputVariable.Type) ?? typeof(JsonElement); - var json = FunctionResult.GetValue(); - return JsonSerializer.Deserialize(json, targetType); - } + + } \ No newline at end of file diff --git a/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs b/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs index a40458c5..907caae4 100644 --- a/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs +++ b/src/modules/agents/Elsa.Agents.Core/Services/AgentInvoker.cs @@ -1,12 +1,14 @@ using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; +using Microsoft.SemanticKernel.PromptTemplates.Handlebars; #pragma warning disable SKEXP0010 #pragma warning disable SKEXP0001 namespace Elsa.Agents; -public class AgentInvoker(KernelFactory kernelFactory, IKernelConfigProvider kernelConfigProvider) +public class AgentInvoker(IKernelFactory kernelFactory, IKernelConfigProvider kernelConfigProvider) { public async Task InvokeAgentAsync(string agentName, IDictionary input, CancellationToken cancellationToken = default) { @@ -21,9 +23,9 @@ public async Task InvokeAgentAsync(string agentName, IDiction MaxTokens = executionSettings.MaxTokens, PresencePenalty = executionSettings.PresencePenalty, FrequencyPenalty = executionSettings.FrequencyPenalty, - ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions, ResponseFormat = executionSettings.ResponseFormat, ChatSystemPrompt = agentConfig.PromptTemplate, + FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; var promptExecutionSettingsDictionary = new Dictionary @@ -46,10 +48,36 @@ public async Task InvokeAgentAsync(string agentName, IDiction AllowDangerouslySetContent = true }).ToList() }; - - var kernelFunction = kernel.CreateFunctionFromPrompt(promptTemplateConfig); + + var templateFactory = new HandlebarsPromptTemplateFactory(); + + var promptConfig = new PromptTemplateConfig + { + Template = agentConfig.PromptTemplate, + TemplateFormat = "handlebars", + Name = agentConfig.FunctionName + }; + + var promptTemplate = templateFactory.Create(promptConfig); + var kernelArguments = new KernelArguments(input); - var result = await kernelFunction.InvokeAsync(kernel, kernelArguments, cancellationToken: cancellationToken); - return new(agentConfig, result); + string renderedPrompt = await promptTemplate.RenderAsync(kernel, kernelArguments); + + ChatHistory chatHistory = []; + chatHistory.AddUserMessage(renderedPrompt); + + IChatCompletionService chatCompletion = kernel.GetRequiredService(); + + OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new() + { + FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() + }; + + var response = await chatCompletion.GetChatMessageContentAsync( + chatHistory, + executionSettings: openAIPromptExecutionSettings, + kernel: kernel); + + return new(agentConfig, response); } } \ No newline at end of file diff --git a/src/modules/agents/Elsa.Agents.Core/Services/KernelFactory.cs b/src/modules/agents/Elsa.Agents.Core/Services/KernelFactory.cs index bdc738f4..9b8dfaf8 100644 --- a/src/modules/agents/Elsa.Agents.Core/Services/KernelFactory.cs +++ b/src/modules/agents/Elsa.Agents.Core/Services/KernelFactory.cs @@ -7,7 +7,7 @@ namespace Elsa.Agents; -public class KernelFactory(IPluginDiscoverer pluginDiscoverer, IServiceDiscoverer serviceDiscoverer, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, ILogger logger) +public class KernelFactory(IPluginDiscoverer pluginDiscoverer, IServiceDiscoverer serviceDiscoverer, ILoggerFactory loggerFactory, IServiceProvider serviceProvider, ILogger logger) : IKernelFactory { public Kernel CreateKernel(KernelConfig kernelConfig, string agentName) { diff --git a/src/modules/alterations/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs b/src/modules/alterations/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs index 3f9a7aa9..f98fc601 100644 --- a/src/modules/alterations/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs +++ b/src/modules/alterations/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs @@ -99,13 +99,26 @@ public void Succeed(Func commitAction) CommitAction = commitAction; } + /// + /// Marks the alteration as succeeded. + /// + public void Succeed(Action commitAction) + { + Succeed(); + CommitAction = () => + { + commitAction(); + return Task.CompletedTask; + }; + } + /// /// Marks the alteration as succeeded. /// public void Succeed(string message) { HasSucceeded = true; - Log($"Alteration {Alteration.GetType().Name} succeeded", message, LogLevel.Information); + Log($"Alteration {Alteration.GetType().Name} succeeded", message); } /// @@ -116,6 +129,19 @@ public void Succeed(string message, Func commitAction) Succeed(message); CommitAction = commitAction; } + + /// + /// Marks the alteration as succeeded. + /// + public void Succeed(string message, Action commitAction) + { + Succeed(message); + CommitAction = () => + { + commitAction(); + return Task.CompletedTask; + }; + } /// /// Marks the alteration as failed. @@ -126,4 +152,4 @@ public void Fail(string? message = null) HasFailed = true; Log($"Alteration {Alteration.GetType().Name} failed", message ?? $"{Alteration.GetType().Name} failed", LogLevel.Error); } -} \ No newline at end of file +} diff --git a/src/modules/alterations/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs b/src/modules/alterations/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs index 65e3b9ac..e9180a56 100644 --- a/src/modules/alterations/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs +++ b/src/modules/alterations/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs @@ -1,34 +1,25 @@ using Elsa.Alterations.Core.Contracts; using Elsa.Mediator.Contracts; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Alterations.Services; /// /// Dispatches an alteration job for execution using an in-memory channel. /// -public class BackgroundAlterationJobDispatcher : IAlterationJobDispatcher +public class BackgroundAlterationJobDispatcher(IJobQueue jobQueue, IServiceScopeFactory scopeFactory) : IAlterationJobDispatcher { - private readonly IJobQueue _jobQueue; - private readonly IAlterationJobRunner _alterationJobRunner; - - /// - /// Initializes a new instance of the class. - /// - public BackgroundAlterationJobDispatcher(IJobQueue jobQueue, IAlterationJobRunner alterationJobRunner) - { - _jobQueue = jobQueue; - _alterationJobRunner = alterationJobRunner; - } - /// public ValueTask DispatchAsync(string jobId, CancellationToken cancellationToken = default) { - _jobQueue.Enqueue(ct => ExecuteJobAsync(jobId, ct)); + jobQueue.Enqueue(ct => ExecuteJobAsync(jobId, ct)); return default; } private async Task ExecuteJobAsync(string alterationJobId, CancellationToken cancellationToken) { - await _alterationJobRunner.RunAsync(alterationJobId, cancellationToken); + using var scope = scopeFactory.CreateScope(); + var alterationJobRunner = scope.ServiceProvider.GetRequiredService(); + await alterationJobRunner.RunAsync(alterationJobId, cancellationToken); } -} \ No newline at end of file +} diff --git a/src/modules/dropins/Elsa.DropIns/HostedServices/DropInDirectoryMonitorHostedService.cs b/src/modules/dropins/Elsa.DropIns/HostedServices/DropInDirectoryMonitorHostedService.cs index dda2fec5..61c91404 100644 --- a/src/modules/dropins/Elsa.DropIns/HostedServices/DropInDirectoryMonitorHostedService.cs +++ b/src/modules/dropins/Elsa.DropIns/HostedServices/DropInDirectoryMonitorHostedService.cs @@ -67,7 +67,14 @@ private async void OnChanged(object sender, FileSystemEventArgs e) if (task == null) return; - await task; + try + { + await task; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error processing change for path {Path}", e.FullPath); + } } private async void OnDeleted(object sender, FileSystemEventArgs e) @@ -77,7 +84,14 @@ private async void OnDeleted(object sender, FileSystemEventArgs e) if (task == null) return; - await task; + try + { + await task; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error processing delete for path {Path}", e.FullPath); + } } private async Task LoadDropInAssemblyAsync(string fullPath) @@ -140,4 +154,4 @@ public override void Dispose() base.Dispose(); } -} \ No newline at end of file +} diff --git a/src/modules/persistence/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowExecutionLogStore.cs b/src/modules/persistence/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowExecutionLogStore.cs index ee914012..7885f6bc 100644 --- a/src/modules/persistence/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowExecutionLogStore.cs +++ b/src/modules/persistence/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowExecutionLogStore.cs @@ -103,4 +103,5 @@ private bool ShouldSerializePayload(WorkflowExecutionLogRecord source) } private static IQueryable Filter(IQueryable queryable, WorkflowExecutionLogRecordFilter filter) => filter.Apply(queryable); -} \ No newline at end of file +} +} diff --git a/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs b/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs index 6e6fd31b..af4371a0 100644 --- a/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs +++ b/src/modules/persistence/Elsa.Persistence.MongoDb/Common/MongoDbStore.cs @@ -134,10 +134,11 @@ public async Task SaveManyAsync(IEnumerable documents, string primary await collection.BulkWriteAsync(writes, cancellationToken: cancellationToken); } - public async Task UpdatePartialAsync( + public async Task UpdatePartialAsync( string id, IDictionary updatedFields, string primaryKey = nameof(Entity.Id), + bool throwIfNotFound = true, CancellationToken cancellationToken = default) { if (string.IsNullOrEmpty(id)) @@ -154,7 +155,14 @@ public async Task UpdatePartialAsync( var updateResult = await collection.UpdateOneAsync(filter, updateDefinition, cancellationToken: cancellationToken); if (updateResult.MatchedCount == 0) + { + if (!throwIfNotFound) + return false; + throw new InvalidOperationException($"No document found with ID '{id}'."); + } + + return updateResult.ModifiedCount > 0; } /// @@ -455,4 +463,4 @@ private void ApplyTenantId(IEnumerable documents) tenantDocument.TenantId = tenantId.EmptyToNull(); } } -} \ No newline at end of file +} diff --git a/src/modules/persistence/Elsa.Persistence.MongoDb/Features/MongoDbFeature.cs b/src/modules/persistence/Elsa.Persistence.MongoDb/Features/MongoDbFeature.cs index faa45040..517f6582 100644 --- a/src/modules/persistence/Elsa.Persistence.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/persistence/Elsa.Persistence.MongoDb/Features/MongoDbFeature.cs @@ -39,6 +39,11 @@ public class MongoDbFeature(IModule module) : FeatureBase(module) /// public Func CollectionNamingStrategy { get; set; } = sp => sp.GetRequiredService(); + public override void ConfigureHostedServices() + { + Module.ConfigureHostedService(-10); + } + /// public override void Apply() { @@ -47,24 +52,13 @@ public override void Apply() var mongoUrl = new MongoUrl(ConnectionString); Services.AddSingleton(sp => CreateMongoClient(sp, mongoUrl)); Services.AddScoped(sp => CreateDatabase(sp, mongoUrl)); - + Services.TryAddScoped(); Services.AddScoped(CollectionNamingStrategy); - RegisterSerializers(); RegisterClassMaps(); } - private static void RegisterSerializers() - { - TryRegisterSerializerOrSkipWhenExist(typeof(object), new PolymorphicSerializer()); - TryRegisterSerializerOrSkipWhenExist(typeof(Type), new TypeSerializer()); - TryRegisterSerializerOrSkipWhenExist(typeof(Variable), new VariableSerializer()); - TryRegisterSerializerOrSkipWhenExist(typeof(Version), new VersionSerializer()); - TryRegisterSerializerOrSkipWhenExist(typeof(JsonElement), new JsonElementSerializer()); - TryRegisterSerializerOrSkipWhenExist(typeof(JsonNode), new JsonNodeBsonConverter()); - } - private static void RegisterClassMaps() { BsonClassMap.TryRegisterClassMap(cm => @@ -78,19 +72,14 @@ private static void RegisterClassMaps() map.SetIgnoreExtraElements(true); // Needed for missing ID property map.MapProperty(x => x.Key); // Needed for non-setter property }); + + BsonClassMap.TryRegisterClassMap(map => + { + map.AutoMap(); + map.SetIgnoreExtraElements(true); + }); } - private static void TryRegisterSerializerOrSkipWhenExist(Type type, IBsonSerializer serializer) - { - try - { - BsonSerializer.TryRegisterSerializer(type, serializer); - } - catch (BsonSerializationException ex) - { - } - } - private static IMongoClient CreateMongoClient(IServiceProvider sp, MongoUrl mongoUrl) { var options = sp.GetRequiredService>().Value; @@ -99,7 +88,7 @@ private static IMongoClient CreateMongoClient(IServiceProvider sp, MongoUrl mong // TODO: Uncomment once https://github.com/jbogard/MongoDB.Driver.Core.Extensions.DiagnosticSources/pull/41 is merged and deployed. //settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber()); - + settings.ApplicationName = GetApplicationName(settings); settings.WriteConcern = options.WriteConcern; settings.ReadConcern = options.ReadConcern; @@ -121,4 +110,4 @@ private static string GetApplicationName(MongoClientSettings settings) { return string.IsNullOrWhiteSpace(settings.ApplicationName) ? "elsa_workflows" : settings.ApplicationName; } -} \ No newline at end of file +} diff --git a/src/modules/persistence/Elsa.Persistence.MongoDb/Helpers/ExpressionHelpers.cs b/src/modules/persistence/Elsa.Persistence.MongoDb/Helpers/ExpressionHelpers.cs index 42a9e255..8c7c977d 100644 --- a/src/modules/persistence/Elsa.Persistence.MongoDb/Helpers/ExpressionHelpers.cs +++ b/src/modules/persistence/Elsa.Persistence.MongoDb/Helpers/ExpressionHelpers.cs @@ -5,10 +5,10 @@ namespace Elsa.Persistence.MongoDb.Helpers; -internal class ExpressionHelpers +internal static class ExpressionHelpers { public static readonly Expression> WorkflowDefinitionSummary = - workflowDefinition => new WorkflowDefinitionSummary + workflowDefinition => new() { Id = workflowDefinition.Id, DefinitionId = workflowDefinition.DefinitionId, @@ -18,11 +18,14 @@ internal class ExpressionHelpers IsLatest = workflowDefinition.IsLatest, IsPublished = workflowDefinition.IsPublished, MaterializerName = workflowDefinition.MaterializerName, - CreatedAt = workflowDefinition.CreatedAt + CreatedAt = workflowDefinition.CreatedAt, + IsReadonly = workflowDefinition.IsReadonly, + ProviderName = workflowDefinition.ProviderName, + ToolVersion = workflowDefinition.ToolVersion }; public static readonly Expression> WorkflowInstanceSummary = - workflowInstance => new WorkflowInstanceSummary + workflowInstance => new() { Id = workflowInstance.Id, DefinitionId = workflowInstance.DefinitionId, @@ -34,16 +37,17 @@ internal class ExpressionHelpers Name = workflowInstance.Name, CreatedAt = workflowInstance.CreatedAt, UpdatedAt = workflowInstance.UpdatedAt, - FinishedAt = workflowInstance.FinishedAt + FinishedAt = workflowInstance.FinishedAt, + IncidentCount = workflowInstance.IncidentCount, }; - public static readonly Expression> WorkflowInstanceId = workflowInstance => new WorkflowInstanceId + public static readonly Expression> WorkflowInstanceId = workflowInstance => new() { Id = workflowInstance.Id }; public static readonly Expression> ActivityExecutionRecordSummary = - workflowInstance => new ActivityExecutionRecordSummary + workflowInstance => new() { Id = workflowInstance.Id, Status = workflowInstance.Status, @@ -53,6 +57,11 @@ internal class ExpressionHelpers ActivityTypeVersion = workflowInstance.ActivityTypeVersion, ActivityName = workflowInstance.ActivityName, StartedAt = workflowInstance.StartedAt, - HasBookmarks = workflowInstance.HasBookmarks + HasBookmarks = workflowInstance.HasBookmarks, + CompletedAt = workflowInstance.CompletedAt, + AggregateFaultCount = workflowInstance.AggregateFaultCount, + Metadata = workflowInstance.Metadata, + WorkflowInstanceId = workflowInstance.WorkflowInstanceId, + TenantId = workflowInstance.TenantId, }; -} \ No newline at end of file +} diff --git a/src/modules/persistence/Elsa.Persistence.MongoDb/Modules/Management/WorkflowInstanceStore.cs b/src/modules/persistence/Elsa.Persistence.MongoDb/Modules/Management/WorkflowInstanceStore.cs index 4d7043cb..57f70ef6 100644 --- a/src/modules/persistence/Elsa.Persistence.MongoDb/Modules/Management/WorkflowInstanceStore.cs +++ b/src/modules/persistence/Elsa.Persistence.MongoDb/Modules/Management/WorkflowInstanceStore.cs @@ -18,7 +18,7 @@ namespace Elsa.Persistence.MongoDb.Modules.Management; /// A MongoDb implementation of . /// [UsedImplicitly] -public class MongoWorkflowInstanceStore(MongoDbStore mongoDbStore) : IWorkflowInstanceStore +public class MongoWorkflowInstanceStore(MongoDbStore mongoDbStore, ILogger logger) : IWorkflowInstanceStore { /// public async ValueTask FindAsync(WorkflowInstanceFilter filter, CancellationToken cancellationToken = default) @@ -130,7 +130,10 @@ public async Task UpdateUpdatedTimestampAsync(string workflowInstanceId, DateTim [nameof(WorkflowInstance.UpdatedAt)] = value }; - await mongoDbStore.UpdatePartialAsync(workflowInstanceId, props, cancellationToken: cancellationToken); + var updated = await mongoDbStore.UpdatePartialAsync(workflowInstanceId, props, throwIfNotFound: false, cancellationToken: cancellationToken); + + if (!updated) + logger.LogDebug("Failed to update the 'UpdatedAt' timestamp for workflow instance with ID '{WorkflowInstanceId}'. This means this workflow does not yet exist in the DB.", workflowInstanceId); } /// @@ -175,4 +178,4 @@ private IQueryable OrderAndPaginate(IQueryable