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 = EnvOps::try_get_canister_type()
102 .map(|ty| ty.to_string())
103 .unwrap_or_else(|_| "unknown".to_string());
104
105 let q = Request::CreateCanister(CreateCanisterRequest {
107 canister_role: canister_role.clone(),
108 parent,
109 extra_arg: encoded,
110 });
111
112 match request(q).await {
113 Ok(Response::CreateCanister(res)) => Ok(res),
114 Ok(_) => {
115 log!(
116 Topic::CanisterLifecycle,
117 Warn,
118 "create_canister_request: invalid response type (caller={caller_ty}, role={role}, parent={parent_desc})"
119 );
120
121 Err(RequestOpsError::InvalidResponseType.into())
122 }
123 Err(err) => {
124 log!(
125 Topic::CanisterLifecycle,
126 Warn,
127 "create_canister_request failed (caller={caller_ty}, role={role}, parent={parent_desc}): {err}"
128 );
129
130 Err(err)
131 }
132 }
133}
134
135pub async fn upgrade_canister_request(
137 canister_pid: Principal,
138) -> Result<UpgradeCanisterResponse, Error> {
139 let canister = SubnetCanisterChildrenOps::find_by_pid(&canister_pid)
141 .ok_or(RequestOpsError::ChildNotFound(canister_pid))?;
142
143 let q = Request::UpgradeCanister(UpgradeCanisterRequest {
145 canister_pid: canister.pid,
146 canister_type: canister.ty,
147 });
148
149 match request(q).await? {
150 Response::UpgradeCanister(res) => Ok(res),
151 _ => Err(RequestOpsError::InvalidResponseType.into()),
152 }
153}
154
155pub async fn cycles_request(cycles: u128) -> Result<CyclesResponse, Error> {
157 OpsError::deny_root()?;
158
159 let q = Request::Cycles(CyclesRequest { cycles });
160
161 match request(q).await? {
162 Response::Cycles(res) => Ok(res),
163 _ => Err(RequestOpsError::InvalidResponseType.into()),
164 }
165}