Skip to content

Commit

Permalink
Merge pull request #1849 from demergent-labs/class_syntax_heartbeat
Browse files Browse the repository at this point in the history
Class syntax heartbeat
  • Loading branch information
lastmjs authored Jun 20, 2024
2 parents d6df7f9 + 62728c1 commit 7387e46
Show file tree
Hide file tree
Showing 26 changed files with 4,169 additions and 16 deletions.
1 change: 1 addition & 0 deletions examples/heartbeat/package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"name": "heartbeat_end_to_end_test_functional_syntax",
"scripts": {
"pre_tests": "ts-node --transpile-only --ignore=false test/pretest.ts",
"tests": "npm run pre_tests && jest",
Expand Down
2 changes: 1 addition & 1 deletion examples/heartbeat/src/heartbeat_async/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { blob, Canister, heartbeat, ic, query } from 'azle';
import { managementCanister } from 'azle/canisters/management';
import { blob, Canister, heartbeat, ic, query } from 'azle/experimental';

let initialized: blob = Uint8Array.from([]);

Expand Down
2 changes: 1 addition & 1 deletion examples/heartbeat/src/heartbeat_sync/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { bool, Canister, heartbeat, query } from 'azle';
import { bool, Canister, heartbeat, query } from 'azle/experimental';

let initialized = false;

Expand Down
2 changes: 0 additions & 2 deletions src/lib/canister_methods/methods/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ export function init<
callback === undefined
? undefined
: (...args: any[]) => {
globalThis._azleInitCalled = true;

executeMethod(
'init',
args,
Expand Down
2 changes: 0 additions & 2 deletions src/lib/canister_methods/methods/post_upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ export function postUpgrade<
callback === undefined
? undefined
: (...args: any[]) => {
globalThis._azlePostUpgradeCalled = true;

executeMethod(
'postUpgrade',
args,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type CanisterMethodMode =
| 'postUpgrade'
| 'preUpgrade';

export function executeMethod(
export function executeWithCandidSerde(
mode: CanisterMethodMode,
args: any[],
callback: any,
Expand Down
33 changes: 33 additions & 0 deletions src/lib/stable/heartbeat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function heartbeat<T>(
_target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void {
globalThis._azleCanisterMethods.heartbeat = {
name: propertyKey as string
};

globalThis._azleCanisterMethods.callbacks[propertyKey as string] =
descriptor.value as any;

return descriptor;
}

// TODO do we need this?
// TODO it would be nice if QuickJS would just panic
// TODO on all uncaught exceptions
// function executeHeartbeat(callback: any) {
// const result = callback();

// if (
// result !== undefined &&
// result !== null &&
// typeof result.then === 'function'
// ) {
// result.catch((error: any) => {
// ic.trap(error.toString());
// });
// }

// return;
// }
76 changes: 76 additions & 0 deletions src/lib/stable/ic_apis/call.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { v4 } from 'uuid'; // TODO is uuid experimental?

import { IDL, Principal } from '../';

export async function call(
canisterId: Principal | string,
method: string,
options?: {
paramIdls?: IDL.Type[];
returnIdl?: IDL.Type;
args?: any[];
payment?: bigint;
}
): Promise<any> {
// TODO this should use a Result remember
return new Promise((resolve, reject) => {
if (globalThis._azleIc === undefined) {
return undefined as any;
}

const promiseId = v4();
const globalResolveId = `_resolve_${promiseId}`;
const globalRejectId = `_reject_${promiseId}`;

const returnIdl = options?.returnIdl;

// TODO perhaps we should be more robust
// TODO for example, we can keep the time with these
// TODO if they are over a certain amount old we can delete them
globalThis._azleResolveIds[globalResolveId] = (result: ArrayBuffer) => {
if (returnIdl === undefined) {
resolve(undefined);
} else {
resolve(IDL.decode([returnIdl], result)[0]);
}

delete globalThis._azleResolveIds[globalResolveId];
delete globalThis._azleRejectIds[globalRejectId];
};

globalThis._azleRejectIds[globalRejectId] = (error: any) => {
reject(error);

delete globalThis._azleResolveIds[globalResolveId];
delete globalThis._azleRejectIds[globalRejectId];
};

const paramIdls = options?.paramIdls ?? [];
const args = options?.args ?? [];
const payment = options?.payment ?? 0n;

const canisterIdPrincipal =
typeof canisterId === 'string'
? Principal.fromText(canisterId)
: canisterId;
const canisterIdBytes = canisterIdPrincipal.toUint8Array().buffer;
const argsRawBuffer = new Uint8Array(IDL.encode(paramIdls, args))
.buffer;
const paymentString = payment.toString();

// TODO consider finally, what if deletion goes wrong
try {
globalThis._azleIc.callRaw(
promiseId,
canisterIdBytes,
method,
argsRawBuffer,
paymentString
);
} catch (error) {
delete globalThis._azleResolveIds[globalResolveId];
delete globalThis._azleRejectIds[globalRejectId];
throw error;
}
});
}
1 change: 1 addition & 0 deletions src/lib/stable/ic_apis/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { call } from './call';
2 changes: 2 additions & 0 deletions src/lib/stable/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export * from '../ic';
export * from '../stable_structures/stable_b_tree_map';
export * from '../stable_structures/stable_json';
export { heartbeat } from './heartbeat';
export * from './ic_apis';
export { init } from './init';
export { inspectMessage } from './inspect_message';
export { postUpgrade } from './post_upgrade';
Expand Down
4 changes: 2 additions & 2 deletions src/lib/stable/init.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IDL } from '@dfinity/candid';

import { executeMethod } from './execute_method';
import { executeWithCandidSerde } from './execute_with_candid_serde';

export function init(paramIdls: IDL.Type[]): MethodDecorator {
return <T>(
Expand All @@ -11,7 +11,7 @@ export function init(paramIdls: IDL.Type[]): MethodDecorator {
const originalMethod = (descriptor.value as any).bind(target);

const methodCallback = (...args: any[]) => {
executeMethod(
executeWithCandidSerde(
'init',
args,
originalMethod,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/stable/post_upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IDL } from '@dfinity/candid';

import { executeMethod } from './execute_method';
import { executeWithCandidSerde } from './execute_with_candid_serde';

export function postUpgrade(paramIdls: IDL.Type[]): MethodDecorator {
return <T>(
Expand All @@ -11,7 +11,7 @@ export function postUpgrade(paramIdls: IDL.Type[]): MethodDecorator {
const originalMethod = (descriptor.value as any).bind(target);

const methodCallback = (...args: any[]) => {
executeMethod(
executeWithCandidSerde(
'postUpgrade',
args,
originalMethod,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/stable/query.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IDL } from '@dfinity/candid';

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

export function query(
paramIdls: IDL.Type[],
Expand All @@ -15,7 +15,7 @@ export function query(
const originalMethod = (descriptor.value as any).bind(target);

const methodCallback = (...args: any[]) => {
executeMethod(
executeWithCandidSerde(
'query',
args,
originalMethod,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/stable/update.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IDL } from '@dfinity/candid';

import { executeMethod } from './execute_method';
import { executeWithCandidSerde } from './execute_with_candid_serde';

export function update(
paramIdls: IDL.Type[],
Expand All @@ -14,7 +14,7 @@ export function update(
const originalMethod = (descriptor.value as any).bind(target);

const methodCallback = (...args: any[]) => {
executeMethod(
executeWithCandidSerde(
'update',
args,
originalMethod,
Expand Down
4 changes: 4 additions & 0 deletions tests/end_to_end/candid_rpc/class_syntax/heartbeat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.azle
.dfx
dfx_generated
node_modules
25 changes: 25 additions & 0 deletions tests/end_to_end/candid_rpc/class_syntax/heartbeat/dfx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"canisters": {
"heartbeat_async": {
"type": "azle",
"main": "src/heartbeat_async/index.ts",
"candid": "src/heartbeat_async/index.did",
"candid_gen": "custom",
"env": ["AZLE_TEST_FETCH"],
"declarations": {
"output": "test/dfx_generated/heartbeat_async",
"node_compatibility": true
}
},
"heartbeat_sync": {
"type": "azle",
"main": "src/heartbeat_sync/index.ts",
"candid": "src/heartbeat_sync/index.did",
"candid_gen": "custom",
"declarations": {
"output": "test/dfx_generated/heartbeat_sync",
"node_compatibility": true
}
}
}
}
Loading

0 comments on commit 7387e46

Please sign in to comment.