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}