Skip to content

Commit

Permalink
service example tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
lastmjs committed Sep 23, 2023
1 parent f6c32c2 commit 4852099
Show file tree
Hide file tree
Showing 13 changed files with 258 additions and 100 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
# "examples/robust_imports",
# "examples/run_time_errors",
# "examples/rust_type_conversions",
# "examples/service",
# "examples/stable_structures",
# "examples/tuple_types",

Expand Down Expand Up @@ -119,6 +118,7 @@ jobs:
"examples/principal",
"examples/query",
"examples/randomness",
"examples/service",
"examples/simple_erc20",
"examples/simple_user_accounts",
"examples/stable_memory",
Expand Down
69 changes: 28 additions & 41 deletions examples/service/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,25 @@
import {
candid,
ic,
Principal,
query,
Record,
Service,
text,
update,
Vec
} from 'azle';

import { ic, Principal, query, Record, Service, text, update, Vec } from 'azle';
import SomeService from './some_service';

class Wrapper extends Record {
@candid(SomeService)
someService: SomeService;
}
const Wrapper = Record({
someService: SomeService
});

export default class extends Service {
@query([SomeService], SomeService)
serviceParam(someService: SomeService): SomeService {
export default Service({
serviceParam: query([SomeService], SomeService, (someService) => {
return someService;
}

@query([], SomeService)
serviceReturnType(): SomeService {
return new SomeService(
}),
serviceReturnType: query([], SomeService, () => {
return SomeService(
Principal.fromText(
process.env.SOME_SERVICE_PRINCIPAL ??
ic.trap('process.env.SOME_SERVICE_PRINCIPAL is undefined')
)
);
}

@update([], Wrapper)
serviceNestedReturnType(): Wrapper {
}),
serviceNestedReturnType: update([], Wrapper, () => {
return {
someService: new SomeService(
someService: SomeService(
Principal.fromText(
process.env.SOME_SERVICE_PRINCIPAL ??
ic.trap(
Expand All @@ -45,15 +28,19 @@ export default class extends Service {
)
)
};
}

@update([Vec(SomeService)], Vec(SomeService))
serviceList(someServices: Vec<SomeService>): Vec<SomeService> {
return someServices;
}

@update([SomeService], text)
async serviceCrossCanisterCall(someService: SomeService): Promise<text> {
return await ic.call(someService.update1);
}
}
}),
serviceList: update(
[Vec(SomeService)],
Vec(SomeService),
(someServices) => {
return someServices;
}
),
serviceCrossCanisterCall: update(
[SomeService],
text,
async (someService) => {
return await ic.call(someService.update1);
}
)
});
15 changes: 6 additions & 9 deletions examples/service/src/some_service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { bool, query, Service, text, update } from 'azle';

export default class extends Service {
@query([], bool)
query1(): bool {
export default Service({
query1: query([], bool, () => {
return true;
}

@update([], text)
update1(): text {
}),
update1: update([], text, () => {
return 'SomeService update1';
}
}
})
});
Original file line number Diff line number Diff line change
Expand Up @@ -280,15 +280,16 @@ fn main() -> Result<(), String> {
let exports = global.get_property("exports").unwrap();
let canister_methods = exports.get_property("canisterMethods").unwrap();
let callbacks = canister_methods.get_property("callbacks").unwrap();
let method = callbacks.get_property(function_name).unwrap();
let methodCallbacks = callbacks.get_property(function_name).unwrap();
let canisterMethodCallback = methodCallbacks.get_property("canisterCallback").unwrap();

let candid_args = if pass_arg_data { ic_cdk::api::call::arg_data_raw() } else { vec![] };

let candid_args_js_value: JSValue = candid_args.into();
let candid_args_js_value_ref = to_qjs_value(&context, &candid_args_js_value).unwrap();

// TODO I am not sure what the first parameter to call is supposed to be
method.call(&method, &[candid_args_js_value_ref]).unwrap();
canisterMethodCallback.call(&canisterMethodCallback, &[candid_args_js_value_ref]).unwrap();

context.execute_pending().unwrap();
});
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/typescript_to_rust/generate_rust_canister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export function generateRustCanister(
ts_root: join(process.cwd(), canisterConfig.ts),
alias_tables: aliasTables,
alias_lists: aliasLists,
canister_methods: canisterMethods
// TODO The spread is because canisterMethods is a function with properties
canister_methods: {
...canisterMethods
} // TODO we should probably just grab the props out that we need
};

const compilerInfoPath = join(
Expand Down
190 changes: 160 additions & 30 deletions src/lib_functional/candid/reference/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Principal, TypeMapping } from '../../';
import { serviceCall } from '../../../lib_new';
import { IDL, ServiceFunctionInfo, serviceCall } from '../../../lib_new';
import {
Parent,
toParamIDLTypes,
toReturnIDLType
} from '../../../lib_new/utils';
import { CanisterMethodInfo } from '../../canister_methods';

type ServiceOptions = {
Expand All @@ -17,32 +22,45 @@ type ServiceReturn<T extends ServiceOptions> = {
: never;
};

type CallableObject<T extends ServiceOptions> = {
(principal: Principal): CallableObject<T>;
} & ServiceReturn<T>;

export function Service<T extends ServiceOptions>(
serviceOptions: T,
principal?: Principal
): ServiceReturn<T> {
serviceOptions: T
): CallableObject<T> {
const callbacks = Object.entries(serviceOptions).reduce((acc, entry) => {
const key = entry[0];
const value = entry[1];

if (value.callback === undefined) {
return {
...acc,
[key]: (...args: any[]) => {
// if (principal === undefined) {
// return {
// ...acc,
// [key]: (...args: any[]) => {
// return serviceCall(
// principal as any,
// key,
// value.paramsIdls,
// value.returnIdl
// )(...args);
// }
// };
// } else {
return {
...acc,
[key]: {
canisterCallback: value.callback,
crossCanisterCallback: (...args: any[]) => {
return serviceCall(
principal as any,
this.principal as any,
key,
value.paramsIdls,
value.returnIdl
)(...args);
}
};
} else {
return {
...acc,
[key]: value.callback
};
}
}
};
// }
}, {});

const candidTypes = Object.values(serviceOptions).reduce(
Expand All @@ -57,7 +75,7 @@ export function Service<T extends ServiceOptions>(
const key = entry[0];
const value = entry[1];

return value.type === 'query';
return value.mode === 'query';
})
.map((entry) => {
const key = entry[0];
Expand All @@ -73,7 +91,7 @@ export function Service<T extends ServiceOptions>(
const key = entry[0];
const value = entry[1];

return value.type === 'update';
return value.mode === 'update';
})
.map((entry) => {
const key = entry[0];
Expand All @@ -84,22 +102,134 @@ export function Service<T extends ServiceOptions>(
};
});

// TODO loop through each key and simply grab the candid off
// TODO grab the init/post_upgrade candid as well
return {
candid: `${
candidTypes.length === 0 ? '' : candidTypes.join('\n') + '\n'
}service: () -> {
let returnFunction = (principal: Principal) => {
const callbacks = Object.entries(serviceOptions).reduce(
(acc, entry) => {
const key = entry[0];
const value = entry[1];

// if (principal === undefined) {
// return {
// ...acc,
// [key]: (...args: any[]) => {
// return serviceCall(
// principal as any,
// key,
// value.paramsIdls,
// value.returnIdl
// )(...args);
// }
// };
// } else {
return {
...acc,
[key]: {
canisterCallback: value.callback,
crossCanisterCallback: (...args: any[]) => {
return serviceCall(
principal as any,
key,
value.paramsIdls,
value.returnIdl
)(...args);
}
}
};
// }
},
{}
);

return {
...callbacks,
principal
};
};

returnFunction.candid = `${
candidTypes.length === 0 ? '' : candidTypes.join('\n') + '\n'
}service: () -> {
${Object.entries(serviceOptions)
.map((entry) => {
return `${entry[0]}: ${entry[1].candid}`;
})
.join('\n ')}
}
`,
queries,
updates,
callbacks,
...callbacks // TODO then we can't use any names that could collide in this object
} as any;
`;

returnFunction.queries = queries;
returnFunction.updates = updates;
returnFunction.callbacks = callbacks;
returnFunction.getIDL = (parents: Parent[]): IDL.ServiceClass => {
const serviceFunctionInfo: ServiceFunctionInfo = serviceOptions;

const record = Object.entries(serviceFunctionInfo).reduce(
(accumulator, [methodName, functionInfo]) => {
const paramRealIdls = toParamIDLTypes(functionInfo.paramsIdls);
const returnRealIdl = toReturnIDLType(functionInfo.returnIdl);

const annotations =
functionInfo.mode === 'update' ? [] : ['query'];

return {
...accumulator,
[methodName]: IDL.Func(
paramRealIdls,
returnRealIdl,
annotations
)
};
},
{} as Record<string, IDL.FuncClass>
);

return IDL.Service(record);
};

return returnFunction;

// TODO loop through each key and simply grab the candid off
// TODO grab the init/post_upgrade candid as well
// return {
// candid: `${
// candidTypes.length === 0 ? '' : candidTypes.join('\n') + '\n'
// }service: () -> {
// ${Object.entries(serviceOptions)
// .map((entry) => {
// return `${entry[0]}: ${entry[1].candid}`;
// })
// .join('\n ')}
// }
// `,
// queries,
// updates,
// callbacks,
// principal,
// ...callbacks, // TODO then we can't use any names that could collide in this object
// getIDL(parents: Parent[]): IDL.ServiceClass {
// const serviceFunctionInfo: ServiceFunctionInfo = serviceOptions;

// const record = Object.entries(serviceFunctionInfo).reduce(
// (accumulator, [methodName, functionInfo]) => {
// const paramRealIdls = toParamIDLTypes(functionInfo.paramsIdls);
// const returnRealIdl = toReturnIDLType(functionInfo.returnIdl);

// const annotations =
// functionInfo.mode === 'update' ? [] : ['query'];

// return {
// ...accumulator,
// [methodName]: IDL.Func(
// paramRealIdls,
// returnRealIdl,
// annotations
// )
// };
// },
// {} as Record<string, IDL.FuncClass>
// );

// return IDL.Service(record);
// }
// } as any;
}
Loading

0 comments on commit 4852099

Please sign in to comment.