Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions src/Dapr.Workflow.Abstractions/HistoryEventKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// ------------------------------------------------------------------------
// Copyright 2026 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

namespace Dapr.Workflow;

/// <summary>
/// Identifies the kind of a workflow history event returned in propagated history.
/// </summary>
public enum HistoryEventKind
{
/// <summary>
/// Unknown or unsupported event type.
/// </summary>
Unknown = 0,

/// <summary>
/// The workflow execution started.
/// </summary>
ExecutionStarted,

/// <summary>
/// The workflow execution completed.
/// </summary>
ExecutionCompleted,

/// <summary>
/// The workflow execution was terminated.
/// </summary>
ExecutionTerminated,

/// <summary>
/// An activity task was scheduled.
/// </summary>
TaskScheduled,

/// <summary>
/// An activity task completed successfully.
/// </summary>
TaskCompleted,

/// <summary>
/// An activity task failed.
/// </summary>
TaskFailed,

/// <summary>
/// A child workflow instance was created.
/// </summary>
SubOrchestrationInstanceCreated,

/// <summary>
/// A child workflow instance completed successfully.
/// </summary>
SubOrchestrationInstanceCompleted,

/// <summary>
/// A child workflow instance failed.
/// </summary>
SubOrchestrationInstanceFailed,

/// <summary>
/// A durable timer was created.
/// </summary>
TimerCreated,

/// <summary>
/// A durable timer fired.
/// </summary>
TimerFired,

/// <summary>
/// The orchestrator started a processing turn.
/// </summary>
OrchestratorStarted,

/// <summary>
/// The orchestrator completed a processing turn.
/// </summary>
OrchestratorCompleted,

/// <summary>
/// An event was sent to another workflow instance.
/// </summary>
EventSent,

/// <summary>
/// An external event was raised for this workflow instance.
/// </summary>
EventRaised,

/// <summary>
/// The workflow continued as new.
/// </summary>
ContinueAsNew,

/// <summary>
/// The workflow execution was suspended.
/// </summary>
ExecutionSuspended,

/// <summary>
/// The workflow execution was resumed.
/// </summary>
ExecutionResumed
}
36 changes: 36 additions & 0 deletions src/Dapr.Workflow.Abstractions/HistoryPropagationScope.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// ------------------------------------------------------------------------
// Copyright 2026 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

namespace Dapr.Workflow;

/// <summary>
/// Defines the scope of workflow history that is propagated to a child workflow.
/// </summary>
public enum HistoryPropagationScope
{
/// <summary>
/// No history is propagated to child workflows. This is the default behavior.
/// </summary>
None = 0,

/// <summary>
/// Only the calling workflow's own history events are propagated to the child.
/// Ancestor history is excluded, acting as a trust boundary.
/// </summary>
OwnHistory = 1,

/// <summary>
/// The calling workflow's history and all ancestor history (the full lineage) is propagated.
/// </summary>
Lineage = 2,
}
85 changes: 85 additions & 0 deletions src/Dapr.Workflow.Abstractions/PropagatedHistory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// ------------------------------------------------------------------------
// Copyright 2026 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

namespace Dapr.Workflow;

using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Contains the workflow history that was propagated from ancestor workflow instances.
/// Each entry corresponds to a single ancestor's history.
/// </summary>
/// <remarks>
/// A workflow receives propagated history when it is scheduled with a
/// <see cref="HistoryPropagationScope"/> other than <see cref="HistoryPropagationScope.None"/>.
/// Use <see cref="WorkflowContext.GetPropagatedHistory"/> to retrieve the propagated history
/// inside a workflow implementation.
/// </remarks>
public sealed class PropagatedHistory
{
private readonly IReadOnlyList<PropagatedHistoryEntry> _entries;

/// <summary>
/// Initializes a new instance of <see cref="PropagatedHistory"/> with the given entries.
/// </summary>
/// <param name="entries">The propagated history entries from ancestor workflows.</param>
public PropagatedHistory(IReadOnlyList<PropagatedHistoryEntry> entries)
{
_entries = entries ?? throw new ArgumentNullException(nameof(entries));
}

/// <summary>
/// Gets the ordered list of propagated history entries.
/// The first entry corresponds to the immediate parent workflow; subsequent entries
/// correspond to progressively older ancestors when <see cref="HistoryPropagationScope.Lineage"/> is used.
/// </summary>
public IReadOnlyList<PropagatedHistoryEntry> Entries => _entries;

/// <summary>
/// Returns a new <see cref="PropagatedHistory"/> containing only entries from the specified App ID.
/// </summary>
/// <param name="appId">The Dapr App ID to filter by.</param>
/// <returns>A filtered <see cref="PropagatedHistory"/> instance.</returns>
public PropagatedHistory FilterByAppId(string appId)
{
ArgumentException.ThrowIfNullOrWhiteSpace(appId);
return new PropagatedHistory(
_entries.Where(e => string.Equals(e.AppId, appId, StringComparison.OrdinalIgnoreCase)).ToList());
}

/// <summary>
/// Returns a new <see cref="PropagatedHistory"/> containing only the entry with the specified instance ID.
/// </summary>
/// <param name="instanceId">The workflow instance ID to filter by.</param>
/// <returns>A filtered <see cref="PropagatedHistory"/> instance.</returns>
public PropagatedHistory FilterByInstanceId(string instanceId)
{
ArgumentException.ThrowIfNullOrWhiteSpace(instanceId);
return new PropagatedHistory(
_entries.Where(e => string.Equals(e.InstanceId, instanceId, StringComparison.Ordinal)).ToList());
}

/// <summary>
/// Returns a new <see cref="PropagatedHistory"/> containing only entries for the specified workflow name.
/// </summary>
/// <param name="workflowName">The workflow name to filter by.</param>
/// <returns>A filtered <see cref="PropagatedHistory"/> instance.</returns>
public PropagatedHistory FilterByWorkflowName(string workflowName)
{
ArgumentException.ThrowIfNullOrWhiteSpace(workflowName);
return new PropagatedHistory(
_entries.Where(e => string.Equals(e.WorkflowName, workflowName, StringComparison.Ordinal)).ToList());
}
}
29 changes: 29 additions & 0 deletions src/Dapr.Workflow.Abstractions/PropagatedHistoryEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// ------------------------------------------------------------------------
// Copyright 2026 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

namespace Dapr.Workflow;

using System.Collections.Generic;

/// <summary>
/// Represents a segment of propagated workflow history originating from a single ancestor workflow instance.
/// </summary>
/// <param name="AppId">The Dapr App ID of the application that ran the ancestor workflow.</param>
/// <param name="InstanceId">The instance ID of the ancestor workflow.</param>
/// <param name="WorkflowName">The name of the ancestor workflow.</param>
/// <param name="Events">The ordered list of history events from the ancestor workflow.</param>
public sealed record PropagatedHistoryEntry(
string AppId,
string InstanceId,
string WorkflowName,
IReadOnlyList<PropagatedHistoryEvent> Events);
24 changes: 24 additions & 0 deletions src/Dapr.Workflow.Abstractions/PropagatedHistoryEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// ------------------------------------------------------------------------
// Copyright 2026 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

namespace Dapr.Workflow;

using System;

/// <summary>
/// Represents a single event in a propagated workflow history segment.
/// </summary>
/// <param name="EventId">The unique event ID within the workflow instance history.</param>
/// <param name="Kind">The kind of history event.</param>
/// <param name="Timestamp">The UTC timestamp when the event occurred.</param>
public sealed record PropagatedHistoryEvent(int EventId, HistoryEventKind Kind, DateTimeOffset Timestamp);
29 changes: 26 additions & 3 deletions src/Dapr.Workflow.Abstractions/WorkflowContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,14 @@ public virtual async Task<T> WaitForExternalEventAsync<T>(string eventName, Time
Task winner = await Task.WhenAny(timeoutTask, externalEventTask);
if (winner == externalEventTask)
{
timerCts.Cancel();
await timerCts.CancelAsync();
}
else
{
eventCts.Cancel();
await eventCts.CancelAsync();
}

// This will either return the received value or throw if the task was cancelled.
// This will either return the received value or throw if the task was canceled.
return await externalEventTask;
}

Expand Down Expand Up @@ -330,4 +330,27 @@ public virtual Task CallChildWorkflowAsync(
/// </remarks>
/// <returns>The new <see cref="Guid"/> value.</returns>
public abstract Guid NewGuid();

/// <summary>
/// Gets the workflow history that was propagated from ancestor workflow instances, or <c>null</c>
/// if no history was propagated to this workflow.
/// </summary>
/// <remarks>
/// <para>
/// A workflow receives propagated history when it is scheduled as a child workflow and the parent
/// specified a <see cref="HistoryPropagationScope"/> other than <see cref="HistoryPropagationScope.None"/>.
/// </para>
/// <para>
/// Use <see cref="PropagatedHistory.FilterByAppId"/>, <see cref="PropagatedHistory.FilterByInstanceId"/>,
/// or <see cref="PropagatedHistory.FilterByWorkflowName"/> to narrow down the returned entries.
/// </para>
/// <para>
/// This method always returns the same value regardless of whether the workflow is replaying.
/// </para>
/// </remarks>
/// <returns>
/// A <see cref="PropagatedHistory"/> containing entries from ancestor workflows,
/// or <c>null</c> if no history was propagated to this workflow instance.
/// </returns>
public abstract PropagatedHistory? GetPropagatedHistory();
}
20 changes: 17 additions & 3 deletions src/Dapr.Workflow.Abstractions/WorkflowTaskOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,21 @@ public record WorkflowTaskOptions(WorkflowRetryPolicy? RetryPolicy = null, strin
/// <param name="InstanceId">The instance ID to use for the child workflow.</param>
/// <param name="RetryPolicy">The child workflow's retry policy.</param>
/// <param name="TargetAppId">The App ID indicating the app in which to find the named child workflow to run.</param>
/// <param name="PropagationScope">
/// Determines which ancestor history events are propagated to the child workflow.
/// Defaults to <see cref="HistoryPropagationScope.None"/>, meaning no history is propagated.
/// </param>
public record ChildWorkflowTaskOptions(
string? InstanceId = null,
WorkflowRetryPolicy? RetryPolicy = null,
string? TargetAppId = null) : WorkflowTaskOptions(RetryPolicy, TargetAppId);
string? InstanceId = null,
WorkflowRetryPolicy? RetryPolicy = null,
string? TargetAppId = null,
HistoryPropagationScope PropagationScope = HistoryPropagationScope.None) : WorkflowTaskOptions(RetryPolicy, TargetAppId)
{
/// <summary>
/// Returns a new <see cref="ChildWorkflowTaskOptions"/> with the specified history propagation scope.
/// </summary>
/// <param name="scope">The propagation scope to apply.</param>
/// <returns>A new options instance with the propagation scope set.</returns>
public ChildWorkflowTaskOptions WithHistoryPropagation(HistoryPropagationScope scope) =>
this with { PropagationScope = scope };
}
Loading
Loading