Skip to content

Commit

Permalink
Update rejections to functional runtime syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
dansteren committed Sep 26, 2023
1 parent b551467 commit 359f6f9
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 81 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -116,6 +115,7 @@ jobs:
"examples/principal",
"examples/query",
"examples/randomness",
"examples/rejections",
"examples/service",
"examples/simple_erc20",
"examples/simple_user_accounts",
Expand Down
92 changes: 55 additions & 37 deletions examples/rejections/canisters/rejections/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<RejectionCode> {
await ic.call(this.someService.accept);
await ic.call(someService.accept);

return ic.rejectCode();
}
}),

@update([], RejectionCode)
async getRejectionCodeDestinationInvalid(): Promise<RejectionCode> {
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<RejectionCode> {
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<RejectionCode> {
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<text> {
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();
}
}
})
});
37 changes: 21 additions & 16 deletions examples/rejections/canisters/some_service/index.ts
Original file line number Diff line number Diff line change
@@ -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<T> = 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<empty> {
ic.reject(message);
}

@query([], bool)
accept(): bool {
accept: query([], bool, () => {
return true;
}
}),

@query([], empty, { manual: true })
error(): Manual<empty> {
// 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 }
)
});
6 changes: 4 additions & 2 deletions examples/rejections/dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
14 changes: 14 additions & 0 deletions src/lib_functional/candid/reference/variant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(obj: T): RequireExactlyOne<{
[K in keyof T]: TypeMapping<T[K]>;
Expand Down Expand Up @@ -36,3 +37,16 @@ type RequireExactlyOne<
Partial<Record<Exclude<KeysType, Key>, never>>;
}[KeysType] &
Omit<ObjectType, KeysType>;

/**
* 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
});
35 changes: 10 additions & 25 deletions src/lib_new/system_types.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit 359f6f9

Please sign in to comment.