diff --git a/src/FluenceClient.ts b/src/FluenceClient.ts new file mode 100644 index 00000000..789b43ad --- /dev/null +++ b/src/FluenceClient.ts @@ -0,0 +1,141 @@ +import log from 'loglevel'; +import Multiaddr from 'multiaddr'; +import PeerId, { isPeerId } from 'peer-id'; + +import { AquaCallHandler } from './internal/AquaHandler'; +import { ClientImpl } from './internal/ClientImpl'; +import { PeerIdB58 } from './internal/commonTypes'; +import { generatePeerId, seedToPeerId } from './internal/peerIdUtils'; +import { RequestFlow } from './internal/RequestFlow'; +import { RequestFlowBuilder } from './internal/RequestFlowBuilder'; + +/** + * The class represents interface to Fluence Platform. To create a client use @see {@link createClient} function. + */ +export interface FluenceClient { + /** + * { string } Gets the base58 representation of the current peer id. Read only + */ + readonly relayPeerId: PeerIdB58 | undefined; + + /** + * { string } Gets the base58 representation of the connected relay's peer id. Read only + */ + readonly selfPeerId: PeerIdB58; + + /** + * { string } True if the client is connected to network. False otherwise. Read only + */ + readonly isConnected: boolean; + + /** + * The base handler which is used by every RequestFlow executed by this FluenceClient. + * Please note, that the handler is combined with the handler from RequestFlow before the execution occures. + * After this combination, middlewares from RequestFlow are executed before client handler's middlewares. + */ + readonly aquaCallHandler: AquaCallHandler; + + /** + * Disconnects the client from the network + */ + disconnect(): Promise; + + /** + * Establish a connection to the node. If the connection is already established, disconnect and reregister all services in a new connection. + * + * @param multiaddr + */ + connect(multiaddr: string | Multiaddr): Promise; + + /** + * Initiates RequestFlow execution @see { @link RequestFlow } + * @param { RequestFlow } [ request ] - RequestFlow to start the execution of + */ + initiateFlow(request: RequestFlow): Promise; +} + +type Node = { + peerId: string; + multiaddr: string; +}; + +/** + * Creates a Fluence client. If the `connectTo` is specified connects the client to the network + * @param { string | Multiaddr | Node } [connectTo] - Node in Fluence network to connect to. If not specified client will not be connected to the n + * @param { PeerId | string } [peerIdOrSeed] - The Peer Id of the created client. Specified either as PeerId structure or as seed string. Will be generated randomly if not specified + * @returns { Promise } Promise which will be resolved with the created FluenceClient + */ +export const createClient = async ( + connectTo?: string | Multiaddr | Node, + peerIdOrSeed?: PeerId | string, +): Promise => { + let peerId; + if (!peerIdOrSeed) { + peerId = await generatePeerId(); + } else if (isPeerId(peerIdOrSeed)) { + // keep unchanged + peerId = peerIdOrSeed; + } else { + // peerId is string, therefore seed + peerId = await seedToPeerId(peerIdOrSeed); + } + + const client = new ClientImpl(peerId); + await client.initAquamarineRuntime(); + + if (connectTo) { + let theAddress: Multiaddr; + let fromNode = (connectTo as any).multiaddr; + if (fromNode) { + theAddress = new Multiaddr(fromNode); + } else { + theAddress = new Multiaddr(connectTo as string); + } + + await client.connect(theAddress); + if (!(await checkConnection(client))) { + throw new Error('Connection check failed. Check if the node is working or try to connect to another node'); + } + } + + return client; +}; + +/** + * Checks the network connection by sending a ping-like request to relat node + * @param { FluenceClient } client - The Fluence Client instance. + */ +export const checkConnection = async (client: FluenceClient): Promise => { + if (!client.isConnected) { + return false; + } + + const msg = Math.random().toString(36).substring(7); + const callbackFn = 'checkConnection'; + const callbackService = '_callback'; + + const [request, promise] = new RequestFlowBuilder() + .withRawScript( + `(seq + (call init_relay ("op" "identity") [msg] result) + (call %init_peer_id% ("${callbackService}" "${callbackFn}") [result]) + )`, + ) + .withVariables({ + msg, + }) + .buildAsFetch<[[string]]>(callbackService, callbackFn); + + await client.initiateFlow(request); + + try { + const [[result]] = await promise; + if (result != msg) { + log.warn("unexpected behavior. 'identity' must return arguments the passed arguments."); + } + return true; + } catch (e) { + log.error('Error on establishing connection: ', e); + return false; + } +}; diff --git a/src/__test__/integration/builtins.spec.ts b/src/__test__/integration/builtins.spec.ts index 0bcfbc2e..6d7d6275 100644 --- a/src/__test__/integration/builtins.spec.ts +++ b/src/__test__/integration/builtins.spec.ts @@ -9,7 +9,7 @@ import { uploadModule, } from '../../internal/builtins'; import { ModuleConfig } from '../../internal/moduleConfig'; -import { createClient, FluenceClient } from '../../api.unstable'; +import { createClient, FluenceClient } from '../../FluenceClient'; import { nodes } from '../connection'; let client: FluenceClient; diff --git a/src/__test__/integration/client.spec.ts b/src/__test__/integration/client.spec.ts index fcdb2128..0968e155 100644 --- a/src/__test__/integration/client.spec.ts +++ b/src/__test__/integration/client.spec.ts @@ -1,4 +1,4 @@ -import { checkConnection, createClient, FluenceClient } from '../../api.unstable'; +import { checkConnection, createClient, FluenceClient } from '../../FluenceClient'; import Multiaddr from 'multiaddr'; import { nodes } from '../connection'; import { RequestFlowBuilder } from '../../internal/RequestFlowBuilder'; diff --git a/src/__test__/integration/legacy.api.spec.ts b/src/__test__/integration/legacy.api.spec.ts index c1cf6668..ec3bf3e4 100644 --- a/src/__test__/integration/legacy.api.spec.ts +++ b/src/__test__/integration/legacy.api.spec.ts @@ -1,12 +1,5 @@ -import { - createClient, - Particle, - FluenceClient, - sendParticle, - registerServiceFunction, - subscribeToEvent, - sendParticleAsFetch, -} from '../../api'; +import { Particle, sendParticle, registerServiceFunction, subscribeToEvent, sendParticleAsFetch } from '../../api'; +import { FluenceClient, createClient } from '../../FluenceClient'; import { nodes } from '../connection'; let client: FluenceClient; diff --git a/src/__test__/unit/air.spec.ts b/src/__test__/unit/air.spec.ts index 738f868e..04723f52 100644 --- a/src/__test__/unit/air.spec.ts +++ b/src/__test__/unit/air.spec.ts @@ -1,4 +1,4 @@ -import { createClient, FluenceClient } from '../../api.unstable'; +import { createClient, FluenceClient } from '../../FluenceClient'; import { RequestFlow } from '../../internal/RequestFlow'; import { RequestFlowBuilder } from '../../internal/RequestFlowBuilder'; diff --git a/src/api.ts b/src/api.ts index dac22011..6e48c5d6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,65 +1,6 @@ -import Multiaddr from 'multiaddr'; -import PeerId from 'peer-id'; -import { PeerIdB58, SecurityTetraplet } from './internal/commonTypes'; -import * as unstable from './api.unstable'; -import { ClientImpl } from './internal/ClientImpl'; +import { SecurityTetraplet } from './internal/commonTypes'; import { RequestFlowBuilder } from './internal/RequestFlowBuilder'; -import { RequestFlow } from './internal/RequestFlow'; - -/** - * The class represents interface to Fluence Platform. To create a client @see {@link createClient} function. - */ -export interface FluenceClient { - /** - * { string } Gets the base58 representation of the current peer id. Read only - */ - readonly relayPeerId: PeerIdB58 | undefined; - - /** - * { string } Gets the base58 representation of the connected relay's peer id. Read only - */ - readonly selfPeerId: PeerIdB58; - - /** - * { string } True if the client is connected to network. False otherwise. Read only - */ - readonly isConnected: boolean; - - /** - * Disconnects the client from the network - */ - disconnect(): Promise; - - /** - * Establish a connection to the node. If the connection is already established, disconnect and reregister all services in a new connection. - * - * @param {string | Multiaddr} [multiaddr] - Address of the node in Fluence network. - */ - connect(multiaddr: string | Multiaddr): Promise; -} - -type Node = { - peerId: string; - multiaddr: string; -}; - -/** - * Creates a Fluence client. If the `connectTo` is specified connects the client to the network - * @param { string | Multiaddr | Node } [connectTo] - Node in Fluence network to connect to. If not specified client will not be connected to the n - * @param { PeerId | string } [peerIdOrSeed] - The Peer Id of the created client. Specified either as PeerId structure or as seed string. Will be generated randomly if not specified - * @returns { Promise } Promise which will be resolved with the created FluenceClient - */ -export const createClient = async ( - connectTo?: string | Multiaddr | Node, - peerIdOrSeed?: PeerId | string, -): Promise => { - const res = await unstable.createClient(connectTo, peerIdOrSeed); - return res as any; -}; - -export const checkConnection = async (client: FluenceClient): Promise => { - return unstable.checkConnection(client as any); -}; +import { FluenceClient } from './FluenceClient'; /** * The class representing Particle - a data structure used to perform operations on Fluence Network. It originates on some peer in the network, travels the network through a predefined path, triggering function execution along its way. @@ -102,7 +43,6 @@ export const sendParticle = async ( particle: Particle, onError?: (err) => void, ): Promise => { - const c = client as ClientImpl; const [req, errorPromise] = new RequestFlowBuilder() .withRawScript(particle.script) .withVariables(particle.data) @@ -111,7 +51,7 @@ export const sendParticle = async ( errorPromise.catch(onError); - await c.initiateFlow(req); + await client.initiateFlow(req); return req.id; }; @@ -139,7 +79,7 @@ export const registerServiceFunction = ( fnName: string, handler: (args: any[], tetraplets: SecurityTetraplet[][]) => object, ) => { - const unregister = (client as ClientImpl).aquaCallHandler.on(serviceId, fnName, handler); + const unregister = client.aquaCallHandler.on(serviceId, fnName, handler); handlersUnregistratorsMap.set(makeKey(client, serviceId, fnName), unregister); }; @@ -212,7 +152,7 @@ export const sendParticleAsFetch = async ( .withTTL(particle.ttl) .buildAsFetch(callbackServiceId, callbackFnName); - await (client as ClientImpl).initiateFlow(request); + await client.initiateFlow(request); return promise; }; diff --git a/src/api.unstable.ts b/src/api.unstable.ts index 1f5ec873..5cce1b75 100644 --- a/src/api.unstable.ts +++ b/src/api.unstable.ts @@ -1,142 +1,2 @@ -import Multiaddr from 'multiaddr'; -import PeerId, { isPeerId } from 'peer-id'; -import { generatePeerId, seedToPeerId } from './internal/peerIdUtils'; -import { ClientImpl } from './internal/ClientImpl'; -import log from 'loglevel'; -import { RequestFlowBuilder } from './internal/RequestFlowBuilder'; -import { PeerIdB58 } from './internal/commonTypes'; -import { AquaCallHandler } from './internal/AquaHandler'; -import { RequestFlow } from './internal/RequestFlow'; - export { RequestFlowBuilder } from './internal/RequestFlowBuilder'; - -/** - * The class represents interface to Fluence Platform. To create a client use @see {@link createClient} function. - */ -export interface FluenceClient { - /** - * { string } Gets the base58 representation of the current peer id. Read only - */ - readonly relayPeerId: PeerIdB58 | undefined; - - /** - * { string } Gets the base58 representation of the connected relay's peer id. Read only - */ - readonly selfPeerId: PeerIdB58; - - /** - * { string } True if the client is connected to network. False otherwise. Read only - */ - readonly isConnected: boolean; - - /** - * The base handler which is used by every RequestFlow executed by this FluenceClient. - * Please note, that the handler is combined with the handler from RequestFlow before the execution occures. - * After this combination, middlewares from RequestFlow are executed before client handler's middlewares. - */ - readonly aquaCallHandler: AquaCallHandler; - - /** - * Disconnects the client from the network - */ - disconnect(): Promise; - - /** - * Establish a connection to the node. If the connection is already established, disconnect and reregister all services in a new connection. - * - * @param multiaddr - */ - connect(multiaddr: string | Multiaddr): Promise; - - /** - * Initiates RequestFlow execution @see { @link RequestFlow } - * @param { RequestFlow } [ request ] - RequestFlow to start the execution of - */ - initiateFlow(request: RequestFlow): Promise; -} - -type Node = { - peerId: string; - multiaddr: string; -}; - -/** - * Creates a Fluence client. If the `connectTo` is specified connects the client to the network - * @param { string | Multiaddr | Node } [connectTo] - Node in Fluence network to connect to. If not specified client will not be connected to the n - * @param { PeerId | string } [peerIdOrSeed] - The Peer Id of the created client. Specified either as PeerId structure or as seed string. Will be generated randomly if not specified - * @returns { Promise } Promise which will be resolved with the created FluenceClient - */ -export const createClient = async ( - connectTo?: string | Multiaddr | Node, - peerIdOrSeed?: PeerId | string, -): Promise => { - let peerId; - if (!peerIdOrSeed) { - peerId = await generatePeerId(); - } else if (isPeerId(peerIdOrSeed)) { - // keep unchanged - peerId = peerIdOrSeed; - } else { - // peerId is string, therefore seed - peerId = await seedToPeerId(peerIdOrSeed); - } - - const client = new ClientImpl(peerId); - await client.initAquamarineRuntime(); - - if (connectTo) { - let theAddress: Multiaddr; - let fromNode = (connectTo as any).multiaddr; - if (fromNode) { - theAddress = new Multiaddr(fromNode); - } else { - theAddress = new Multiaddr(connectTo as string); - } - - await client.connect(theAddress); - if (!(await checkConnection(client))) { - throw new Error('Connection check failed. Check if the node is working or try to connect to another node'); - } - } - - return client; -}; - -/** - * Checks the network connection by sending a ping-like request to relat node - * @param { FluenceClient } client - The Fluence Client instance. - */ -export const checkConnection = async (client: FluenceClient): Promise => { - if (!client.isConnected) { - return false; - } - - const msg = Math.random().toString(36).substring(7); - const callbackFn = 'checkConnection'; - const callbackService = '_callback'; - - const [request, promise] = new RequestFlowBuilder() - .withRawScript( - `(seq - (call init_relay ("op" "identity") [msg] result) - (call %init_peer_id% ("${callbackService}" "${callbackFn}") [result]) - )`, - ) - .withVariables({ - msg, - }) - .buildAsFetch<[[string]]>(callbackService, callbackFn); - - await client.initiateFlow(request); - - try { - const [[result]] = await promise; - if (result != msg) { - log.warn("unexpected behavior. 'identity' must return arguments the passed arguments."); - } - return true; - } catch (e) { - log.error('Error on establishing connection: ', e); - return false; - } -}; +export * from './internal/AquaHandler'; diff --git a/src/index.ts b/src/index.ts index 48c2b3e6..0a2b9670 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ export { seedToPeerId, peerIdToSeed, generatePeerId } from './internal/peerIdUtils'; export { SecurityTetraplet, PeerIdB58 } from './internal/commonTypes'; export * from './api'; +export * from './FluenceClient'; export * from './internal/builtins'; import log, { LogLevelDesc } from 'loglevel'; diff --git a/src/internal/ClientImpl.ts b/src/internal/ClientImpl.ts index f35b2c2c..9a164c4f 100644 --- a/src/internal/ClientImpl.ts +++ b/src/internal/ClientImpl.ts @@ -19,7 +19,7 @@ import Multiaddr from 'multiaddr'; import { FluenceConnection } from './FluenceConnection'; import { CallServiceResult, ParticleHandler, PeerIdB58, SecurityTetraplet } from './commonTypes'; -import { FluenceClient } from 'src'; +import { FluenceClient } from '../FluenceClient'; import { RequestFlow } from './RequestFlow'; import { AquaCallHandler, errorHandler, fnHandler } from './AquaHandler'; import { loadRelayFn, loadVariablesService } from './RequestFlowBuilder'; diff --git a/src/internal/builtins.ts b/src/internal/builtins.ts index 92d2fb42..b90e25d2 100644 --- a/src/internal/builtins.ts +++ b/src/internal/builtins.ts @@ -14,14 +14,9 @@ * limitations under the License. */ +import { FluenceClient } from '../FluenceClient'; import { ModuleConfig } from './moduleConfig'; import { RequestFlowBuilder } from './RequestFlowBuilder'; -import { FluenceClient as Unstable } from '../api.unstable'; -import { FluenceClient as Stable } from '..'; - -// HACK:: A little hack to supress compiler errors in proto-distributor. -// Will be wiped out when the HLL is ready -type FluenceClient = Unstable | Stable; const nodeIdentityCall = (client: FluenceClient): string => { return `(call "${client.relayPeerId}" ("op" "identity") [])`; @@ -64,7 +59,7 @@ const requestResponse = async ( .withVariables(data) .withTTL(ttl) .buildAsFetch('_callback', name); - await (client as any).initiateFlow(request); + await client.initiateFlow(request); const res = await promise; return handleResponse(res); }; @@ -91,7 +86,7 @@ export const getModules = async (client: FluenceClient, ttl?: number): Promise('_callback', callbackFn); - (client as any).initiateFlow(req); + client.initiateFlow(req); const [res] = await promise; return res; @@ -131,7 +126,7 @@ export const getInterfaces = async (client: FluenceClient, ttl?: number): Promis .withTTL(ttl) .buildAsFetch<[string[]]>('_callback', callbackFn); - (client as any).initiateFlow(req); + client.initiateFlow(req); const [res] = await promise; return res; @@ -184,7 +179,7 @@ export const uploadModule = async ( .withTTL(ttl) .buildAsFetch<[string[]]>('_callback', 'getModules'); - await (client as any).initiateFlow(req); + await client.initiateFlow(req); await promise; };