Skip to content

Commit

Permalink
Merge pull request #263 from petarvujovic98/develop
Browse files Browse the repository at this point in the history
[RFC] feat: Middlewares addition
  • Loading branch information
volovyks authored Oct 17, 2022
2 parents fd6db41 + 3672a68 commit 04717e5
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 15 deletions.
6 changes: 5 additions & 1 deletion lib/build-tools/near-bindgen-exporter.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions lib/cli/cli.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions lib/cli/utils.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions lib/near-bindgen.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions lib/near-bindgen.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion src/build-tools/near-bindgen-exporter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { PluginPass } from "@babel/core";
import { Node, Visitor } from "@babel/traverse";
import * as t from "@babel/types";
import signal from "signale";

const { Signale } = signal;

/**
* A list of supported method types/decorators.
Expand Down Expand Up @@ -388,7 +391,9 @@ export default function (): { visitor: Visitor } {
);

if (verbose) {
console.log(`Babel ${child.key.name} method export done`);
new Signale({
scope: "near-bindgen-exporter",
}).info(`Babel ${child.key.name} method export done.`);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function buildCom(
const TARGET_DIR = dirname(target);
const TARGET_EXT = target.split(".").pop();
const TARGET_FILE_NAME = basename(target, `.${TARGET_EXT}`);
const signale = new Signale({ scope: "build", interactive: true });
const signale = new Signale({ scope: "build", interactive: !verbose });

if (TARGET_EXT !== "wasm") {
signale.error(
Expand Down Expand Up @@ -143,7 +143,9 @@ async function createMethodsHeaderFile(rollupTarget: string, verbose = false) {
const buildPath = path.dirname(rollupTarget);

if (verbose) {
console.log(rollupTarget);
new Signale({scope: "method-header"}).info(
rollupTarget
)
}

const mod = await import(`${PROJECT_DIR}/${rollupTarget}`);
Expand Down
13 changes: 9 additions & 4 deletions src/cli/utils.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import childProcess from "child_process";
import { promisify } from "util";
import signal from "signale"

const {Signale} = signal;

const exec = promisify(childProcess.exec);

export async function executeCommand(
command: string,
verbose = false
): Promise<string> {
const signale = new Signale({scope: "exec", interactive: !verbose})

if (verbose) {
console.log(command);
signale.info(`Running command: ${command}`);
}

try {
const { stdout, stderr } = await exec(command);

if (stderr && verbose) {
console.error(stderr);
signale.error(stderr);
}

if (verbose) {
console.log(stdout);
signale.info(`Command output: ${stdout}`);
}

return stdout.trim();
} catch (error) {
console.log(error);
signale.error(error);
process.exit(1);
}
}
44 changes: 44 additions & 0 deletions src/near-bindgen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,50 @@ export function call({
};
}

/**
* The interface that a middleware has to implement in order to be used as a middleware function/class.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
interface Middleware<Arguments extends Array<any>> {
/**
* The method that gets called with the same arguments that are passed to the function it is wrapping.
*
* @param args - Arguments that will be passed to the function - immutable.
*/
(...args: Arguments): void;
}

/**
* Tells the SDK to apply an array of passed in middleware to the function execution.
*
* @param middlewares - The middlewares to be executed.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function middleware<Arguments extends Array<any>>(
...middlewares: Middleware<Arguments>[]
): DecoratorFunction {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return function <AnyFunction extends (...args: Arguments) => any>(
_target: object,
_key: string | symbol,
descriptor: TypedPropertyDescriptor<AnyFunction>
): void {
const originalMethod = descriptor.value;

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
descriptor.value = function (...args: Arguments): ReturnType<AnyFunction> {
try {
middlewares.forEach((middleware) => middleware(...args));
} catch (error) {
throw new Error(error);
}

return originalMethod.apply(this, args);
};
};
}

/**
* Extends this class with the methods needed to make the contract storable/serializable and readable/deserializable to and from the blockchain.
* Also tells the SDK to capture and expose all view, call and initialize functions.
Expand Down
99 changes: 99 additions & 0 deletions tests/__tests__/test-middlewares.ava.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Worker } from "near-workspaces";
import test from "ava";

test.beforeEach(async (t) => {
// Init the worker and start a Sandbox server
const worker = await Worker.init();

// Prepare sandbox for tests, create accounts, deploy contracts, etx.
const root = worker.rootAccount;

// Deploy the contract.
const middlewares = await root.devDeploy("build/middlewares.wasm");

// Create the init args.
const args = JSON.stringify({ randomData: "anything" });
// Capture the result of the init function call.
const result = await middlewares.callRaw(middlewares, "init", args);

// Extract the logs.
const { logs } = result.result.receipts_outcome[0].outcome;
// Create the expected logs.
const expectedLogs = [`Log from middleware: ${args}`];

// Check for correct logs.
t.deepEqual(logs, expectedLogs);

// Create test users
const ali = await root.createSubAccount("ali");

// Save state for test runs
t.context.worker = worker;
t.context.accounts = { root, middlewares, ali };
});

test.afterEach.always(async (t) => {
await t.context.worker.tearDown().catch((error) => {
console.log("Failed to tear down the worker:", error);
});
});

test("The middleware logs with call functions", async (t) => {
const { ali, middlewares } = t.context.accounts;

// Create the arguments which will be passed to the function.
const args = JSON.stringify({ id: "1", text: "hello" });
// Call the function.
const result = await ali.callRaw(middlewares, "add", args);
// Extract the logs.
const { logs } = result.result.receipts_outcome[0].outcome;
// Create the expected logs.
const expectedLogs = [`Log from middleware: ${args}`];

t.deepEqual(logs, expectedLogs);
});

test("The middleware logs with view functions", async (t) => {
const { ali, middlewares } = t.context.accounts;

// Create the arguments which will be passed to the function.
const args = JSON.stringify({ id: "1", accountId: "hello" });
// Call the function.
const result = await ali.callRaw(middlewares, "get", args);
// Extract the logs.
const { logs } = result.result.receipts_outcome[0].outcome;
// Create the expected logs.
const expectedLogs = [`Log from middleware: ${args}`];

t.deepEqual(logs, expectedLogs);
});

test("The middleware logs with two middleware functions", async (t) => {
const { ali, middlewares } = t.context.accounts;

// Create the arguments which will be passed to the function.
const args = JSON.stringify({ id: "1", accountId: "hello" });
// Call the function.
const result = await ali.callRaw(middlewares, "get_two", args);
// Extract the logs.
const { logs } = result.result.receipts_outcome[0].outcome;
// Create the expected logs.
const expectedLogs = [`Log from middleware: ${args}`, "Second log!"];

t.deepEqual(logs, expectedLogs);
});

test("The middleware logs with private functions", async (t) => {
const { ali, middlewares } = t.context.accounts;

// Create the arguments which will be passed to the function.
const args = { id: "test", accountId: "tset" };
// Call the function.
const result = await ali.callRaw(middlewares, "get_private", "");
// Extract the logs.
const { logs } = result.result.receipts_outcome[0].outcome;
// Create the expected logs.
const expectedLogs = [`Log from middleware: ${args}`];

t.deepEqual(logs, expectedLogs);
});
4 changes: 3 additions & 1 deletion tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"build:private": "near-sdk-js build src/decorators/private.ts build/private.wasm",
"build:bigint-serialization": "near-sdk-js build src/bigint-serialization.ts build/bigint-serialization.wasm",
"build:date-serialization": "near-sdk-js build src/date-serialization.ts build/date-serialization.wasm",
"build:middlewares": "near-sdk-js build src/middlewares.ts build/middlewares.wasm",
"test": "ava",
"test:context-api": "ava __tests__/test_context_api.ava.js",
"test:math-api": "ava __tests__/test_math_api.ava.js",
Expand All @@ -49,7 +50,8 @@
"test:private": "ava __tests__/decorators/private.ava.js",
"test:bigint-serialization": "ava __tests__/test-bigint-serialization.ava.js",
"test:date-serialization": "ava __tests__/test-date-serialization.ava.js",
"test:serialization": "ava __tests__/test-serialization.ava.js"
"test:serialization": "ava __tests__/test-serialization.ava.js",
"test:middlewares": "ava __tests__/test-middlewares.ava.js"
},
"author": "Near Inc <hello@nearprotocol.com>",
"license": "Apache-2.0",
Expand Down
Loading

0 comments on commit 04717e5

Please sign in to comment.