11namespace TemporalioSamples . ContextPropagation ;
22
33using System . Threading . Tasks ;
4+ using NexusRpc . Handlers ;
45using Temporalio . Api . Common . V1 ;
56using Temporalio . Client ;
67using Temporalio . Client . Interceptors ;
@@ -39,6 +40,10 @@ public WorkflowInboundInterceptor InterceptWorkflow(WorkflowInboundInterceptor n
3940 public ActivityInboundInterceptor InterceptActivity ( ActivityInboundInterceptor nextInterceptor ) =>
4041 new ContextPropagationActivityInboundInterceptor ( this , nextInterceptor ) ;
4142
43+ public NexusOperationInboundInterceptor InterceptNexusOperation (
44+ NexusOperationInboundInterceptor nextInterceptor ) =>
45+ new ContextPropagationNexusOperationInboundInterceptor ( this , nextInterceptor ) ;
46+
4247 private Dictionary < string , Payload > HeaderFromContext ( IDictionary < string , Payload > ? existing )
4348 {
4449 var ret = existing != null ?
@@ -67,6 +72,28 @@ private TResult WithHeadersApplied<TResult>(
6772 return func ( ) ;
6873 }
6974
75+ private Dictionary < string , string > HeaderFromContextForNexus ( IDictionary < string , string > ? existing )
76+ {
77+ var ret = existing != null ?
78+ new Dictionary < string , string > ( existing ) : new Dictionary < string , string > ( 1 ) ;
79+ // Nexus headers are string-based, so serialize context value to JSON.
80+ // Alternative approach: could use payload converter and put entire payload as JSON on header.
81+ ret [ headerKey ] = System . Text . Json . JsonSerializer . Serialize ( context . Value ) ;
82+ return ret ;
83+ }
84+
85+ private Task < TResult > WithHeadersAppliedForNexusAsync < TResult > (
86+ IReadOnlyDictionary < string , string > ? headers , Func < Task < TResult > > func )
87+ {
88+ if ( headers ? . TryGetValue ( headerKey , out var value ) == true )
89+ {
90+ // Deserialize can return null for nullable types, which is expected
91+ context . Value = System . Text . Json . JsonSerializer . Deserialize < T > ( value ) ! ;
92+ }
93+ // These are async local, no need to unapply afterwards
94+ return func ( ) ;
95+ }
96+
7097 private class ContextPropagationClientOutboundInterceptor : ClientOutboundInterceptor
7198 {
7299 private readonly ContextPropagationInterceptor < T > root ;
@@ -153,6 +180,11 @@ public override Task<ChildWorkflowHandle<TWorkflow, TResult>> StartChildWorkflow
153180 StartChildWorkflowInput input ) =>
154181 Next . StartChildWorkflowAsync < TWorkflow , TResult > (
155182 input with { Headers = root . HeaderFromContext ( input . Headers ) } ) ;
183+
184+ public override Task < NexusOperationHandle < TResult > > StartNexusOperationAsync < TResult > (
185+ StartNexusOperationInput input ) =>
186+ Next . StartNexusOperationAsync < TResult > (
187+ input with { Headers = root . HeaderFromContextForNexus ( input . Headers ) } ) ;
156188 }
157189
158190 private class ContextPropagationActivityInboundInterceptor : ActivityInboundInterceptor
@@ -166,4 +198,19 @@ public ContextPropagationActivityInboundInterceptor(
166198 public override Task < object ? > ExecuteActivityAsync ( ExecuteActivityInput input ) =>
167199 root . WithHeadersApplied ( input . Headers , ( ) => Next . ExecuteActivityAsync ( input ) ) ;
168200 }
201+
202+ private class ContextPropagationNexusOperationInboundInterceptor : NexusOperationInboundInterceptor
203+ {
204+ private readonly ContextPropagationInterceptor < T > root ;
205+
206+ public ContextPropagationNexusOperationInboundInterceptor (
207+ ContextPropagationInterceptor < T > root , NexusOperationInboundInterceptor next )
208+ : base ( next ) => this . root = root ;
209+
210+ public override Task < OperationStartResult < object ? > > ExecuteNexusOperationStartAsync (
211+ ExecuteNexusOperationStartInput input ) =>
212+ root . WithHeadersAppliedForNexusAsync (
213+ input . Context . Headers ,
214+ ( ) => base . ExecuteNexusOperationStartAsync ( input ) ) ;
215+ }
169216}
0 commit comments