Skip to main content

aptos_client_icp/client/
mod.rs

1use std::str::FromStr;
2
3use ic_cdk::api::management_canister::http_request::{
4    http_request, CanisterHttpRequestArgument, HttpHeader, HttpMethod, TransformContext,
5};
6use serde::{de::DeserializeOwned, Serialize};
7use url::Url;
8
9use crate::http::{Request, Response};
10
11#[derive(Clone)]
12pub struct CallOptions {
13    pub max_response_bytes: Option<u64>,
14    pub transform: Option<TransformContext>,
15}
16
17impl Default for CallOptions {
18    fn default() -> Self {
19        Self {
20            max_response_bytes: None,
21            transform: None,
22        }
23    }
24}
25
26// Calcurate cycles for http_request
27// NOTE:
28//   v0.11: https://github.com/dfinity/cdk-rs/blob/0b14facb80e161de79264c8f88b1a0c8e18ffcb6/examples/management_canister/src/caller/lib.rs#L7-L19
29//   v0.8: https://github.com/dfinity/cdk-rs/blob/a8454cb37420c200c7b224befd6f68326a01442e/src/ic-cdk/src/api/management_canister/http_request.rs#L290-L299
30fn http_request_required_cycles(arg: &CanisterHttpRequestArgument) -> u128 {
31    let max_response_bytes = match arg.max_response_bytes {
32        Some(ref n) => *n as u128,
33        None => 2 * 1024 * 1024u128, // default 2MiB
34    };
35    let arg_raw = candid::utils::encode_args((arg,)).expect("Failed to encode arguments.");
36    // The fee is for a 13-node subnet to demonstrate a typical usage.
37    (3_000_000u128
38        + 60_000u128 * 13
39        + (arg_raw.len() as u128 + "http_request".len() as u128) * 400
40        + max_response_bytes * 800)
41        * 13
42}
43
44#[derive(Clone)]
45pub struct HttpProvider {}
46
47impl HttpProvider {
48    pub fn new() -> Self {
49        Self {}
50    }
51}
52
53impl HttpProvider {
54    pub async fn send(&self, request: Request, opts: CallOptions) -> Response {
55        let body = request.body().unwrap_or_default();
56        let method = match request.method() {
57            crate::http::HttpMethod::GET => HttpMethod::GET,
58            crate::http::HttpMethod::POST => HttpMethod::POST,
59            crate::http::HttpMethod::HEAD => HttpMethod::HEAD,
60            _ => HttpMethod::GET,
61        };
62        let mut url = request.url.clone();
63        if !request.query().is_empty() {
64            let mut queries = request
65                .query()
66                .iter()
67                .map(|(k, v)| (k.to_string(), v.to_string()))
68                .collect::<Vec<(String, String)>>();
69            let first = queries.remove(0);
70            url = format!("{}?{}={}", url, first.0, first.1);
71            queries.iter().for_each(|(k, v)| {
72                url = format!("&{}?{}={}", url, k, v);
73            });
74        }
75
76        let headers = request
77            .headers()
78            .iter()
79            .map(|(k, v)| HttpHeader {
80                name: k.as_str().to_string(),
81                value: v.to_string(),
82            })
83            .collect();
84
85        let args: CanisterHttpRequestArgument = CanisterHttpRequestArgument {
86            body: Some(body.to_vec()),
87            url: url,
88            headers,
89            max_response_bytes: opts.max_response_bytes,
90            method,
91            transform: opts.transform, //TODO
92        };
93        let cycles = http_request_required_cycles(&args);
94        match http_request(args, cycles).await {
95            Ok(response) => {
96                let status: u16 = TryFrom::try_from(response.0.status.0).unwrap();
97                Response::new(
98                    status,
99                    String::from_utf8_lossy(response.0.body.as_slice()).to_string(),
100                )
101            }
102            Err(e) => Response::new(500, e.1),
103        }
104    }
105}
106
107#[derive(Clone)]
108pub enum Provider {
109    Http(HttpProvider),
110}
111
112impl Provider {
113    pub fn new() -> Self {
114        Self::Http(HttpProvider::new())
115    }
116}