cloud_storage/client/bucket.rs
1use crate::{
2 bucket::{IamPolicy, TestIamPermission},
3 error::GoogleResponse,
4 object::percent_encode,
5 resources::common::ListResponse,
6 Bucket, NewBucket,
7};
8
9/// Operations on [`Bucket`]()s.
10#[derive(Debug)]
11pub struct BucketClient<'a>(pub(super) &'a super::Client);
12
13impl<'a> BucketClient<'a> {
14 /// Creates a new `Bucket`. There are many options that you can provide for creating a new
15 /// bucket, so the `NewBucket` resource contains all of them. Note that `NewBucket` implements
16 /// `Default`, so you don't have to specify the fields you're not using. And error is returned
17 /// if that bucket name is already taken.
18 /// ### Example
19 /// ```
20 /// # #[tokio::main]
21 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
22 /// use cloud_storage::Client;
23 /// use cloud_storage::bucket::{Bucket, NewBucket};
24 /// use cloud_storage::bucket::{Location, MultiRegion};
25 ///
26 /// let client = Client::default();
27 /// let new_bucket = NewBucket {
28 /// name: "cloud-storage-rs-doc-1".to_string(), // this is the only mandatory field
29 /// location: Location::Multi(MultiRegion::Eu),
30 /// ..Default::default()
31 /// };
32 /// let bucket = client.bucket().create(&new_bucket).await?;
33 /// # client.bucket().delete(bucket).await?;
34 /// # Ok(())
35 /// # }
36 /// ```
37 pub async fn create(&self, new_bucket: &NewBucket) -> crate::Result<Bucket> {
38 let url = format!("{}/b/", crate::BASE_URL);
39 let project = &crate::SERVICE_ACCOUNT.project_id;
40 let query = [("project", project)];
41 let result: GoogleResponse<Bucket> = self
42 .0
43 .client
44 .post(&url)
45 .headers(self.0.get_headers().await?)
46 .query(&query)
47 .json(new_bucket)
48 .send()
49 .await?
50 .json()
51 .await?;
52 match result {
53 GoogleResponse::Success(s) => Ok(s),
54 GoogleResponse::Error(e) => Err(e.into()),
55 }
56 }
57
58 /// Returns all `Bucket`s within this project.
59 ///
60 /// ### Note
61 /// When using incorrect permissions, this function fails silently and returns an empty list.
62 ///
63 /// ### Example
64 /// ```
65 /// # #[tokio::main]
66 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
67 /// use cloud_storage::Client;
68 /// use cloud_storage::Bucket;
69 ///
70 /// let client = Client::default();
71 /// let buckets = client.bucket().list().await?;
72 /// # Ok(())
73 /// # }
74 /// ```
75 pub async fn list(&self) -> crate::Result<Vec<Bucket>> {
76 let url = dbg!(format!("{}/b/", crate::BASE_URL));
77 let project = &crate::SERVICE_ACCOUNT.project_id;
78 let query = [("project", project)];
79 dbg!(
80 self.0
81 .client
82 .get(&url)
83 .headers(self.0.get_headers().await?)
84 .query(&query)
85 .send()
86 .await?
87 .text()
88 .await
89 )?;
90 let result: GoogleResponse<ListResponse<Bucket>> = self
91 .0
92 .client
93 .get(&url)
94 .headers(self.0.get_headers().await?)
95 .query(&query)
96 .send()
97 .await?
98 .json()
99 .await?;
100 match result {
101 GoogleResponse::Success(s) => Ok(s.items),
102 GoogleResponse::Error(e) => Err(e.into()),
103 }
104 }
105
106 /// Returns a single `Bucket` by its name. If the Bucket does not exist, an error is returned.
107 /// ### Example
108 /// ```
109 /// # #[tokio::main]
110 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
111 /// use cloud_storage::Client;
112 /// use cloud_storage::Bucket;
113 ///
114 /// let client = Client::default();
115 /// # use cloud_storage::bucket::NewBucket;
116 /// # let new_bucket = NewBucket {
117 /// # name: "cloud-storage-rs-doc-2".to_string(),
118 /// # ..Default::default()
119 /// # };
120 /// # let _ = client.bucket().create(&new_bucket).await?;
121 ///
122 /// let bucket = client.bucket().read("cloud-storage-rs-doc-2").await?;
123 /// # client.bucket().delete(bucket).await?;
124 /// # Ok(())
125 /// # }
126 /// ```
127 pub async fn read(&self, name: &str) -> crate::Result<Bucket> {
128 let url = format!("{}/b/{}", crate::BASE_URL, percent_encode(name),);
129 let result: GoogleResponse<Bucket> = self
130 .0
131 .client
132 .get(&url)
133 .headers(self.0.get_headers().await?)
134 .send()
135 .await?
136 .json()
137 .await?;
138 match result {
139 GoogleResponse::Success(s) => Ok(s),
140 GoogleResponse::Error(e) => Err(e.into()),
141 }
142 }
143
144 /// Update an existing `Bucket`. If you declare you bucket as mutable, you can edit its fields.
145 /// You can then flush your changes to Google Cloud Storage using this method.
146 /// ### Example
147 /// ```
148 /// # #[tokio::main]
149 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
150 /// use cloud_storage::Client;
151 /// use cloud_storage::bucket::{Bucket, RetentionPolicy};
152 ///
153 /// let client = Client::default();
154 /// # use cloud_storage::bucket::NewBucket;
155 /// # let new_bucket = NewBucket {
156 /// # name: "cloud-storage-rs-doc-3".to_string(),
157 /// # ..Default::default()
158 /// # };
159 /// # let _ = client.bucket().create(&new_bucket).await?;
160 ///
161 /// let mut bucket = client.bucket().read("cloud-storage-rs-doc-3").await?;
162 /// bucket.retention_policy = Some(RetentionPolicy {
163 /// retention_period: 50,
164 /// effective_time: chrono::Utc::now() + chrono::Duration::seconds(50),
165 /// is_locked: Some(false),
166 /// });
167 /// client.bucket().update(&bucket).await?;
168 /// # client.bucket().delete(bucket).await?;
169 /// # Ok(())
170 /// # }
171 /// ```
172 pub async fn update(&self, bucket: &Bucket) -> crate::Result<Bucket> {
173 let url = format!("{}/b/{}", crate::BASE_URL, percent_encode(&bucket.name),);
174 let result: GoogleResponse<Bucket> = self
175 .0
176 .client
177 .put(&url)
178 .headers(self.0.get_headers().await?)
179 .json(bucket)
180 .send()
181 .await?
182 .json()
183 .await?;
184 match result {
185 GoogleResponse::Success(s) => Ok(s),
186 GoogleResponse::Error(e) => Err(e.into()),
187 }
188 }
189
190 /// Delete an existing `Bucket`. This permanently removes a bucket from Google Cloud Storage.
191 /// An error is returned when you don't have sufficient permissions, or when the
192 /// `retention_policy` prevents you from deleting your Bucket.
193 /// ### Example
194 /// ```no_run
195 /// # #[tokio::main]
196 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
197 /// use cloud_storage::Client;
198 /// use cloud_storage::Bucket;
199 ///
200 /// let client = Client::default();
201 /// # use cloud_storage::bucket::NewBucket;
202 /// # let new_bucket = NewBucket {
203 /// # name: "unnecessary-bucket".to_string(),
204 /// # ..Default::default()
205 /// # };
206 /// # let _ = client.bucket().create(&new_bucket).await?;
207 ///
208 /// let bucket = client.bucket().read("unnecessary-bucket").await?;
209 /// client.bucket().delete(bucket).await?;
210 /// # Ok(())
211 /// # }
212 /// ```
213 pub async fn delete(&self, bucket: Bucket) -> crate::Result<()> {
214 let url = format!("{}/b/{}", crate::BASE_URL, percent_encode(&bucket.name));
215 let response = self
216 .0
217 .client
218 .delete(&url)
219 .headers(self.0.get_headers().await?)
220 .send()
221 .await?;
222 if response.status().is_success() {
223 Ok(())
224 } else {
225 Err(crate::Error::Google(response.json().await?))
226 }
227 }
228
229 /// Returns the [IAM Policy](https://cloud.google.com/iam/docs/) for this bucket.
230 /// ### Example
231 /// ```
232 /// # #[tokio::main]
233 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
234 /// use cloud_storage::Client;
235 /// use cloud_storage::Bucket;
236 ///
237 /// let client = Client::default();
238 /// # use cloud_storage::bucket::NewBucket;
239 /// # let new_bucket = NewBucket {
240 /// # name: "cloud-storage-rs-doc-4".to_string(),
241 /// # ..Default::default()
242 /// # };
243 /// # let _ = client.bucket().create(&new_bucket).await?;
244 ///
245 /// let bucket = client.bucket().read("cloud-storage-rs-doc-4").await?;
246 /// let policy = client.bucket().get_iam_policy(&bucket).await?;
247 /// # client.bucket().delete(bucket).await?;
248 /// # Ok(())
249 /// # }
250 /// ```
251 pub async fn get_iam_policy(&self, bucket: &Bucket) -> crate::Result<IamPolicy> {
252 let url = format!("{}/b/{}/iam", crate::BASE_URL, percent_encode(&bucket.name));
253 let result: GoogleResponse<IamPolicy> = self
254 .0
255 .client
256 .get(&url)
257 .headers(self.0.get_headers().await?)
258 .send()
259 .await?
260 .json()
261 .await?;
262 match result {
263 GoogleResponse::Success(s) => Ok(s),
264 GoogleResponse::Error(e) => Err(e.into()),
265 }
266 }
267
268 /// Updates the [IAM Policy](https://cloud.google.com/iam/docs/) for this bucket.
269 /// ### Example
270 /// ```
271 /// # #[tokio::main]
272 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
273 /// use cloud_storage::Client;
274 /// use cloud_storage::Bucket;
275 /// use cloud_storage::bucket::{IamPolicy, Binding, IamRole, StandardIamRole, Entity};
276 ///
277 /// let client = Client::default();
278 /// # use cloud_storage::bucket::NewBucket;
279 /// # let new_bucket = NewBucket {
280 /// # name: "cloud-storage-rs-doc-5".to_string(),
281 /// # ..Default::default()
282 /// # };
283 /// # let _ = client.bucket().create(&new_bucket).await?;
284 ///
285 /// let bucket = client.bucket().read("cloud-storage-rs-doc-5").await?;
286 /// let iam_policy = IamPolicy {
287 /// version: 1,
288 /// bindings: vec![
289 /// Binding {
290 /// role: IamRole::Standard(StandardIamRole::ObjectViewer),
291 /// members: vec!["allUsers".to_string()],
292 /// condition: None,
293 /// }
294 /// ],
295 /// ..Default::default()
296 /// };
297 /// let policy = client.bucket().set_iam_policy(&bucket, &iam_policy).await?;
298 /// # client.bucket().delete(bucket).await?;
299 /// # Ok(())
300 /// # }
301 /// ```
302 pub async fn set_iam_policy(
303 &self,
304 bucket: &Bucket,
305 iam: &IamPolicy,
306 ) -> crate::Result<IamPolicy> {
307 let url = format!("{}/b/{}/iam", crate::BASE_URL, percent_encode(&bucket.name));
308 let result: GoogleResponse<IamPolicy> = self
309 .0
310 .client
311 .put(&url)
312 .headers(self.0.get_headers().await?)
313 .json(iam)
314 .send()
315 .await?
316 .json()
317 .await?;
318 match result {
319 GoogleResponse::Success(s) => Ok(s),
320 GoogleResponse::Error(e) => Err(e.into()),
321 }
322 }
323
324 /// Checks whether the user provided in the service account has this permission.
325 /// ### Example
326 /// ```no_run
327 /// # #[tokio::main]
328 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
329 /// use cloud_storage::Client;
330 /// use cloud_storage::Bucket;
331 ///
332 /// let client = Client::default();
333 /// let bucket = client.bucket().read("my-bucket").await?;
334 /// client.bucket().test_iam_permission(&bucket, "storage.buckets.get").await?;
335 /// # Ok(())
336 /// # }
337 /// ```
338 pub async fn test_iam_permission(
339 &self,
340 bucket: &Bucket,
341 permission: &str,
342 ) -> crate::Result<TestIamPermission> {
343 if permission == "storage.buckets.list" || permission == "storage.buckets.create" {
344 return Err(crate::Error::new(
345 "tested permission must not be `storage.buckets.list` or `storage.buckets.create`",
346 ));
347 }
348 let url = format!(
349 "{}/b/{}/iam/testPermissions",
350 crate::BASE_URL,
351 percent_encode(&bucket.name)
352 );
353 let result: GoogleResponse<TestIamPermission> = self
354 .0
355 .client
356 .get(&url)
357 .headers(self.0.get_headers().await?)
358 .query(&[("permissions", permission)])
359 .send()
360 .await?
361 .json()
362 .await?;
363 match result {
364 GoogleResponse::Success(s) => Ok(s),
365 GoogleResponse::Error(e) => Err(e.into()),
366 }
367 }
368}