Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions base_folder/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "devrev-snapin",
"name": "airdrop-snap-in",
"version": "1.1.6",
"description": "DevRev ADaaS (Airdrop-as-a-service) Connector",
"description": "DevRev Airdrop Snap-in",
"main": "./dist/index.js",
"scripts": {
"lint": "eslint .",
Expand Down Expand Up @@ -52,7 +52,8 @@
"yargs": "^17.6.2"
},
"dependencies": {
"@devrev/ts-adaas": "1.4.2",
"@devrev/ts-adaas": "1.5.1",
"@devrev/typescript-sdk": "1.1.63",
"axios": "^1.9.0",
"dotenv": "^16.0.3",
"js-jsonl": "^1.1.1",
Expand Down
120 changes: 117 additions & 3 deletions base_folder/test/runner.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
/*
* Copyright (c) 2024 DevRev Inc. All rights reserved.

Disclaimer:
The code provided herein is intended solely for testing purposes.
Under no circumstances should it be utilized in a production environment. Use of
this code in live systems, production environments, or any situation where
reliability and stability are critical is strongly discouraged. The code is
provided as-is, without any warranties or guarantees of any kind, and the user
assumes all risks associated with its use. It is the responsibility of the user
to ensure that proper testing and validation procedures are carried out before
deploying any code into production environments.
*/

import bodyParser from 'body-parser';
import express, { Express, Handler, Request, Response } from 'express';

Expand All @@ -17,6 +31,17 @@ import {
SnapInsSystemUpdateResponse,
} from './types';


import {
Context as SnapInContext,
ExecuteOperationResult,
ExecuteOperationResult_SerializationFormat,
ExecutionMetadata,
FunctionExecutionError,
FunctionInput,
OperationOutput,
} from '@devrev/typescript-sdk/dist/snap-ins';

const app: Express = express();
app.use(bodyParser.json(), bodyParser.urlencoded({ extended: false }));

Expand Down Expand Up @@ -112,14 +137,17 @@ async function handleEvent(events: any[], isAsync: boolean, resp: Response) {
console.error(e);
}

// post processing. result is updated in the function
await postRun(event, error, result);
// Any common post processing goes here. The function returns
// only if the function execution was by an operation
}
const opResult = await postRun(event, error, result);

// Return result.
let res: ExecutionResult = {};

if (result !== undefined) {
if (opResult !== undefined) {
res.function_result = opResult;
} else if (result !== undefined) {
res.function_result = result;
}

Expand All @@ -137,11 +165,16 @@ async function handleEvent(events: any[], isAsync: boolean, resp: Response) {
// post processing
async function postRun(event: any, handlerError: HandlerError, result: any) {
console.debug('Function execution complete');
// Check if the function was invoked by an operation.
if (isInvokedFromOperation(event)) {
return handleOperationInvocationResult(event, handlerError, result);
}
if (isActivateHook(event)) {
handleActivateHookResult(event, handlerError, result);
} else if (isDeactivateHook(event)) {
handleDeactivateHookResult(event, handlerError, result);
}
return undefined;
}

function isActivateHook(event: any): boolean {
Expand All @@ -152,6 +185,10 @@ function isDeactivateHook(event: any): boolean {
return event.execution_metadata.event_type === 'hook:snap_in_deactivate';
}

function isInvokedFromOperation(event: any): boolean {
return event.execution_metadata.operation_slug !== undefined;
}

function handleActivateHookResult(event: any, handlerError: HandlerError, result: any) {
let update_req: SnapInsSystemUpdateRequest = {
id: event.context.snap_in_id,
Expand Down Expand Up @@ -247,3 +284,80 @@ function getDeactivateHookResult(input: any): DeactivateHookResult {
}
return res;
}

async function handleOperationInvocationResult(
event: any,
handlerError: HandlerError,
result: any,
): Promise<ExecuteOperationResult> {
if (result === undefined) {
result = generateOperationOutputFromError(handlerError);
}

const operationMethod = getOperationExecutionOperationMethod(event) || '';
if (operationMethod === 'OperationMethod_NameEnumSchemaHandler') {
return createExecuteOperationResult(result, ExecuteOperationResult_SerializationFormat.JSON);
}

return createExecuteOperationResult(result, ExecuteOperationResult_SerializationFormat.Proto);
}

function generateOperationOutputFromError(handlerError: HandlerError): OperationOutput {
const errorDetails: FailedExecutionError = {
source: FailedExecutionErrorSource.DeveloperFunction,
error:
handlerError instanceof FunctionExecutionError
? handlerError
: new FunctionExecutionError((handlerError as unknown as Error)?.message, false),
};

return OperationOutput.fromJSON({ error: errorDetails }) as OperationOutput;
}


export enum FailedExecutionErrorSource {
// Source of the error not known.
Unknown = 'unknown',
// Error is returned from the developer in the function.
DeveloperFunction = 'developer_function',
// Error is returned from the platform.
Platform = 'platform',
}

export type FailedExecutionError = {
source: FailedExecutionErrorSource;
error: FunctionExecutionError;
};

function createExecuteOperationResult(
responseData: any,
format: ExecuteOperationResult_SerializationFormat,
): ExecuteOperationResult {
let data: string;

try {
switch (format) {
case ExecuteOperationResult_SerializationFormat.Proto:
const uint8array: Uint8Array = OperationOutput.encode(responseData).finish();
data = Buffer.from(uint8array).toString('base64');
break;
case ExecuteOperationResult_SerializationFormat.JSON:
data = Buffer.from(JSON.stringify(responseData)).toString('base64');
break;
default:
throw new Error(`Unsupported serialization format: ${format}`);
}
} catch (error) {
console.error('Error creating operation result:', error);
throw new Error('Failed to create operation result');
}

return {
serialization_format: format,
data: data,
} as ExecuteOperationResult;
}

function getOperationExecutionOperationMethod(event: any): string | undefined {
return event.execution_metadata.operation_method;
}
2 changes: 1 addition & 1 deletion devrev-snapin-template.plain
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

- The Snap-In will run on Node.js as the runtime environment.

- The Snap-In should use The Airdrop SDK Library version "1.4.2" for handling data extraction and loading, pushing data, event-driven actions, state management, and artifact handling.
- The Snap-In should use The Airdrop SDK Library version "1.5.1" for handling data extraction and loading, pushing data, event-driven actions, state management, and artifact handling.
- The documentation for the The Airdrop SDK Library is provided in the resource [airdrop_sdk_library_documentation.md](docs/airdrop_sdk_library_documentation.md).
- Please note that 'processTask' function **only** executes task if 'processTask' function is called from a worker thread. Therefore 'processTask' function should be called from a worker thread spawned using 'Spawn' class.

Expand Down
Loading