diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1c499a8866..166d6cff31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,7 +72,6 @@ # "examples/rust_type_conversions", # "examples/service", # "examples/simple_erc20", -# "examples/simple_user_accounts", # "examples/stable_memory", # "examples/stable_structures", # "examples/timers", @@ -123,6 +122,7 @@ jobs: EXAMPLE_DIRECTORIES=$(cat << END [ "examples/query", + "examples/simple_user_accounts", "examples/update" ] END diff --git a/examples/simple_user_accounts/src/index.ts b/examples/simple_user_accounts/src/index.ts index edff5b15fb..9b532416aa 100644 --- a/examples/simple_user_accounts/src/index.ts +++ b/examples/simple_user_accounts/src/index.ts @@ -1,5 +1,4 @@ import { - candid, None, Opt, query, @@ -13,46 +12,36 @@ import { type Db = { users: { - [id: string]: User; + [id: string]: typeof User; }; }; -class User extends Record { - @candid(text) - id: text; - - @candid(text) - username: text; -} - -export default class extends Service { - db: Db = { - users: {} - }; +let db: Db = { + users: {} +}; - @query([text], Opt(User)) - getUserById(id: text): Opt { - const userOrUndefined = this.db.users[id]; +const User = Record({ + id: text, + username: text +}); +export default Service({ + getUserById: query([text], Opt(User), (id) => { + const userOrUndefined = db.users[id]; return userOrUndefined ? Some(userOrUndefined) : None; - } - - @query([], Vec(User)) - getAllUsers(): Vec { - return Object.values(this.db.users); - } - - @update([text], User) - createUser(username: text): User { - const id = Object.keys(this.db.users).length.toString(); - - const user = { + }), + getAllUsers: query([], Vec(User), () => { + return Object.values(db.users); + }), + createUser: update([text], User, (username) => { + const id = Object.keys(db.users).length.toString(); + const user: typeof User = { id, username }; - this.db.users[id] = user; + db.users[id] = user; return user; - } -} + }) +}); diff --git a/src/compiler/generate_candid_and_canister_methods.ts b/src/compiler/generate_candid_and_canister_methods.ts index 5644417480..9be7eb072a 100644 --- a/src/compiler/generate_candid_and_canister_methods.ts +++ b/src/compiler/generate_candid_and_canister_methods.ts @@ -8,6 +8,17 @@ export function generateCandidAndCanisterMethods(mainJs: string): { const sandbox = { globalThis: {}, + crypto: { + getRandomValues: () => { + let array = new Uint8Array(32); + + for (let i = 0; i < array.length; i++) { + array[i] = Math.floor(Math.random() * 256); + } + + return array; + } + }, exports: {}, console, TextDecoder, diff --git a/src/lib_functional/candid/index.ts b/src/lib_functional/candid/index.ts index 175670ee6a..392378e3c8 100644 --- a/src/lib_functional/candid/index.ts +++ b/src/lib_functional/candid/index.ts @@ -1,8 +1,13 @@ export * from './reference'; import { IDL } from '@dfinity/candid'; +import { AzleVec, AzleOpt } from '../../lib_new'; export type TypeMapping = T extends IDL.TextClass ? string : T extends never[] ? void + : T extends AzleVec + ? TypeMapping[] + : T extends AzleOpt + ? [TypeMapping] | [] : T; diff --git a/src/lib_functional/candid/reference/index.ts b/src/lib_functional/candid/reference/index.ts index f78beabc33..a787610408 100644 --- a/src/lib_functional/candid/reference/index.ts +++ b/src/lib_functional/candid/reference/index.ts @@ -1 +1,2 @@ +export * from './record'; export * from './service'; diff --git a/src/lib_functional/candid/reference/record.ts b/src/lib_functional/candid/reference/record.ts index e69de29bb2..e6732dd705 100644 --- a/src/lib_functional/candid/reference/record.ts +++ b/src/lib_functional/candid/reference/record.ts @@ -0,0 +1,28 @@ +import { TypeMapping } from '..'; +import { IDL } from '@dfinity/candid'; +import { processMap } from '../../../lib_new/utils'; +import { v4 } from 'uuid'; + +export function Record(obj: T): { + [K in keyof T]: TypeMapping; +} { + const name = v4(); + + return { + getIDL(parents: any) { + const idl = IDL.Rec(); + idl.fill( + IDL.Record( + processMap(obj as any, [ + ...parents, + { + idl: idl, + name + } + ]) + ) + ); + return idl; + } + } as any; +} diff --git a/src/lib_functional/candid/reference/service.ts b/src/lib_functional/candid/reference/service.ts index bec3beec67..6133270370 100644 --- a/src/lib_functional/candid/reference/service.ts +++ b/src/lib_functional/candid/reference/service.ts @@ -16,6 +16,13 @@ export function Service(serviceOptions: ServiceOptions): CanisterMethods { }; }, {}); + const candidTypes = Object.values(serviceOptions).reduce( + (acc: string[], canisterMethodInfo) => { + return [...acc, ...canisterMethodInfo.candidTypes]; + }, + [] + ); + const queries = Object.entries(serviceOptions) .filter((entry) => { const key = entry[0]; @@ -51,7 +58,9 @@ export function Service(serviceOptions: ServiceOptions): CanisterMethods { // TODO loop through each key and simply grab the candid off // TODO grab the init/post_upgrade candid as well return { - candid: `service: () -> { + candid: `${ + candidTypes.length === 0 ? '' : candidTypes.join('\n') + '\n' + }service: () -> { ${Object.entries(serviceOptions) .map((entry) => { return `${entry[0]}: ${entry[1].candid}`; diff --git a/src/lib_functional/canister_methods/index.ts b/src/lib_functional/canister_methods/index.ts index cbd2980533..7489bf9517 100644 --- a/src/lib_functional/canister_methods/index.ts +++ b/src/lib_functional/canister_methods/index.ts @@ -9,6 +9,7 @@ export type CanisterMethodInfo = { type: 'query' | 'update'; callback: (...args: any) => any; candid: string; + candidTypes: string[]; }; // TODO this doesn't produce a TS error when the user returns a non-void value in a void function diff --git a/src/lib_functional/canister_methods/query.ts b/src/lib_functional/canister_methods/query.ts index 46afdc4a31..1615b7ad73 100644 --- a/src/lib_functional/canister_methods/query.ts +++ b/src/lib_functional/canister_methods/query.ts @@ -1,6 +1,7 @@ import { handleRecursiveParams, - handleRecursiveReturn + handleRecursiveReturn, + newTypesToStingArr } from '../../lib_new/method_decorators'; import { Callback, CanisterMethodInfo, executeMethod } from '.'; @@ -20,6 +21,7 @@ export function query( callback: (...args) => { executeMethod(paramCandid, returnCandid, args, callback); }, - candid: `(${paramCandid[1].join(', ')}) -> (${returnCandid[1]}) query;` + candid: `(${paramCandid[1].join(', ')}) -> (${returnCandid[1]}) query;`, + candidTypes: newTypesToStingArr(returnCandid[2]) }; } diff --git a/src/lib_functional/canister_methods/update.ts b/src/lib_functional/canister_methods/update.ts index a3c0af5b5b..60ad69c9ca 100644 --- a/src/lib_functional/canister_methods/update.ts +++ b/src/lib_functional/canister_methods/update.ts @@ -1,6 +1,7 @@ import { handleRecursiveParams, - handleRecursiveReturn + handleRecursiveReturn, + newTypesToStingArr } from '../../lib_new/method_decorators'; import { Callback, CanisterMethodInfo, executeMethod } from '.'; @@ -20,6 +21,7 @@ export function update( callback: (...args) => { executeMethod(paramCandid, returnCandid, args, callback); }, - candid: `(${paramCandid[1].join(', ')}) -> (${returnCandid[1]});` + candid: `(${paramCandid[1].join(', ')}) -> (${returnCandid[1]});`, + candidTypes: newTypesToStingArr(returnCandid[2]) }; } diff --git a/src/lib_new/method_decorators.ts b/src/lib_new/method_decorators.ts index 05832e4f12..fa6ef2be1c 100644 --- a/src/lib_new/method_decorators.ts +++ b/src/lib_new/method_decorators.ts @@ -168,9 +168,9 @@ export function update( }; } -function newTypesToStingArr(newTypes: CandidTypesDefs): string[] { +export function newTypesToStingArr(newTypes: CandidTypesDefs): string[] { return Object.entries(newTypes).map( - ([name, candid]) => `type ${name} = ${candid}` + ([name, candid]) => `type ${name} = ${candid};` ); } diff --git a/src/lib_new/primitives.ts b/src/lib_new/primitives.ts index d259007edd..b7cf26497c 100644 --- a/src/lib_new/primitives.ts +++ b/src/lib_new/primitives.ts @@ -63,12 +63,12 @@ export function Some(value: T): [T] { export const None: [] = []; // TODO what happens if we pass something to Opt() that can't be converted to CandidClass? -export function Opt(t: IDL.Type | any): AzleOpt { +export function Opt(t: T): AzleOpt { // return IDL.Opt(toCandidClass(t)); return new AzleOpt(t); } -export class AzleOpt { +export class AzleOpt { constructor(t: any) { this._azleType = t; } @@ -78,7 +78,7 @@ export class AzleOpt { } } -export class AzleVec { +export class AzleVec { constructor(t: any) { this._azleType = t; } @@ -101,7 +101,7 @@ export class AzleTuple { } } -export function Vec(t: IDL.Type | any): AzleVec { +export function Vec(t: T): AzleVec { // return IDL.Vec(toCandidClass(t)); return new AzleVec(t); }