mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2024-12-04 18:00:18 +00:00
Implement handlers for built-in calls from avm (#52)
This commit is contained in:
parent
c01c6478b7
commit
53385c556a
127
package-lock.json
generated
127
package-lock.json
generated
@ -842,6 +842,15 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/bs58": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.1.tgz",
|
||||
"integrity": "sha512-yfAgiWgVLjFCmRv8zAcOIHywYATEwiTVccTLnRp6UxTNavT55M9d/uhK3T03St/+8/z/wW+CRjGKUNmEqoHHCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base-x": "^3.0.6"
|
||||
}
|
||||
},
|
||||
"@types/graceful-fs": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
|
||||
@ -3500,16 +3509,105 @@
|
||||
}
|
||||
},
|
||||
"jest-each": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz",
|
||||
"integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==",
|
||||
"version": "27.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.0.2.tgz",
|
||||
"integrity": "sha512-OLMBZBZ6JkoXgUenDtseFRWA43wVl2BwmZYIWQws7eS7pqsIvePqj/jJmEnfq91ALk3LNphgwNK/PRFBYi7ITQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^26.6.2",
|
||||
"@jest/types": "^27.0.2",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^26.3.0",
|
||||
"jest-util": "^26.6.2",
|
||||
"pretty-format": "^26.6.2"
|
||||
"jest-get-type": "^27.0.1",
|
||||
"jest-util": "^27.0.2",
|
||||
"pretty-format": "^27.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jest/types": {
|
||||
"version": "27.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.0.2.tgz",
|
||||
"integrity": "sha512-XpjCtJ/99HB4PmyJ2vgmN7vT+JLP7RW1FBT9RgnMFS4Dt7cvIyBee8O3/j98aUZ34ZpenPZFqmaaObWSeL65dg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"@types/istanbul-reports": "^3.0.0",
|
||||
"@types/node": "*",
|
||||
"@types/yargs": "^16.0.0",
|
||||
"chalk": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@types/yargs": {
|
||||
"version": "16.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.3.tgz",
|
||||
"integrity": "sha512-YlFfTGS+zqCgXuXNV26rOIeETOkXnGQXP/pjjL9P0gO/EP9jTmc7pUBhx+jVEIxpq41RX33GQ7N3DzOSfZoglQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz",
|
||||
"integrity": "sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A==",
|
||||
"dev": true
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.6",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
|
||||
"integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-ci": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.0.tgz",
|
||||
"integrity": "sha512-kDXyttuLeslKAHYL/K28F2YkM3x5jvFPEw3yXbRptXydjD9rpLEz+C5K5iutY9ZiUu6AP41JdvRQwF4Iqs4ZCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ci-info": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"jest-get-type": {
|
||||
"version": "27.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.0.1.tgz",
|
||||
"integrity": "sha512-9Tggo9zZbu0sHKebiAijyt1NM77Z0uO4tuWOxUCujAiSeXv30Vb5D4xVF4UR4YWNapcftj+PbByU54lKD7/xMg==",
|
||||
"dev": true
|
||||
},
|
||||
"jest-util": {
|
||||
"version": "27.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.0.2.tgz",
|
||||
"integrity": "sha512-1d9uH3a00OFGGWSibpNYr+jojZ6AckOMCXV2Z4K3YXDnzpkAaXQyIpY14FOJPiUmil7CD+A6Qs+lnnh6ctRbIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.0.2",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.4",
|
||||
"is-ci": "^3.0.0",
|
||||
"picomatch": "^2.2.3"
|
||||
}
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
|
||||
"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
|
||||
"dev": true
|
||||
},
|
||||
"pretty-format": {
|
||||
"version": "27.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.0.2.tgz",
|
||||
"integrity": "sha512-mXKbbBPnYTG7Yra9qFBtqj+IXcsvxsvOBco3QHxtxTl+hHKq6QdzMZ+q0CtL4ORHZgwGImRr2XZUX2EWzORxig==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^27.0.2",
|
||||
"ansi-regex": "^5.0.0",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^17.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jest-environment-jsdom": {
|
||||
@ -3601,6 +3699,21 @@
|
||||
"jest-util": "^26.6.2",
|
||||
"pretty-format": "^26.6.2",
|
||||
"throat": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jest-each": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz",
|
||||
"integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jest/types": "^26.6.2",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^26.3.0",
|
||||
"jest-util": "^26.6.2",
|
||||
"pretty-format": "^26.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"jest-leak-detector": {
|
||||
|
@ -37,8 +37,10 @@
|
||||
"uuid": "8.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bs58": "^4.0.1",
|
||||
"@types/jest": "^26.0.22",
|
||||
"jest": "^26.6.3",
|
||||
"jest-each": "^27.0.2",
|
||||
"ts-jest": "^26.5.4",
|
||||
"typescript": "^3.9.5"
|
||||
}
|
||||
|
@ -133,14 +133,14 @@ export const checkConnection = async (client: FluenceClient, ttl?: number): Prom
|
||||
.withVariables({
|
||||
msg,
|
||||
})
|
||||
.buildAsFetch<[[string]]>(callbackService, callbackFn);
|
||||
.buildAsFetch<[string]>(callbackService, callbackFn);
|
||||
|
||||
await client.initiateFlow(request);
|
||||
|
||||
try {
|
||||
const [[result]] = await promise;
|
||||
const [result] = await promise;
|
||||
if (result != msg) {
|
||||
log.warn("unexpected behavior. 'identity' must return arguments the passed arguments.");
|
||||
log.warn("unexpected behavior. 'identity' must return the passed arguments.");
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
|
@ -26,11 +26,11 @@ describe('Typescript usage suite', () => {
|
||||
(call %init_peer_id% ("callback" "callback") [result])
|
||||
)`,
|
||||
)
|
||||
.buildAsFetch<[[string]]>('callback', 'callback');
|
||||
.buildAsFetch<[string]>('callback', 'callback');
|
||||
await client.initiateFlow(request);
|
||||
|
||||
// assert
|
||||
const [[result]] = await promise;
|
||||
const [result] = await promise;
|
||||
expect(result).toBe('hello world!');
|
||||
});
|
||||
|
||||
|
60
src/__test__/unit/builtInHandler.spec.ts
Normal file
60
src/__test__/unit/builtInHandler.spec.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import each from 'jest-each';
|
||||
import { CallServiceData } from '../../internal/CallServiceHandler';
|
||||
import makeDefaultClientHandler from '../../internal/defaultClientHandler';
|
||||
|
||||
describe('Tests for default handler', () => {
|
||||
// prettier-ignore
|
||||
each`
|
||||
fnName | args | retCode | result
|
||||
${'identity'} | ${[]} | ${0} | ${{}}
|
||||
${'identity'} | ${[1]} | ${0} | ${1}
|
||||
${'identity'} | ${[1, 2]} | ${1} | ${'identity accepts up to 1 arguments, received 2 arguments'}
|
||||
|
||||
${'noop'} | ${[1, 2]} | ${0} | ${{}}
|
||||
|
||||
${'array'} | ${[1, 2, 3]} | ${0} | ${[1, 2, 3]}
|
||||
|
||||
${'concat'} | ${[[1, 2], [3, 4], [5, 6]]} | ${0} | ${[1, 2, 3, 4, 5, 6]}
|
||||
${'concat'} | ${[[1, 2]]} | ${0} | ${[1, 2]}
|
||||
${'concat'} | ${[]} | ${0} | ${[]}
|
||||
${'concat'} | ${[1, [1, 2], 1]} | ${1} | ${"All arguments of 'concat' must be arrays: arguments 0, 2 are not"}
|
||||
|
||||
${'string_to_b58'} | ${["test"]} | ${0} | ${"3yZe7d"}
|
||||
${'string_to_b58'} | ${["test", 1]} | ${1} | ${"string_to_b58 accepts only one string argument"}
|
||||
|
||||
${'string_from_b58'} | ${["3yZe7d"]} | ${0} | ${"test"}
|
||||
${'string_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"string_from_b58 accepts only one string argument"}
|
||||
|
||||
${'bytes_to_b58'} | ${[[116, 101, 115, 116]]} | ${0} | ${"3yZe7d"}
|
||||
${'bytes_to_b58'} | ${[[116, 101, 115, 116], 1]} | ${1} | ${"bytes_to_b58 accepts only single argument: array of numbers"}
|
||||
|
||||
${'bytes_from_b58'} | ${["3yZe7d"]} | ${0} | ${[116, 101, 115, 116]}
|
||||
${'bytes_from_b58'} | ${["3yZe7d", 1]} | ${1} | ${"bytes_from_b58 accepts only one string argument"}
|
||||
|
||||
`.test(
|
||||
//
|
||||
'$fnName with $args expected retcode: $retCode and result: $result',
|
||||
({ fnName, args, retCode, result }) => {
|
||||
// arrange
|
||||
const req: CallServiceData = {
|
||||
serviceId: 'Op',
|
||||
fnName: fnName,
|
||||
args: args,
|
||||
tetraplets: [],
|
||||
particleContext: {
|
||||
particleId: 'some',
|
||||
},
|
||||
};
|
||||
|
||||
// act
|
||||
const res = makeDefaultClientHandler().execute(req);
|
||||
|
||||
// assert
|
||||
expect(res).toMatchObject({
|
||||
retCode: retCode,
|
||||
result: result,
|
||||
});
|
||||
const handler = makeDefaultClientHandler();
|
||||
},
|
||||
);
|
||||
});
|
@ -21,7 +21,7 @@ interface ParticleContext {
|
||||
/**
|
||||
* Represents the information passed from AVM when a `call` air instruction is executed on the local peer
|
||||
*/
|
||||
interface CallServiceData {
|
||||
export interface CallServiceData {
|
||||
/**
|
||||
* Service ID as specified in `call` air instruction
|
||||
*/
|
||||
@ -58,7 +58,7 @@ export type CallServiceResultType = object | boolean | number | string;
|
||||
/**
|
||||
* Represents the result of the `call` air instruction to be returned into AVM
|
||||
*/
|
||||
interface CallServiceResult {
|
||||
export interface CallServiceResult {
|
||||
/**
|
||||
* Return code to be returned to AVM
|
||||
*/
|
||||
|
@ -21,18 +21,12 @@ import { FluenceConnection, FluenceConnectionOptions } from './FluenceConnection
|
||||
import { PeerIdB58 } from './commonTypes';
|
||||
import { FluenceClient } from '../FluenceClient';
|
||||
import { RequestFlow } from './RequestFlow';
|
||||
import { CallServiceHandler, errorHandler, fnHandler } from './CallServiceHandler';
|
||||
import { CallServiceHandler } from './CallServiceHandler';
|
||||
import { loadRelayFn, loadVariablesService } from './RequestFlowBuilder';
|
||||
import { logParticle, Particle } from './particle';
|
||||
import log from 'loglevel';
|
||||
import { AirInterpreter, CallServiceResult, ParticleHandler, SecurityTetraplet } from '@fluencelabs/avm';
|
||||
|
||||
const makeDefaultClientHandler = (): CallServiceHandler => {
|
||||
const res = new CallServiceHandler();
|
||||
res.use(errorHandler);
|
||||
res.use(fnHandler('op', 'identity', (args, _) => args));
|
||||
return res;
|
||||
};
|
||||
import { AirInterpreter, ParticleHandler, SecurityTetraplet, CallServiceResult } from '@fluencelabs/avm';
|
||||
import makeDefaultClientHandler from './defaultClientHandler';
|
||||
|
||||
export class ClientImpl implements FluenceClient {
|
||||
readonly selfPeerIdFull: PeerId;
|
||||
|
97
src/internal/defaultClientHandler.ts
Normal file
97
src/internal/defaultClientHandler.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import { encode, decode } from 'bs58';
|
||||
import {
|
||||
CallServiceData,
|
||||
CallServiceHandler,
|
||||
CallServiceResult,
|
||||
CallServiceResultType,
|
||||
errorHandler,
|
||||
Middleware,
|
||||
} from './CallServiceHandler';
|
||||
|
||||
const makeDefaultClientHandler = (): CallServiceHandler => {
|
||||
const success = (resp: CallServiceResult, result: CallServiceResultType) => {
|
||||
resp.retCode = 0;
|
||||
resp.result = result;
|
||||
};
|
||||
const error = (resp: CallServiceResult, errorMsg: string) => {
|
||||
resp.retCode = 1;
|
||||
resp.result = errorMsg;
|
||||
};
|
||||
const mw: Middleware = (req: CallServiceData, resp: CallServiceResult, next: Function) => {
|
||||
if (req.serviceId === 'Op') {
|
||||
switch (req.fnName) {
|
||||
case 'noop':
|
||||
success(resp, {});
|
||||
return;
|
||||
|
||||
case 'array':
|
||||
success(resp, req.args);
|
||||
return;
|
||||
|
||||
case 'identity':
|
||||
if (req.args.length > 1) {
|
||||
error(resp, `identity accepts up to 1 arguments, received ${req.args.length} arguments`);
|
||||
} else {
|
||||
success(resp, req.args.length === 0 ? {} : req.args[0]);
|
||||
}
|
||||
return;
|
||||
|
||||
case 'concat':
|
||||
const incorrectArgIndices = req.args //
|
||||
.map((x, i) => [Array.isArray(x), i])
|
||||
.filter(([isArray, _]) => !isArray)
|
||||
.map(([_, index]) => index);
|
||||
|
||||
if (incorrectArgIndices.length > 0) {
|
||||
const str = incorrectArgIndices.join(', ');
|
||||
error(resp, `All arguments of 'concat' must be arrays: arguments ${str} are not`);
|
||||
} else {
|
||||
success(resp, [].concat.apply([], req.args));
|
||||
}
|
||||
return;
|
||||
|
||||
case 'string_to_b58':
|
||||
if (req.args.length !== 1) {
|
||||
error(resp, 'string_to_b58 accepts only one string argument');
|
||||
} else {
|
||||
success(resp, encode(new TextEncoder().encode(req.args[0])));
|
||||
}
|
||||
return;
|
||||
|
||||
case 'string_from_b58':
|
||||
if (req.args.length !== 1) {
|
||||
error(resp, 'string_from_b58 accepts only one string argument');
|
||||
} else {
|
||||
success(resp, new TextDecoder().decode(decode(req.args[0])));
|
||||
}
|
||||
return;
|
||||
|
||||
case 'bytes_to_b58':
|
||||
if (req.args.length !== 1 || !Array.isArray(req.args[0])) {
|
||||
error(resp, 'bytes_to_b58 accepts only single argument: array of numbers');
|
||||
} else {
|
||||
const argumentArray = req.args[0] as number[];
|
||||
success(resp, encode(new Uint8Array(argumentArray)));
|
||||
}
|
||||
return;
|
||||
|
||||
case 'bytes_from_b58':
|
||||
if (req.args.length !== 1) {
|
||||
error(resp, 'bytes_from_b58 accepts only one string argument');
|
||||
} else {
|
||||
success(resp, Array.from(decode(req.args[0])));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
const res = new CallServiceHandler();
|
||||
res.use(errorHandler);
|
||||
res.use(mw);
|
||||
return res;
|
||||
};
|
||||
|
||||
export default makeDefaultClientHandler;
|
@ -3,7 +3,6 @@
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/libp2p-ts/types",
|
||||
"./types"
|
||||
],
|
||||
"outDir": "./dist/",
|
||||
"baseUrl": ".",
|
||||
|
19
types/ipfs-only-hash/index.d.ts
vendored
19
types/ipfs-only-hash/index.d.ts
vendored
@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare module 'ipfs-only-hash' {
|
||||
export function of(data: Buffer): Promise<string>
|
||||
}
|
20
types/it-length-prefixed/index.d.ts
vendored
20
types/it-length-prefixed/index.d.ts
vendored
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Fluence Labs Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
declare module 'it-length-prefixed' {
|
||||
export function decode(): any
|
||||
export function encode(): any
|
||||
}
|
Loading…
Reference in New Issue
Block a user