From cabbe89845130d64dc2778a1cec6a43783bcb59f Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 9 Jun 2021 20:39:04 +0300 Subject: [PATCH] Fixed issue when a variable passed to Request flow could be undefined (#55) --- src/__test__/integration/client.spec.ts | 24 ++++++++++++++++++++++++ src/internal/CallServiceHandler.ts | 2 +- src/internal/ClientImpl.ts | 7 +++++++ src/internal/RequestFlowBuilder.ts | 10 ++++++---- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/__test__/integration/client.spec.ts b/src/__test__/integration/client.spec.ts index 5210120b..0439dcd7 100644 --- a/src/__test__/integration/client.spec.ts +++ b/src/__test__/integration/client.spec.ts @@ -2,6 +2,7 @@ import { checkConnection, createClient, FluenceClient } from '../../FluenceClien import Multiaddr from 'multiaddr'; import { nodes } from '../connection'; import { RequestFlowBuilder } from '../../internal/RequestFlowBuilder'; +import log from 'loglevel'; let client: FluenceClient; @@ -245,6 +246,29 @@ describe('Typescript usage suite', () => { }); }); + it('Should not crash if undefined is passed as a variable', async () => { + // arrange + client = await createClient(); + const [request, promise] = new RequestFlowBuilder() + .withRawScript( + ` + (seq + (call %init_peer_id% ("op" "identity") [arg] res) + (call %init_peer_id% ("return" "return") [res]) + ) + `, + ) + .withVariable('arg', undefined as any) + .buildAsFetch('return', 'return'); + + // act + await client.initiateFlow(request); + const [res] = await promise; + + // assert + expect(res).toBe(null); + }); + it('Should throw correct error when the client tries to send a particle not to the relay', async () => { // arrange client = await createClient(); diff --git a/src/internal/CallServiceHandler.ts b/src/internal/CallServiceHandler.ts index 93c0e3cd..ad16fa9c 100644 --- a/src/internal/CallServiceHandler.ts +++ b/src/internal/CallServiceHandler.ts @@ -53,7 +53,7 @@ export interface CallServiceData { /** * Type for all the possible ovjects that can be return to the AVM */ -export type CallServiceResultType = object | boolean | number | string; +export type CallServiceResultType = object | boolean | number | string | null; /** * Represents the result of the `call` air instruction to be returned into AVM diff --git a/src/internal/ClientImpl.ts b/src/internal/ClientImpl.ts index 55e88294..4ab533eb 100644 --- a/src/internal/ClientImpl.ts +++ b/src/internal/ClientImpl.ts @@ -207,6 +207,13 @@ export class ClientImpl implements FluenceClient { }, }); + if (res.result === undefined) { + log.error( + `Call to serviceId=${serviceId} fnName=${fnName} unexpectedly returned undefined result, falling back to null`, + ); + res.result = null; + } + return { ret_code: res.retCode, result: JSON.stringify(res.result), diff --git a/src/internal/RequestFlowBuilder.ts b/src/internal/RequestFlowBuilder.ts index 9b949d5b..6e4cafb5 100644 --- a/src/internal/RequestFlowBuilder.ts +++ b/src/internal/RequestFlowBuilder.ts @@ -1,5 +1,5 @@ import log from 'loglevel'; -import { CallServiceHandler } from './CallServiceHandler'; +import { CallServiceHandler, CallServiceResultType } from './CallServiceHandler'; import { DEFAULT_TTL, RequestFlow } from './RequestFlow'; export const loadVariablesService = 'load'; @@ -100,7 +100,7 @@ export class RequestFlowBuilder { private shouldInjectRelay: boolean = true; private ttl: number = DEFAULT_TTL; - private variables = new Map(); + private variables = new Map(); private handlerConfigs: Array<(handler: CallServiceHandler, request: RequestFlow) => void> = []; private buildScriptActions: Array<(sb: ScriptBuilder) => void> = []; private onTimeout: () => void; @@ -268,7 +268,7 @@ export class RequestFlowBuilder { /** * Adds a variable to the list of injected variables */ - withVariable(name: string, value: any): RequestFlowBuilder { + withVariable(name: string, value: CallServiceResultType): RequestFlowBuilder { this.variables.set(name, value); return this; } @@ -277,7 +277,9 @@ export class RequestFlowBuilder { * Adds a multiple variable to the list of injected variables. * Variables can be specified in form of either object or a map where keys correspond to variable names */ - withVariables(data: Map | Record): RequestFlowBuilder { + withVariables( + data: Map | Record, + ): RequestFlowBuilder { if (data instanceof Map) { this.variables = new Map([...Array.from(this.variables.entries()), ...Array.from(data.entries())]); } else {