Skip to main content

canic_core/dto/
blob_storage.rs

1use crate::dto::prelude::*;
2
3#[cfg(feature = "blob-storage-billing")]
4pub use crate::domain::blob_storage::{
5    BlobStorageBillingWarning, BlobStorageFundingStatus, BlobStorageGatewayPrincipalSyncAction,
6    BlobStoragePaymentModelStatus, BlobStorageReadinessBlocker,
7};
8#[cfg(feature = "blob-storage-billing")]
9use candid::Int;
10
11///
12/// CreateCertificateResult
13///
14/// Passive DTO returned by the blob-storage create-certificate endpoint.
15///
16
17#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
18pub struct CreateCertificateResult {
19    pub method: String,
20    pub blob_hash: String,
21}
22
23///
24/// BlobStorageLocalCounters
25///
26/// Passive DTO for host-owned blob-storage status wrappers.
27///
28
29#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
30pub struct BlobStorageLocalCounters {
31    pub stored_blobs: u64,
32    pub pending_deletions: u64,
33    pub gateway_principals: u64,
34}
35
36impl BlobStorageLocalCounters {
37    #[must_use]
38    pub const fn new(stored_blobs: u64, pending_deletions: u64, gateway_principals: u64) -> Self {
39        Self {
40            stored_blobs,
41            pending_deletions,
42            gateway_principals,
43        }
44    }
45}
46
47///
48/// BlobStorageCashierDebtTarget
49///
50/// Passive DTO for the Cashier account balance debt-target variant.
51///
52
53#[cfg(feature = "blob-storage-billing")]
54#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
55pub enum BlobStorageCashierDebtTarget {
56    Prepaid,
57    Ledger,
58}
59
60///
61/// BlobStorageCashierAccountCycleBalances
62///
63/// Passive DTO for Cashier cycle-balance records.
64///
65
66#[cfg(feature = "blob-storage-billing")]
67#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
68pub struct BlobStorageCashierAccountCycleBalances {
69    pub total: Int,
70    pub cycles_prepaid: Int,
71    pub cycles_promo: Int,
72    pub debt_target: BlobStorageCashierDebtTarget,
73    pub cycles_ledger: Int,
74}
75
76///
77/// BlobStorageCashierAccountBalanceGetRequest
78///
79/// Passive DTO for `account_balance_get_v1` requests.
80///
81
82#[cfg(feature = "blob-storage-billing")]
83#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
84pub struct BlobStorageCashierAccountBalanceGetRequest {
85    pub account: Principal,
86}
87
88///
89/// BlobStorageCashierAccountBalanceGetOk
90///
91/// Passive DTO for successful `account_balance_get_v1` responses.
92///
93
94#[cfg(feature = "blob-storage-billing")]
95#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
96pub struct BlobStorageCashierAccountBalanceGetOk {
97    pub account_cycle_balances: BlobStorageCashierAccountCycleBalances,
98    pub account: Principal,
99}
100
101///
102/// BlobStorageCashierAccountBalanceGetError
103///
104/// Passive DTO for Cashier `account_balance_get_v1` error variants.
105///
106
107#[cfg(feature = "blob-storage-billing")]
108#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
109pub enum BlobStorageCashierAccountBalanceGetError {
110    AccountNotFound,
111    InternalError(String),
112}
113
114///
115/// BlobStorageCashierAccountBalanceGetResult
116///
117/// Passive DTO for Cashier `account_balance_get_v1` results.
118///
119
120#[cfg(feature = "blob-storage-billing")]
121#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
122pub enum BlobStorageCashierAccountBalanceGetResult {
123    Ok(BlobStorageCashierAccountBalanceGetOk),
124    Err(BlobStorageCashierAccountBalanceGetError),
125}
126
127///
128/// BlobStorageCashierAccountTopUpRequest
129///
130/// Passive DTO for `account_top_up_v1` request records.
131///
132
133#[cfg(feature = "blob-storage-billing")]
134#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
135pub struct BlobStorageCashierAccountTopUpRequest {
136    pub target_balance: Option<Nat>,
137    pub account: Option<Principal>,
138}
139
140///
141/// BlobStorageCashierAccountTopUpOk
142///
143/// Passive DTO for successful `account_top_up_v1` responses.
144///
145
146#[cfg(feature = "blob-storage-billing")]
147#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
148pub struct BlobStorageCashierAccountTopUpOk {
149    pub balance: BlobStorageCashierAccountCycleBalances,
150    pub message: String,
151}
152
153///
154/// BlobStorageCashierAccountTopUpError
155///
156/// Passive DTO for Cashier `account_top_up_v1` error variants.
157///
158
159#[cfg(feature = "blob-storage-billing")]
160#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
161pub enum BlobStorageCashierAccountTopUpError {
162    NotAuthorized(Principal),
163    AccountBalanceOverflow,
164    InternalError(String),
165    TopUpWithoutCycles,
166}
167
168///
169/// BlobStorageCashierAccountTopUpResult
170///
171/// Passive DTO for Cashier `account_top_up_v1` results.
172///
173
174#[cfg(feature = "blob-storage-billing")]
175#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
176pub enum BlobStorageCashierAccountTopUpResult {
177    Ok(BlobStorageCashierAccountTopUpOk),
178    Err(BlobStorageCashierAccountTopUpError),
179}
180
181///
182/// BlobStorageBillingConfig
183///
184/// Passive DTO for internal blob-storage billing configuration.
185///
186
187#[cfg(feature = "blob-storage-billing")]
188#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
189pub struct BlobStorageBillingConfig {
190    pub cashier_canister_id: Principal,
191    pub project_cycles_reserve: Nat,
192    pub min_upload_balance: Nat,
193    pub target_upload_balance: Nat,
194    pub gateway_principal_limit: u64,
195}
196
197///
198/// BlobProjectCyclesTopUpReport
199///
200/// Passive DTO returned by `_immutableObjectStorageFundFromProjectCycles`.
201///
202
203#[cfg(feature = "blob-storage-billing")]
204#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
205pub struct BlobProjectCyclesTopUpReport {
206    pub requested_cycles: Nat,
207    pub attached_cycles: Nat,
208    pub project_cycles_before: Nat,
209    pub project_cycles_after: Nat,
210    pub reserve_cycles: Nat,
211    pub cashier_total_after: Nat,
212    pub skipped_reason: Option<String>,
213}
214
215///
216/// BlobStorageStatusRequest
217///
218/// Passive DTO for backend blob-storage billing status requests.
219///
220
221#[cfg(feature = "blob-storage-billing")]
222#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
223pub struct BlobStorageStatusRequest {
224    pub sync_gateway_principals: bool,
225}
226
227///
228/// BlobStorageStatusResponse
229///
230/// Passive DTO returned by `get_blob_storage_status`.
231///
232
233#[cfg(feature = "blob-storage-billing")]
234#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
235pub struct BlobStorageStatusResponse {
236    pub payment_model: BlobStoragePaymentModelStatus,
237    pub cashier_canister_id: Option<Principal>,
238    pub payment_account: Option<Principal>,
239    pub cashier_balance: Option<Nat>,
240    pub min_upload_balance: Option<Nat>,
241    pub target_upload_balance: Option<Nat>,
242    pub project_cycles_reserve: Option<Nat>,
243    pub project_cycles_available: Nat,
244    pub gateway_principal_count: u64,
245    pub last_gateway_principal_sync_at_ns: Option<u64>,
246    pub gateway_principal_sync_action: BlobStorageGatewayPrincipalSyncAction,
247    pub funding_status: BlobStorageFundingStatus,
248    pub ready: bool,
249    pub blockers: Vec<BlobStorageReadinessBlocker>,
250    pub warnings: Vec<BlobStorageBillingWarning>,
251}
252
253#[cfg(all(test, feature = "blob-storage-billing"))]
254mod tests {
255    use super::*;
256    use candid::{CandidType, Decode, Encode};
257    use serde::de::DeserializeOwned;
258    use std::fmt::Debug;
259
260    #[test]
261    fn billing_status_enums_roundtrip_candid_through_dto_path() {
262        assert_enum_candid_contract(BlobStoragePaymentModelStatus::ProjectAsPaymentAccount);
263        assert_enum_candid_contract(BlobStorageGatewayPrincipalSyncAction::SkippedReadOnlyStatus);
264        assert_enum_candid_contract(BlobStorageFundingStatus::FundingRequired {
265            requested_cycles: Nat::from(42_u64),
266        });
267        assert_enum_candid_contract(BlobStorageReadinessBlocker::InsufficientCashierBalance);
268        assert_enum_candid_contract(BlobStorageBillingWarning::SyncRequestedButStatusIsReadOnly);
269    }
270
271    fn assert_enum_candid_contract<T>(value: T)
272    where
273        T: CandidType + Clone + Debug + DeserializeOwned + Eq,
274    {
275        let bytes = Encode!(&value).expect("encode blob-storage status enum");
276        let decoded = Decode!(&bytes, T).expect("decode blob-storage status enum");
277
278        assert_eq!(decoded, value);
279    }
280}