From dcb15b7d3ccd9949b8317056110760edb0b1feec Mon Sep 17 00:00:00 2001 From: Pavel Date: Tue, 27 Dec 2022 16:30:17 +0300 Subject: [PATCH] Add a more convenient API for calling aqua functions programmatically (#233) --- packages/fluence-js/package.json | 2 +- .../compilerSupport/v3impl/callFunction.ts | 69 +++++++++++++------ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/packages/fluence-js/package.json b/packages/fluence-js/package.json index cdcbc10c..98b28d43 100644 --- a/packages/fluence-js/package.json +++ b/packages/fluence-js/package.json @@ -1,6 +1,6 @@ { "name": "@fluencelabs/fluence", - "version": "0.27.4", + "version": "0.27.5", "description": "TypeScript implementation of Fluence Peer", "main": "./dist/index.js", "typings": "./dist/index.d.ts", diff --git a/packages/fluence-js/src/internal/compilerSupport/v3impl/callFunction.ts b/packages/fluence-js/src/internal/compilerSupport/v3impl/callFunction.ts index 6b9e3f20..95c117e6 100644 --- a/packages/fluence-js/src/internal/compilerSupport/v3impl/callFunction.ts +++ b/packages/fluence-js/src/internal/compilerSupport/v3impl/callFunction.ts @@ -1,10 +1,8 @@ import { FnConfig, FunctionCallDef } from './interface'; import { FluencePeer } from '../../FluencePeer'; import { Fluence } from '../../../index'; -import { Particle } from '../../Particle'; import { injectRelayService, - argToServiceDef, registerParticleScopeService, responseService, errorHandlingService, @@ -21,18 +19,31 @@ import { * @param def - function definition generated by the Aqua compiler * @param script - air script with function execution logic generated by the Aqua compiler */ -export function callFunction(rawFnArgs: Array, def: FunctionCallDef, script: string) { - if (def.arrow.domain.tag !== 'labeledProduct') { - throw new Error('Should be impossible'); - } +export function callFunction(rawFnArgs: Array, def: FunctionCallDef, script: string): Promise { + const { args, peer, config } = extractArgs(rawFnArgs, def); - const argumentTypes = Object.entries(def.arrow.domain.fields); - const expectedNumberOfArguments = argumentTypes.length; - const { args, peer, config } = extractArgs(rawFnArgs, expectedNumberOfArguments); + return callFunctionImpl(def, script, config || {}, peer, args); +} - if (args.length !== expectedNumberOfArguments) { - throw new Error('Incorrect number of arguments. Expecting ${def.argDefs.length}'); - } +/** + * Convenience function which does all the internal work of creating particles + * and making necessary service registrations in order to support Aqua function calls + * + * @param def - function definition generated by the Aqua compiler + * @param script - air script with function execution logic generated by the Aqua compiler + * @param config - options to configure Aqua function execution + * @param peer - Fluence Peer to invoke the function at + * @param args - args in the form of JSON where each key corresponds to the name of the argument + * @returns + */ +export function callFunctionImpl( + def: FunctionCallDef, + script: string, + config: FnConfig, + peer: FluencePeer, + args: { [key: string]: any }, +): Promise { + const argumentTypes = getArgumentTypes(def); const promise = new Promise((resolve, reject) => { const particle = peer.internals.createNewParticle(script, config?.ttl); @@ -41,13 +52,13 @@ export function callFunction(rawFnArgs: Array, def: FunctionCallDef, script return reject(particle.message); } - for (let i = 0; i < expectedNumberOfArguments; i++) { - const [name, type] = argumentTypes[i]; + for (let [name, argVal] of Object.entries(args)) { + const type = argumentTypes[name]; let service: ServiceDescription; if (type.tag === 'arrow') { - service = userHandlerService(def.names.callbackSrv, [name, type], args[i]); + service = userHandlerService(def.names.callbackSrv, [name, type], argVal); } else { - service = injectValueService(def.names.getDataSrv, name, type, args[i]); + service = injectValueService(def.names.getDataSrv, name, type, argVal); } registerParticleScopeService(peer, particle, service); } @@ -105,15 +116,19 @@ const isReturnTypeVoid = (def: FunctionCallDef) => { */ const extractArgs = ( args: any[], - numberOfExpectedArgs: number, + def: FunctionCallDef, ): { peer: FluencePeer; config?: FnConfig; - args: any[]; + args: { [key: string]: any }; } => { + const argumentTypes = getArgumentTypes(def); + const argumentNames = Object.keys(argumentTypes); + const numberOfExpectedArgs = argumentNames.length; + let peer: FluencePeer; let structuredArgs: any[]; - let config: any; + let config: FnConfig; if (FluencePeer.isInstance(args[0])) { peer = args[0]; structuredArgs = args.slice(1, numberOfExpectedArgs + 1); @@ -124,9 +139,23 @@ const extractArgs = ( config = args[numberOfExpectedArgs]; } + if (structuredArgs.length !== numberOfExpectedArgs) { + throw new Error(`Incorrect number of arguments. Expecting ${numberOfExpectedArgs}`); + } + + const argsRes = argumentNames.reduce((acc, name, index) => ({ ...acc, [name]: structuredArgs[index] }), {}); + return { peer: peer, config: config, - args: structuredArgs, + args: argsRes, }; }; + +const getArgumentTypes = (def: FunctionCallDef) => { + if (def.arrow.domain.tag !== 'labeledProduct') { + throw new Error('Should be impossible'); + } + + return def.arrow.domain.fields; +};