From 044246e284f045a16911f8ca9d362a9979fcf4e4 Mon Sep 17 00:00:00 2001 From: Benjamin DeMann Date: Fri, 22 Sep 2023 13:29:40 -0600 Subject: [PATCH] add support for Funcs --- canisters/ledger/index.ts | 5 +- canisters/management/http_request.ts | 5 +- .../canisters/func_types/func_types.ts | 206 +++++++++--------- .../canisters/notifiers/notifiers.did | 4 +- .../canisters/notifiers/notifiers.ts | 26 ++- .../func_types/canisters/notifiers/types.ts | 8 - examples/func_types/test/tests.ts | 7 +- examples/list_of_lists/src/index.ts | 3 +- .../motoko_examples/http_counter/src/index.ts | 5 +- src/lib_new/func.ts | 13 +- src/lib_new/utils.ts | 5 +- 11 files changed, 151 insertions(+), 136 deletions(-) delete mode 100644 examples/func_types/canisters/notifiers/types.ts diff --git a/canisters/ledger/index.ts b/canisters/ledger/index.ts index 2d1c719b43..d66e9e0b20 100644 --- a/canisters/ledger/index.ts +++ b/canisters/ledger/index.ts @@ -25,7 +25,8 @@ import { Variant, Vec, candid, - principal + principal, + Func } from '../../src/lib_new'; import { ICRC1Account, @@ -279,7 +280,7 @@ export class QueryArchiveResult extends Variant { // A function that is used for fetching archived ledger blocks. @func([GetBlocksArgs], QueryArchiveResult, 'query') -class QueryArchiveFn {} +class QueryArchiveFn extends Func {} class ArchivedBlock extends Record { // The index of the first archived block that can be fetched using the callback. diff --git a/canisters/management/http_request.ts b/canisters/management/http_request.ts index 27d0cc76e3..49e225610f 100644 --- a/canisters/management/http_request.ts +++ b/canisters/management/http_request.ts @@ -9,7 +9,8 @@ import { Null, nat64, nat, - func + func, + Func } from '../../src/lib_new'; export class HttpHeader extends Record { @@ -63,7 +64,7 @@ export class HttpTransformArgs extends Record { } @func([HttpTransformArgs], HttpResponse, 'query') -export class HttpTransformFunc {} +export class HttpTransformFunc extends Func {} export class HttpTransform extends Record { /** diff --git a/examples/func_types/canisters/func_types/func_types.ts b/examples/func_types/canisters/func_types/func_types.ts index 8db374ad31..c101a5491e 100644 --- a/examples/func_types/canisters/func_types/func_types.ts +++ b/examples/func_types/canisters/func_types/func_types.ts @@ -1,123 +1,127 @@ import { Func, - Query, - Update, - $init, + init, ic, - match, nat64, Opt, Principal, - $query, + query, Record, - Result, + Service, StableBTreeMap, - $update, + update, Variant, - Vec + Vec, + candid, + text, + func, + Void, + Null } from 'azle'; -import { Notifier, NotifierFunc } from '../notifiers/types'; +import Notifier, { NotifierFunc } from '../notifiers/notifiers'; -let stableStorage = new StableBTreeMap(0, 25, 1_000); +@func([text], text, 'query') +class BasicFunc extends Func {} -type User = Record<{ - id: string; +@func([User, Reaction], nat64, 'update') +class ComplexFunc extends Func {} + +class User extends Record { + @candid(text) + id: text; + + @candid(BasicFunc) basicFunc: BasicFunc; + + @candid(ComplexFunc) complexFunc: ComplexFunc; -}>; +} -type Reaction = Variant<{ +class Reaction extends Variant { Good: null; Bad: null; BasicFunc: BasicFunc; ComplexFunc: ComplexFunc; -}>; - -type BasicFunc = Func string>>; -type ComplexFunc = Func nat64>>; -type StableFunc = Func void>>; -type NullFunc = Func< - Query< - ( - param1: Opt, - param2: Vec, - param3: null, - param4: Vec>, - param5: Vec> - ) => null - > ->; - -$init; -export function init(): void { - stableStorage.insert('stableFunc', [ - Principal.from('aaaaa-aa'), - 'start_canister' - ]); -} - -$query; -export function getStableFunc(): StableFunc { - return match(stableStorage.get('stableFunc'), { - Some: (func) => func, - None: () => [Principal.from('aaaaa-aa'), 'raw_rand'] as StableFunc - }); -} - -$query; -export function basicFuncParam(basicFunc: BasicFunc): BasicFunc { - return basicFunc; -} - -$query; -export function nullFuncParam(nullFunc: NullFunc): NullFunc { - return nullFunc; -} - -$query; -export function basicFuncParamArray(basicFunc: Vec): Vec { - return basicFunc; -} - -$query; -export function basicFuncReturnType(): BasicFunc { - return [Principal.fromText('aaaaa-aa'), 'create_canister']; -} - -$query; -export function basicFuncReturnTypeArray(): Vec { - return [ - [Principal.fromText('aaaaa-aa'), 'create_canister'], - [Principal.fromText('aaaaa-aa'), 'update_settings'], - [Principal.fromText('aaaaa-aa'), 'install_code'] - ]; -} - -$query; -export function complexFuncParam(complexFunc: ComplexFunc): ComplexFunc { - return complexFunc; -} - -$query; -export function complexFuncReturnType(): ComplexFunc { - return [Principal.fromText('aaaaa-aa'), 'stop_canister']; } -$update; -export async function getNotifierFromNotifiersCanister(): Promise< - Result -> { - const notifiersCanister: Notifier = new Notifier( - Principal.fromText( - process.env.NOTIFIERS_PRINCIPAL ?? - ic.trap('process.env.NOTIFIERS_PRINCIPAL is undefined') - ) - ); - - const result = await notifiersCanister.getNotifier().call(); - - return match(result, { - Ok: (ok) => ({ Ok: ok }), - Err: (err) => ({ Err: err }) - }); +@func([nat64, text], Void, 'query') +class StableFunc extends Func {} + +@func( + [Opt(Null), Vec(Null), Null, Vec(Vec(Null)), Vec(Opt(Null))], + Null, + 'query' +) +class NullFunc extends Func {} + +export default class extends Service { + stableStorage = new StableBTreeMap(text, StableFunc, 0); + + @init([]) + init() { + this.stableStorage.insert( + 'stableFunc', + new StableFunc(Principal.from('aaaaa-aa'), 'start_canister') + ); + } + + @query([], StableFunc) + getStableFunc(): StableFunc { + const stableFuncOpt = this.stableStorage.get('stableFunc'); + if (stableFuncOpt.length === 1) { + return stableFuncOpt[0]; + } + return new StableFunc(Principal.from('aaaaa-aa'), 'raw_rand'); + } + + @query([BasicFunc], BasicFunc) + basicFuncParam(basicFunc: BasicFunc): BasicFunc { + return basicFunc; + } + + @query([NullFunc], NullFunc) + nullFuncParam(nullFunc: NullFunc): NullFunc { + return nullFunc; + } + + @query([Vec(BasicFunc)], Vec(BasicFunc)) + basicFuncParamArray(basicFunc: Vec): Vec { + return basicFunc; + } + + @query([], BasicFunc) + basicFuncReturnType(): BasicFunc { + return new BasicFunc(Principal.fromText('aaaaa-aa'), 'create_canister'); + } + + @query([], Vec(BasicFunc)) + basicFuncReturnTypeArray(): Vec { + return [ + new BasicFunc(Principal.fromText('aaaaa-aa'), 'create_canister'), + new BasicFunc(Principal.fromText('aaaaa-aa'), 'update_settings'), + new BasicFunc(Principal.fromText('aaaaa-aa'), 'install_code') + ]; + } + + @query([ComplexFunc], ComplexFunc) + complexFuncParam(complexFunc: ComplexFunc): ComplexFunc { + return complexFunc; + } + + @query([], ComplexFunc) + complexFuncReturnType(): ComplexFunc { + return [Principal.fromText('aaaaa-aa'), 'stop_canister']; + } + + @update([], NotifierFunc) + async getNotifierFromNotifiersCanister(): Promise { + const notifiersCanister: Notifier = new Notifier( + Principal.fromText( + process.env.NOTIFIERS_PRINCIPAL ?? + ic.trap('process.env.NOTIFIERS_PRINCIPAL is undefined') + ) + ); + + return await ic.call(notifiersCanister.getNotifier); + } } diff --git a/examples/func_types/canisters/notifiers/notifiers.did b/examples/func_types/canisters/notifiers/notifiers.did index 6be686c88b..9ea58c8334 100644 --- a/examples/func_types/canisters/notifiers/notifiers.did +++ b/examples/func_types/canisters/notifiers/notifiers.did @@ -1 +1,3 @@ -service : () -> { getNotifier : () -> (func (vec nat8) -> () oneway) query } \ No newline at end of file +service: () -> { + getNotifier: () -> (func (vec nat8) -> () oneway) query; +} diff --git a/examples/func_types/canisters/notifiers/notifiers.ts b/examples/func_types/canisters/notifiers/notifiers.ts index f5ac0101dd..ddfc393c19 100644 --- a/examples/func_types/canisters/notifiers/notifiers.ts +++ b/examples/func_types/canisters/notifiers/notifiers.ts @@ -1,13 +1,17 @@ -import { ic, Principal, $query } from 'azle'; -import { NotifierFunc } from './types'; +import { ic, Principal, query, blob, Func, Service, Void, func } from 'azle'; -$query; -export function getNotifier(): NotifierFunc { - return [ - Principal.fromText( - process.env.NOTIFIERS_PRINCIPAL ?? - ic.trap('process.env.NOTIFIERS_PRINCIPAL is undefined') - ), - 'notify' - ]; +@func([blob], Void, 'oneway') +export class NotifierFunc extends Func {} + +export default class extends Service { + @query([], NotifierFunc) + getNotifier(): NotifierFunc { + return new NotifierFunc( + Principal.fromText( + process.env.NOTIFIERS_PRINCIPAL ?? + ic.trap('process.env.NOTIFIERS_PRINCIPAL is undefined') + ), + 'notify' + ); + } } diff --git a/examples/func_types/canisters/notifiers/types.ts b/examples/func_types/canisters/notifiers/types.ts deleted file mode 100644 index 1dc3865b36..0000000000 --- a/examples/func_types/canisters/notifiers/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { blob, CallResult, Func, Oneway, Service, serviceQuery } from 'azle'; - -export type NotifierFunc = Func void>>; - -export class Notifier extends Service { - @serviceQuery - getNotifier: () => CallResult; -} diff --git a/examples/func_types/test/tests.ts b/examples/func_types/test/tests.ts index de3027a73a..3b9aaeed9f 100644 --- a/examples/func_types/test/tests.ts +++ b/examples/func_types/test/tests.ts @@ -127,14 +127,13 @@ export function getTests(funcTypesCanister: ActorSubclass<_SERVICE>): Test[] { name: 'getNotifierFromNotifiersCanister', test: async () => { // TODO agent-js seems to be creating incorrect types here: https://github.com/dfinity/agent-js/issues/583 - const result: any = + const result = await funcTypesCanister.getNotifierFromNotifiersCanister(); return { Ok: - 'Ok' in result && - result.Ok[0].toText() === getCanisterId('notifiers') && - result.Ok[1] === 'notify' + result[0].toText() === getCanisterId('notifiers') && + result[1] === 'notify' }; } } diff --git a/examples/list_of_lists/src/index.ts b/examples/list_of_lists/src/index.ts index 11bc801afd..ce953a52cc 100644 --- a/examples/list_of_lists/src/index.ts +++ b/examples/list_of_lists/src/index.ts @@ -5,6 +5,7 @@ import { empty, float32, float64, + Func, func, int, int16, @@ -49,7 +50,7 @@ class State extends Variant { } @func([text], text, 'query') -class BasicFunc {} +class BasicFunc extends Func {} export default class extends Service { @query([Vec(text)], Vec(text)) diff --git a/examples/motoko_examples/http_counter/src/index.ts b/examples/motoko_examples/http_counter/src/index.ts index cb61abe26b..5ed99bdcca 100644 --- a/examples/motoko_examples/http_counter/src/index.ts +++ b/examples/motoko_examples/http_counter/src/index.ts @@ -18,7 +18,8 @@ import { Some, None, bool, - Service + Service, + Func } from 'azle'; class Token extends Record { @@ -36,7 +37,7 @@ class StreamingCallbackHttpResponse extends Record { } @func([text], StreamingCallbackHttpResponse, 'query') -class Callback {} +class Callback extends Func {} class CallbackStrategy extends Record { @candid(Callback) diff --git a/src/lib_new/func.ts b/src/lib_new/func.ts index 98920ebd90..a0355c03bc 100644 --- a/src/lib_new/func.ts +++ b/src/lib_new/func.ts @@ -1,5 +1,10 @@ import { IDL, Principal } from './index'; -import { CandidClass, toReturnIDLType, toParamIDLTypes } from './utils'; +import { + CandidClass, + toReturnIDLType, + toParamIDLTypes, + ReturnCandidClass +} from './utils'; type Mode = 'query' | 'update' | 'oneway'; @@ -9,9 +14,13 @@ const modeToCandid = { update: [] // TODO what is the proper way to do updates }; +export class Func { + constructor(principal: Principal, name: string) {} +} + export function func( paramsIdls: CandidClass[], - returnIdl: CandidClass, + returnIdl: ReturnCandidClass, mode: Mode ) { return (target: any) => { diff --git a/src/lib_new/utils.ts b/src/lib_new/utils.ts index 23a6bdc2be..2deb573883 100644 --- a/src/lib_new/utils.ts +++ b/src/lib_new/utils.ts @@ -1,4 +1,4 @@ -import { IDL, Record, Service, Variant } from './index'; +import { Func, IDL, Record, Service, Variant } from './index'; import { GetIDL } from './primitives'; /* @@ -184,6 +184,7 @@ export type CandidClass = | IDL.VecClass // blob | typeof Record | typeof Variant - | typeof Service; + | typeof Service + | typeof Func; export type ReturnCandidClass = CandidClass | never[];