1use 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 = "ResourceGroupConfiguration")]
12struct ResourceGroupConfig {
13 #[serde(rename = "ResourceGroupId")]
14 resource_group_id: String,
15}
16
17#[derive(Debug, Clone, Deserialize)]
18#[serde(rename = "ResourceGroupConfiguration")]
19struct ResourceGroupConfigResp {
20 #[serde(rename = "ResourceGroupId")]
21 resource_group_id: String,
22}
23
24pub struct PutBucketResourceGroupBuilder {
25 client: Arc<OSSClientInner>,
26 bucket: BucketName,
27 group_id: String,
28}
29impl PutBucketResourceGroupBuilder {
30 pub(crate) fn new(
31 client: Arc<OSSClientInner>,
32 bucket: BucketName,
33 group_id: impl Into<String>,
34 ) -> Self {
35 Self {
36 client,
37 bucket,
38 group_id: group_id.into(),
39 }
40 }
41 pub async fn send(self) -> Result<PutBucketResourceGroupOutput> {
42 let ep = self.client.endpoint.clone();
43 let uri = format!("https://{}.{}?resourceGroup", self.bucket.as_str(), ep);
44 let qp = vec![("resourceGroup".into(), String::new())];
45 let cfg = ResourceGroupConfig {
46 resource_group_id: self.group_id,
47 };
48 let xml = crate::util::xml::to_xml(&cfg)?;
49 let req = HttpRequest::builder()
50 .method(http::Method::PUT)
51 .uri(&uri)
52 .body(bytes::Bytes::from(xml))
53 .build();
54 let r = self
55 .client
56 .send_signed(req, Some(&self.bucket), qp)
57 .await
58 .map_err(|e| OssError {
59 kind: OssErrorKind::TransportError,
60 context: Box::new(ErrorContext {
61 operation: Some("PutBucketResourceGroup".into()),
62 bucket: Some(self.bucket.to_string()),
63 endpoint: Some(ep),
64 ..Default::default()
65 }),
66 source: Some(Box::new(e)),
67 })?;
68 if r.status().is_success() {
69 Ok(PutBucketResourceGroupOutput {
70 request_id: r
71 .headers
72 .get("x-oss-request-id")
73 .and_then(|v| v.to_str().ok())
74 .unwrap_or("")
75 .to_string(),
76 })
77 } else {
78 Err(OssError {
79 kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
80 status_code: r.status().as_u16(),
81 code: String::new(),
82 message: String::new(),
83 request_id: String::new(),
84 host_id: String::new(),
85 resource: Some(self.bucket.to_string()),
86 string_to_sign: None,
87 })),
88 context: Box::new(ErrorContext {
89 operation: Some("PutBucketResourceGroup".into()),
90 bucket: Some(self.bucket.to_string()),
91 ..Default::default()
92 }),
93 source: None,
94 })
95 }
96 }
97}
98#[derive(Debug, Clone)]
99pub struct PutBucketResourceGroupOutput {
100 pub request_id: String,
101}
102
103pub struct GetBucketResourceGroupBuilder {
104 client: Arc<OSSClientInner>,
105 bucket: BucketName,
106}
107impl GetBucketResourceGroupBuilder {
108 pub(crate) fn new(client: Arc<OSSClientInner>, bucket: BucketName) -> Self {
109 Self { client, bucket }
110 }
111 pub async fn send(self) -> Result<GetBucketResourceGroupOutput> {
112 let ep = self.client.endpoint.clone();
113 let uri = format!("https://{}.{}?resourceGroup", self.bucket.as_str(), ep);
114 let qp = vec![("resourceGroup".into(), String::new())];
115 let req = HttpRequest::builder()
116 .method(http::Method::GET)
117 .uri(&uri)
118 .build();
119 let r = self
120 .client
121 .send_signed(req, Some(&self.bucket), qp)
122 .await
123 .map_err(|e| OssError {
124 kind: OssErrorKind::TransportError,
125 context: Box::new(ErrorContext {
126 operation: Some("GetBucketResourceGroup".into()),
127 bucket: Some(self.bucket.to_string()),
128 endpoint: Some(ep),
129 ..Default::default()
130 }),
131 source: Some(Box::new(e)),
132 })?;
133 if r.is_success() {
134 let c: ResourceGroupConfigResp =
135 crate::util::xml::from_xml(r.body_as_str().unwrap_or("")).map_err(|e| {
136 OssError {
137 kind: OssErrorKind::DeserializationError,
138 context: Box::new(ErrorContext {
139 operation: Some("parse XML".into()),
140 bucket: Some(self.bucket.to_string()),
141 ..Default::default()
142 }),
143 source: Some(Box::new(e)),
144 }
145 })?;
146 Ok(GetBucketResourceGroupOutput {
147 resource_group_id: c.resource_group_id,
148 })
149 } else {
150 Err(OssError {
151 kind: OssErrorKind::ServiceError(Box::new(crate::error::OssServiceError {
152 status_code: r.status().as_u16(),
153 code: String::new(),
154 message: String::new(),
155 request_id: String::new(),
156 host_id: String::new(),
157 resource: Some(self.bucket.to_string()),
158 string_to_sign: None,
159 })),
160 context: Box::new(ErrorContext {
161 operation: Some("GetBucketResourceGroup".into()),
162 bucket: Some(self.bucket.to_string()),
163 ..Default::default()
164 }),
165 source: None,
166 })
167 }
168 }
169}
170#[derive(Debug, Clone)]
171pub struct GetBucketResourceGroupOutput {
172 pub resource_group_id: String,
173}
174
175impl BucketOperations {
176 pub fn put_resource_group(&self, group_id: impl Into<String>) -> PutBucketResourceGroupBuilder {
177 PutBucketResourceGroupBuilder::new(
178 self.client_inner().clone(),
179 self.bucket_name().clone(),
180 group_id,
181 )
182 }
183 pub fn get_resource_group(&self) -> GetBucketResourceGroupBuilder {
184 GetBucketResourceGroupBuilder::new(self.client_inner().clone(), self.bucket_name().clone())
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::client::OSSClientInner;
192 use crate::config::credentials::Credentials;
193 use crate::http::client::{HttpClient, HttpRequest, HttpResponse};
194 use crate::types::region::Region;
195 use std::sync::Mutex;
196
197 struct Rc {
198 r: Arc<Mutex<Vec<HttpRequest>>>,
199 }
200 #[async_trait::async_trait]
201 impl HttpClient for Rc {
202 async fn send(&self, req: HttpRequest) -> crate::error::Result<HttpResponse> {
203 self.r.lock().unwrap().push(req);
204 let mut h = http::HeaderMap::new();
205 h.insert("x-oss-request-id", http::HeaderValue::from_static("rid"));
206 Ok(HttpResponse {
207 status: http::StatusCode::OK,
208 headers: h,
209 body: bytes::Bytes::new(),
210 })
211 }
212 }
213 fn ci() -> (Arc<OSSClientInner>, Arc<Mutex<Vec<HttpRequest>>>) {
214 let rq = Arc::new(Mutex::new(Vec::new()));
215 let h = Arc::new(Rc { r: rq.clone() });
216 let cr = Arc::new(crate::config::credentials::StaticCredentialsProvider::new(
217 Credentials::builder()
218 .access_key_id("ak")
219 .access_key_secret("sk")
220 .build()
221 .unwrap(),
222 ));
223 (
224 Arc::new(OSSClientInner {
225 http: h,
226 credentials: cr,
227 signer: Arc::from(crate::signer::create_signer(crate::signer::SignVersion::V4)),
228 region: Region::CnHangzhou,
229 endpoint: "oss-cn-hangzhou.aliyuncs.com".into(),
230 }),
231 rq,
232 )
233 }
234
235 #[test]
236 fn resource_group_xml() {
237 let c = ResourceGroupConfig {
238 resource_group_id: "rg-xxx".into(),
239 };
240 let x = crate::util::xml::to_xml(&c).unwrap();
241 assert!(x.contains("<ResourceGroupId>rg-xxx</ResourceGroupId>"));
242 }
243 #[tokio::test]
244 async fn put_sends_request() {
245 let (i, r) = ci();
246 PutBucketResourceGroupBuilder::new(i, BucketName::new("test-bucket").unwrap(), "rg-1")
247 .send()
248 .await
249 .unwrap();
250 assert_eq!(r.lock().unwrap()[0].method, http::Method::PUT);
251 }
252}