1
- import { HandlerContext as BaseHandlerContext , getHandlerContext } from 'nexus-rpc/lib/handler' ;
2
- import { Logger , LogLevel , LogMetadata } from '@temporalio/common' ;
3
- import { Client } from '@temporalio/client' ;
1
+ import * as nexus from 'nexus-rpc' ;
2
+ import { HandlerContext as BaseHandlerContext , getHandlerContext , handlerLinks } from 'nexus-rpc/lib/handler' ;
3
+ import { Logger , LogLevel , LogMetadata , Workflow } from '@temporalio/common' ;
4
+ import { Client , WorkflowStartOptions } from '@temporalio/client' ;
5
+ import { temporal } from '@temporalio/proto' ;
6
+ import { InternalWorkflowStartOptionsKey , InternalWorkflowStartOptions } from '@temporalio/client/lib/internal' ;
7
+ import { generateWorkflowRunOperationToken , loadWorkflowRunOperationToken } from './token' ;
8
+ import { convertNexusLinkToWorkflowEventLink , convertWorkflowEventLinkToNexusLink } from './link-converter' ;
4
9
5
10
export interface HandlerContext extends BaseHandlerContext {
6
11
log : Logger ;
7
12
client : Client ;
13
+ namespace : string ;
8
14
}
9
15
10
16
function getLogger ( ) {
@@ -35,6 +41,8 @@ export const log: Logger = {
35
41
} ,
36
42
} ;
37
43
44
+ // TODO: also support getting a metrics handler.
45
+
38
46
/**
39
47
* Returns a client to be used in a Nexus Operation's context, this Client is powered by the same Connection that the
40
48
* worker was created with.
@@ -43,4 +51,74 @@ export function getClient(): Client {
43
51
return getHandlerContext < HandlerContext > ( ) . client ;
44
52
}
45
53
46
- // TODO: also support getting a metrics handler.
54
+ export interface WorkflowHandle < _T > {
55
+ readonly workflowId : string ;
56
+ readonly runId : string ;
57
+ }
58
+
59
+ export async function startWorkflow < T extends Workflow > ( workflowTypeOrFunc : string | T , workflowOptions : WorkflowStartOptions < T > , nexusOptions : nexus . StartOperationOptions ) : Promise < WorkflowHandle < T > > {
60
+ const links = Array < temporal . api . common . v1 . ILink > ( ) ;
61
+ if ( nexusOptions . links ?. length > 0 ) {
62
+ for ( const l of nexusOptions . links ) {
63
+ try {
64
+ links . push ( {
65
+ workflowEvent : convertNexusLinkToWorkflowEventLink ( l ) ,
66
+ } ) ;
67
+ } catch ( error ) {
68
+ log . warn ( 'failed to convert Nexus link to Workflow event link' , { error } ) ;
69
+ }
70
+ }
71
+ }
72
+ const internalOptions : InternalWorkflowStartOptions = { links, requestId : nexusOptions . requestId } ;
73
+
74
+ if ( workflowOptions . workflowIdConflictPolicy === 'USE_EXISTING' ) {
75
+ internalOptions . onConflictOptions = {
76
+ attachLinks : true ,
77
+ attachCompletionCallbacks : true ,
78
+ attachRequestId : true ,
79
+ } ;
80
+ }
81
+
82
+ if ( nexusOptions . callbackURL ) {
83
+ internalOptions . completionCallbacks = [
84
+ {
85
+ nexus : { url : nexusOptions . callbackURL , header : nexusOptions . callbackHeaders } ,
86
+ links, // pass in links here as well, the server dedupes them.
87
+ } ,
88
+ ] ;
89
+ }
90
+ ( workflowOptions as any ) [ InternalWorkflowStartOptionsKey ] = internalOptions ;
91
+ const handle = await getClient ( ) . workflow . start < T > ( workflowTypeOrFunc , workflowOptions ) ;
92
+ if ( internalOptions . backLink ?. workflowEvent != null ) {
93
+ try {
94
+ handlerLinks ( ) . push ( convertWorkflowEventLinkToNexusLink ( internalOptions . backLink . workflowEvent ) ) ;
95
+ } catch ( error ) {
96
+ log . warn ( 'failed to convert Workflow event link to Nexus link' , { error } ) ;
97
+ }
98
+ }
99
+ return { workflowId : handle . workflowId , runId : handle . firstExecutionRunId } ;
100
+ }
101
+
102
+ export type WorkflowRunOperationHandler < I , O > = ( input : I , options : nexus . StartOperationOptions ) => Promise < WorkflowHandle < O > > ;
103
+
104
+ export class WorkflowRunOperation < I , O > implements nexus . OperationHandler < I , O > {
105
+ constructor ( readonly handler : WorkflowRunOperationHandler < I , O > ) { }
106
+
107
+ async start ( input : I , options : nexus . StartOperationOptions ) : Promise < nexus . HandlerStartOperationResult < O > > {
108
+ const { namespace } = getHandlerContext < HandlerContext > ( ) ;
109
+ const handle = await this . handler ( input , options ) ;
110
+ return { token : generateWorkflowRunOperationToken ( namespace , handle . workflowId ) } ;
111
+ }
112
+ getResult ( _token : string , _options : nexus . GetOperationResultOptions ) : Promise < O > {
113
+ // Not implemented in Temporal yet.
114
+ throw new nexus . HandlerError ( { type : 'NOT_IMPLEMENTED' , message : 'Method not implemented' } ) ;
115
+ }
116
+ getInfo ( _token : string , _options : nexus . GetOperationInfoOptions ) : Promise < nexus . OperationInfo > {
117
+ // Not implemented in Temporal yet.
118
+ throw new nexus . HandlerError ( { type : 'NOT_IMPLEMENTED' , message : 'Method not implemented' } ) ;
119
+ }
120
+ async cancel ( token : string , _options : nexus . CancelOperationOptions ) : Promise < void > {
121
+ const decoded = loadWorkflowRunOperationToken ( token ) ;
122
+ await getClient ( ) . workflow . getHandle ( decoded . wid ) . cancel ( ) ;
123
+ }
124
+ }
0 commit comments