Skip to main content

aliyun_oss/operations/
object_restore.rs

1//! Archive object restore operations.
2
3use std::sync::Arc;
4
5use serde::Serialize;
6
7use crate::client::{BucketOperations, OSSClientInner};
8use crate::error::{ErrorContext, OssError, OssErrorKind, Result};
9use crate::http::client::HttpRequest;
10use crate::types::bucket::BucketName;
11use crate::types::object::ObjectKey;
12use crate::util::uri::oss_endpoint_url;
13
14#[derive(Debug, Clone, Serialize)]
15#[serde(rename = "RestoreRequest")]
16struct RestoreRequest {
17    #[serde(rename = "Days")]
18    days: i32,
19    #[serde(rename = "Tier", skip_serializing_if = "Option::is_none")]
20    tier: Option<String>,
21}
22
23pub struct RestoreObjectBuilder {
24    client: Arc<OSSClientInner>,
25    bucket: BucketName,
26    key: ObjectKey,
27    days: i32,
28    tier: Option<String>,
29}
30
31impl RestoreObjectBuilder {
32    pub(crate) fn new(
33        client: Arc<OSSClientInner>,
34        bucket: BucketName,
35        key: ObjectKey,
36        days: i32,
37    ) -> Self {
38        Self {
39            client,
40            bucket,
41            key,
42            days,
43            tier: None,
44        }
45    }
46
47    pub fn tier(mut self, t: impl Into<String>) -> Self {
48        self.tier = Some(t.into());
49        self
50    }
51
52    pub async fn send(self) -> Result<RestoreObjectOutput> {
53        let endpoint = self.client.endpoint.clone();
54        let uri = oss_endpoint_url(
55            &endpoint,
56            Some(self.bucket.as_str()),
57            Some(self.key.as_str()),
58        );
59        let full_uri = format!("{}?restore", uri);
60        let query_params: Vec<(String, String)> = vec![("restore".into(), String::new())];
61
62        let config = RestoreRequest {
63            days: self.days,
64            tier: self.tier,
65        };
66        let body_xml = crate::util::xml::to_xml(&config)?;
67
68        let request = HttpRequest::builder()
69            .method(http::Method::POST)
70            .uri(&full_uri)
71            .body(bytes::Bytes::from(body_xml))
72            .build();
73
74        let response = self
75            .client
76            .send_signed(request, Some(&self.bucket), query_params)
77            .await
78            .map_err(|e| OssError {
79                kind: OssErrorKind::TransportError,
80                context: Box::new(ErrorContext {
81                    operation: Some("RestoreObject".into()),
82                    bucket: Some(self.bucket.to_string()),
83                    object_key: Some(self.key.to_string()),
84                    endpoint: Some(endpoint),
85                    ..Default::default()
86                }),
87                source: Some(Box::new(e)),
88            })?;
89
90        if response.status().is_success() {
91            Ok(RestoreObjectOutput {
92                request_id: response
93                    .headers
94                    .get("x-oss-request-id")
95                    .and_then(|v| v.to_str().ok())
96                    .unwrap_or("")
97                    .to_string(),
98            })
99        } else {
100            Err(OssError {
101                kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
102                    status_code: response.status().as_u16(),
103                    code: String::new(),
104                    message: String::new(),
105                    request_id: String::new(),
106                    host_id: String::new(),
107                    resource: Some(self.key.to_string()),
108                    string_to_sign: None,
109                })),
110                context: Box::new(ErrorContext {
111                    operation: Some("RestoreObject".into()),
112                    bucket: Some(self.bucket.to_string()),
113                    object_key: Some(self.key.to_string()),
114                    ..Default::default()
115                }),
116                source: None,
117            })
118        }
119    }
120}
121
122#[derive(Debug, Clone)]
123pub struct RestoreObjectOutput {
124    pub request_id: String,
125}
126
127impl BucketOperations {
128    pub fn restore_object(
129        &self,
130        key: impl Into<String>,
131        days: i32,
132    ) -> Result<RestoreObjectBuilder> {
133        Ok(RestoreObjectBuilder::new(
134            self.client_inner().clone(),
135            self.bucket_name().clone(),
136            ObjectKey::new(key.into())?,
137            days,
138        ))
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145    use crate::client::OSSClientInner;
146    use crate::config::credentials::Credentials;
147    use crate::http::client::{HttpClient, HttpRequest, HttpResponse};
148    use crate::types::region::Region;
149    use std::sync::Mutex;
150
151    struct Rec {
152        requests: Arc<Mutex<Vec<HttpRequest>>>,
153    }
154    #[async_trait::async_trait]
155    impl HttpClient for Rec {
156        async fn send(&self, request: HttpRequest) -> crate::error::Result<HttpResponse> {
157            self.requests.lock().unwrap().push(request);
158            let mut h = http::HeaderMap::new();
159            h.insert("x-oss-request-id", http::HeaderValue::from_static("rid"));
160            Ok(HttpResponse {
161                status: http::StatusCode::OK,
162                headers: h,
163                body: bytes::Bytes::new(),
164            })
165        }
166    }
167    fn c() -> (Arc<OSSClientInner>, Arc<Mutex<Vec<HttpRequest>>>) {
168        let r = Arc::new(Mutex::new(Vec::new()));
169        let h = Arc::new(Rec {
170            requests: r.clone(),
171        });
172        let cr = Arc::new(crate::config::credentials::StaticCredentialsProvider::new(
173            Credentials::builder()
174                .access_key_id("ak")
175                .access_key_secret("sk")
176                .build()
177                .unwrap(),
178        ));
179        (
180            Arc::new(OSSClientInner {
181                http: h,
182                credentials: cr,
183                signer: Arc::from(crate::signer::create_signer(crate::signer::SignVersion::V4)),
184                region: Region::CnHangzhou,
185                endpoint: "oss-cn-hangzhou.aliyuncs.com".into(),
186            }),
187            r,
188        )
189    }
190
191    #[test]
192    fn restore_xml_generation() {
193        let r = RestoreRequest {
194            days: 3,
195            tier: Some("Standard".into()),
196        };
197        let x = crate::util::xml::to_xml(&r).unwrap();
198        assert!(x.contains("<Days>3</Days>"));
199        assert!(x.contains("<Tier>Standard</Tier>"));
200    }
201
202    #[tokio::test]
203    async fn restore_sends_post() {
204        let (i, rq) = c();
205        RestoreObjectBuilder::new(
206            i,
207            BucketName::new("test-bucket").unwrap(),
208            ObjectKey::new("k").unwrap(),
209            3,
210        )
211        .send()
212        .await
213        .unwrap();
214        assert_eq!(rq.lock().unwrap()[0].method, http::Method::POST);
215    }
216}