diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d41d35d99f..63cf25bdf4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,6 @@ # "examples/generics", # "examples/motoko_examples/superheroes", # blocked by recursive # "examples/motoko_examples/whoami", # blocked by postUpgrade -# "examples/rejections", # "examples/robust_imports", # "examples/run_time_errors", # "examples/rust_type_conversions", @@ -116,6 +115,7 @@ jobs: "examples/principal", "examples/query", "examples/randomness", + "examples/rejections", "examples/service", "examples/simple_erc20", "examples/simple_user_accounts", diff --git a/examples/rejections/canisters/rejections/index.ts b/examples/rejections/canisters/rejections/index.ts index d6812446da..37e29a1d0f 100644 --- a/examples/rejections/canisters/rejections/index.ts +++ b/examples/rejections/canisters/rejections/index.ts @@ -9,63 +9,81 @@ import { } from 'azle'; import { default as SomeService } from '../some_service'; -class Nonexistent extends Service { - @update([], Void) - method: () => Void; -} +export default Service({ + getRejectionCodeNoError: update([], RejectionCode, async () => { + const someService = SomeService( + Principal.fromText( + process.env.SOME_SERVICE_PRINCIPAL ?? + ic.trap('process.env.SOME_SERVICE_PRINCIPAL is undefined') + ) + ); -export default class extends Service { - nonexistentCanister = new Nonexistent( - Principal.fromText('rkp4c-7iaaa-aaaaa-aaaca-cai') - ); - - someService = new SomeService( - Principal.fromText( - process.env.SOME_SERVICE_PRINCIPAL ?? - ic.trap('process.env.SOME_SERVICE_PRINCIPAL is undefined') - ) - ); - - @update([], RejectionCode) - async getRejectionCodeNoError(): Promise { - await ic.call(this.someService.accept); + await ic.call(someService.accept); return ic.rejectCode(); - } + }), - @update([], RejectionCode) - async getRejectionCodeDestinationInvalid(): Promise { + getRejectionCodeDestinationInvalid: update([], RejectionCode, async () => { try { - await ic.call(this.nonexistentCanister.method); + const Nonexistent = Service({ + method: update([], Void) + }); + + const nonexistentCanister = Nonexistent( + Principal.fromText('rkp4c-7iaaa-aaaaa-aaaca-cai') + ); + + await ic.call(nonexistentCanister.method); } catch (error) {} return ic.rejectCode(); - } + }), - @update([], RejectionCode) - async getRejectionCodeCanisterReject(): Promise { + getRejectionCodeCanisterReject: update([], RejectionCode, async () => { try { - await ic.call(this.someService.reject, { args: ['reject'] }); + const someService = SomeService( + Principal.fromText( + process.env.SOME_SERVICE_PRINCIPAL ?? + ic.trap( + 'process.env.SOME_SERVICE_PRINCIPAL is undefined' + ) + ) + ); + await ic.call(someService.reject, { args: ['reject'] }); } catch (error) {} return ic.rejectCode(); - } + }), - @update([], RejectionCode) - async getRejectionCodeCanisterError(): Promise { + getRejectionCodeCanisterError: update([], RejectionCode, async () => { try { - await ic.call(this.someService.error); + const someService = SomeService( + Principal.fromText( + process.env.SOME_SERVICE_PRINCIPAL ?? + ic.trap( + 'process.env.SOME_SERVICE_PRINCIPAL is undefined' + ) + ) + ); + await ic.call(someService.error); } catch (error) {} return ic.rejectCode(); - } + }), - @update([text], text) - async getRejectionMessage(message: text): Promise { + getRejectionMessage: update([text], text, async (message: text) => { try { - await ic.call(this.someService.reject, { args: [message] }); + const someService = SomeService( + Principal.fromText( + process.env.SOME_SERVICE_PRINCIPAL ?? + ic.trap( + 'process.env.SOME_SERVICE_PRINCIPAL is undefined' + ) + ) + ); + await ic.call(someService.reject, { args: [message] }); } catch (error) {} return ic.rejectMessage(); - } -} + }) +}); diff --git a/examples/rejections/canisters/some_service/index.ts b/examples/rejections/canisters/some_service/index.ts index 03b309adfa..b1e9544360 100644 --- a/examples/rejections/canisters/some_service/index.ts +++ b/examples/rejections/canisters/some_service/index.ts @@ -1,20 +1,25 @@ -import { bool, empty, ic, query, Service, text } from 'azle'; +import { bool, empty, ic, Manual, query, Service, text } from 'azle'; -type Manual = void; +export default Service({ + reject: query( + [text], + Manual(empty), + (message) => { + ic.reject(message); + }, + { manual: true } + ), -export default class extends Service { - @query([text], empty, { manual: true }) - reject(message: text): Manual { - ic.reject(message); - } - - @query([], bool) - accept(): bool { + accept: query([], bool, () => { return true; - } + }), - @query([], empty, { manual: true }) - error(): Manual { - // This errors because neither ic.reject nor ic.reply were called - } -} + error: query( + [], + Manual(empty), + () => { + // This errors because neither ic.reject nor ic.reply were called + }, + { manual: true } + ) +}); diff --git a/examples/rejections/dfx.json b/examples/rejections/dfx.json index 0326e4c996..651260f12f 100644 --- a/examples/rejections/dfx.json +++ b/examples/rejections/dfx.json @@ -6,7 +6,8 @@ "root": "canisters/rejections", "ts": "canisters/rejections/index.ts", "candid": "canisters/rejections/index.did", - "wasm": ".azle/rejections/rejections.wasm.gz", + "wasm": ".azle/rejections/rejections.wasm", + "gzip": true, "declarations": { "output": "test/dfx_generated/rejections", "node_compatibility": true @@ -19,7 +20,8 @@ "root": "canisters/some_service", "ts": "canisters/some_service/index.ts", "candid": "canisters/some_service/index.did", - "wasm": ".azle/some_service/some_service.wasm.gz", + "wasm": ".azle/some_service/some_service.wasm", + "gzip": true, "declarations": { "output": "test/dfx_generated/some_service", "node_compatibility": true diff --git a/src/lib_functional/candid/reference/variant.ts b/src/lib_functional/candid/reference/variant.ts index f530380bb9..3bde886ec9 100644 --- a/src/lib_functional/candid/reference/variant.ts +++ b/src/lib_functional/candid/reference/variant.ts @@ -2,6 +2,7 @@ import { TypeMapping } from '..'; import { IDL } from '@dfinity/candid'; import { processMap } from '../../../lib_new/utils'; import { v4 } from 'uuid'; +import { Null } from '../../../lib_new/primitives'; export function Variant(obj: T): RequireExactlyOne<{ [K in keyof T]: TypeMapping; @@ -36,3 +37,16 @@ type RequireExactlyOne< Partial, never>>; }[KeysType] & Omit; + +/** + * Indicates an error was encountered during a canister method. + */ +export const RejectionCode = Variant({ + NoError: Null, + SysFatal: Null, + SysTransient: Null, + DestinationInvalid: Null, + CanisterReject: Null, + CanisterError: Null, + Unknown: Null +}); diff --git a/src/lib_new/system_types.ts b/src/lib_new/system_types.ts index 28d6fb9e30..354fb26738 100644 --- a/src/lib_new/system_types.ts +++ b/src/lib_new/system_types.ts @@ -1,32 +1,17 @@ -import { candid } from './index'; -import { Null } from './primitives'; -import { Variant } from './variant'; +import { RequireExactlyOne } from './variant'; /** * Indicates an error was encountered during a canister method. */ -export class RejectionCode extends Variant { - @candid(Null) - NoError?: null; - - @candid(Null) - SysFatal?: null; - - @candid(Null) - SysTransient?: null; - - @candid(Null) - DestinationInvalid?: null; - - @candid(Null) - CanisterReject?: null; - - @candid(Null) - CanisterError?: null; - - @candid(Null) - Unknown?: null; -} +export type RejectionCode = RequireExactlyOne<{ + NoError: null; + SysFatal: null; + SysTransient: null; + DestinationInvalid: null; + CanisterReject: null; + CanisterError: null; + Unknown: null; +}>; // TODO we have decided to not use callresult or notifyresult // TODO remove once we are more mature in the project