1use std::sync::Arc;
4
5use serde::{Deserialize, Serialize};
6
7use crate::client::{BucketOperations, OSSClientInner};
8use crate::error::{ErrorContext, OssError, OssErrorKind, Result};
9use crate::http::client::HttpRequest;
10use crate::types::bucket::BucketName;
11
12#[derive(Debug, Clone, Serialize)]
13#[serde(rename = "ArchiveDirectReadConfiguration")]
14struct ArchiveDirectReadConfiguration {
15 #[serde(rename = "Enabled")]
16 enabled: bool,
17}
18
19#[derive(Debug, Clone, Deserialize)]
20#[serde(rename = "ArchiveDirectReadConfiguration")]
21struct ArchiveDirectReadConfigurationResponse {
22 #[serde(rename = "Enabled")]
23 enabled: bool,
24}
25
26pub struct PutBucketArchiveReadBuilder {
27 client: Arc<OSSClientInner>,
28 bucket: BucketName,
29 enabled: bool,
30}
31
32impl PutBucketArchiveReadBuilder {
33 pub(crate) fn new(client: Arc<OSSClientInner>, bucket: BucketName, enabled: bool) -> Self {
34 Self {
35 client,
36 bucket,
37 enabled,
38 }
39 }
40
41 pub async fn send(self) -> Result<PutBucketArchiveReadOutput> {
42 let endpoint = self.client.endpoint.clone();
43 let uri = format!(
44 "https://{}.{}?archiveDirectRead",
45 self.bucket.as_str(),
46 endpoint
47 );
48 let query_params: Vec<(String, String)> = vec![("archiveDirectRead".into(), String::new())];
49
50 let config = ArchiveDirectReadConfiguration {
51 enabled: self.enabled,
52 };
53 let body_xml = crate::util::xml::to_xml(&config)?;
54
55 let request = HttpRequest::builder()
56 .method(http::Method::PUT)
57 .uri(&uri)
58 .body(bytes::Bytes::from(body_xml))
59 .build();
60
61 let response = self
62 .client
63 .send_signed(request, Some(&self.bucket), query_params)
64 .await
65 .map_err(|e| OssError {
66 kind: OssErrorKind::TransportError,
67 context: Box::new(ErrorContext {
68 operation: Some("PutBucketArchiveRead".into()),
69 bucket: Some(self.bucket.to_string()),
70 endpoint: Some(endpoint),
71 ..Default::default()
72 }),
73 source: Some(Box::new(e)),
74 })?;
75
76 if response.status().is_success() {
77 Ok(PutBucketArchiveReadOutput {
78 request_id: response
79 .headers
80 .get("x-oss-request-id")
81 .and_then(|v| v.to_str().ok())
82 .unwrap_or("")
83 .to_string(),
84 })
85 } else {
86 Err(OssError {
87 kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
88 status_code: response.status().as_u16(),
89 code: String::new(),
90 message: String::new(),
91 request_id: String::new(),
92 host_id: String::new(),
93 resource: Some(self.bucket.to_string()),
94 string_to_sign: None,
95 })),
96 context: Box::new(ErrorContext {
97 operation: Some("PutBucketArchiveRead".into()),
98 bucket: Some(self.bucket.to_string()),
99 ..Default::default()
100 }),
101 source: None,
102 })
103 }
104 }
105}
106
107#[derive(Debug, Clone)]
108pub struct PutBucketArchiveReadOutput {
109 pub request_id: String,
110}
111
112pub struct GetBucketArchiveReadBuilder {
113 client: Arc<OSSClientInner>,
114 bucket: BucketName,
115}
116
117impl GetBucketArchiveReadBuilder {
118 pub(crate) fn new(client: Arc<OSSClientInner>, bucket: BucketName) -> Self {
119 Self { client, bucket }
120 }
121
122 pub async fn send(self) -> Result<GetBucketArchiveReadOutput> {
123 let endpoint = self.client.endpoint.clone();
124 let uri = format!(
125 "https://{}.{}?archiveDirectRead",
126 self.bucket.as_str(),
127 endpoint
128 );
129 let query_params: Vec<(String, String)> = vec![("archiveDirectRead".into(), String::new())];
130
131 let request = HttpRequest::builder()
132 .method(http::Method::GET)
133 .uri(&uri)
134 .build();
135
136 let response = self
137 .client
138 .send_signed(request, Some(&self.bucket), query_params)
139 .await
140 .map_err(|e| OssError {
141 kind: OssErrorKind::TransportError,
142 context: Box::new(ErrorContext {
143 operation: Some("GetBucketArchiveRead".into()),
144 bucket: Some(self.bucket.to_string()),
145 endpoint: Some(endpoint),
146 ..Default::default()
147 }),
148 source: Some(Box::new(e)),
149 })?;
150
151 if response.is_success() {
152 let body_str = response.body_as_str().unwrap_or("");
153 let config: ArchiveDirectReadConfigurationResponse =
154 crate::util::xml::from_xml(body_str).map_err(|e| OssError {
155 kind: OssErrorKind::DeserializationError,
156 context: Box::new(ErrorContext {
157 operation: Some("GetBucketArchiveRead: parse XML".into()),
158 bucket: Some(self.bucket.to_string()),
159 ..Default::default()
160 }),
161 source: Some(Box::new(e)),
162 })?;
163
164 Ok(GetBucketArchiveReadOutput {
165 enabled: config.enabled,
166 })
167 } else {
168 Err(OssError {
169 kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
170 status_code: response.status().as_u16(),
171 code: String::new(),
172 message: String::new(),
173 request_id: String::new(),
174 host_id: String::new(),
175 resource: Some(self.bucket.to_string()),
176 string_to_sign: None,
177 })),
178 context: Box::new(ErrorContext {
179 operation: Some("GetBucketArchiveRead".into()),
180 bucket: Some(self.bucket.to_string()),
181 ..Default::default()
182 }),
183 source: None,
184 })
185 }
186 }
187}
188
189#[derive(Debug, Clone)]
190pub struct GetBucketArchiveReadOutput {
191 pub enabled: bool,
192}
193
194impl BucketOperations {
195 pub fn put_archive_direct_read(&self, enabled: bool) -> PutBucketArchiveReadBuilder {
196 PutBucketArchiveReadBuilder::new(
197 self.client_inner().clone(),
198 self.bucket_name().clone(),
199 enabled,
200 )
201 }
202
203 pub fn get_archive_direct_read(&self) -> GetBucketArchiveReadBuilder {
204 GetBucketArchiveReadBuilder::new(self.client_inner().clone(), self.bucket_name().clone())
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use std::sync::Mutex;
211
212 use crate::client::OSSClientInner;
213 use crate::config::credentials::Credentials;
214 use crate::http::client::{HttpClient, HttpRequest, HttpResponse};
215 use crate::types::region::Region;
216
217 use super::*;
218
219 struct RecordingHttpClient {
220 requests: Arc<Mutex<Vec<HttpRequest>>>,
221 status_code: http::StatusCode,
222 response_body: bytes::Bytes,
223 }
224
225 #[async_trait::async_trait]
226 impl HttpClient for RecordingHttpClient {
227 async fn send(&self, request: HttpRequest) -> crate::error::Result<HttpResponse> {
228 self.requests.lock().unwrap().push(request);
229 let mut headers = http::HeaderMap::new();
230 headers.insert("x-oss-request-id", http::HeaderValue::from_static("rid-ar"));
231 Ok(HttpResponse {
232 status: self.status_code,
233 headers,
234 body: self.response_body.clone(),
235 })
236 }
237 }
238
239 fn create_test_inner(
240 body: bytes::Bytes,
241 ) -> (Arc<OSSClientInner>, Arc<Mutex<Vec<HttpRequest>>>) {
242 let requests = Arc::new(Mutex::new(Vec::new()));
243 let http = Arc::new(RecordingHttpClient {
244 requests: requests.clone(),
245 status_code: http::StatusCode::OK,
246 response_body: body,
247 });
248 let credentials = Arc::new(crate::config::credentials::StaticCredentialsProvider::new(
249 Credentials::builder()
250 .access_key_id("test-ak")
251 .access_key_secret("test-sk")
252 .build()
253 .unwrap(),
254 ));
255 let inner = Arc::new(OSSClientInner {
256 http,
257 credentials,
258 signer: Arc::from(crate::signer::create_signer(crate::signer::SignVersion::V4)),
259 region: Region::CnHangzhou,
260 endpoint: "oss-cn-hangzhou.aliyuncs.com".into(),
261 });
262 (inner, requests)
263 }
264
265 #[test]
266 fn archive_direct_read_xml() {
267 let config = ArchiveDirectReadConfiguration { enabled: true };
268 let xml = crate::util::xml::to_xml(&config).unwrap();
269 assert!(xml.contains("<Enabled>true</Enabled>"));
270 }
271
272 #[tokio::test]
273 async fn get_archive_direct_read_parses_response() {
274 let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
275<ArchiveDirectReadConfiguration><Enabled>false</Enabled></ArchiveDirectReadConfiguration>"#;
276 let (inner, _) = create_test_inner(bytes::Bytes::from(xml));
277 let builder =
278 GetBucketArchiveReadBuilder::new(inner, BucketName::new("test-bucket").unwrap());
279 let output = builder.send().await.unwrap();
280 assert!(!output.enabled);
281 }
282}