@@ -10,11 +10,12 @@ import {
10
10
IllegalStateError ,
11
11
LoadedDataConverter ,
12
12
Payload ,
13
+ PayloadConverter ,
13
14
SdkComponent ,
14
15
} from '@temporalio/common' ;
15
16
import { temporal , coresdk } from '@temporalio/proto' ;
16
17
import { HandlerContext } from '@temporalio/nexus/lib/context' ;
17
- import { decodeFromPayload , encodeToPayload , encodeErrorToFailure } from '@temporalio/common/lib/internal-non-workflow' ;
18
+ import { encodeToPayload , encodeErrorToFailure , decodeOptionalSingle } from '@temporalio/common/lib/internal-non-workflow' ;
18
19
import { fixBuffers } from '@temporalio/common/lib/proto-utils' ;
19
20
import { isAbortError } from '@temporalio/common/lib/type-helpers' ;
20
21
import { isGrpcServiceError , ServiceError } from '@temporalio/client' ;
@@ -76,10 +77,7 @@ export class NexusHandler {
76
77
public readonly taskToken : Uint8Array ,
77
78
public readonly info : nexus . HandlerInfo ,
78
79
public readonly abortController : AbortController ,
79
- public readonly handler :
80
- | nexus . OperationHandler < unknown , unknown >
81
- | nexus . SyncOperationHandler < unknown , unknown >
82
- | undefined ,
80
+ public readonly serviceRegistry : nexus . ServiceRegistry ,
83
81
public readonly dataConverter : LoadedDataConverter ,
84
82
/**
85
83
* Logger bound to `sdkComponent: worker`, with metadata from this Nexus task.
@@ -118,24 +116,15 @@ export class NexusHandler {
118
116
options : nexus . StartOperationOptions
119
117
) : Promise < coresdk . nexus . INexusTaskCompletion > {
120
118
try {
121
- const input = await decodeFromPayload ( this . dataConverter , payload ) ;
122
- if ( typeof this . handler === 'function' ) {
123
- const handler = this . handler as nexus . SyncOperationHandler < unknown , unknown > ;
124
- const output = await this . invokeUserCode ( 'startOperation' , handler . bind ( undefined , input , options ) ) ;
125
- return {
126
- taskToken : this . taskToken ,
127
- completed : {
128
- startOperation : {
129
- syncSuccess : {
130
- payload : await encodeToPayload ( this . dataConverter , output ) ,
131
- links : nexus . handlerLinks ( ) . map ( nexusLinkToProtoLink ) ,
132
- } ,
133
- } ,
134
- } ,
135
- } ;
136
- }
137
- const handler = this . handler as nexus . OperationHandler < unknown , unknown > ;
138
- const result = await this . invokeUserCode ( 'startOperation' , handler . start . bind ( handler , input , options ) ) ;
119
+ const decoded = await decodeOptionalSingle ( this . dataConverter . payloadCodecs , payload ) ;
120
+ // Nexus headers have string values and Temporal Payloads have binary values. Instead of converting Payload
121
+ // instances into Content instances, we embed the Payload in the serializer and pretend we are deserializing an
122
+ // empty Content.
123
+ const input = new nexus . LazyValue (
124
+ new PayloadSerializer ( this . dataConverter . payloadConverter , decoded ?? undefined ) ,
125
+ { } ,
126
+ ) ;
127
+ const result = await this . invokeUserCode ( 'startOperation' , this . serviceRegistry . start . bind ( this . serviceRegistry , this . info . service , this . info . operation , input , options ) ) ;
139
128
if ( isAsyncResult ( result ) ) {
140
129
return {
141
130
taskToken : this . taskToken ,
@@ -191,14 +180,7 @@ export class NexusHandler {
191
180
options : nexus . CancelOperationOptions
192
181
) : Promise < coresdk . nexus . INexusTaskCompletion > {
193
182
try {
194
- if ( typeof this . handler === 'function' ) {
195
- throw new nexus . HandlerError ( {
196
- type : 'NOT_IMPLEMENTED' ,
197
- message : 'cancel not implemented for this operation' ,
198
- } ) ;
199
- }
200
- const handler = this . handler as nexus . OperationHandler < unknown , unknown > ;
201
- await this . invokeUserCode ( 'cancelOperation' , handler . cancel . bind ( handler , token , options ) ) ;
183
+ await this . invokeUserCode ( 'cancelOperation' , this . serviceRegistry . cancel . bind ( this . serviceRegistry , this . info . service , this . info . operation , token , options ) ) ;
202
184
return {
203
185
taskToken : this . taskToken ,
204
186
completed : {
@@ -358,11 +340,11 @@ function convertKnownErrors(err: unknown): nexus.HandlerError {
358
340
case ( status . ABORTED , status . UNAVAILABLE ) :
359
341
return new nexus . HandlerError ( { type : 'UNAVAILABLE' , cause : err } ) ;
360
342
case ( status . CANCELLED ,
361
- status . DATA_LOSS ,
362
- status . INTERNAL ,
363
- status . UNKNOWN ,
364
- status . UNAUTHENTICATED ,
365
- status . PERMISSION_DENIED ) :
343
+ status . DATA_LOSS ,
344
+ status . INTERNAL ,
345
+ status . UNKNOWN ,
346
+ status . UNAUTHENTICATED ,
347
+ status . PERMISSION_DENIED ) :
366
348
// Note that UNAUTHENTICATED and PERMISSION_DENIED have Nexus error types but we convert to internal because
367
349
// this is not a client auth error and happens when the handler fails to auth with Temporal and should be
368
350
// considered retryable.
@@ -431,3 +413,25 @@ function nexusLinkToProtoLink(nlink: nexus.Link): temporal.api.nexus.v1.ILink {
431
413
type : nlink . type ,
432
414
} ;
433
415
}
416
+
417
+ /**
418
+ * An adapter from a Temporal PayloadConverer and a Nexus Serializer.
419
+ */
420
+ class PayloadSerializer implements nexus . Serializer {
421
+ constructor (
422
+ readonly payloadConverter : PayloadConverter ,
423
+ readonly payload ?: Payload ,
424
+ ) { }
425
+
426
+ deserialize < T > ( ) : T {
427
+ if ( this . payload == null ) {
428
+ return undefined as T ;
429
+ }
430
+ return this . payloadConverter . fromPayload ( this . payload ) ;
431
+ }
432
+
433
+ /** Not used in this path */
434
+ serialize ( ) : nexus . Content {
435
+ throw new Error ( "not implemented" ) ;
436
+ }
437
+ }
0 commit comments