Skip to content

Commit 9c5382b

Browse files
authored
Merge branch 'main' into dependabot/go_modules/src/ActivityWorker/go-workflow/google.golang.org/grpc-1.79.3
2 parents 27065ea + 07e6472 commit 9c5382b

27 files changed

Lines changed: 978 additions & 13 deletions

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ jobs:
2727
run: "echo head_ref: ${{ github.head_ref }}, ref: ${{ github.ref }}, os: ${{ matrix.os }}"
2828

2929
- name: Checkout repository
30-
uses: actions/checkout@v4
30+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
3131

3232
- name: Setup .NET
33-
uses: actions/setup-dotnet@v4
33+
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5
3434
with:
3535
dotnet-version: 8
3636

@@ -46,7 +46,7 @@ jobs:
4646

4747
- name: Upload test failure
4848
if: ${{ failure() }}
49-
uses: actions/upload-artifact@v4
49+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
5050
with:
5151
name: test-fail-${{ matrix.os }}
5252
path: tests/TestResults

Directory.Build.props

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
</PropertyGroup>
2020

2121
<ItemGroup>
22-
<PackageReference Include="Temporalio" Version="1.12.0" />
23-
<PackageReference Include="Temporalio.Extensions.DiagnosticSource" Version="1.12.0" />
24-
<PackageReference Include="Temporalio.Extensions.Hosting" Version="1.12.0" />
25-
<PackageReference Include="Temporalio.Extensions.OpenTelemetry" Version="1.12.0" />
22+
<PackageReference Include="Temporalio" Version="1.13.0" />
23+
<PackageReference Include="Temporalio.Extensions.DiagnosticSource" Version="1.13.0" />
24+
<PackageReference Include="Temporalio.Extensions.Hosting" Version="1.13.0" />
25+
<PackageReference Include="Temporalio.Extensions.OpenTelemetry" Version="1.13.0" />
2626
<!--
2727
Can also reference the SDK downloaded to a local directory:
2828
<ProjectReference Include="$(MSBuildThisFileDirectory)..\temporal-sdk-dotnet\src\Temporalio\Temporalio.csproj" />

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ Prerequisites:
2828
* [Mutex](src/Mutex) - How to implement a mutex as a workflow. Demonstrates how to avoid race conditions or parallel mutually exclusive operations on the same resource.
2929
* [NexusCancellation](src/NexusCancellation) - Demonstrates how to cancel a running Nexus operation from a caller workflow.
3030
* [NexusContextPropagation](src/NexusContextPropagation) - Context propagation through Nexus services.
31+
* [Nexus Messaging](src/NexusMessaging): Demonstrates how send signal, update and query messages through Nexus.
32+
This contains two samples, one sending messages to an existing workflow and a second that creates a workflow through Nexus
33+
and sends messages to it.
3134
* [NexusMultiArg](src/NexusMultiArg) - Nexus service implementation calling a workflow with multiple arguments.
3235
* [NexusSimple](src/NexusSimple) - Simple Nexus service implementation.
3336
* [OpenTelemetry](src/OpenTelemetry) - Demonstrates how to set up OpenTelemetry tracing and metrics for both the client and worker, using both the .NET metrics API and internal forwarding from the Core SDK.

TemporalioSamples.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StandaloneActivity", "Stand
111111
EndProject
112112
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.StandaloneActivity", "src\StandaloneActivity\TemporalioSamples.StandaloneActivity.csproj", "{240517A1-13B5-4A67-8519-BFCF2C4591B9}"
113113
EndProject
114+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemporalioSamples.NexusMessaging", "src\NexusMessaging\TemporalioSamples.NexusMessaging.csproj", "{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}"
115+
EndProject
114116
Global
115117
GlobalSection(SolutionConfigurationPlatforms) = preSolution
116118
Debug|Any CPU = Debug|Any CPU
@@ -649,6 +651,18 @@ Global
649651
{240517A1-13B5-4A67-8519-BFCF2C4591B9}.Release|x64.Build.0 = Release|Any CPU
650652
{240517A1-13B5-4A67-8519-BFCF2C4591B9}.Release|x86.ActiveCfg = Release|Any CPU
651653
{240517A1-13B5-4A67-8519-BFCF2C4591B9}.Release|x86.Build.0 = Release|Any CPU
654+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
655+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Debug|Any CPU.Build.0 = Debug|Any CPU
656+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Debug|x64.ActiveCfg = Debug|Any CPU
657+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Debug|x64.Build.0 = Debug|Any CPU
658+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Debug|x86.ActiveCfg = Debug|Any CPU
659+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Debug|x86.Build.0 = Debug|Any CPU
660+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Release|Any CPU.ActiveCfg = Release|Any CPU
661+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Release|Any CPU.Build.0 = Release|Any CPU
662+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Release|x64.ActiveCfg = Release|Any CPU
663+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Release|x64.Build.0 = Release|Any CPU
664+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Release|x86.ActiveCfg = Release|Any CPU
665+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48}.Release|x86.Build.0 = Release|Any CPU
652666
EndGlobalSection
653667
GlobalSection(SolutionProperties) = preSolution
654668
HideSolutionNode = FALSE
@@ -704,5 +718,6 @@ Global
704718
{7123C63D-3158-4C9A-8EAD-6D4F1295BC04} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
705719
{EAB0C45A-7620-D2D2-2901-5E7FCBFFDA77} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
706720
{240517A1-13B5-4A67-8519-BFCF2C4591B9} = {EAB0C45A-7620-D2D2-2901-5E7FCBFFDA77}
721+
{5D493692-53AB-4FAA-BA4D-33B1E54E9A48} = {1A647B41-53D0-4638-AE5A-6630BAAE45FC}
707722
EndGlobalSection
708723
EndGlobal

src/NexusCancellation/Caller/HelloCallerWorkflow.workflow.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
public class HelloCallerWorkflow
99
{
1010
private static readonly IHelloService.HelloLanguage[] Languages =
11-
[IHelloService.HelloLanguage.En, IHelloService.HelloLanguage.Fr, IHelloService.HelloLanguage.De,
12-
IHelloService.HelloLanguage.Es, IHelloService.HelloLanguage.Tr];
11+
[IHelloService.HelloLanguage.En,
12+
IHelloService.HelloLanguage.Fr,
13+
IHelloService.HelloLanguage.De,
14+
IHelloService.HelloLanguage.Es,
15+
IHelloService.HelloLanguage.Tr];
1316

1417
[WorkflowRun]
1518
public async Task<string> RunAsync(string name)

src/NexusCancellation/TemporalioSamples.NexusCancellation.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="NexusRpc" Version="0.2.0" />
8+
<PackageReference Include="NexusRpc" Version="0.3.0" />
99
</ItemGroup>
1010

1111
</Project>

src/NexusContextPropagation/TemporalioSamples.NexusContextPropagation.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<ItemGroup>
88
<ProjectReference Include="$(MSBuildThisFileDirectory)..\ContextPropagation\TemporalioSamples.ContextPropagation.csproj" />
9-
<PackageReference Include="NexusRpc" Version="0.2.0" />
9+
<PackageReference Include="NexusRpc" Version="0.3.0" />
1010
</ItemGroup>
1111

1212
</Project>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace TemporalioSamples.NexusMessaging.CallerPattern.Caller;
2+
3+
using Temporalio.Workflows;
4+
using TemporalioSamples.NexusMessaging.CallerPattern;
5+
using TemporalioSamples.NexusMessaging.Common;
6+
7+
[Workflow]
8+
public class CallerWorkflow
9+
{
10+
[WorkflowRun]
11+
public async Task<string[]> RunAsync(string userId)
12+
{
13+
var log = new List<string>();
14+
var client = Workflow.CreateNexusWorkflowClient<INexusGreetingService>(
15+
INexusGreetingService.EndpointName);
16+
17+
// GetLanguages - query entity workflow for supported languages
18+
var languagesOutput = await client.ExecuteNexusOperationAsync(
19+
svc => svc.GetLanguages(new INexusGreetingService.GetLanguagesInput(false, userId)));
20+
log.Add($"Supported languages: {string.Join(", ", languagesOutput.Languages)}");
21+
22+
// GetLanguage - query entity workflow for current language
23+
var currentLanguage = await client.ExecuteNexusOperationAsync(
24+
svc => svc.GetLanguage(new INexusGreetingService.GetLanguageInput(userId)));
25+
log.Add($"Current language: {currentLanguage}");
26+
27+
// SetLanguage - update entity workflow to change language
28+
var previousLanguage = await client.ExecuteNexusOperationAsync(
29+
svc => svc.SetLanguage(new INexusGreetingService.SetLanguageInput(Language.Chinese, userId)));
30+
log.Add($"Set language from {previousLanguage} to {Language.Chinese}");
31+
32+
// Approve - signal entity workflow to complete
33+
await client.ExecuteNexusOperationAsync(
34+
svc => svc.Approve(new INexusGreetingService.ApproveInput("CallerWorkflow", userId)));
35+
log.Add("Approved workflow");
36+
37+
return log.ToArray();
38+
}
39+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
namespace TemporalioSamples.NexusMessaging.CallerPattern.Handler;
2+
3+
using Temporalio.Exceptions;
4+
using Temporalio.Workflows;
5+
using TemporalioSamples.NexusMessaging.CallerPattern;
6+
using TemporalioSamples.NexusMessaging.Common;
7+
8+
[Workflow]
9+
public class GreetingWorkflow
10+
{
11+
private readonly Dictionary<Language, string> greetings = new()
12+
{
13+
[Language.Chinese] = "你好,世界",
14+
[Language.English] = "Hello, world",
15+
};
16+
17+
private Language currentLanguage = Language.English;
18+
private bool approved;
19+
private string approvedBy = string.Empty;
20+
21+
[WorkflowRun]
22+
public async Task<string> RunAsync(string userId)
23+
{
24+
// Wait for approve signal and all handlers to finish
25+
await Workflow.WaitConditionAsync(() => approved && Workflow.AllHandlersFinished);
26+
27+
var greeting = greetings.TryGetValue(currentLanguage, out var g) ? g : "Hello, world";
28+
return $"{greeting} (approved by {approvedBy})";
29+
}
30+
31+
[WorkflowQuery]
32+
public INexusGreetingService.GetLanguagesOutput QueryLanguages(bool includeUnsupported)
33+
{
34+
if (includeUnsupported)
35+
{
36+
return new(Enum.GetValues<Language>());
37+
}
38+
39+
return new(greetings.Keys.ToArray());
40+
}
41+
42+
[WorkflowQuery]
43+
public Language QueryLanguage() => currentLanguage;
44+
45+
[WorkflowUpdate]
46+
public async Task<Language> SetLanguageAsync(Language newLanguage)
47+
{
48+
var prev = currentLanguage;
49+
50+
// If language not yet in our greetings map, fetch from activity
51+
if (!greetings.ContainsKey(newLanguage))
52+
{
53+
var allGreetings = await Workflow.ExecuteActivityAsync(
54+
(GreetingActivities a) => a.GetAllGreetingsAsync(),
55+
new() { StartToCloseTimeout = TimeSpan.FromSeconds(10) });
56+
foreach (var kvp in allGreetings)
57+
{
58+
greetings[kvp.Key] = kvp.Value;
59+
}
60+
}
61+
62+
currentLanguage = newLanguage;
63+
return prev;
64+
}
65+
66+
[WorkflowUpdateValidator(nameof(SetLanguageAsync))]
67+
public void ValidateSetLanguage(Language newLanguage)
68+
{
69+
if (!Enum.IsDefined(newLanguage))
70+
{
71+
throw new ApplicationFailureException($"Unsupported language: {newLanguage}");
72+
}
73+
}
74+
75+
[WorkflowSignal]
76+
public Task ApproveAsync(string name)
77+
{
78+
approved = true;
79+
approvedBy = name;
80+
return Task.CompletedTask;
81+
}
82+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
namespace TemporalioSamples.NexusMessaging.CallerPattern.Handler;
2+
3+
using NexusRpc.Handlers;
4+
using Temporalio.Nexus;
5+
using TemporalioSamples.NexusMessaging.CallerPattern;
6+
using TemporalioSamples.NexusMessaging.Common;
7+
8+
// Entity pattern: the handler worker pre-starts a GreetingWorkflow per user at boot time.
9+
// This service routes each Nexus operation to that existing workflow by deriving the
10+
// workflow ID from the caller-supplied UserId.
11+
[NexusServiceHandler(typeof(INexusGreetingService))]
12+
public class NexusGreetingService
13+
{
14+
// OperationHandler.Sync means the result is returned inline to the Nexus caller
15+
// (as opposed to WorkflowRunOperationHandler, which returns an async operation token).
16+
// The lambda may still be async internally.
17+
18+
// Query: read-only, no state mutation — uses workflow query
19+
[NexusOperationHandler]
20+
public IOperationHandler<INexusGreetingService.GetLanguagesInput, INexusGreetingService.GetLanguagesOutput> GetLanguages() =>
21+
OperationHandler.Sync<INexusGreetingService.GetLanguagesInput, INexusGreetingService.GetLanguagesOutput>(
22+
async (ctx, input) =>
23+
{
24+
// Access the Temporal client from the Nexus operation context
25+
var client = NexusOperationExecutionContext.Current.TemporalClient;
26+
var handle = client.GetWorkflowHandle<GreetingWorkflow>(WorkflowIdForUser(input.UserId));
27+
return await handle.QueryAsync(wf => wf.QueryLanguages(input.IncludeUnsupported));
28+
});
29+
30+
// Query: read-only — returns the workflow's current language
31+
[NexusOperationHandler]
32+
public IOperationHandler<INexusGreetingService.GetLanguageInput, Language> GetLanguage() =>
33+
OperationHandler.Sync<INexusGreetingService.GetLanguageInput, Language>(
34+
async (ctx, input) =>
35+
{
36+
var client = NexusOperationExecutionContext.Current.TemporalClient;
37+
var handle = client.GetWorkflowHandle<GreetingWorkflow>(WorkflowIdForUser(input.UserId));
38+
return await handle.QueryAsync(wf => wf.QueryLanguage());
39+
});
40+
41+
// Update: mutates state and returns the previous value — uses workflow update
42+
[NexusOperationHandler]
43+
public IOperationHandler<INexusGreetingService.SetLanguageInput, Language> SetLanguage() =>
44+
OperationHandler.Sync<INexusGreetingService.SetLanguageInput, Language>(
45+
async (ctx, input) =>
46+
{
47+
var client = NexusOperationExecutionContext.Current.TemporalClient;
48+
var handle = client.GetWorkflowHandle<GreetingWorkflow>(WorkflowIdForUser(input.UserId));
49+
return await handle.ExecuteUpdateAsync(wf => wf.SetLanguageAsync(input.Language));
50+
});
51+
52+
// Signal: fire-and-forget, no return value needed — uses workflow signal
53+
[NexusOperationHandler]
54+
public IOperationHandler<INexusGreetingService.ApproveInput, NoValue> Approve() =>
55+
OperationHandler.Sync<INexusGreetingService.ApproveInput, NoValue>(
56+
async (ctx, input) =>
57+
{
58+
var client = NexusOperationExecutionContext.Current.TemporalClient;
59+
var handle = client.GetWorkflowHandle<GreetingWorkflow>(WorkflowIdForUser(input.UserId));
60+
await handle.SignalAsync(wf => wf.ApproveAsync(input.Name));
61+
return default;
62+
});
63+
64+
private static string WorkflowIdForUser(string userId) => $"GreetingWorkflow_for_{userId}";
65+
}

0 commit comments

Comments
 (0)