Skip to main content

aliyun_oss/operations/
bucket_redundancy.rs

1//! Redundancy transition operations.
2
3use crate::client::{BucketOperations, OSSClientInner};
4use crate::error::{ErrorContext, OssError, OssErrorKind, Result};
5use crate::http::client::HttpRequest;
6use crate::types::bucket::BucketName;
7use serde::{Deserialize, Serialize};
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Serialize)]
11#[serde(rename = "RedundancyTransitionConfiguration")]
12struct RedundancyTransitionConfig {
13    #[serde(rename = "TaskId")]
14    task_id: String,
15    #[serde(rename = "Status", skip_serializing_if = "Option::is_none")]
16    status: Option<String>,
17}
18
19#[derive(Debug, Clone, Deserialize)]
20#[serde(rename = "RedundancyTransitionConfiguration")]
21struct RedundancyTransitionConfigResp {
22    #[serde(rename = "TaskId")]
23    task_id: String,
24    #[serde(rename = "Status", default)]
25    status: String,
26}
27
28pub struct PutBucketRedundancyBuilder {
29    client: Arc<OSSClientInner>,
30    bucket: BucketName,
31    task_id: String,
32}
33impl PutBucketRedundancyBuilder {
34    pub(crate) fn new(
35        client: Arc<OSSClientInner>,
36        bucket: BucketName,
37        task_id: impl Into<String>,
38    ) -> Self {
39        Self {
40            client,
41            bucket,
42            task_id: task_id.into(),
43        }
44    }
45    pub async fn send(self) -> Result<PutBucketRedundancyOutput> {
46        let ep = self.client.endpoint.clone();
47        let uri = format!(
48            "https://{}.{}?redundancyTransition",
49            self.bucket.as_str(),
50            ep
51        );
52        let qp = vec![("redundancyTransition".into(), String::new())];
53        let cfg = RedundancyTransitionConfig {
54            task_id: self.task_id,
55            status: None,
56        };
57        let xml = crate::util::xml::to_xml(&cfg)?;
58        let req = HttpRequest::builder()
59            .method(http::Method::PUT)
60            .uri(&uri)
61            .body(bytes::Bytes::from(xml))
62            .build();
63        let r = self
64            .client
65            .send_signed(req, Some(&self.bucket), qp)
66            .await
67            .map_err(|e| OssError {
68                kind: OssErrorKind::TransportError,
69                context: Box::new(ErrorContext {
70                    operation: Some("PutBucketRedundancy".into()),
71                    bucket: Some(self.bucket.to_string()),
72                    endpoint: Some(ep),
73                    ..Default::default()
74                }),
75                source: Some(Box::new(e)),
76            })?;
77        if r.status().is_success() {
78            Ok(PutBucketRedundancyOutput {
79                request_id: r
80                    .headers
81                    .get("x-oss-request-id")
82                    .and_then(|v| v.to_str().ok())
83                    .unwrap_or("")
84                    .to_string(),
85            })
86        } else {
87            Err(OssError {
88                kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
89                    status_code: r.status().as_u16(),
90                    code: String::new(),
91                    message: String::new(),
92                    request_id: String::new(),
93                    host_id: String::new(),
94                    resource: Some(self.bucket.to_string()),
95                    string_to_sign: None,
96                })),
97                context: Box::new(ErrorContext {
98                    operation: Some("PutBucketRedundancy".into()),
99                    bucket: Some(self.bucket.to_string()),
100                    ..Default::default()
101                }),
102                source: None,
103            })
104        }
105    }
106}
107#[derive(Debug, Clone)]
108pub struct PutBucketRedundancyOutput {
109    pub request_id: String,
110}
111
112pub struct GetBucketRedundancyBuilder {
113    client: Arc<OSSClientInner>,
114    bucket: BucketName,
115}
116impl GetBucketRedundancyBuilder {
117    pub(crate) fn new(client: Arc<OSSClientInner>, bucket: BucketName) -> Self {
118        Self { client, bucket }
119    }
120    pub async fn send(self) -> Result<GetBucketRedundancyOutput> {
121        let ep = self.client.endpoint.clone();
122        let uri = format!(
123            "https://{}.{}?redundancyTransition",
124            self.bucket.as_str(),
125            ep
126        );
127        let qp = vec![("redundancyTransition".into(), String::new())];
128        let req = HttpRequest::builder()
129            .method(http::Method::GET)
130            .uri(&uri)
131            .build();
132        let r = self
133            .client
134            .send_signed(req, Some(&self.bucket), qp)
135            .await
136            .map_err(|e| OssError {
137                kind: OssErrorKind::TransportError,
138                context: Box::new(ErrorContext {
139                    operation: Some("GetBucketRedundancy".into()),
140                    bucket: Some(self.bucket.to_string()),
141                    endpoint: Some(ep),
142                    ..Default::default()
143                }),
144                source: Some(Box::new(e)),
145            })?;
146        if r.is_success() {
147            let c: RedundancyTransitionConfigResp =
148                crate::util::xml::from_xml(r.body_as_str().unwrap_or("")).map_err(|e| {
149                    OssError {
150                        kind: OssErrorKind::DeserializationError,
151                        context: Box::new(ErrorContext {
152                            operation: Some("parse XML".into()),
153                            bucket: Some(self.bucket.to_string()),
154                            ..Default::default()
155                        }),
156                        source: Some(Box::new(e)),
157                    }
158                })?;
159            Ok(GetBucketRedundancyOutput {
160                task_id: c.task_id,
161                status: c.status,
162            })
163        } else {
164            Err(OssError {
165                kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
166                    status_code: r.status().as_u16(),
167                    code: String::new(),
168                    message: String::new(),
169                    request_id: String::new(),
170                    host_id: String::new(),
171                    resource: Some(self.bucket.to_string()),
172                    string_to_sign: None,
173                })),
174                context: Box::new(ErrorContext {
175                    operation: Some("GetBucketRedundancy".into()),
176                    bucket: Some(self.bucket.to_string()),
177                    ..Default::default()
178                }),
179                source: None,
180            })
181        }
182    }
183}
184#[derive(Debug, Clone)]
185pub struct GetBucketRedundancyOutput {
186    pub task_id: String,
187    pub status: String,
188}
189
190pub struct DeleteBucketRedundancyBuilder {
191    client: Arc<OSSClientInner>,
192    bucket: BucketName,
193    task_id: String,
194}
195impl DeleteBucketRedundancyBuilder {
196    pub(crate) fn new(
197        client: Arc<OSSClientInner>,
198        bucket: BucketName,
199        task_id: impl Into<String>,
200    ) -> Self {
201        Self {
202            client,
203            bucket,
204            task_id: task_id.into(),
205        }
206    }
207    pub async fn send(self) -> Result<DeleteBucketRedundancyOutput> {
208        let ep = self.client.endpoint.clone();
209        let uri = format!(
210            "https://{}.{}?redundancyTransition&taskId={}",
211            self.bucket.as_str(),
212            ep,
213            self.task_id
214        );
215        let qp = vec![
216            ("redundancyTransition".into(), String::new()),
217            ("taskId".into(), self.task_id),
218        ];
219        let req = HttpRequest::builder()
220            .method(http::Method::DELETE)
221            .uri(&uri)
222            .build();
223        let r = self
224            .client
225            .send_signed(req, Some(&self.bucket), qp)
226            .await
227            .map_err(|e| OssError {
228                kind: OssErrorKind::TransportError,
229                context: Box::new(ErrorContext {
230                    operation: Some("DeleteBucketRedundancy".into()),
231                    bucket: Some(self.bucket.to_string()),
232                    endpoint: Some(ep),
233                    ..Default::default()
234                }),
235                source: Some(Box::new(e)),
236            })?;
237        if r.status().is_success() {
238            Ok(DeleteBucketRedundancyOutput {
239                request_id: r
240                    .headers
241                    .get("x-oss-request-id")
242                    .and_then(|v| v.to_str().ok())
243                    .unwrap_or("")
244                    .to_string(),
245            })
246        } else {
247            Err(OssError {
248                kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
249                    status_code: r.status().as_u16(),
250                    code: String::new(),
251                    message: String::new(),
252                    request_id: String::new(),
253                    host_id: String::new(),
254                    resource: Some(self.bucket.to_string()),
255                    string_to_sign: None,
256                })),
257                context: Box::new(ErrorContext {
258                    operation: Some("DeleteBucketRedundancy".into()),
259                    bucket: Some(self.bucket.to_string()),
260                    ..Default::default()
261                }),
262                source: None,
263            })
264        }
265    }
266}
267#[derive(Debug, Clone)]
268pub struct DeleteBucketRedundancyOutput {
269    pub request_id: String,
270}
271
272impl BucketOperations {
273    pub fn put_redundancy_transition(
274        &self,
275        task_id: impl Into<String>,
276    ) -> PutBucketRedundancyBuilder {
277        PutBucketRedundancyBuilder::new(
278            self.client_inner().clone(),
279            self.bucket_name().clone(),
280            task_id,
281        )
282    }
283    pub fn get_redundancy_transition(&self) -> GetBucketRedundancyBuilder {
284        GetBucketRedundancyBuilder::new(self.client_inner().clone(), self.bucket_name().clone())
285    }
286    pub fn delete_redundancy_transition(
287        &self,
288        task_id: impl Into<String>,
289    ) -> DeleteBucketRedundancyBuilder {
290        DeleteBucketRedundancyBuilder::new(
291            self.client_inner().clone(),
292            self.bucket_name().clone(),
293            task_id,
294        )
295    }
296}
297
298#[cfg(test)]
299mod tests {
300    use super::*;
301    use crate::client::OSSClientInner;
302    use crate::config::credentials::Credentials;
303    use crate::http::client::{HttpClient, HttpRequest, HttpResponse};
304    use crate::types::region::Region;
305    use std::sync::Mutex;
306
307    struct Rc {
308        r: Arc<Mutex<Vec<HttpRequest>>>,
309    }
310    #[async_trait::async_trait]
311    impl HttpClient for Rc {
312        async fn send(&self, req: HttpRequest) -> crate::error::Result<HttpResponse> {
313            self.r.lock().unwrap().push(req);
314            let mut h = http::HeaderMap::new();
315            h.insert("x-oss-request-id", http::HeaderValue::from_static("rid"));
316            Ok(HttpResponse {
317                status: http::StatusCode::OK,
318                headers: h,
319                body: bytes::Bytes::new(),
320            })
321        }
322    }
323    fn ci() -> (Arc<OSSClientInner>, Arc<Mutex<Vec<HttpRequest>>>) {
324        let rq = Arc::new(Mutex::new(Vec::new()));
325        let h = Arc::new(Rc { r: rq.clone() });
326        let cr = Arc::new(crate::config::credentials::StaticCredentialsProvider::new(
327            Credentials::builder()
328                .access_key_id("ak")
329                .access_key_secret("sk")
330                .build()
331                .unwrap(),
332        ));
333        (
334            Arc::new(OSSClientInner {
335                http: h,
336                credentials: cr,
337                signer: Arc::from(crate::signer::create_signer(crate::signer::SignVersion::V4)),
338                region: Region::CnHangzhou,
339                endpoint: "oss-cn-hangzhou.aliyuncs.com".into(),
340            }),
341            rq,
342        )
343    }
344
345    #[test]
346    fn redundancy_xml() {
347        let c = RedundancyTransitionConfig {
348            task_id: "task-xxx".into(),
349            status: None,
350        };
351        let x = crate::util::xml::to_xml(&c).unwrap();
352        assert!(x.contains("<TaskId>task-xxx</TaskId>"));
353    }
354    #[tokio::test]
355    async fn delete_sends_request() {
356        let (i, r) = ci();
357        DeleteBucketRedundancyBuilder::new(i, BucketName::new("test-bucket").unwrap(), "task-1")
358            .send()
359            .await
360            .unwrap();
361        assert_eq!(r.lock().unwrap()[0].method, http::Method::DELETE);
362    }
363}