oci_api/services/object_storage/
client.rs1use crate::client::Oci;
4use crate::error::{Error, Result};
5use crate::services::object_storage::models::*;
6use serde::Deserialize;
7use serde::Serialize;
8use serde::de::DeserializeOwned;
9
10#[derive(Clone)]
12pub struct ObjectStorage {
13 oci_client: Oci,
15 pub namespace: String,
17 endpoint: String,
19 protocol: String,
21}
22
23impl ObjectStorage {
24 pub fn new(oci_client: &Oci, namespace: impl Into<String>) -> Self {
30 let region = oci_client.region().to_string();
31 let endpoint = format!("objectstorage.{region}.oraclecloud.com");
32
33 Self {
34 oci_client: oci_client.clone(),
35 namespace: namespace.into(),
36 endpoint,
37 protocol: "https".to_string(),
38 }
39 }
40
41 pub async fn get_bucket(&self, bucket_name: &str) -> Result<Bucket> {
46 let path = format!("/n/{}/b/{}/", self.namespace, bucket_name);
48 let url = format!("{}://{}{}", self.protocol, self.endpoint, path);
49
50 let (date_header, auth_header) =
51 self.oci_client
52 .signer()
53 .sign_request("GET", &path, &self.endpoint, None)?;
54
55 let response = self
56 .oci_client
57 .client()
58 .get(&url)
59 .header("host", &self.endpoint)
60 .header("date", &date_header)
61 .header("authorization", &auth_header)
62 .send()
63 .await?;
64
65 if !response.status().is_success() {
66 let status = response.status();
67 let body = response.text().await?;
68 return Err(Error::ApiError {
69 code: status.to_string(),
70 message: body,
71 });
72 }
73
74 Ok(Bucket {
76 oci_client: self.oci_client.clone(),
77 namespace: self.namespace.clone(),
78 name: bucket_name.to_string(),
79 endpoint: self.endpoint.clone(),
80 protocol: self.protocol.clone(),
81 })
82 }
83}
84
85#[derive(Clone)]
87pub struct Bucket {
88 oci_client: Oci,
90 pub namespace: String,
92 pub name: String,
94 endpoint: String,
96 protocol: String,
98}
99
100impl Bucket {
101 async fn request<T, B>(&self, method: &str, path: &str, body: Option<B>) -> Result<T>
103 where
104 T: DeserializeOwned,
105 B: Serialize,
106 {
107 let url = format!("{}://{}{}", self.protocol, self.endpoint, path);
108 let body_str = if let Some(b) = &body {
109 Some(serde_json::to_string(b)?)
110 } else {
111 None
112 };
113
114 let (date_header, auth_header) = self.oci_client.signer().sign_request(
115 method,
116 path,
117 &self.endpoint,
118 body_str.as_deref(),
119 )?;
120
121 let mut request_builder = match method {
122 "GET" => self.oci_client.client().get(&url),
123 "POST" => self.oci_client.client().post(&url),
124 "PUT" => self.oci_client.client().put(&url),
125 "DELETE" => self.oci_client.client().delete(&url),
126 _ => return Err(Error::Other(format!("Unsupported method: {}", method))),
127 };
128
129 request_builder = request_builder
130 .header("host", &self.endpoint)
131 .header("date", &date_header)
132 .header("authorization", &auth_header);
133
134 if let Some(b_str) = body_str {
135 request_builder = request_builder
136 .header("content-type", "application/json")
137 .header("content-length", b_str.len().to_string())
138 .body(b_str);
139 }
140
141 let response = request_builder.send().await?;
142
143 if !response.status().is_success() {
144 let status = response.status();
145 let body = response.text().await?;
146 return Err(Error::ApiError {
147 code: status.to_string(),
148 message: body,
149 });
150 }
151
152 let text = response.text().await?;
153 serde_json::from_str(&text).map_err(Into::into)
154 }
155
156 async fn request_no_content<B>(&self, method: &str, path: &str, body: Option<B>) -> Result<()>
157 where
158 B: Serialize,
159 {
160 let url = format!("{}://{}{}", self.protocol, self.endpoint, path);
161 let body_str = if let Some(b) = &body {
162 Some(serde_json::to_string(b)?)
163 } else {
164 None
165 };
166
167 let (date_header, auth_header) = self.oci_client.signer().sign_request(
168 method,
169 path,
170 &self.endpoint,
171 body_str.as_deref(),
172 )?;
173
174 let mut request_builder = match method {
175 "GET" => self.oci_client.client().get(&url),
176 "POST" => self.oci_client.client().post(&url),
177 "PUT" => self.oci_client.client().put(&url),
178 "DELETE" => self.oci_client.client().delete(&url),
179 _ => return Err(Error::Other(format!("Unsupported method: {}", method))),
180 };
181
182 request_builder = request_builder
183 .header("host", &self.endpoint)
184 .header("date", &date_header)
185 .header("authorization", &auth_header);
186
187 if let Some(b_str) = body_str {
188 request_builder = request_builder
189 .header("content-type", "application/json")
190 .header("content-length", b_str.len().to_string())
191 .body(b_str);
192 }
193
194 let response = request_builder.send().await?;
195
196 if !response.status().is_success() {
197 let status = response.status();
198 let body = response.text().await?;
199 return Err(Error::ApiError {
200 code: status.to_string(),
201 message: body,
202 });
203 }
204
205 Ok(())
206 }
207
208 pub async fn put_object(&self, object_name: &str, content: &str) -> Result<Object> {
214 let path = format!("/n/{}/b/{}/o/{}", self.namespace, self.name, object_name);
215 let url = format!("{}://{}{}", self.protocol, self.endpoint, path);
216
217 let (date_header, auth_header) =
218 self.oci_client
219 .signer()
220 .sign_request("PUT", &path, &self.endpoint, Some(content))?;
221
222 let response = self
223 .oci_client
224 .client()
225 .put(&url)
226 .header("host", &self.endpoint)
227 .header("date", &date_header)
228 .header("authorization", &auth_header)
229 .header("content-length", content.len().to_string())
230 .body(content.to_string())
231 .send()
232 .await?;
233
234 if !response.status().is_success() {
235 let status = response.status();
236 let body = response.text().await?;
237 return Err(Error::ApiError {
238 code: status.to_string(),
239 message: body,
240 });
241 }
242
243 Ok(Object {
244 name: object_name.to_string(),
245 value: content.to_string(),
246 })
247 }
248
249 pub async fn get_object(&self, object_name: &str) -> Result<Object> {
254 let path = format!("/n/{}/b/{}/o/{}", self.namespace, self.name, object_name);
255 let url = format!("{}://{}{}", self.protocol, self.endpoint, path);
256
257 let (date_header, auth_header) =
258 self.oci_client
259 .signer()
260 .sign_request("GET", &path, &self.endpoint, None)?;
261
262 let response = self
263 .oci_client
264 .client()
265 .get(&url)
266 .header("host", &self.endpoint)
267 .header("date", &date_header)
268 .header("authorization", &auth_header)
269 .send()
270 .await?;
271
272 if !response.status().is_success() {
273 let status = response.status();
274 let body = response.text().await?;
275 return Err(Error::ApiError {
276 code: status.to_string(),
277 message: body,
278 });
279 }
280
281 let value = response.text().await?;
282
283 Ok(Object {
284 name: object_name.to_string(),
285 value,
286 })
287 }
288
289 pub async fn get_or_create_object(&self, object_name: &str, content: &str) -> Result<Object> {
297 match self.get_object(object_name).await {
298 Ok(obj) => Ok(obj),
299 Err(Error::ApiError { code, .. }) if code.contains("404") => {
300 self.put_object(object_name, content).await
301 }
302 Err(e) => Err(e),
303 }
304 }
305
306 pub async fn get_retention_rules(&self) -> Result<Vec<RetentionRule>> {
308 let path = format!("/n/{}/b/{}/retentionRules", self.namespace, self.name);
309
310 #[derive(Deserialize)]
311 struct ResponseWrapper {
312 items: Vec<RetentionRule>,
313 }
314
315 let wrapper: ResponseWrapper = self
316 .request::<ResponseWrapper, ()>("GET", &path, None)
317 .await?;
318 Ok(wrapper.items)
319 }
320
321 pub async fn create_retention_rule(
323 &self,
324 details: RetentionRuleDetails,
325 ) -> Result<RetentionRule> {
326 let path = format!("/n/{}/b/{}/retentionRules", self.namespace, self.name);
327 self.request("POST", &path, Some(details)).await
328 }
329
330 pub async fn get_retention_rule(&self, rule_id: &str) -> Result<RetentionRule> {
332 let path = format!(
333 "/n/{}/b/{}/retentionRules/{}",
334 self.namespace, self.name, rule_id
335 );
336 self.request("GET", &path, None::<()>).await
337 }
338
339 pub async fn update_retention_rule(
341 &self,
342 rule_or_id: impl Into<String>,
343 details: RetentionRuleDetails,
344 ) -> Result<RetentionRule> {
345 let rule_id = rule_or_id.into();
346 let path = format!(
347 "/n/{}/b/{}/retentionRules/{}",
348 self.namespace, self.name, rule_id
349 );
350 self.request("PUT", &path, Some(details)).await
351 }
352
353 pub async fn delete_retention_rule(&self, rule_or_id: impl Into<String>) -> Result<()> {
355 let rule_id = rule_or_id.into();
356 let path = format!(
357 "/n/{}/b/{}/retentionRules/{}",
358 self.namespace, self.name, rule_id
359 );
360 self.request_no_content("DELETE", &path, None::<()>).await
361 }
362}
363
364#[cfg(test)]
365#[path = "tests.rs"]
366mod tests;