mirror of
https://github.com/fluencelabs/fluence-js.git
synced 2025-05-11 17:07:13 +00:00
Complete rethinking and refactoring of the codebase. The codebase basically consists these 5 moving parts now: 1. Fluence client (and the Particle processor which might be merged with the client) - This part is responsible for initiating Request flows, managing existing requests flows (it keeps the queue of received particles), pulling right strings on request flows to update their state etc 2. Fluence connection - This part is responsible for connecting to network, sending\receiving particles 3. RequestFlow - This is where the state of particle execution process is kept. It is basically a state storage with some control levers to update the state. Each request flow contains some particle lifecycle methods and the AquaCallHandler where all callback logic is kept 4. RequestFlowBuilder - This is where requests are prepared by the user (the developer of the client application) before they are ready to be sent into the network. 5. AquaCallHandler - This is how interpreter callbacks are handled. It is very similar to express.js app and is made of middlewares. Aqua handler is the unified api for both callbacks for our Request flows and non-ours (i.e services that are expected to be called be other peers). See `AquaHandler.ts` for details
228 lines
7.5 KiB
TypeScript
228 lines
7.5 KiB
TypeScript
import { ResultCodes, SecurityTetraplet } from './commonTypes';
|
|
|
|
/**
|
|
* Particle context. Contains additional information about particle which triggered `call` air instruction from Aquamarine interpreter
|
|
*/
|
|
interface ParticleContext {
|
|
/**
|
|
* The particle ID
|
|
*/
|
|
particleId: string;
|
|
[x: string]: any;
|
|
}
|
|
|
|
/**
|
|
* Represents the information passed from Aquamarine interpreter when a `call` air instruction is executed on the local peer
|
|
*/
|
|
interface AquaCall {
|
|
/**
|
|
* Service ID as specified in `call` air instruction
|
|
*/
|
|
serviceId: string;
|
|
|
|
/**
|
|
* Function name as specified in `call` air instruction
|
|
*/
|
|
fnName: string;
|
|
|
|
/**
|
|
* Arguments as specified in `call` air instruction
|
|
*/
|
|
args: any[];
|
|
|
|
/**
|
|
* Security Tetraplets recieved from Aquamarine interpreter
|
|
*/
|
|
tetraplets: SecurityTetraplet[][];
|
|
|
|
/**
|
|
* Particle context, @see {@link ParticleContext}
|
|
*/
|
|
particleContext: ParticleContext;
|
|
|
|
[x: string]: any;
|
|
}
|
|
|
|
/**
|
|
* Represents the result of the `call` air instruction to be returned into Aquamarine interpreter
|
|
*/
|
|
interface AquaCallResult {
|
|
/**
|
|
* Return code to be returned to Aquamarine interpreter
|
|
*/
|
|
retCode: ResultCodes;
|
|
|
|
/**
|
|
* Resul object to be returned to Aquamarine interpreter
|
|
*/
|
|
result?: any;
|
|
[x: string]: any;
|
|
}
|
|
|
|
/**
|
|
* Type for the middleware used in AquaCallHandler middleware chain.
|
|
* In a nutshell middelware is a function of request, response and function to trigger the next middleware in chain.
|
|
* Each middleware is free to write additional properties to either request or response object.
|
|
* When the chain finishes the response is passed back to Aquamarine interpreter
|
|
* @param { AquaCall } req - information about the air `call` instruction
|
|
* @param { AquaCallResult } resp - response to be passed to Aquamarine interpreter
|
|
* @param { Function } next - function which invokes next middleware in chain
|
|
*/
|
|
export type Middleware = (req: AquaCall, resp: AquaCallResult, next: Function) => void;
|
|
|
|
/**
|
|
* Convenience middleware factory. Registeres a handler for a pair of 'serviceId/fnName'.
|
|
* The return value of the handler is passed back to Aquamarine
|
|
* @param { string } serviceId - The identifier of service which would be used to make calls from Aquamarine
|
|
* @param { string } fnName - The identifier of function which would be used to make calls from Aquamarine
|
|
* @param { (args: any[], tetraplets: SecurityTetraplet[][]) => object } handler - The handler which should handle the call. The result is any object passed back to Aquamarine
|
|
*/
|
|
export const fnHandler = (
|
|
serviceId: string,
|
|
fnName: string,
|
|
handler: (args: any[], tetraplets: SecurityTetraplet[][]) => any,
|
|
) => {
|
|
return (req: AquaCall, resp: AquaCallResult, next: Function): void => {
|
|
if (req.fnName === fnName && req.serviceId === serviceId) {
|
|
const res = handler(req.args, req.tetraplets);
|
|
resp.retCode = ResultCodes.success;
|
|
resp.result = res;
|
|
}
|
|
next();
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Convenience middleware factory. Registeres a handler for a pair of 'serviceId/fnName'.
|
|
* Similar to @see { @link fnHandler } but instead returns and empty object immediately runs the handler asynchronously
|
|
* @param { string } serviceId - The identifier of service which would be used to make calls from Aquamarine
|
|
* @param { string } fnName - The identifier of function which would be used to make calls from Aquamarine
|
|
* @param { (args: any[], tetraplets: SecurityTetraplet[][]) => void } handler - The handler which should handle the call.
|
|
*/
|
|
export const fnAsEventHandler = (
|
|
serviceId: string,
|
|
fnName: string,
|
|
handler: (args: any[], tetraplets: SecurityTetraplet[][]) => void,
|
|
) => {
|
|
return (req: AquaCall, resp: AquaCallResult, next: Function): void => {
|
|
if (req.fnName === fnName && req.serviceId === serviceId) {
|
|
setTimeout(() => {
|
|
handler(req.args, req.tetraplets);
|
|
}, 0);
|
|
|
|
resp.retCode = ResultCodes.success;
|
|
resp.result = {};
|
|
}
|
|
next();
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Error catching middleware
|
|
*/
|
|
export const errorHandler: Middleware = (req: AquaCall, resp: AquaCallResult, next: Function): void => {
|
|
try {
|
|
next();
|
|
} catch (e) {
|
|
resp.retCode = ResultCodes.exceptionInHandler;
|
|
resp.result = e.toString();
|
|
}
|
|
};
|
|
|
|
type AquaCallFunction = (req: AquaCall, resp: AquaCallResult) => void;
|
|
|
|
/**
|
|
* Class defines the handling of a `call` air intruction executed by aquamarine on the local peer.
|
|
* All the execution process is defined by the chain of middlewares - architecture popular among backend web frameworks.
|
|
* Each middleware has the form of `(req: AquaCall, resp: AquaCallResult, next: Function) => void;`
|
|
* A handler starts with an empty middleware chain and does nothing.
|
|
* To execute the handler use @see { @link execute } function
|
|
*/
|
|
export class AquaCallHandler {
|
|
private middlewares: Middleware[] = [];
|
|
|
|
/**
|
|
* Appends middleware to the chain of middlewares
|
|
* @param { Middleware } middleware
|
|
*/
|
|
use(middleware: Middleware): AquaCallHandler {
|
|
this.middlewares.push(middleware);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Removes the middleware from the chain of middlewares
|
|
* @param { Middleware } middleware
|
|
*/
|
|
unUse(middleware: Middleware): AquaCallHandler {
|
|
const index = this.middlewares.indexOf(middleware);
|
|
if (index !== -1) {
|
|
this.middlewares.splice(index, 1);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Combine handler with another one. Combintaion is done by copying middleware chain from the argument's handler into current one.
|
|
* Please note, that current handler's middlewares take precedence over the ones from handler to be combined with
|
|
* @param { AquaCallHandler } other - AquaCallHandler to be combined with
|
|
*/
|
|
combineWith(other: AquaCallHandler): AquaCallHandler {
|
|
this.middlewares = [...this.middlewares, ...other.middlewares];
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Convinience method for registring @see { @link fnHandler } middleware
|
|
*/
|
|
on(serviceId: string, fnName: string, handler: (args: any[], tetraplets: SecurityTetraplet[][]) => any): Function {
|
|
const mw = fnHandler(serviceId, fnName, handler);
|
|
this.use(mw);
|
|
return () => {
|
|
this.unUse(mw);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Convinience method for registring @see { @link fnAsEventHandler } middleware
|
|
*/
|
|
onEvent(
|
|
serviceId: string,
|
|
fnName: string,
|
|
handler: (args: any[], tetraplets: SecurityTetraplet[][]) => void,
|
|
): Function {
|
|
const mw = fnAsEventHandler(serviceId, fnName, handler);
|
|
this.use(mw);
|
|
return () => {
|
|
this.unUse(mw);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Collapses middleware chain into a single function.
|
|
*/
|
|
buildFunction(): AquaCallFunction {
|
|
const result = this.middlewares.reduceRight<AquaCallFunction>(
|
|
(agg, cur) => {
|
|
return (req, resp) => {
|
|
cur(req, resp, () => agg(req, resp));
|
|
};
|
|
},
|
|
(req, res) => {},
|
|
);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Executes the handler with the specified AquaCall request. Return the result response
|
|
*/
|
|
execute(req: AquaCall): AquaCallResult {
|
|
const res: AquaCallResult = {
|
|
retCode: ResultCodes.unkownError,
|
|
};
|
|
this.buildFunction()(req, res);
|
|
return res;
|
|
}
|
|
}
|