canic_core/ops/request/
request.rs1use crate::{
2 Error,
3 cdk::call::Call,
4 ids::CanisterRole,
5 log::Topic,
6 ops::{
7 model::memory::{EnvOps, topology::SubnetCanisterChildrenOps},
8 prelude::*,
9 request::{
10 CreateCanisterResponse, CyclesResponse, RequestOpsError, Response,
11 UpgradeCanisterResponse,
12 },
13 },
14};
15use candid::encode_one;
16
17#[derive(CandidType, Clone, Debug, Deserialize)]
23pub enum Request {
24 CreateCanister(CreateCanisterRequest),
25 UpgradeCanister(UpgradeCanisterRequest),
26 Cycles(CyclesRequest),
27}
28
29#[derive(CandidType, Clone, Debug, Deserialize)]
35pub struct CreateCanisterRequest {
36 pub canister_role: CanisterRole,
37 pub parent: CreateCanisterParent,
38 pub extra_arg: Option<Vec<u8>>,
39}
40
41#[derive(CandidType, Clone, Debug, Deserialize)]
47pub enum CreateCanisterParent {
48 Root,
49 ThisCanister,
51 Parent,
53 Canister(Principal),
54 Directory(CanisterRole),
55}
56
57#[derive(CandidType, Clone, Debug, Deserialize)]
63pub struct UpgradeCanisterRequest {
64 pub canister_pid: Principal,
65 pub canister_type: CanisterRole,
66}
67
68#[derive(CandidType, Clone, Debug, Deserialize)]
74pub struct CyclesRequest {
75 pub cycles: u128,
76}
77
78async fn request(request: Request) -> Result<Response, Error> {
80 let root_pid = EnvOps::try_get_root_pid().map_err(|_| RequestOpsError::RootNotFound)?;
81
82 let call_response = Call::unbounded_wait(root_pid, "canic_response")
83 .with_arg(&request)
84 .await?;
85
86 call_response.candid::<Result<Response, Error>>()?
87}
88
89pub async fn create_canister_request<A>(
91 canister_role: &CanisterRole,
92 parent: CreateCanisterParent,
93 extra: Option<A>,
94) -> Result<CreateCanisterResponse, Error>
95where
96 A: CandidType + Send + Sync,
97{
98 let encoded = extra.map(|v| encode_one(v)).transpose()?;
99 let role = canister_role.clone();
100 let parent_desc = format!("{:?}", &parent);
101 let caller_ty =
102 EnvOps::try_get_canister_type().map_or_else(|_| "unknown".to_string(), |ty| ty.to_string());
103
104 let q = Request::CreateCanister(CreateCanisterRequest {
106 canister_role: canister_role.clone(),
107 parent,
108 extra_arg: encoded,
109 });
110
111 match request(q).await {
112 Ok(Response::CreateCanister(res)) => Ok(res),
113 Ok(_) => {
114 log!(
115 Topic::CanisterLifecycle,
116 Warn,
117 "create_canister_request: invalid response type (caller={caller_ty}, role={role}, parent={parent_desc})"
118 );
119
120 Err(RequestOpsError::InvalidResponseType.into())
121 }
122 Err(err) => {
123 log!(
124 Topic::CanisterLifecycle,
125 Warn,
126 "create_canister_request failed (caller={caller_ty}, role={role}, parent={parent_desc}): {err}"
127 );
128
129 Err(err)
130 }
131 }
132}
133
134pub async fn upgrade_canister_request(
136 canister_pid: Principal,
137) -> Result<UpgradeCanisterResponse, Error> {
138 let canister = SubnetCanisterChildrenOps::find_by_pid(&canister_pid)
140 .ok_or(RequestOpsError::ChildNotFound(canister_pid))?;
141
142 let q = Request::UpgradeCanister(UpgradeCanisterRequest {
144 canister_pid: canister.pid,
145 canister_type: canister.ty,
146 });
147
148 match request(q).await? {
149 Response::UpgradeCanister(res) => Ok(res),
150 _ => Err(RequestOpsError::InvalidResponseType.into()),
151 }
152}
153
154pub async fn cycles_request(cycles: u128) -> Result<CyclesResponse, Error> {
156 OpsError::deny_root()?;
157
158 let q = Request::Cycles(CyclesRequest { cycles });
159
160 match request(q).await? {
161 Response::Cycles(res) => Ok(res),
162 _ => Err(RequestOpsError::InvalidResponseType.into()),
163 }
164}