diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs
index a1f185917..fd39e76be 100644
--- a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs
+++ b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Contracts/IWorkflowDefinitionsApi.cs
@@ -21,7 +21,7 @@ public interface IWorkflowDefinitionsApi
/// The version options.
/// The cancellation token.
[Get("/workflow-definitions?versionOptions={versionOptions}")]
- Task ListAsync([Query]ListWorkflowDefinitionsRequest request, [Query]VersionOptions? versionOptions = default, CancellationToken cancellationToken = default);
+ Task> ListAsync([Query]ListWorkflowDefinitionsRequest request, [Query]VersionOptions? versionOptions = default, CancellationToken cancellationToken = default);
///
/// Gets a workflow definition by definition ID.
@@ -42,6 +42,15 @@ public interface IWorkflowDefinitionsApi
[Get("/workflow-definitions/by-id/{id}?includeCompositeRoot={includeCompositeRoot}")]
Task GetByIdAsync(string id, bool includeCompositeRoot = false, CancellationToken cancellationToken = default);
+ ///
+ /// Gets a workflow definition by ID.
+ ///
+ /// The IDs of the workflow definition versions to get.
+ /// Whether to include the root activity of composite activities.
+ /// The cancellation token.
+ [Get("/workflow-definitions/many-by-id")]
+ Task> GetManyByIdAsync([Query(CollectionFormat.Multi)]ICollection ids, bool includeCompositeRoot = false, CancellationToken cancellationToken = default);
+
///
/// Gets the number of workflow definitions.
///
diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Responses/ListWorkflowDefinitionsResponse.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Responses/ListWorkflowDefinitionsResponse.cs
deleted file mode 100644
index f967304e9..000000000
--- a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Responses/ListWorkflowDefinitionsResponse.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Elsa.Api.Client.Resources.WorkflowDefinitions.Models;
-
-namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Responses;
-
-///
-/// Represents a response from listing workflow definitions.
-///
-/// The workflow definitions.
-/// The total number of workflow definitions.
-public record ListWorkflowDefinitionsResponse(ICollection Items, int TotalCount);
\ No newline at end of file
diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs
index 49e49daab..e0fb69dec 100644
--- a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs
+++ b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Contracts/IWorkflowInstancesApi.cs
@@ -25,7 +25,7 @@ public interface IWorkflowInstancesApi
Task GetAsync(string id, CancellationToken cancellationToken = default);
///
- /// Returns a workflow instance.
+ /// Returns a page of journal records for the specified workflow instance.
///
/// The ID of the workflow instance for which to return the journal.
/// The number of records to skip.
@@ -34,6 +34,17 @@ public interface IWorkflowInstancesApi
[Get("/workflow-instances/{workflowInstanceId}/journal")]
Task> GetJournalAsync(string workflowInstanceId, int? skip = default, int? take = default, CancellationToken cancellationToken = default);
+ ///
+ /// Returns a page of journal records for the specified workflow instance.
+ ///
+ /// The ID of the workflow instance for which to return the journal.
+ /// The filter to apply.
+ /// The number of records to skip.
+ /// The number of records to return.
+ /// The cancellation token.
+ [Post("/workflow-instances/{workflowInstanceId}/journal")]
+ Task> GetFilteredJournalAsync(string workflowInstanceId, GetFilteredJournalRequest? filter, int? skip = default, int? take = default, CancellationToken cancellationToken = default);
+
///
/// Deletes a workflow instance.
///
diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Requests/GetFilteredJournalRequest.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Requests/GetFilteredJournalRequest.cs
new file mode 100644
index 000000000..e41797d99
--- /dev/null
+++ b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Requests/GetFilteredJournalRequest.cs
@@ -0,0 +1,12 @@
+namespace Elsa.Api.Client.Resources.WorkflowInstances.Requests;
+
+///
+/// Represents a request to list journal records.
+///
+public class GetFilteredJournalRequest
+{
+ ///
+ /// Gets or sets the filter to apply.
+ ///
+ public JournalFilter? Filter { get; set; }
+}
\ No newline at end of file
diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Requests/JournalFilter.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Requests/JournalFilter.cs
new file mode 100644
index 000000000..ed4eee2ef
--- /dev/null
+++ b/src/clients/Elsa.Api.Client/Resources/WorkflowInstances/Requests/JournalFilter.cs
@@ -0,0 +1,13 @@
+namespace Elsa.Api.Client.Resources.WorkflowInstances.Requests;
+
+///
+/// Represents a request to list journal records.
+///
+public class JournalFilter
+{
+ ///
+ /// Gets or sets the activity ids to filter by.
+ ///
+ public ICollection? ActivityIds { get; set; }
+
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Common/Models/PageArgs.cs b/src/modules/Elsa.Common/Models/PageArgs.cs
index 60cce764e..9c0264440 100644
--- a/src/modules/Elsa.Common/Models/PageArgs.cs
+++ b/src/modules/Elsa.Common/Models/PageArgs.cs
@@ -34,8 +34,8 @@ public record PageArgs
if(offset != null && limit != null)
return FromRange(offset, limit);
-
- throw new ArgumentException("Either page and pageSize or offset and limit must be specified.");
+
+ return FromPage(0, 100);
}
///
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperBookmarkStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperBookmarkStore.cs
index 6aba18009..09971b276 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperBookmarkStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperBookmarkStore.cs
@@ -54,6 +54,7 @@ public class DapperBookmarkStore : IBookmarkStore
query
.Is(nameof(StoredBookmarkRecord.Hash), filter.Hash)
.Is(nameof(StoredBookmarkRecord.WorkflowInstanceId), filter.WorkflowInstanceId)
+ .In(nameof(StoredBookmarkRecord.WorkflowInstanceId), filter.WorkflowInstanceIds)
.Is(nameof(StoredBookmarkRecord.CorrelationId), filter.CorrelationId)
.Is(nameof(StoredBookmarkRecord.ActivityTypeName), filter.ActivityTypeName)
.In(nameof(StoredBookmarkRecord.ActivityTypeName), filter.ActivityTypeNames)
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperTriggerStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperTriggerStore.cs
index bb37ef38d..c3e6931b9 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperTriggerStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperTriggerStore.cs
@@ -71,6 +71,8 @@ public class DapperTriggerStore : ITriggerStore
.In(nameof(StoredTriggerRecord.Id), filter.Ids)
.Is(nameof(StoredTriggerRecord.WorkflowDefinitionId), filter.WorkflowDefinitionId)
.In(nameof(StoredTriggerRecord.WorkflowDefinitionId), filter.WorkflowDefinitionIds)
+ .Is(nameof(StoredTriggerRecord.WorkflowDefinitionVersionId), filter.WorkflowDefinitionVersionId)
+ .In(nameof(StoredTriggerRecord.WorkflowDefinitionVersionId), filter.WorkflowDefinitionVersionIds)
.Is(nameof(StoredTriggerRecord.Name), filter.Name)
.In(nameof(StoredTriggerRecord.Name), filter.Names)
.Is(nameof(StoredTriggerRecord.Hash), filter.Hash)
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperWorkflowExecutionLogStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperWorkflowExecutionLogStore.cs
index c19a06365..100b904b7 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperWorkflowExecutionLogStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Services/DapperWorkflowExecutionLogStore.cs
@@ -89,7 +89,9 @@ public class DapperWorkflowExecutionLogStore : IWorkflowExecutionLogStore
query
.Is(nameof(WorkflowExecutionLogRecordRecord.Id), filter.Id)
.In(nameof(WorkflowExecutionLogRecordRecord.Id), filter.Ids)
+ .Is(nameof(WorkflowExecutionLogRecordRecord.ParentActivityInstanceId), filter.ParentActivityInstanceId)
.Is(nameof(WorkflowExecutionLogRecordRecord.ActivityId), filter.ActivityId)
+ .In(nameof(WorkflowExecutionLogRecordRecord.ActivityId), filter.ActivityIds)
.Is(nameof(WorkflowExecutionLogRecordRecord.WorkflowInstanceId), filter.WorkflowInstanceId)
.In(nameof(WorkflowExecutionLogRecordRecord.WorkflowInstanceId), filter.WorkflowInstanceIds)
.Is(nameof(WorkflowExecutionLogRecordRecord.EventName), filter.EventName)
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetById/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetById/Endpoint.cs
index 7ee33287b..b38618d57 100644
--- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetById/Endpoint.cs
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetById/Endpoint.cs
@@ -36,7 +36,7 @@ internal class GetById : ElsaEndpoint
Id = request.Id
};
- var definition = (await _store.FindManyAsync(filter, cancellationToken)).FirstOrDefault();
+ var definition = await _store.FindAsync(filter, cancellationToken);
if (definition == null)
{
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetManyById/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetManyById/Endpoint.cs
new file mode 100644
index 000000000..867200462
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetManyById/Endpoint.cs
@@ -0,0 +1,52 @@
+using Elsa.Abstractions;
+using Elsa.Models;
+using Elsa.Workflows.Core.Contracts;
+using Elsa.Workflows.Core.Serialization.Converters;
+using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management.Filters;
+using Elsa.Workflows.Management.Mappers;
+using Elsa.Workflows.Management.Models;
+using JetBrains.Annotations;
+using Microsoft.AspNetCore.Http;
+
+namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.GetManyById;
+
+[PublicAPI]
+internal class GetManyById : ElsaEndpoint
+{
+ private readonly IWorkflowDefinitionStore _store;
+ private readonly IApiSerializer _apiSerializer;
+ private readonly WorkflowDefinitionMapper _mapper;
+
+ public GetManyById(IWorkflowDefinitionStore store, IApiSerializer apiSerializer, WorkflowDefinitionMapper mapper)
+ {
+ _store = store;
+ _apiSerializer = apiSerializer;
+ _mapper = mapper;
+ }
+
+ public override void Configure()
+ {
+ Get("/workflow-definitions/many-by-id");
+ ConfigurePermissions("read:workflow-definitions");
+ }
+
+ public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
+ {
+ var filter = new WorkflowDefinitionFilter
+ {
+ Ids = request.Ids
+ };
+
+ var definitions = (await _store.FindManyAsync(filter, cancellationToken)).ToList();
+ var models = (await _mapper.MapAsync(definitions, cancellationToken)).ToList();
+ var serializerOptions = _apiSerializer.CreateOptions();
+
+ // If the root of composite activities is not requested, exclude them from being serialized.
+ if (!request.IncludeCompositeRoot)
+ serializerOptions.Converters.Add(new JsonIgnoreCompositeRootConverterFactory());
+
+ var response = new ListResponse(models);
+ await HttpContext.Response.WriteAsJsonAsync(response, serializerOptions, cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetManyById/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetManyById/Models.cs
new file mode 100644
index 000000000..250db765f
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/GetManyById/Models.cs
@@ -0,0 +1,11 @@
+namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.GetManyById;
+
+internal class Request
+{
+ public ICollection Ids { get; set; } = default!;
+
+ ///
+ /// True if the response should include the root activity of composite activities.
+ ///
+ public bool IncludeCompositeRoot { get; set; }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs
index cb5dae8ab..4e9e47b0d 100644
--- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs
@@ -1,6 +1,7 @@
using Elsa.Abstractions;
using Elsa.Common.Entities;
using Elsa.Common.Models;
+using Elsa.Models;
using Elsa.Workflows.Api.Models;
using Elsa.Workflows.Management.Contracts;
using Elsa.Workflows.Management.Filters;
@@ -10,7 +11,7 @@ using JetBrains.Annotations;
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.List;
[PublicAPI]
-internal class List : ElsaEndpoint
+internal class List : ElsaEndpoint>
{
private readonly IWorkflowDefinitionStore _store;
@@ -25,13 +26,12 @@ internal class List : ElsaEndpoint
ConfigurePermissions("read:workflow-definitions");
}
- public override async Task ExecuteAsync(Request request, CancellationToken cancellationToken)
+ public override async Task> ExecuteAsync(Request request, CancellationToken cancellationToken)
{
var pageArgs = PageArgs.FromPage(request.Page, request.PageSize);
var filter = CreateFilter(request);
-
var summaries = await FindAsync(request, filter, pageArgs, cancellationToken);
- return new Response(summaries.Items, summaries.TotalCount);
+ return new PagedListResponse(summaries);
}
private WorkflowDefinitionFilter CreateFilter(Request request)
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs
index bbc36e974..e2dfe782b 100644
--- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs
@@ -17,16 +17,4 @@ internal class Request
public OrderByWorkflowDefinition? OrderBy { get; set; }
public OrderDirection? OrderDirection { get; set; }
public string? SearchTerm { get; set; }
-}
-
-internal class Response
-{
- public Response(ICollection items, long totalCount)
- {
- Items = items;
- TotalCount = totalCount;
- }
-
- public ICollection Items { get; set; }
- public long TotalCount { get; set; }
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Version/Revert.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Version/Revert.cs
index af43e4005..64f0e0589 100644
--- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Version/Revert.cs
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Version/Revert.cs
@@ -1,9 +1,11 @@
using Elsa.Workflows.Management.Contracts;
using FastEndpoints;
+using JetBrains.Annotations;
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Version;
-public class RevertVersion : EndpointWithoutRequest
+[PublicAPI]
+internal class RevertVersion : EndpointWithoutRequest
{
private readonly IWorkflowDefinitionManager _workflowDefinitionManager;
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/FilteredList/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/FilteredList/Endpoint.cs
new file mode 100644
index 000000000..ada4e7086
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/FilteredList/Endpoint.cs
@@ -0,0 +1,64 @@
+using Elsa.Abstractions;
+using Elsa.Common.Entities;
+using Elsa.Common.Models;
+using Elsa.Workflows.Runtime.Contracts;
+using Elsa.Workflows.Runtime.Filters;
+using Elsa.Workflows.Runtime.OrderDefinitions;
+using JetBrains.Annotations;
+
+namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Journal.FilteredList;
+
+///
+/// Gets the journal for a workflow instance.
+///
+[PublicAPI]
+internal class Get : ElsaEndpoint
+{
+ private readonly IWorkflowExecutionLogStore _store;
+
+ ///
+ public Get(IWorkflowExecutionLogStore store)
+ {
+ _store = store;
+ }
+
+ ///
+ public override void Configure()
+ {
+ Post("/workflow-instances/{id}/journal");
+ ConfigurePermissions("read:workflow-instances");
+ }
+
+ ///
+ public override async Task ExecuteAsync(Request request, CancellationToken cancellationToken)
+ {
+ var pageArgs = PageArgs.From(request.Page, request.PageSize, request.Skip, request.Take);
+ var filter = new WorkflowExecutionLogRecordFilter
+ {
+ WorkflowInstanceId = request.WorkflowInstanceId,
+ ActivityIds = request.Filter?.ActivityIds
+ };
+ var order = new WorkflowExecutionLogRecordOrder(x => x.Sequence, OrderDirection.Ascending);
+ var pageOfRecords = await _store.FindManyAsync(filter, pageArgs, order, cancellationToken);
+
+ var models = pageOfRecords.Items.Select(x =>
+ new ExecutionLogRecord(
+ x.Id,
+ x.ActivityInstanceId,
+ x.ParentActivityInstanceId,
+ x.ActivityId,
+ x.ActivityType,
+ x.ActivityTypeVersion,
+ x.NodeId,
+ x.Timestamp,
+ x.Sequence,
+ x.EventName,
+ x.Message,
+ x.Source,
+ x.ActivityState,
+ x.Payload))
+ .ToList();
+
+ return new(models, pageOfRecords.TotalCount);
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/FilteredList/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/FilteredList/Models.cs
new file mode 100644
index 000000000..6589f0777
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowInstances/Journal/FilteredList/Models.cs
@@ -0,0 +1,74 @@
+using FastEndpoints;
+
+// ReSharper disable NotAccessedPositionalProperty.Global
+
+namespace Elsa.Workflows.Api.Endpoints.WorkflowInstances.Journal.FilteredList;
+
+///
+/// Represents a request for a page of workflow execution log records.
+///
+internal class Request
+{
+ ///
+ /// The ID of the workflow instance to get the execution log for.
+ ///
+ [BindFrom("id")] public string WorkflowInstanceId { get; set; } = default!;
+
+ ///
+ /// The filter to apply.
+ ///
+ public JournalFilter? Filter { get; set; }
+
+ ///
+ /// The zero-based page number to get.
+ ///
+ public int? Page { get; set; }
+
+ ///
+ /// The size of the page to get.
+ ///
+ public int? PageSize { get; set; }
+
+ ///
+ /// The number of records to skip.
+ ///
+ public int? Skip { get; set; }
+
+ ///
+ /// The number of records to take.
+ ///
+ public int? Take { get; set; }
+}
+
+internal class JournalFilter
+{
+ public ICollection? ActivityIds { get; set; }
+}
+
+internal class Response
+{
+ public Response(ICollection items, long totalCount)
+ {
+ Items = items;
+ TotalCount = totalCount;
+ }
+
+ public ICollection Items { get; }
+ public long TotalCount { get; }
+}
+
+internal record ExecutionLogRecord(
+ string Id,
+ string ActivityInstanceId,
+ string? ParentActivityInstanceId,
+ string ActivityId,
+ string ActivityType,
+ int ActivityTypeVersion,
+ string NodeId,
+ DateTimeOffset Timestamp,
+ long Sequence,
+ string? EventName,
+ string? Message,
+ string? Source,
+ IDictionary? ActivityState,
+ object? Payload);
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Management/Mappers/WorkflowDefinitionMapper.cs b/src/modules/Elsa.Workflows.Management/Mappers/WorkflowDefinitionMapper.cs
index a7229965c..02e17f55a 100644
--- a/src/modules/Elsa.Workflows.Management/Mappers/WorkflowDefinitionMapper.cs
+++ b/src/modules/Elsa.Workflows.Management/Mappers/WorkflowDefinitionMapper.cs
@@ -80,6 +80,15 @@ public class WorkflowDefinitionMapper
source.IsReadonly);
}
+ ///
+ /// Maps many s to many s.
+ ///
+ /// The source s.
+ /// An optional cancellation token.
+ /// The mapped s.
+ public async Task> MapAsync(IEnumerable source, CancellationToken cancellationToken = default) =>
+ await Task.WhenAll(source.Select(async x => await MapAsync(x, cancellationToken)));
+
///
/// Maps a to a .
///
diff --git a/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowExecutionLogRecordFilter.cs b/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowExecutionLogRecordFilter.cs
index 3d47c73ec..f4c707e1e 100644
--- a/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowExecutionLogRecordFilter.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Filters/WorkflowExecutionLogRecordFilter.cs
@@ -27,10 +27,20 @@ public class WorkflowExecutionLogRecordFilter
///
public ICollection? WorkflowInstanceIds { get; set; }
+ ///
+ /// The ID of the parent activity instance.
+ ///
+ public string? ParentActivityInstanceId { get; set; }
+
///
/// The ID of the activity.
///
public string? ActivityId { get; set; }
+
+ ///
+ /// The IDs of the activities.
+ ///
+ public ICollection? ActivityIds { get; set; }
///
/// The name of the event.
@@ -41,7 +51,7 @@ public class WorkflowExecutionLogRecordFilter
/// Match any of these event names.
///
public ICollection? AnyEventName { get; set; }
-
+
///
/// Applies the filter to the specified queryable.
///
@@ -52,7 +62,9 @@ public class WorkflowExecutionLogRecordFilter
if (filter.Ids != null) queryable = queryable.Where(x => filter.Ids.Contains(x.Id!));
if (filter.WorkflowInstanceId != null) queryable = queryable.Where(x => x.WorkflowInstanceId == filter.WorkflowInstanceId);
if (filter.WorkflowInstanceIds != null) queryable = queryable.Where(x => filter.WorkflowInstanceIds.Contains(x.WorkflowInstanceId!));
+ if (filter.ParentActivityInstanceId != null) queryable = queryable.Where(x => x.ParentActivityInstanceId == filter.ParentActivityInstanceId);
if (filter.ActivityId != null) queryable = queryable.Where(x => x.ActivityId == filter.ActivityId);
+ if (filter.ActivityIds != null) queryable = queryable.Where(x => filter.ActivityIds.Contains(x.ActivityId));
if (filter.EventName != null) queryable = queryable.Where(x => x.EventName == filter.EventName);
if (filter.AnyEventName != null) queryable = queryable.Where(x => filter.AnyEventName.Contains(x.EventName!));