Skip to content

Commit

Permalink
first attempt at restructuring tests and implementing class syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
lastmjs committed Jun 13, 2024
1 parent b6c1c47 commit 2f8bb75
Show file tree
Hide file tree
Showing 36 changed files with 8,182 additions and 30 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ jobs:
"property_tests/tests/text",
"property_tests/tests/tuple",
"property_tests/tests/variant",
"property_tests/tests/vec"
"property_tests/tests/vec",
"tests/end_to_end/candid_rpc/class_syntax/query",
"tests/end_to_end/candid_rpc/class_syntax/simple_erc20"
]
END
)
Expand Down
3 changes: 2 additions & 1 deletion examples/query/package-lock.json

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

1 change: 1 addition & 0 deletions examples/query/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"name": "query_end_to_end_test_canister_syntax",
"scripts": {
"pretest": "ts-node --transpile-only --ignore=false test/pretest.ts",
"test": "ts-node --transpile-only --ignore=false test/test.ts"
Expand Down
2 changes: 1 addition & 1 deletion examples/query/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Canister, query, text } from 'azle';
import { Canister, query, text } from 'azle/experimental';

export default Canister({
simpleQuery: query([], text, () => {
Expand Down
3 changes: 2 additions & 1 deletion examples/simple_erc20/package-lock.json

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

1 change: 1 addition & 0 deletions examples/simple_erc20/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"name": "simple_erc20_end_to_end_test_canister_syntax",
"scripts": {
"pretest": "ts-node --transpile-only --ignore=false test/pretest.ts",
"test": "ts-node --transpile-only --ignore=false test/test.ts"
Expand Down
1 change: 1 addition & 0 deletions experimental/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '../src/lib';
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"bin": {
"azle": "./bin.js"
},
"main": "./src/lib/index.ts",
"main": "./src/lib/stable/index.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/demergent-labs/azle.git"
Expand Down
58 changes: 36 additions & 22 deletions src/compiler/compile_typescript_code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function compileTypeScriptToJavaScript(
import 'azle';
// TODO remove the ethersGetUrl registration once we implement lower-level http for ethers
import { ethersGetUrl, ic, Server } from 'azle';
import { ethersGetUrl, ic, Server } from 'azle/src/lib/index';
import { ethers } from 'ethers';
ethers.FetchRequest.registerGetUrl(ethersGetUrl);
Expand All @@ -31,33 +31,47 @@ export async function compileTypeScriptToJavaScript(
export * from './${main}';
import * as CanisterMethods from './${main}';
// TODO This setTimeout is here to allow asynchronous operations during canister initialization
// for Server canisters that have chosen not to use export default Server
// This seems to work no matter how many async tasks are awaited, but I am still unsure about how it will
// behave in all async situations
setTimeout(() => {
const canisterMethods = CanisterMethods.default !== undefined ? CanisterMethods.default() : Server(() => globalThis._azleNodeServer)();
if (typeof CanisterMethods !== 'function') {
const canister = new CanisterMethods.default();
globalThis.candidInfoFunction = () => {
const candidInfo = canisterMethods.getIdl([]).accept(new DidVisitor(), {
...getDefaultVisitorData(),
isFirstService: true,
systemFuncs: canisterMethods.getSystemFunctionIdls()
});
return JSON.stringify({
candid: toDidString(candidInfo),
canisterMethods: {
// TODO The spread is because canisterMethods is a function with properties
// TODO we should probably just grab the props out that we need
...canisterMethods
}
candid: '',
canisterMethods: globalThis._azleCanisterMethods
});
};
// TODO I do not know how to get the module exports yet with wasmedge_quickjs
globalThis.exports.canisterMethods = canisterMethods;
});
globalThis.exports.canisterMethods = globalThis._azleCanisterMethods;
}
else {
// TODO This setTimeout is here to allow asynchronous operations during canister initialization
// for Server canisters that have chosen not to use export default Server
// This seems to work no matter how many async tasks are awaited, but I am still unsure about how it will
// behave in all async situations
setTimeout(() => {
const canisterMethods = CanisterMethods.default !== undefined ? CanisterMethods.default() : Server(() => globalThis._azleNodeServer)();
globalThis.candidInfoFunction = () => {
const candidInfo = canisterMethods.getIdl([]).accept(new DidVisitor(), {
...getDefaultVisitorData(),
isFirstService: true,
systemFuncs: canisterMethods.getSystemFunctionIdls()
});
return JSON.stringify({
candid: toDidString(candidInfo),
canisterMethods: {
// TODO The spread is because canisterMethods is a function with properties
// TODO we should probably just grab the props out that we need
...canisterMethods
}
});
};
// TODO I do not know how to get the module exports yet with wasmedge_quickjs
globalThis.exports.canisterMethods = canisterMethods;
});
}
`;

const bundledJavaScript = await bundleFromString(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { handleUncaughtError } from '../../../../../error';
import { handleUncaughtError } from '../../../../../stable/error';
import { CanisterOptions } from '.';

type QueryMethod = {
Expand Down
2 changes: 1 addition & 1 deletion src/lib/canister_methods/execute_method.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { CandidType } from '../candid/candid_type';
import { decode } from '../candid/serde/decode';
import { encode } from '../candid/serde/encode';
import { handleUncaughtError } from '../error';
import { ic } from '../ic';
import { handleUncaughtError } from '../stable/error';
import { CanisterMethodInfo } from './types/canister_method_info';

export function executeMethod(
Expand Down
29 changes: 29 additions & 0 deletions src/lib/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ import { ic } from './ic';
import { AzleIc } from './ic/types/azle_ic';
import { jsonReplacer } from './stable_structures/stable_json';

type CanisterMethods = {
candid: string;
queries: CanisterMethod[];
updates: CanisterMethod[];
init?: CanisterMethod;
pre_upgrade?: CanisterMethod;
post_upgrade?: CanisterMethod;
heartbeat?: CanisterMethod;
inspect_message?: CanisterMethod;
callbacks: {
[key: string]: (...args: any) => any;
};
};

type CanisterMethod = {
name: string;
composite?: boolean;
guard?: () => void;
};

declare global {
// eslint-disable-next-line no-var
var _azleInsideCanister: boolean;
Expand Down Expand Up @@ -42,12 +62,21 @@ declare global {
var _azleInitCalled: boolean;
// eslint-disable-next-line no-var
var _azlePostUpgradeCalled: boolean;
// eslint-disable-next-line no-var
var _azleCanisterMethods: CanisterMethods;
}

globalThis._azleInsideCanister =
globalThis._azleIc === undefined ? false : true;

if (globalThis._azleInsideCanister) {
globalThis._azleCanisterMethods = {
candid: '',
queries: [],
updates: [],
callbacks: {}
};

globalThis._azleInitCalled = false;
globalThis._azlePostUpgradeCalled = false;

Expand Down
2 changes: 1 addition & 1 deletion src/lib/error.ts → src/lib/stable/error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ic } from '.';
import { ic } from '../ic';

export function handleUncaughtError(rawError: any) {
const error = rawError instanceof Error ? rawError : new Error(rawError);
Expand Down
64 changes: 64 additions & 0 deletions src/lib/stable/execute_method.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { IDL } from '@dfinity/candid';

import { ic } from '../ic';
import { handleUncaughtError } from './error';

type CanisterMethodMode =
| 'query'
| 'update'
| 'init'
| 'heartbeat'
| 'inspectMessage'
| 'postUpgrade'
| 'preUpgrade';

export function executeMethod(
mode: CanisterMethodMode,
args: any[],
callback: any,
paramIdls: IDL.Type[],
returnIdl: IDL.Type,
manual: boolean
) {
const decodedArgs = IDL.decode(paramIdls, args[0]);

const result = getResult(decodedArgs, callback);

if (
mode === 'init' ||
mode === 'postUpgrade' ||
mode === 'inspectMessage'
) {
return;
}

if (
result !== undefined &&
result !== null &&
typeof result.then === 'function'
) {
result
.then((result: any) => {
if (!manual) {
ic.replyRaw(
new Uint8Array(IDL.encode([returnIdl], [result]))
);
}
})
.catch((error: any) => {
handleUncaughtError(error);
});
} else {
if (!manual) {
ic.replyRaw(new Uint8Array(IDL.encode([returnIdl], [result])));
}
}
}

function getResult(args: any[], callback: any): any {
try {
return callback(...args);
} catch (error) {
handleUncaughtError(error);
}
}
3 changes: 3 additions & 0 deletions src/lib/stable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { query } from './query';
export { update } from './update';
export { IDL } from '@dfinity/candid';
5 changes: 5 additions & 0 deletions src/lib/stable/mark_experimental.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function markExperimental() {
if (process.env.AZLE_EXPERIMENTAL !== 'true') {
throw new Error(`Azle: the experimental flag must be set`);
}
}
68 changes: 68 additions & 0 deletions src/lib/stable/query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { IDL } from '@dfinity/candid';

import { isAsync } from '../canister_methods/is_async';
import { executeMethod } from './execute_method';

export function query(
paramIdls: IDL.Type[],
returnIdl: IDL.Type
): MethodDecorator {
return <T>(
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void => {
const originalMethod = descriptor.value;

const methodCallback = (...args: any[]) => {
executeMethod(
'query',
args,
originalMethod,
paramIdls,
returnIdl,
false // TODO implement manual check
);
};

descriptor.value = methodCallback as any;

globalThis._azleCanisterMethods.queries.push({
name: propertyKey as string,
composite: isAsync(originalMethod)
// TODO implement guard
});

globalThis._azleCanisterMethods.callbacks[propertyKey as string] =
methodCallback;

return descriptor;
};
}

// TODO this implementation is for native decorators
// export function query<This, Args extends any[], Return>(
// paramIdls: IDL.Type[],
// returnIdl: IDL.Type
// ) {
// return (
// originalMethod: (this: This, ...args: Args) => Return,
// _context: ClassMethodDecoratorContext<
// This,
// (this: This, ...args: Args) => Return
// >
// ) => {
// return function (this: This, ...args: Args): Return {
// executeMethod(
// 'query',
// args,
// originalMethod,
// paramIdls,
// returnIdl,
// false // TODO implement manual check
// );

// return undefined as Return;
// };
// };
// }
39 changes: 39 additions & 0 deletions src/lib/stable/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { IDL } from '@dfinity/candid';

import { executeMethod } from './execute_method';

export function update(
paramIdls: IDL.Type[],
returnIdl: IDL.Type
): MethodDecorator {
return <T>(
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void => {
const originalMethod = descriptor.value;

const methodCallback = (...args: any[]) => {
executeMethod(
'update',
args,
originalMethod,
paramIdls,
returnIdl,
false // TODO implement manual check
);
};

descriptor.value = methodCallback as any;

globalThis._azleCanisterMethods.updates.push({
name: propertyKey as string
// TODO implement guard
});

globalThis._azleCanisterMethods.callbacks[propertyKey as string] =
methodCallback;

return descriptor;
};
}
Loading

0 comments on commit 2f8bb75

Please sign in to comment.