feat: Simplify JS Client public API (#257)

This commit is contained in:
Pavel
2023-02-16 14:38:48 +03:00
committed by GitHub
parent 35dfb1bfbc
commit 9daaf41096
24 changed files with 774 additions and 476 deletions

View File

@ -6,6 +6,7 @@ import {
getArgumentTypes,
isReturnTypeVoid,
IFluenceClient,
CallAquaFunction,
} from '@fluencelabs/interfaces';
import {
@ -29,13 +30,7 @@ import {
* @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: IFluenceClient,
args: { [key: string]: any },
): Promise<unknown> {
export const callAquaFunction: CallAquaFunction = ({ def, script, config, peer, args }) => {
const argumentTypes = getArgumentTypes(def);
const promise = new Promise((resolve, reject) => {
@ -87,4 +82,4 @@ export function callFunctionImpl(
});
return promise;
}
};

View File

@ -1,20 +1,7 @@
import type { IFluenceClient, ServiceDef } from '@fluencelabs/interfaces';
import type { RegisterService } from '@fluencelabs/interfaces';
import { registerGlobalService, userHandlerService } from './services.js';
export const registerServiceImpl = (
peer: IFluenceClient,
def: ServiceDef,
serviceId: string | undefined,
service: any,
) => {
// TODO: TBH service registration is just putting some stuff into a hashmap
// there should not be such a check at all
if (!peer.getStatus().isInitialized) {
throw new Error(
'Could not register the service because the peer is not initialized. Are you passing the wrong peer to the register function?',
);
}
export const registerService: RegisterService = ({ peer, def, serviceId, service }) => {
// Checking for missing keys
const requiredKeys = def.functions.tag === 'nil' ? [] : Object.keys(def.functions.fields);
const incorrectServiceDefinitions = requiredKeys.filter((f) => !(f in service));

View File

@ -30,7 +30,7 @@ export const injectRelayService = (def: FunctionCallDef, peer: IFluenceClient) =
handler: () => {
return {
retCode: ResultCodes.success,
result: peer.getStatus().relayPeerId,
result: peer.internals.getRelayPeerId(),
};
},
};

View File

@ -27,12 +27,10 @@ import {
import type {
PeerIdB58,
IFluenceClient,
PeerStatus,
CallFunctionArgs,
RegisterServiceArgs,
ClientOptions,
KeyPairOptions,
RelayOptions,
ClientOptions,
ConnectionState,
} from '@fluencelabs/interfaces/dist/fluenceClient';
import { Particle, ParticleExecutionStage, ParticleQueueItem } from './Particle.js';
import { dataToString, jsonify, isString, ServiceError } from './utils.js';
@ -49,18 +47,46 @@ import { LogLevel } from '@fluencelabs/marine-js/dist/types';
import { NodeUtils, Srv } from './builtins/SingleModuleSrv.js';
import { registerNodeUtils } from './_aqua/node-utils.js';
import type { MultiaddrInput } from '@multiformats/multiaddr';
import { callFunctionImpl } from '../compilerSupport/callFunction.js';
import { registerServiceImpl } from '../compilerSupport/registerService.js';
const DEFAULT_TTL = 7000;
export type PeerConfig = ClientOptions;
export type PeerConfig = ClientOptions & { relay?: RelayOptions };
type PeerStatus =
| {
isInitialized: false;
peerId: null;
isConnected: false;
relayPeerId: null;
}
| {
isInitialized: true;
peerId: PeerIdB58;
isConnected: false;
relayPeerId: null;
}
| {
isInitialized: true;
peerId: PeerIdB58;
isConnected: true;
relayPeerId: PeerIdB58;
}
| {
isInitialized: true;
peerId: PeerIdB58;
isConnected: true;
isDirect: true;
relayPeerId: null;
};
/**
* This class implements the Fluence protocol for javascript-based environments.
* It provides all the necessary features to communicate with Fluence network
*/
export class FluencePeer implements IFluenceClient {
connectionState: ConnectionState = 'disconnected';
connectionStateChangeHandler: (state: ConnectionState) => void = () => {};
constructor(private marine: IMarine, private avmRunner: IAvmRunner) {}
/**
@ -72,10 +98,10 @@ export class FluencePeer implements IFluenceClient {
__isFluenceAwesome = true;
/**
* Get the peer's status
* TODO: remove this from here. Switch to `ConnectionState` instead
* @deprecated
*/
getStatus(): PeerStatus {
// TODO:: use explicit mechanism for peer's state
if (this._keyPair === undefined) {
return {
isInitialized: false,
@ -112,10 +138,11 @@ export class FluencePeer implements IFluenceClient {
};
}
/**
* Return peers SK
*/
getSk(): Uint8Array {
getPeerId(): string {
return this.getStatus().peerId!;
}
getPeerSecretKey(): Uint8Array {
if (!this._keyPair) {
throw new Error("Can't get key pair: peer is not initialized");
}
@ -123,20 +150,44 @@ export class FluencePeer implements IFluenceClient {
return this._keyPair.toEd25519PrivateKey();
}
onConnectionStateChange(handler: (state: ConnectionState) => void): ConnectionState {
this.connectionStateChangeHandler = handler;
return this.connectionState;
}
/**
* Connect to the Fluence network
* @param relay - relay node to connect to
* @param options - client options
*/
async connect(relay: RelayOptions, options?: ClientOptions): Promise<void> {
return this.start({ relay, ...options });
}
/**
* Disconnect from the Fluence network
*/
disconnect(): Promise<void> {
return this.stop();
}
/**
* Initializes the peer: starts the Aqua VM, initializes the default call service handlers
* and (optionally) connect to the Fluence network
* @param config - object specifying peer configuration
*/
async start(config: PeerConfig = {}): Promise<void> {
this.changeConnectionState('connecting');
const keyPair = await makeKeyPair(config.keyPair);
await this.init(config, keyPair);
const conn = await configToConnection(keyPair, config?.relay, config?.connectionOptions?.dialTimeoutMs);
const conn = await configToConnection(keyPair, config.relay, config.connectionOptions?.dialTimeoutMs);
if (conn !== null) {
await this.connect(conn);
await this._connect(conn);
}
this.changeConnectionState('connected');
}
getServices() {
@ -182,9 +233,10 @@ export class FluencePeer implements IFluenceClient {
* and disconnects from the Fluence network
*/
async stop() {
this.changeConnectionState('disconnecting');
this._keyPair = undefined; // This will set peer to non-initialized state and stop particle processing
this._stopParticleProcessing();
await this.disconnect();
await this._disconnect();
await this.marine.stop();
await this.avmRunner.stop();
this._classServices = undefined;
@ -192,25 +244,20 @@ export class FluencePeer implements IFluenceClient {
this._particleSpecificHandlers.clear();
this._commonHandlers.clear();
this._marineServices.clear();
this.changeConnectionState('disconnected');
}
// internal api
get compilerSupport() {
return {
callFunction: (args: CallFunctionArgs): Promise<unknown> => {
return callFunctionImpl(args.def, args.script, args.config, this, args.args);
},
registerService: (args: RegisterServiceArgs): void => {
return registerServiceImpl(this, args.def, args.serviceId, args.service);
},
};
}
/**
* @private Is not intended to be used manually. Subject to change
*/
get internals() {
return {
getConnectionState: () => this.connectionState,
getRelayPeerId: () => this.getStatus().relayPeerId,
parseAst: async (air: string): Promise<{ success: boolean; data: any }> => {
const status = this.getStatus();
@ -352,7 +399,7 @@ export class FluencePeer implements IFluenceClient {
/**
* @private Subject to change. Do not use this method directly
*/
async connect(connection: FluenceConnection): Promise<void> {
async _connect(connection: FluenceConnection): Promise<void> {
if (this.connection) {
await this.connection.disconnect();
}
@ -364,12 +411,17 @@ export class FluencePeer implements IFluenceClient {
/**
* @private Subject to change. Do not use this method directly
*/
async disconnect(): Promise<void> {
async _disconnect(): Promise<void> {
await this.connection?.disconnect();
}
// private
private changeConnectionState(state: ConnectionState) {
this.connectionState = state;
this.connectionStateChangeHandler(state);
}
// Queues for incoming and outgoing particles
private _incomingParticles = new Subject<ParticleQueueItem>();

View File

@ -6,7 +6,7 @@ import { Particle } from '../Particle.js';
import { MakeServiceCall } from '../utils.js';
import { avmModuleLoader, controlModuleLoader } from '../utilsForNode.js';
import { ServiceDef } from '@fluencelabs/interfaces';
import { callFunctionImpl } from '../../compilerSupport/callFunction.js';
import { callAquaFunction } from '../../compilerSupport/callFunction.js';
import { marineLogFunction } from '../utils.js';
import { MarineBackgroundRunner } from '../../marine/worker/index.js';
@ -40,7 +40,13 @@ export const compileAqua = async (aquaFile: string): Promise<CompiledFile> => {
const functions = Object.entries(compilationResult.functions)
.map(([name, fnInfo]) => {
const callFn = (peer: FluencePeer, args: { [key: string]: any }) => {
return callFunctionImpl(fnInfo.funcDef, fnInfo.script, {}, peer, args);
return callAquaFunction({
def: fnInfo.funcDef,
script: fnInfo.script,
config: {},
peer: peer,
args,
});
};
return { [name]: callFn };
})

View File

@ -7,7 +7,7 @@
*
*/
import { CallParams } from '@fluencelabs/interfaces';
import { registerServiceImpl } from '../../compilerSupport/registerService.js';
import { registerServiceImpl } from './util.js';
import { FluencePeer } from '../FluencePeer.js';
// Services

View File

@ -7,7 +7,7 @@
*
*/
import { CallParams } from '@fluencelabs/interfaces';
import { registerServiceImpl } from '../../compilerSupport/registerService.js';
import { registerServiceImpl } from './util.js';
import { FluencePeer } from '../FluencePeer.js';
// Services

View File

@ -7,7 +7,7 @@
*
*/
import { CallParams } from '@fluencelabs/interfaces';
import { registerServiceImpl } from '../../compilerSupport/registerService.js';
import { registerServiceImpl } from './util.js';
import { FluencePeer } from '../FluencePeer.js';
// Services

View File

@ -0,0 +1,9 @@
import { IFluenceClient, ServiceDef } from '@fluencelabs/interfaces';
import { registerService } from '../../compilerSupport/registerService.js';
export const registerServiceImpl = (
peer: IFluenceClient,
def: ServiceDef,
serviceId: string | undefined,
service: any,
) => registerService({ peer, def, service, serviceId });

View File

@ -157,7 +157,7 @@ export class EphemeralNetwork {
sendParticle = sendParticle;
};
await peer.connect(new connectionCtor());
await peer._connect(new connectionCtor());
const peerId = peer.getStatus().peerId!;
const ephPeer: PeerAdapter = {
@ -181,7 +181,7 @@ export class EphemeralNetwork {
log.debug('Shutting down ephemeral network...');
const peers = Array.from(this._peers.entries());
const promises = peers.map(([k, p]) => {
return p.isEphemeral ? p.peer.stop() : p.peer.disconnect();
return p.isEphemeral ? p.peer.stop() : p.peer._disconnect();
});
await Promise.all(promises);
this._peers.clear();