Fix more comments

This commit is contained in:
Akim Mamedov 2023-11-16 20:10:18 +07:00
parent 166475e26c
commit 26bfc36985
14 changed files with 127 additions and 72 deletions

View File

@ -45,7 +45,7 @@ describe("Aqua to js/ts compiler", () => {
};
});
it("matches js snapshot", async () => {
it("matches js snapshots", async () => {
const jsResult = generateSources(res, "js", pkg);
const jsTypes = generateTypes(res, pkg);
@ -58,7 +58,7 @@ describe("Aqua to js/ts compiler", () => {
);
});
it("matches ts snapshot", async () => {
it("matches ts snapshots", async () => {
const tsResult = generateSources(res, "ts", pkg);
await expect(tsResult).toMatchFileSnapshot(

View File

@ -39,6 +39,7 @@ ${
: ""
}
// Making aliases to reduce chance of accidental name collision
import {
v5_callFunction as callFunction$$,
v5_registerService as registerService$$,

View File

@ -119,19 +119,24 @@ export class TSTypeGenerator implements TypeGenerator {
const serviceDecl = `service: ${srvName}Def`;
const serviceIdDecl = `serviceId: string`;
const functionOverloadsWithDefaultServiceId = [
[serviceDecl],
[serviceIdDecl, serviceDecl],
[peerDecl, serviceDecl],
[peerDecl, serviceIdDecl, serviceDecl],
];
const functionOverloadsWithoutDefaultServiceId = [
[serviceIdDecl, serviceDecl],
[peerDecl, serviceIdDecl, serviceDecl],
];
const registerServiceArgs =
// This wrong type comes from aqua team. We need to discuss fix with them
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
(srvDef.defaultServiceId as DefaultServiceId).s_Some__f_value != null
? [
[serviceDecl],
[serviceIdDecl, serviceDecl],
[peerDecl, serviceDecl],
[peerDecl, serviceIdDecl, serviceDecl],
]
: [
[serviceIdDecl, serviceDecl],
[peerDecl, serviceIdDecl, serviceDecl],
];
? functionOverloadsWithDefaultServiceId
: functionOverloadsWithoutDefaultServiceId;
return [
interfaces,

View File

@ -20,6 +20,7 @@ import { recursiveRenameLaquaProps } from "../utils.js";
import { TypeGenerator } from "./interfaces.js";
// Actual value of defaultServiceId which comes from aqua-api
export interface DefaultServiceId {
s_Some__f_value?: string;
}

View File

@ -18,12 +18,39 @@ import {
ArrowWithoutCallbacks,
FunctionCallDef,
JSONValue,
ScalarType,
SimpleTypes,
UnlabeledProductType,
} from "@fluencelabs/interfaces";
import { typeToTs } from "../common.js";
const numberTypes = [
"u8",
"u16",
"u32",
"u64",
"i8",
"i16",
"i32",
"i64",
"f32",
"f64",
];
function isScalar(schema: ScalarType, arg: JSONValue) {
if (numberTypes.includes(schema.name)) {
return typeof arg === "number";
} else if (schema.name === "bool") {
return typeof arg === "boolean";
} else if (schema.name === "string") {
return typeof arg === "string";
} else {
// Should not be possible
return false;
}
}
export function validateFunctionCall(
schema: FunctionCallDef,
...args: JSONValue[]
@ -47,12 +74,12 @@ export function validateFunctionCall(
export function validateFunctionCallArg(
schema: SimpleTypes | UnlabeledProductType | ArrowWithoutCallbacks,
arg: JSONValue,
argIndex: number,
argPosition: number,
) {
if (!isTypeMatchesSchema(schema, arg)) {
const expectedType = typeToTs(schema);
throw new Error(
`Argument ${argIndex} doesn't match schema. Expected type: ${expectedType}`,
`Argument ${argPosition} doesn't match schema. Expected type: ${expectedType}`,
);
}
}
@ -66,29 +93,7 @@ export function isTypeMatchesSchema(
} else if (schema.tag === "option") {
return arg === null || isTypeMatchesSchema(schema.type, arg);
} else if (schema.tag === "scalar") {
if (
[
"u8",
"u16",
"u32",
"u64",
"i8",
"i16",
"i32",
"i64",
"f32",
"f64",
].includes(schema.name)
) {
return typeof arg === "number";
} else if (schema.name === "bool") {
return typeof arg === "boolean";
} else if (schema.name === "string") {
return typeof arg === "string";
} else {
// Should not be possible
return false;
}
return isScalar(schema, arg);
} else if (schema.tag === "array") {
return (
Array.isArray(arg) &&

View File

@ -33,6 +33,7 @@ const packageJsonSchema = z.object({
name: z.string(),
version: z.string(),
devDependencies: z.object({
// This version used in header file
["@fluencelabs/aqua-api"]: z.string(),
}),
});

View File

@ -33,6 +33,17 @@ import { FluencePeer } from "./jsPeer/FluencePeer.js";
import { callAquaFunction, Fluence, registerService } from "./index.js";
const isAquaConfig = (
config: JSONValue | ServiceImpl[string] | undefined,
): config is CallAquaFunctionConfig => {
return (
typeof config === "object" &&
config !== null &&
!Array.isArray(config) &&
["undefined", "number"].includes(typeof config["ttl"])
);
};
/**
* Convenience function to support Aqua `func` generation backend
* The compiler only need to generate a call the function and provide the corresponding definitions and the air script
@ -47,9 +58,11 @@ export const v5_callFunction = async (
script: string,
): Promise<unknown> => {
const argNames = Object.keys(def.arrow);
const argCount = argNames.length;
const schemaArgCount = argNames.length;
const functionArgs: Record<string, SimpleTypes | ArrowWithoutCallbacks> =
type FunctionArg = SimpleTypes | ArrowWithoutCallbacks;
const schemaFunctionArgs: Record<string, FunctionArg> =
def.arrow.domain.tag === "nil" ? {} : def.arrow.domain.fields;
let peer: FluencePeer | undefined;
@ -61,26 +74,26 @@ export const v5_callFunction = async (
peer = Fluence.defaultClient;
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const config =
argCount < args.length
? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
(args.pop() as CallAquaFunctionConfig | undefined)
: undefined;
if (peer == null) {
throw new Error(
"Could not register Aqua service because the client is not initialized. Did you forget to call Fluence.connect()?",
);
}
// if args more than expected in schema (schemaArgCount) then last arg is config
const config = schemaArgCount < args.length ? args.pop() : undefined;
if (!isAquaConfig(config)) {
throw new Error("Config should be object type");
}
const callArgs = Object.fromEntries<JSONValue | ServiceImpl[string]>(
args.slice(0, argCount).map((arg, i) => {
const argSchema = functionArgs[argNames[i]];
args.slice(0, schemaArgCount).map((arg, i) => {
const argSchema = schemaFunctionArgs[argNames[i]];
if (argSchema.tag === "arrow") {
if (typeof arg !== "function") {
throw new Error("Argument and schema doesn't match");
throw new Error("Argument and schema don't match");
}
const wrappedFunction = wrapFunction(arg, argSchema);
@ -89,7 +102,7 @@ export const v5_callFunction = async (
}
if (typeof arg === "function") {
throw new Error("Argument and schema doesn't match");
throw new Error("Argument and schema don't match");
}
return [argNames[i], ts2aqua(arg, argSchema)];
@ -111,13 +124,13 @@ export const v5_callFunction = async (
fireAndForget: returnTypeVoid,
});
const valueSchema =
const returnSchema =
def.arrow.codomain.tag === "unlabeledProduct" &&
def.arrow.codomain.items.length === 1
? def.arrow.codomain.items[0]
: def.arrow.codomain;
return aqua2ts(result, valueSchema);
return aqua2ts(result, returnSchema);
};
/**
@ -145,7 +158,13 @@ export const v5_registerService = (args: unknown[], def: ServiceDef): void => {
);
}
if (typeof args[0] === "string") {
if (args.length === 2) {
if (typeof args[0] !== "string") {
throw new Error(
`Service ID should be of type string. ${typeof args[0]} provided.`,
);
}
serviceId = args[0];
}
@ -153,8 +172,10 @@ export const v5_registerService = (args: unknown[], def: ServiceDef): void => {
throw new Error("Service ID is not provided");
}
// Schema for every function in service
const serviceSchema = def.functions.tag === "nil" ? {} : def.functions.fields;
// Wrapping service impl to convert their args ts -> aqua and backwards
const wrappedServiceImpl = Object.fromEntries(
Object.entries(serviceImpl).map(([name, func]) => {
return [name, wrapFunction(func, serviceSchema[name])];

View File

@ -25,8 +25,10 @@ import { checkConnection } from "../checkConnection.js";
import { nodes, RELAY } from "./connection.js";
const ONE_SECOND = 1000;
describe("FluenceClient usage test suite", () => {
it("Should resolve at TTL when fire and forget behavior is used", async () => {
it("Should stop particle processing after TTL is reached", async () => {
await withClient(RELAY, { defaultTtlMs: 600 }, async (peer) => {
const script = `
(seq
@ -36,7 +38,7 @@ describe("FluenceClient usage test suite", () => {
const particle = await peer.internals.createNewParticle(script);
const now = Date.now();
const start = Date.now();
const promise = new Promise<JSONValue>((resolve, reject) => {
registerHandlersHelper(peer, particle, {
@ -51,7 +53,11 @@ describe("FluenceClient usage test suite", () => {
});
await expect(promise).rejects.toThrow(ExpirationError);
expect(Date.now() - 500).toBeGreaterThanOrEqual(now);
expect(
Date.now() - 500,
"Particle processing didn't stop after TTL is reached",
).toBeGreaterThanOrEqual(start);
});
});
@ -209,13 +215,17 @@ describe("FluenceClient usage test suite", () => {
);
});
it("With connection options: defaultTTL", async () => {
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
const isConnected = await checkConnection(peer);
it(
"With connection options: defaultTTL",
async () => {
await withClient(RELAY, { defaultTtlMs: 1 }, async (peer) => {
const isConnected = await checkConnection(peer);
expect(isConnected).toBeFalsy();
});
}, 1000);
expect(isConnected).toBeFalsy();
});
},
ONE_SECOND,
);
});
it.skip("Should throw correct error when the client tries to send a particle not to the relay", async () => {
@ -247,15 +257,11 @@ describe("FluenceClient usage test suite", () => {
particle,
() => {},
(error: Error) => {
if (error instanceof SendError) {
reject(error.message);
}
reject(error);
},
);
});
await promise;
await expect(promise).rejects.toMatch(
"Particle is expected to be sent to only the single peer (relay which client is connected to)",
);

View File

@ -69,6 +69,7 @@ export const callAquaFunction = async ({
const particle = await peer.internals.createNewParticle(script, config.ttl);
return new Promise<JSONValue>((resolve, reject) => {
// Registering function args as a services
for (const [name, argVal] of Object.entries(args)) {
let service: ServiceDescription;

View File

@ -147,12 +147,12 @@ export const wrapFunction = (
const result = await value(...tsArgs, context);
const valueSchema =
const resultSchema =
schema.codomain.tag === "unlabeledProduct" &&
schema.codomain.items.length === 1
? schema.codomain.items[0]
: schema.codomain;
return ts2aqua(result, valueSchema);
return ts2aqua(result, resultSchema);
};
};

View File

@ -37,6 +37,7 @@ export class EphemeralNetworkClient extends FluencePeer {
) {
const workerLoader = new WorkerLoader();
// TODO: use js-client-isomorphic
const controlModuleLoader = new WasmLoaderFromNpm(
"@fluencelabs/marine-js",
"marine-js.wasm",

View File

@ -232,6 +232,7 @@ export class EphemeralNetwork {
// shared worker for all the peers
this.workerLoader = new WorkerLoaderFromFs("../../marine/worker-script");
// TODO: use js-client-isomorphic
this.controlModuleLoader = new WasmLoaderFromNpm(
"@fluencelabs/marine-js",
"marine-js.wasm",

View File

@ -538,6 +538,17 @@ export abstract class FluencePeer {
"id %s. send successful",
newParticle.id,
);
if (
this.jsServiceHost.getHandler(
"callbackSrv",
"response",
item.particle.id,
) == null
) {
// try to finish script if fire-and-forget enabled
item.onSuccess({});
}
})
.catch((e: unknown) => {
log_particle.error(
@ -623,10 +634,9 @@ export abstract class FluencePeer {
"callbackSrv",
"response",
item.particle.id,
) == null &&
item.result.nextPeerPks.length === 0
) == null
) {
// try to finish script
// try to finish script if fire-and-forget enabled
item.onSuccess({});
}
}

View File

@ -148,6 +148,7 @@ export class TestPeer extends FluencePeer {
constructor(keyPair: KeyPair, connection: IConnection) {
const workerLoader = new WorkerLoader();
// TODO: use js-client-isomorphic
const controlModuleLoader = new WasmLoaderFromNpm(
"@fluencelabs/marine-js",
"marine-js.wasm",
@ -193,6 +194,7 @@ export const withClient = async (
) => {
const workerLoader = new WorkerLoader();
// TODO: use js-client-isomorphic
const controlModuleLoader = new WasmLoaderFromNpm(
"@fluencelabs/marine-js",
"marine-js.wasm",