Skip to content

Commit

Permalink
Merge pull request #1258 from demergent-labs/functional_syntax_compos…
Browse files Browse the repository at this point in the history
…ite_queries

Functional syntax composite queries
  • Loading branch information
lastmjs authored Sep 25, 2023
2 parents a88bd62 + 863f27b commit 31eba83
Show file tree
Hide file tree
Showing 20 changed files with 415 additions and 554 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
# The check-basic-integration-tests-success job is designed to ensure that all jobs spun up from the matrix in the basic-integration-tests have succeeded

# All Examples TODO restore when https://github.com/demergent-labs/azle/issues/1192 is resolved
# "examples/candid_encoding",
# "examples/complex_init",
# "examples/complex_types",
# "examples/composite_queries",
# "examples/counter",
# "examples/cross_canister_calls",
# "examples/cycles",
Expand Down Expand Up @@ -114,6 +111,9 @@ jobs:
"examples/blob_array",
"examples/bytes",
"examples/call_raw",
"examples/candid_encoding",
"examples/complex_init",
"examples/composite_queries",
"examples/primitive_types",
"examples/principal",
"examples/query",
Expand Down
3 changes: 2 additions & 1 deletion examples/candid_encoding/dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"root": "src",
"ts": "src/index.ts",
"candid": "src/index.did",
"wasm": ".azle/candid_encoding/candid_encoding.wasm.gz",
"wasm": ".azle/candid_encoding/candid_encoding.wasm",
"gzip": true,
"declarations": {
"output": "test/dfx_generated/candid_encoding",
"node_compatibility": true
Expand Down
15 changes: 6 additions & 9 deletions examples/candid_encoding/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { blob, ic, query, Service, text } from 'azle';

export default class extends Service {
export default Service({
// encodes a Candid string to Candid bytes
@query([text], blob)
candidEncode(candidString: text): blob {
candidEncode: query([text], blob, (candidString) => {
return ic.candidEncode(candidString);
}

}),
// decodes Candid bytes to a Candid string
@query([blob], text)
candidDecode(candidEncoded: blob): text {
candidDecode: query([blob], text, (candidEncoded) => {
return ic.candidDecode(candidEncoded);
}
}
})
});
3 changes: 2 additions & 1 deletion examples/complex_init/dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"root": "src",
"ts": "src/index.ts",
"candid": "src/index.did",
"wasm": ".azle/complex_init/complex_init.wasm.gz",
"wasm": ".azle/complex_init/complex_init.wasm",
"gzip": true,
"declarations": {
"output": "test/dfx_generated/complex_init",
"node_compatibility": true
Expand Down
46 changes: 17 additions & 29 deletions examples/complex_init/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
import {
candid,
init,
Opt,
query,
Record,
Service,
text,
Tuple,
Void
} from 'azle';
import { init, Opt, query, Record, Service, text, Tuple } from 'azle';

class User extends Record {
@candid(text)
id: text;
}
const User = Record({
id: text
});

export default class extends Service {
greeting: text = 'Hello User';
user: Opt<User> = [];
let greeting: text = 'Hello User';
let user: Opt<typeof User> = [];

@init([Tuple(text, User)])
init(tuple: Tuple<[string, User]>): Void {
this.greeting = tuple[0];
this.user = [tuple[1]];
}

@query([], text)
greetUser(): text {
return `${this.greeting} ${this.user[0]?.id ?? '??'}`;
}
}
// TODO tuple types aren't done, they don't have TypeScript types
export default Service({
init: init([Tuple(text, User)], (tuple) => {
greeting = tuple[0];
user = [tuple[1]];
return undefined;
}),
greetUser: query([], text, () => {
return `${greeting} ${user[0]?.id ?? '??'}`;
})
});
141 changes: 72 additions & 69 deletions examples/composite_queries/canisters/canister1/index.ts
Original file line number Diff line number Diff line change
@@ -1,89 +1,92 @@
import { ic, Manual, nat, Principal, query, Service, text, update } from 'azle';
import {
ic,
init,
Manual,
nat,
Principal,
query,
Service,
text,
update
} from 'azle';
import Canister2 from '../canister2';

class Canister1 extends Service {
canister2 = new Canister2(
Principal.fromText(
process.env.CANISTER2_PRINCIPAL ??
ic.trap('process.env.CANISTER2_PRINCIPAL is undefined')
)
);
let canister2: typeof Canister2;
let counter: nat = 0n;

counter: nat = 0n;
const incCounter = query([], nat, async () => {
counter += 1n;

// Composite query calling a query
@query([], text)
async simpleCompositeQuery(): Promise<text> {
return await ic.call(this.canister2.simpleQuery);
}
return counter;
});

const service = Service({
init: init([], () => {
canister2 = Canister2(
Principal.fromText(
process.env.CANISTER2_PRINCIPAL ??
ic.trap('process.env.CANISTER2_PRINCIPAL is undefined')
)
);
}),
// Composite query calling a query
simpleCompositeQuery: query([], text, async () => {
return await ic.call(canister2.simpleQuery);
}),
// Composite query calling a manual query
@query([], text)
async manualQuery(): Promise<text> {
return await ic.call(this.canister2.manualQuery);
}

manualQuery: query([], text, async () => {
return (await ic.call(canister2.manualQuery)) as unknown as string; // TODO is this the best we can do for the types in this situation?
}),
// Manual composite query calling a manual query
@query([], text, { manual: true })
async totallyManualQuery(): Promise<Manual<text>> {
ic.reply(await ic.call(this.canister2.manualQuery), text);
}

totallyManualQuery: query(
[],
Manual(text),
async () => {
ic.reply(await ic.call(canister2.manualQuery), text);
},
{ manual: true }
),
// Composite query calling another composite query
@query([], text)
async deepQuery(): Promise<text> {
return await ic.call(this.canister2.deepQuery);
}

deepQuery: query([], text, async () => {
return await ic.call(canister2.deepQuery);
}),
// Composite query calling an update method. SHOULDN'T WORK
@query([], text)
async updateQuery(): Promise<text> {
return await ic.call(this.canister2.updateQuery);
}

updateQuery: query([], text, async () => {
return await ic.call(canister2.updateQuery);
}),
// Composite query being called by a query method. SHOULDN'T WORK
@query([], text)
async simpleQuery(): Promise<text> {
return await ic.call(this.canister2.simpleQuery);
}

simpleQuery: query([], text, async () => {
return await ic.call(canister2.simpleQuery);
}),
// Composite query being called by an update method. SHOULDN'T WORK
@update([], text)
async simpleUpdate(): Promise<text> {
return await ic.call(this.canister2.deepQuery);
}

simpleUpdate: update([], text, async () => {
return await ic.call(canister2.deepQuery);
}),
// Composite query that modifies the state. Should revert after the call is done
@query([], nat)
async incCounter(): Promise<nat> {
this.counter += 1n;

return this.counter;
}

incCounter,
// Composite query calling queries on the same canister
@query([], nat)
async incCanister1(): Promise<nat> {
this.counter += 1n;

const canister1AResult = await ic.call(this.incCounter);
incCanister1: query([], nat, async () => {
// TODO This is not an ideal solution but will work for now
const self = Service({
incCounter
})(ic.id());

const canister1BResult = await ic.call(this.incCounter);
counter += 1n;

return this.counter + canister1AResult + canister1BResult;
}
const canister1AResult = await ic.call(self.incCounter);
const canister1BResult = await ic.call(self.incCounter);

return counter + canister1AResult + canister1BResult;
}),
// Composite query calling queries that modify the state
@query([], nat)
async incCanister2(): Promise<nat> {
this.counter += 1n;

const canister2AResult = await ic.call(this.canister2.incCounter);
incCanister2: query([], nat, async () => {
counter += 1n;

const canister2BResult = await ic.call(this.canister2.incCounter);
const canister2AResult = await ic.call(canister2.incCounter);
const canister2BResult = await ic.call(canister2.incCounter);

return this.counter + canister2AResult + canister2BResult;
}
}
return counter + canister2AResult + canister2BResult;
})
});

export default Canister1;
export default service;
76 changes: 42 additions & 34 deletions examples/composite_queries/canisters/canister2/index.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,48 @@
import { ic, Manual, nat, Principal, query, Service, text, update } from 'azle';
import {
ic,
init,
Manual,
nat,
Principal,
query,
Service,
text,
update
} from 'azle';
import Canister3 from '../canister3';

export default class extends Service {
canister3 = new Canister3(
Principal.fromText(
process.env.CANISTER3_PRINCIPAL ??
ic.trap('process.env.CANISTER3_PRINCIPAL is undefined')
)
);

counter: nat = 0n;
let canister3: typeof Canister3;
let counter: nat = 0n;

export default Service({
init: init([], () => {
canister3 = Canister3(
Principal.fromText(
process.env.CANISTER3_PRINCIPAL ??
ic.trap('process.env.CANISTER3_PRINCIPAL is undefined')
)
);
}),
// TODO is this supposed to be a query?
@query([], nat)
async incCounter(): Promise<nat> {
this.counter += 1n;
return this.counter;
}

@query([], text)
simpleQuery(): text {
incCounter: query([], nat, () => {
counter += 1n;
return counter;
}),
simpleQuery: query([], text, () => {
return 'Hello from Canister 2';
}

@update([], text)
updateQuery(): text {
}),
updateQuery: update([], text, () => {
return 'Hello from a Canister 2 update';
}

@query([], text, { manual: true })
manualQuery(): Manual<text> {
ic.reply('Hello from Canister 2 manual query', text);
}

@query([], text)
async deepQuery(): Promise<text> {
return await ic.call(this.canister3.deepQuery);
}
}
}),
manualQuery: query(
[],
Manual(text),
() => {
ic.reply('Hello from Canister 2 manual query', text);
},
{ manual: true }
),
deepQuery: query([], text, async () => {
return await ic.call(canister3.deepQuery);
})
});
11 changes: 5 additions & 6 deletions examples/composite_queries/canisters/canister3/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Service, query, text } from 'azle';
import { query, Service, text } from 'azle';

export default class extends Service {
@query([], text)
deepQuery(): text {
export default Service({
deepQuery: query([], text, () => {
return 'Hello from Canister 3';
}
}
})
});
Loading

0 comments on commit 31eba83

Please sign in to comment.