1+ /*
2+ * Copyright (c) 2024 DevRev Inc. All rights reserved.
3+
4+ Disclaimer:
5+ The code provided herein is intended solely for testing purposes.
6+ Under no circumstances should it be utilized in a production environment. Use of
7+ this code in live systems, production environments, or any situation where
8+ reliability and stability are critical is strongly discouraged. The code is
9+ provided as-is, without any warranties or guarantees of any kind, and the user
10+ assumes all risks associated with its use. It is the responsibility of the user
11+ to ensure that proper testing and validation procedures are carried out before
12+ deploying any code into production environments.
13+ */
14+
115import bodyParser from 'body-parser' ;
216import express , { Express , Handler , Request , Response } from 'express' ;
317
@@ -17,6 +31,17 @@ import {
1731 SnapInsSystemUpdateResponse ,
1832} from './types' ;
1933
34+
35+ import {
36+ Context as SnapInContext ,
37+ ExecuteOperationResult ,
38+ ExecuteOperationResult_SerializationFormat ,
39+ ExecutionMetadata ,
40+ FunctionExecutionError ,
41+ FunctionInput ,
42+ OperationOutput ,
43+ } from '@devrev/typescript-sdk/dist/snap-ins' ;
44+
2045const app : Express = express ( ) ;
2146app . use ( bodyParser . json ( ) , bodyParser . urlencoded ( { extended : false } ) ) ;
2247
@@ -112,14 +137,17 @@ async function handleEvent(events: any[], isAsync: boolean, resp: Response) {
112137 console . error ( e ) ;
113138 }
114139
115- // post processing. result is updated in the function
116- await postRun ( event , error , result ) ;
140+ // Any common post processing goes here. The function returns
141+ // only if the function execution was by an operation
117142 }
143+ const opResult = await postRun ( event , error , result ) ;
118144
119145 // Return result.
120146 let res : ExecutionResult = { } ;
121147
122- if ( result !== undefined ) {
148+ if ( opResult !== undefined ) {
149+ res . function_result = opResult ;
150+ } else if ( result !== undefined ) {
123151 res . function_result = result ;
124152 }
125153
@@ -137,11 +165,16 @@ async function handleEvent(events: any[], isAsync: boolean, resp: Response) {
137165// post processing
138166async function postRun ( event : any , handlerError : HandlerError , result : any ) {
139167 console . debug ( 'Function execution complete' ) ;
168+ // Check if the function was invoked by an operation.
169+ if ( isInvokedFromOperation ( event ) ) {
170+ return handleOperationInvocationResult ( event , handlerError , result ) ;
171+ }
140172 if ( isActivateHook ( event ) ) {
141173 handleActivateHookResult ( event , handlerError , result ) ;
142174 } else if ( isDeactivateHook ( event ) ) {
143175 handleDeactivateHookResult ( event , handlerError , result ) ;
144176 }
177+ return undefined ;
145178}
146179
147180function isActivateHook ( event : any ) : boolean {
@@ -152,6 +185,10 @@ function isDeactivateHook(event: any): boolean {
152185 return event . execution_metadata . event_type === 'hook:snap_in_deactivate' ;
153186}
154187
188+ function isInvokedFromOperation ( event : any ) : boolean {
189+ return event . execution_metadata . operation_slug !== undefined ;
190+ }
191+
155192function handleActivateHookResult ( event : any , handlerError : HandlerError , result : any ) {
156193 let update_req : SnapInsSystemUpdateRequest = {
157194 id : event . context . snap_in_id ,
@@ -247,3 +284,80 @@ function getDeactivateHookResult(input: any): DeactivateHookResult {
247284 }
248285 return res ;
249286}
287+
288+ async function handleOperationInvocationResult (
289+ event : any ,
290+ handlerError : HandlerError ,
291+ result : any ,
292+ ) : Promise < ExecuteOperationResult > {
293+ if ( result === undefined ) {
294+ result = generateOperationOutputFromError ( handlerError ) ;
295+ }
296+
297+ const operationMethod = getOperationExecutionOperationMethod ( event ) || '' ;
298+ if ( operationMethod === 'OperationMethod_NameEnumSchemaHandler' ) {
299+ return createExecuteOperationResult ( result , ExecuteOperationResult_SerializationFormat . JSON ) ;
300+ }
301+
302+ return createExecuteOperationResult ( result , ExecuteOperationResult_SerializationFormat . Proto ) ;
303+ }
304+
305+ function generateOperationOutputFromError ( handlerError : HandlerError ) : OperationOutput {
306+ const errorDetails : FailedExecutionError = {
307+ source : FailedExecutionErrorSource . DeveloperFunction ,
308+ error :
309+ handlerError instanceof FunctionExecutionError
310+ ? handlerError
311+ : new FunctionExecutionError ( ( handlerError as unknown as Error ) ?. message , false ) ,
312+ } ;
313+
314+ return OperationOutput . fromJSON ( { error : errorDetails } ) as OperationOutput ;
315+ }
316+
317+
318+ export enum FailedExecutionErrorSource {
319+ // Source of the error not known.
320+ Unknown = 'unknown' ,
321+ // Error is returned from the developer in the function.
322+ DeveloperFunction = 'developer_function' ,
323+ // Error is returned from the platform.
324+ Platform = 'platform' ,
325+ }
326+
327+ export type FailedExecutionError = {
328+ source : FailedExecutionErrorSource ;
329+ error : FunctionExecutionError ;
330+ } ;
331+
332+ function createExecuteOperationResult (
333+ responseData : any ,
334+ format : ExecuteOperationResult_SerializationFormat ,
335+ ) : ExecuteOperationResult {
336+ let data : string ;
337+
338+ try {
339+ switch ( format ) {
340+ case ExecuteOperationResult_SerializationFormat . Proto :
341+ const uint8array : Uint8Array = OperationOutput . encode ( responseData ) . finish ( ) ;
342+ data = Buffer . from ( uint8array ) . toString ( 'base64' ) ;
343+ break ;
344+ case ExecuteOperationResult_SerializationFormat . JSON :
345+ data = Buffer . from ( JSON . stringify ( responseData ) ) . toString ( 'base64' ) ;
346+ break ;
347+ default :
348+ throw new Error ( `Unsupported serialization format: ${ format } ` ) ;
349+ }
350+ } catch ( error ) {
351+ console . error ( 'Error creating operation result:' , error ) ;
352+ throw new Error ( 'Failed to create operation result' ) ;
353+ }
354+
355+ return {
356+ serialization_format : format ,
357+ data : data ,
358+ } as ExecuteOperationResult ;
359+ }
360+
361+ function getOperationExecutionOperationMethod ( event : any ) : string | undefined {
362+ return event . execution_metadata . operation_method ;
363+ }
0 commit comments