google_cloud_storage/http/
storage_client.rs

1use std::sync::Arc;
2
3use futures_util::{Stream, TryStream, TryStreamExt};
4use reqwest::header::{HeaderValue, CONTENT_LENGTH, LOCATION};
5use reqwest::{Body, Request};
6use reqwest_middleware::RequestBuilder;
7
8use google_cloud_token::TokenSource;
9
10use crate::http::bucket_access_controls::delete::DeleteBucketAccessControlRequest;
11use crate::http::bucket_access_controls::get::GetBucketAccessControlRequest;
12use crate::http::bucket_access_controls::insert::InsertBucketAccessControlRequest;
13use crate::http::bucket_access_controls::list::{ListBucketAccessControlsRequest, ListBucketAccessControlsResponse};
14use crate::http::bucket_access_controls::patch::PatchBucketAccessControlRequest;
15use crate::http::bucket_access_controls::BucketAccessControl;
16use crate::http::buckets::delete::DeleteBucketRequest;
17use crate::http::buckets::get::GetBucketRequest;
18use crate::http::buckets::get_iam_policy::GetIamPolicyRequest;
19use crate::http::buckets::insert::InsertBucketRequest;
20use crate::http::buckets::list::{ListBucketsRequest, ListBucketsResponse};
21use crate::http::buckets::patch::PatchBucketRequest;
22use crate::http::buckets::set_iam_policy::SetIamPolicyRequest;
23use crate::http::buckets::test_iam_permissions::{TestIamPermissionsRequest, TestIamPermissionsResponse};
24use crate::http::buckets::{Bucket, Policy};
25use crate::http::default_object_access_controls::delete::DeleteDefaultObjectAccessControlRequest;
26use crate::http::default_object_access_controls::get::GetDefaultObjectAccessControlRequest;
27use crate::http::default_object_access_controls::insert::InsertDefaultObjectAccessControlRequest;
28use crate::http::default_object_access_controls::list::{
29    ListDefaultObjectAccessControlsRequest, ListDefaultObjectAccessControlsResponse,
30};
31use crate::http::default_object_access_controls::patch::PatchDefaultObjectAccessControlRequest;
32use crate::http::hmac_keys::create::{CreateHmacKeyRequest, CreateHmacKeyResponse};
33use crate::http::hmac_keys::delete::DeleteHmacKeyRequest;
34use crate::http::hmac_keys::get::GetHmacKeyRequest;
35use crate::http::hmac_keys::list::{ListHmacKeysRequest, ListHmacKeysResponse};
36use crate::http::hmac_keys::update::UpdateHmacKeyRequest;
37use crate::http::hmac_keys::HmacKeyMetadata;
38use crate::http::notifications::delete::DeleteNotificationRequest;
39use crate::http::notifications::get::GetNotificationRequest;
40use crate::http::notifications::insert::InsertNotificationRequest;
41use crate::http::notifications::list::{ListNotificationsRequest, ListNotificationsResponse};
42use crate::http::notifications::Notification;
43use crate::http::object_access_controls::delete::DeleteObjectAccessControlRequest;
44use crate::http::object_access_controls::get::GetObjectAccessControlRequest;
45use crate::http::object_access_controls::insert::InsertObjectAccessControlRequest;
46use crate::http::object_access_controls::list::ListObjectAccessControlsRequest;
47use crate::http::object_access_controls::patch::PatchObjectAccessControlRequest;
48use crate::http::object_access_controls::ObjectAccessControl;
49use crate::http::objects::compose::ComposeObjectRequest;
50use crate::http::objects::copy::CopyObjectRequest;
51use crate::http::objects::delete::DeleteObjectRequest;
52use crate::http::objects::download::Range;
53use crate::http::objects::get::GetObjectRequest;
54use crate::http::objects::list::{ListObjectsRequest, ListObjectsResponse};
55use crate::http::objects::patch::PatchObjectRequest;
56use crate::http::objects::r#move::MoveObjectRequest;
57use crate::http::objects::rewrite::{RewriteObjectRequest, RewriteObjectResponse};
58use crate::http::objects::upload::{UploadObjectRequest, UploadType};
59use crate::http::objects::Object;
60use crate::http::resumable_upload_client::ResumableUploadClient;
61use crate::http::{
62    bucket_access_controls, buckets, check_response_status, default_object_access_controls, hmac_keys, notifications,
63    object_access_controls, objects, Error,
64};
65
66pub const SCOPES: [&str; 2] = [
67    "https://www.googleapis.com/auth/cloud-platform",
68    "https://www.googleapis.com/auth/devstorage.full_control",
69];
70
71#[derive(Clone)]
72pub struct StorageClient {
73    ts: Option<Arc<dyn TokenSource>>,
74    v1_endpoint: String,
75    v1_upload_endpoint: String,
76    http: reqwest_middleware::ClientWithMiddleware,
77}
78
79impl StorageClient {
80    pub(crate) fn new(
81        ts: Option<Arc<dyn TokenSource>>,
82        endpoint: &str,
83        http: reqwest_middleware::ClientWithMiddleware,
84    ) -> Self {
85        Self {
86            ts,
87            v1_endpoint: format!("{endpoint}/storage/v1"),
88            v1_upload_endpoint: format!("{endpoint}/upload/storage/v1"),
89            http,
90        }
91    }
92
93    /// Deletes the bucket.
94    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/delete
95    ///
96    /// ```
97    /// use google_cloud_storage::client::Client;
98    /// use google_cloud_storage::http::buckets::delete::DeleteBucketRequest;
99    ///
100    /// async fn run(client:Client) {
101    ///     let result = client.delete_bucket(&DeleteBucketRequest {
102    ///         bucket: "bucket".to_string(),
103    ///         ..Default::default()
104    ///     }).await;
105    /// }
106    /// ```
107    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
108    pub async fn delete_bucket(&self, req: &DeleteBucketRequest) -> Result<(), Error> {
109        let builder = buckets::delete::build(self.v1_endpoint.as_str(), &self.http, req);
110        self.send_get_empty(builder).await
111    }
112
113    /// Inserts the bucket.
114    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/insert
115    ///
116    /// ```
117    /// use google_cloud_storage::client::Client;
118    /// use google_cloud_storage::http::buckets::insert::{BucketCreationConfig, InsertBucketParam, InsertBucketRequest};
119    ///
120    /// async fn run(client:Client) {
121    ///     let result = client.insert_bucket(&InsertBucketRequest {
122    ///         name: "bucket".to_string(),
123    ///         param: InsertBucketParam {
124    ///             project: "project_id".to_string(),
125    ///             ..Default::default()
126    ///         },
127    ///         ..Default::default()
128    ///     }).await;
129    /// }
130    /// ```
131    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
132    pub async fn insert_bucket(&self, req: &InsertBucketRequest) -> Result<Bucket, Error> {
133        let builder = buckets::insert::build(self.v1_endpoint.as_str(), &self.http, req);
134        self.send(builder).await
135    }
136
137    /// Gets the bucket.
138    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/get
139    ///
140    /// ```
141    /// use google_cloud_storage::client::Client;
142    /// use google_cloud_storage::http::buckets::get::GetBucketRequest;
143    ///
144    /// async fn run(client:Client) {
145    ///     let result = client.get_bucket(&GetBucketRequest {
146    ///         bucket: "bucket".to_string(),
147    ///         ..Default::default()
148    ///     }).await;
149    /// }
150    /// ```
151    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
152    pub async fn get_bucket(&self, req: &GetBucketRequest) -> Result<Bucket, Error> {
153        let builder = buckets::get::build(self.v1_endpoint.as_str(), &self.http, req);
154        self.send(builder).await
155    }
156
157    /// Patches the bucket.
158    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/patch
159    ///
160    /// ```
161    /// use google_cloud_storage::client::Client;
162    /// use google_cloud_storage::client::ClientConfig;
163    /// use google_cloud_storage::http::buckets::patch::{BucketPatchConfig, PatchBucketRequest};
164    ///
165    /// async fn run(config: ClientConfig) {
166    ///     let mut client = Client::new(config);
167    ///
168    ///     let result = client.patch_bucket(&PatchBucketRequest {
169    ///         bucket: "bucket".to_string(),
170    ///         metadata: Some(BucketPatchConfig {
171    ///             ..Default::default()
172    ///         }),
173    ///         ..Default::default()
174    ///     }).await;
175    /// }
176    /// ```
177    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
178    pub async fn patch_bucket(&self, req: &PatchBucketRequest) -> Result<Bucket, Error> {
179        let builder = buckets::patch::build(self.v1_endpoint.as_str(), &self.http, req);
180        self.send(builder).await
181    }
182
183    /// Lists the bucket.
184    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/list
185    ///
186    /// ```
187    /// use google_cloud_storage::client::Client;
188    /// use google_cloud_storage::http::buckets::list::ListBucketsRequest;
189    ///
190    /// async fn run(client:Client) {
191    ///     let result = client.list_buckets(&ListBucketsRequest{
192    ///         project: "project_id".to_string(),
193    ///         ..Default::default()
194    ///     }).await;
195    /// }
196    /// ```
197    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
198    pub async fn list_buckets(&self, req: &ListBucketsRequest) -> Result<ListBucketsResponse, Error> {
199        let builder = buckets::list::build(self.v1_endpoint.as_str(), &self.http, req);
200        self.send(builder).await
201    }
202
203    /// Sets the iam policy.
204    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/setIamPolicy
205    ///
206    /// ```
207    /// use google_cloud_storage::client::Client;
208    /// use google_cloud_storage::http::buckets::{Binding, Policy};
209    /// use google_cloud_storage::http::buckets::set_iam_policy::SetIamPolicyRequest;
210    ///
211    /// async fn run(client:Client) {
212    ///     let result = client.set_iam_policy(&SetIamPolicyRequest{
213    ///         resource: "bucket".to_string(),
214    ///         policy: Policy {
215    ///             bindings: vec![Binding {
216    ///                 role: "roles/storage.objectViewer".to_string(),
217    ///                 members: vec!["allAuthenticatedUsers".to_string()],
218    ///                 condition: None,
219    ///             }],
220    ///             ..Default::default()
221    ///         },
222    ///         ..Default::default()
223    ///     }).await;
224    /// }
225    /// ```
226    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
227    pub async fn set_iam_policy(&self, req: &SetIamPolicyRequest) -> Result<Policy, Error> {
228        let builder = buckets::set_iam_policy::build(self.v1_endpoint.as_str(), &self.http, req);
229        self.send(builder).await
230    }
231
232    /// Gets the iam policy.
233    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/getIamPolicy
234    ///
235    /// ```
236    /// use google_cloud_storage::client::Client;
237    /// use google_cloud_storage::http::buckets::get_iam_policy::GetIamPolicyRequest;
238    /// use google_cloud_storage::http::buckets::list::ListBucketsRequest;
239    ///
240    /// async fn run(client:Client) {
241    ///     let result = client.get_iam_policy(&GetIamPolicyRequest{
242    ///         resource: "bucket".to_string(),
243    ///         ..Default::default()
244    ///     }).await;
245    /// }
246    /// ```
247    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
248    pub async fn get_iam_policy(&self, req: &GetIamPolicyRequest) -> Result<Policy, Error> {
249        let builder = buckets::get_iam_policy::build(self.v1_endpoint.as_str(), &self.http, req);
250        self.send(builder).await
251    }
252
253    /// Tests the iam permissions.
254    /// https://cloud.google.com/storage/docs/json_api/v1/buckets/testIamPermissions
255    ///
256    /// ```
257    /// use google_cloud_storage::client::Client;
258    /// use google_cloud_storage::http::buckets::test_iam_permissions::TestIamPermissionsRequest;
259    ///
260    /// async fn run(client:Client) {
261    ///     let result = client.test_iam_permissions(&TestIamPermissionsRequest{
262    ///         resource: "bucket".to_string(),
263    ///         permissions: vec!["storage.buckets.get".to_string()],
264    ///     }).await;
265    /// }
266    /// ```
267    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
268    pub async fn test_iam_permissions(
269        &self,
270        req: &TestIamPermissionsRequest,
271    ) -> Result<TestIamPermissionsResponse, Error> {
272        let builder = buckets::test_iam_permissions::build(self.v1_endpoint.as_str(), &self.http, req);
273        self.send(builder).await
274    }
275
276    /// Lists the default object ACL.
277    /// https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls/list
278    ///
279    /// ```
280    /// use google_cloud_storage::client::Client;
281    /// use google_cloud_storage::http::buckets::test_iam_permissions::TestIamPermissionsRequest;
282    /// use google_cloud_storage::http::default_object_access_controls::list::ListDefaultObjectAccessControlsRequest;
283    ///
284    /// async fn run(client:Client) {
285    ///     let result = client.list_default_object_access_controls(&ListDefaultObjectAccessControlsRequest{
286    ///         bucket: "bucket".to_string(),
287    ///         ..Default::default()
288    ///     }).await;
289    /// }
290    /// ```
291    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
292    pub async fn list_default_object_access_controls(
293        &self,
294        req: &ListDefaultObjectAccessControlsRequest,
295    ) -> Result<ListDefaultObjectAccessControlsResponse, Error> {
296        let builder = default_object_access_controls::list::build(self.v1_endpoint.as_str(), &self.http, req);
297        self.send(builder).await
298    }
299
300    /// Gets the default object ACL.
301    /// https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls/get
302    ///
303    /// ```
304    /// use google_cloud_storage::client::Client;
305    /// use google_cloud_storage::http::default_object_access_controls::get::GetDefaultObjectAccessControlRequest;
306    ///
307    /// async fn run(client:Client) {
308    ///     let result = client.get_default_object_access_control(&GetDefaultObjectAccessControlRequest{
309    ///         bucket: "bucket".to_string(),
310    ///         entity: "allAuthenticatedUsers".to_string(),
311    ///     }).await;
312    /// }
313    /// ```
314    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
315    pub async fn get_default_object_access_control(
316        &self,
317        req: &GetDefaultObjectAccessControlRequest,
318    ) -> Result<ObjectAccessControl, Error> {
319        let builder = default_object_access_controls::get::build(self.v1_endpoint.as_str(), &self.http, req);
320        self.send(builder).await
321    }
322
323    /// Inserts the default object ACL.
324    /// https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls/insert
325    ///
326    /// ```
327    /// use google_cloud_storage::client::Client;
328    /// use google_cloud_storage::http::default_object_access_controls::insert::InsertDefaultObjectAccessControlRequest;
329    /// use google_cloud_storage::http::object_access_controls::insert::ObjectAccessControlCreationConfig;
330    /// use google_cloud_storage::http::object_access_controls::ObjectACLRole;
331    ///
332    /// async fn run(client:Client) {
333    ///     let result = client.insert_default_object_access_control(&InsertDefaultObjectAccessControlRequest{
334    ///         bucket: "bucket".to_string(),
335    ///         object_access_control: ObjectAccessControlCreationConfig {
336    ///             entity: "allAuthenticatedUsers".to_string(),
337    ///             role: ObjectACLRole::READER
338    ///         } ,
339    ///     }).await;
340    /// }
341    /// ```
342    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
343    pub async fn insert_default_object_access_control(
344        &self,
345        req: &InsertDefaultObjectAccessControlRequest,
346    ) -> Result<ObjectAccessControl, Error> {
347        let builder = default_object_access_controls::insert::build(self.v1_endpoint.as_str(), &self.http, req);
348        self.send(builder).await
349    }
350
351    /// Patches the default object ACL.
352    /// https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls/patch
353    ///
354    /// ```
355    /// use google_cloud_storage::client::Client;
356    /// use google_cloud_storage::http::default_object_access_controls::patch::PatchDefaultObjectAccessControlRequest;
357    /// use google_cloud_storage::http::object_access_controls::insert::ObjectAccessControlCreationConfig;
358    /// use google_cloud_storage::http::object_access_controls::{ObjectAccessControl, ObjectACLRole};
359    /// use google_cloud_storage::http::object_access_controls::patch::PatchObjectAccessControlRequest;
360    ///
361    /// async fn run(client:Client) {
362    ///     let result = client.patch_default_object_access_control(&PatchDefaultObjectAccessControlRequest{
363    ///         bucket: "bucket".to_string(),
364    ///         entity: "allAuthenticatedUsers".to_string(),
365    ///         object_access_control: ObjectAccessControl {
366    ///             role: ObjectACLRole::READER,
367    ///             ..Default::default()
368    ///         },
369    ///     }).await;
370    /// }
371    /// ```
372    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
373    pub async fn patch_default_object_access_control(
374        &self,
375        req: &PatchDefaultObjectAccessControlRequest,
376    ) -> Result<ObjectAccessControl, Error> {
377        let builder = default_object_access_controls::patch::build(self.v1_endpoint.as_str(), &self.http, req);
378        self.send(builder).await
379    }
380
381    /// Deletes the default object ACL.
382    /// https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls/delete
383    ///
384    /// ```
385    /// use google_cloud_storage::client::Client;
386    /// use google_cloud_storage::http::default_object_access_controls::delete::DeleteDefaultObjectAccessControlRequest;
387    ///
388    /// async fn run(client:Client) {
389    ///     let result = client.delete_default_object_access_control(&DeleteDefaultObjectAccessControlRequest{
390    ///         bucket: "bucket".to_string(),
391    ///         entity: "allAuthenticatedUsers".to_string(),
392    ///     }).await;
393    /// }
394    /// ```
395    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
396    pub async fn delete_default_object_access_control(
397        &self,
398        req: &DeleteDefaultObjectAccessControlRequest,
399    ) -> Result<(), Error> {
400        let builder = default_object_access_controls::delete::build(self.v1_endpoint.as_str(), &self.http, req);
401        self.send_get_empty(builder).await
402    }
403
404    /// Lists the bucket ACL.
405    /// https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/list
406    ///
407    /// ```
408    /// use google_cloud_storage::client::Client;
409    /// use google_cloud_storage::http::bucket_access_controls::list::ListBucketAccessControlsRequest;
410    ///
411    /// async fn run(client:Client) {
412    ///     let result = client.list_bucket_access_controls(&ListBucketAccessControlsRequest{
413    ///         bucket: "bucket".to_string(),
414    ///     }).await;
415    /// }
416    /// ```
417    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
418    pub async fn list_bucket_access_controls(
419        &self,
420        req: &ListBucketAccessControlsRequest,
421    ) -> Result<ListBucketAccessControlsResponse, Error> {
422        let builder = bucket_access_controls::list::build(self.v1_endpoint.as_str(), &self.http, req);
423        self.send(builder).await
424    }
425
426    /// Gets the bucket ACL.
427    /// https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/get
428    ///
429    /// ```
430    /// use google_cloud_storage::client::Client;
431    /// use google_cloud_storage::http::bucket_access_controls::get::GetBucketAccessControlRequest;
432    ///
433    /// async fn run(client:Client) {
434    ///     let result = client.get_bucket_access_control(&GetBucketAccessControlRequest{
435    ///         bucket: "bucket".to_string(),
436    ///         entity: "allAuthenticatedUsers".to_string(),
437    ///     }).await;
438    /// }
439    /// ```
440    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
441    pub async fn get_bucket_access_control(
442        &self,
443        req: &GetBucketAccessControlRequest,
444    ) -> Result<BucketAccessControl, Error> {
445        let builder = bucket_access_controls::get::build(self.v1_endpoint.as_str(), &self.http, req);
446        self.send(builder).await
447    }
448
449    /// Inserts the bucket ACL.
450    /// https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/insert
451    ///
452    /// ```
453    /// use google_cloud_storage::client::Client;
454    /// use google_cloud_storage::http::bucket_access_controls::BucketACLRole;
455    /// use google_cloud_storage::http::bucket_access_controls::insert::{BucketAccessControlCreationConfig, InsertBucketAccessControlRequest};
456    ///
457    /// async fn run(client:Client) {
458    ///     let result = client.insert_bucket_access_control(&InsertBucketAccessControlRequest{
459    ///         bucket: "bucket".to_string(),
460    ///         acl: BucketAccessControlCreationConfig {
461    ///             entity: "allAuthenticatedUsers".to_string(),
462    ///             role: BucketACLRole::READER
463    ///         }
464    ///     }).await;
465    /// }
466    /// ```
467    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
468    pub async fn insert_bucket_access_control(
469        &self,
470        req: &InsertBucketAccessControlRequest,
471    ) -> Result<BucketAccessControl, Error> {
472        let builder = bucket_access_controls::insert::build(self.v1_endpoint.as_str(), &self.http, req);
473        self.send(builder).await
474    }
475
476    /// Patches the bucket ACL.
477    /// https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/patch
478    ///
479    /// ```
480    /// use google_cloud_storage::client::Client;
481    /// use google_cloud_storage::http::bucket_access_controls::BucketAccessControl;
482    /// use google_cloud_storage::http::bucket_access_controls::BucketACLRole;
483    /// use google_cloud_storage::http::bucket_access_controls::patch::PatchBucketAccessControlRequest;
484    ///
485    /// async fn run(client:Client) {
486    ///     let result = client.patch_bucket_access_control(&PatchBucketAccessControlRequest{
487    ///         bucket: "bucket".to_string(),
488    ///         entity: "allAuthenticatedUsers".to_string(),
489    ///         acl: BucketAccessControl {
490    ///             role: BucketACLRole::READER,
491    ///             ..Default::default()
492    ///         }
493    ///     }).await;
494    /// }
495    /// ```
496    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
497    pub async fn patch_bucket_access_control(
498        &self,
499        req: &PatchBucketAccessControlRequest,
500    ) -> Result<BucketAccessControl, Error> {
501        let builder = bucket_access_controls::patch::build(self.v1_endpoint.as_str(), &self.http, req);
502        self.send(builder).await
503    }
504
505    /// Deletes the bucket ACL.
506    /// ```
507    /// use google_cloud_storage::client::Client;
508    /// use google_cloud_storage::http::bucket_access_controls::BucketAccessControl;
509    /// use google_cloud_storage::http::bucket_access_controls::delete::DeleteBucketAccessControlRequest;
510    ///
511    /// async fn run(client:Client) {
512    ///     let result = client.delete_bucket_access_control(&DeleteBucketAccessControlRequest{
513    ///         bucket: "bucket".to_string(),
514    ///         entity: "allAuthenticatedUsers".to_string(),
515    ///     }).await;
516    /// }
517    /// ```
518    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
519    pub async fn delete_bucket_access_control(&self, req: &DeleteBucketAccessControlRequest) -> Result<(), Error> {
520        let builder = bucket_access_controls::delete::build(self.v1_endpoint.as_str(), &self.http, req);
521        self.send_get_empty(builder).await
522    }
523
524    /// Lists the object ACL.
525    /// https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/list
526    ///
527    /// ```
528    /// use google_cloud_storage::client::Client;
529    /// use google_cloud_storage::http::object_access_controls::list::ListObjectAccessControlsRequest;
530    ///
531    /// async fn run(client:Client) {
532    ///     let result = client.list_object_access_controls(&ListObjectAccessControlsRequest{
533    ///         bucket: "bucket".to_string(),
534    ///         object: "filename".to_string(),
535    ///         ..Default::default()
536    ///     }).await;
537    /// }
538    /// ```
539    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
540    pub async fn list_object_access_controls(
541        &self,
542        req: &ListObjectAccessControlsRequest,
543    ) -> Result<ListBucketAccessControlsResponse, Error> {
544        let builder = object_access_controls::list::build(self.v1_endpoint.as_str(), &self.http, req);
545        self.send(builder).await
546    }
547
548    /// Gets the object ACL.
549    /// https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/get
550    ///
551    /// ```
552    /// use google_cloud_storage::client::Client;
553    /// use google_cloud_storage::http::object_access_controls::get::GetObjectAccessControlRequest;
554    ///
555    ///
556    /// async fn run(client:Client) {
557    ///
558    ///     let result = client.get_object_access_control(&GetObjectAccessControlRequest{
559    ///         bucket: "bucket".to_string(),
560    ///         object: "filename".to_string(),
561    ///         entity: "allAuthenticatedUsers".to_string(),
562    ///         ..Default::default()
563    ///     }).await;
564    /// }
565    /// ```
566    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
567    pub async fn get_object_access_control(
568        &self,
569        req: &GetObjectAccessControlRequest,
570    ) -> Result<ObjectAccessControl, Error> {
571        let builder = object_access_controls::get::build(self.v1_endpoint.as_str(), &self.http, req);
572        self.send(builder).await
573    }
574
575    /// Inserts the object ACL.
576    /// https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/insert
577    ///
578    /// ```
579    /// use google_cloud_storage::client::Client;
580    /// use google_cloud_storage::http::object_access_controls::insert::{InsertObjectAccessControlRequest, ObjectAccessControlCreationConfig};
581    /// use google_cloud_storage::http::object_access_controls::ObjectACLRole;
582    ///
583    ///
584    /// async fn run(client:Client) {
585    ///
586    ///     let result = client.insert_object_access_control(&InsertObjectAccessControlRequest{
587    ///         bucket: "bucket".to_string(),
588    ///         object: "filename".to_string(),
589    ///         acl: ObjectAccessControlCreationConfig {
590    ///             entity: "allAuthenticatedUsers".to_string(),
591    ///             role: ObjectACLRole::READER
592    ///         },
593    ///         ..Default::default()
594    ///     }).await;
595    /// }
596    /// ```
597    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
598    pub async fn insert_object_access_control(
599        &self,
600        req: &InsertObjectAccessControlRequest,
601    ) -> Result<ObjectAccessControl, Error> {
602        let builder = object_access_controls::insert::build(self.v1_endpoint.as_str(), &self.http, req);
603        self.send(builder).await
604    }
605
606    /// Patches the bucket ACL.
607    /// https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/patch
608    ///
609    /// ```
610    /// use google_cloud_storage::client::Client;
611    /// use google_cloud_storage::http::object_access_controls::{ObjectAccessControl, ObjectACLRole};
612    /// use google_cloud_storage::http::object_access_controls::patch::PatchObjectAccessControlRequest;
613    ///
614    ///
615    /// async fn run(client:Client) {
616    ///
617    ///     let result = client.patch_object_access_control(&PatchObjectAccessControlRequest{
618    ///         bucket: "bucket".to_string(),
619    ///         object: "filename".to_string(),
620    ///         entity: "allAuthenticatedUsers".to_string(),
621    ///         acl: ObjectAccessControl {
622    ///             role: ObjectACLRole::READER,
623    ///             ..Default::default()
624    ///         },
625    ///         ..Default::default()
626    ///     }).await;
627    /// }
628    /// ```
629    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
630    pub async fn patch_object_access_control(
631        &self,
632        req: &PatchObjectAccessControlRequest,
633    ) -> Result<ObjectAccessControl, Error> {
634        let builder = object_access_controls::patch::build(self.v1_endpoint.as_str(), &self.http, req);
635        self.send(builder).await
636    }
637
638    /// Deletes the bucket ACL.
639    /// https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/delete
640    ///
641    /// ```
642    /// use google_cloud_storage::client::Client;
643    /// use google_cloud_storage::http::object_access_controls::{ObjectAccessControl, ObjectACLRole};
644    /// use google_cloud_storage::http::object_access_controls::delete::DeleteObjectAccessControlRequest;
645    ///
646    /// async fn run(client:Client) {
647    ///     let result = client.delete_object_access_control(&DeleteObjectAccessControlRequest{
648    ///         bucket: "bucket".to_string(),
649    ///         object: "filename".to_string(),
650    ///         entity: "allAuthenticatedUsers".to_string(),
651    ///         ..Default::default()
652    ///     }).await;
653    /// }
654    /// ```
655    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
656    pub async fn delete_object_access_control(&self, req: &DeleteObjectAccessControlRequest) -> Result<(), Error> {
657        let builder = object_access_controls::delete::build(self.v1_endpoint.as_str(), &self.http, req);
658        self.send_get_empty(builder).await
659    }
660
661    /// Lists the notification.
662    /// https://cloud.google.com/storage/docs/json_api/v1/notifications/list
663    ///
664    /// ```
665    /// use google_cloud_storage::client::Client;
666    /// use google_cloud_storage::http::notifications::list::ListNotificationsRequest;
667    ///
668    /// async fn run(client:Client) {
669    ///     let result = client.list_notifications(&ListNotificationsRequest{
670    ///         bucket: "bucket".to_string(),
671    ///         ..Default::default()
672    ///     }).await;
673    /// }
674    /// ```
675    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
676    pub async fn list_notifications(&self, req: &ListNotificationsRequest) -> Result<ListNotificationsResponse, Error> {
677        let builder = notifications::list::build(self.v1_endpoint.as_str(), &self.http, req);
678        self.send(builder).await
679    }
680
681    /// Gets the notification.
682    /// https://cloud.google.com/storage/docs/json_api/v1/notifications/get
683    ///
684    /// ```
685    /// use google_cloud_storage::client::Client;
686    /// use google_cloud_storage::http::notifications::get::GetNotificationRequest;
687    ///
688    ///
689    /// async fn run(client:Client) {
690    ///
691    ///     let result = client.get_notification(&GetNotificationRequest{
692    ///         bucket: "bucket".to_string(),
693    ///         notification: "notification".to_string()
694    ///     }).await;
695    /// }
696    /// ```
697    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
698    pub async fn get_notification(&self, req: &GetNotificationRequest) -> Result<Notification, Error> {
699        let builder = notifications::get::build(self.v1_endpoint.as_str(), &self.http, req);
700        self.send(builder).await
701    }
702
703    /// Inserts the notification.
704    /// https://cloud.google.com/storage/docs/json_api/v1/notifications/insert
705    ///
706    /// ```
707    /// use google_cloud_storage::client::Client;
708    /// use google_cloud_storage::http::notifications::EventType;
709    /// use google_cloud_storage::http::notifications::insert::{InsertNotificationRequest, NotificationCreationConfig};
710    ///
711    ///
712    /// async fn run(client:Client) {
713    ///
714    ///     let result = client.insert_notification(&InsertNotificationRequest {
715    ///         bucket: "bucket".to_string(),
716    ///         notification: NotificationCreationConfig {
717    ///             topic: format!("projects/{}/topics/{}", "project","bucket"),
718    ///             event_types: Some(vec![EventType::ObjectMetadataUpdate, EventType::ObjectDelete]),
719    ///             ..Default::default()
720    ///         }
721    ///     }).await;
722    /// }
723    /// ```
724    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
725    pub async fn insert_notification(&self, req: &InsertNotificationRequest) -> Result<Notification, Error> {
726        let builder = notifications::insert::build(self.v1_endpoint.as_str(), &self.http, req);
727        self.send(builder).await
728    }
729
730    /// Deletes the notification.
731    /// https://cloud.google.com/storage/docs/json_api/v1/notifications/delete
732    ///
733    /// ```
734    /// use google_cloud_storage::client::Client;
735    /// use google_cloud_storage::http::notifications::delete::DeleteNotificationRequest;
736    ///
737    ///
738    /// async fn run(client:Client) {
739    ///
740    ///     let result = client.delete_notification(&DeleteNotificationRequest {
741    ///         bucket: "bucket".to_string(),
742    ///         notification: "notification".to_string()
743    ///     }).await;
744    /// }
745    /// ```
746    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
747    pub async fn delete_notification(&self, req: &DeleteNotificationRequest) -> Result<(), Error> {
748        let builder = notifications::delete::build(self.v1_endpoint.as_str(), &self.http, req);
749        self.send_get_empty(builder).await
750    }
751
752    /// Lists the hmac keys.
753    /// https://cloud.google.com/storage/docs/json_api/v1/projects/hmacKeys/list
754    ///
755    /// ```
756    /// use google_cloud_storage::client::Client;
757    /// use google_cloud_storage::http::hmac_keys::list::ListHmacKeysRequest;
758    ///
759    ///
760    /// async fn run(client:Client) {
761    ///
762    ///     let result = client.list_hmac_keys(&ListHmacKeysRequest {
763    ///         project_id: "project_id".to_string(),
764    ///         ..Default::default()
765    ///     }).await;
766    /// }
767    /// ```
768    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
769    pub async fn list_hmac_keys(&self, req: &ListHmacKeysRequest) -> Result<ListHmacKeysResponse, Error> {
770        let builder = hmac_keys::list::build(self.v1_endpoint.as_str(), &self.http, req);
771        self.send(builder).await
772    }
773
774    /// Gets the hmac keys.
775    /// https://cloud.google.com/storage/docs/json_api/v1/projects/hmacKeys/get
776    ///
777    /// ```
778    /// use google_cloud_storage::client::Client;
779    /// use google_cloud_storage::http::hmac_keys::get::GetHmacKeyRequest;
780    ///
781    ///
782    /// async fn run(client:Client) {
783    ///
784    ///     let result = client.get_hmac_key(&GetHmacKeyRequest {
785    ///         access_id: "access_id".to_string(),
786    ///         project_id: "project_id".to_string(),
787    ///     }).await;
788    /// }
789    /// ```
790    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
791    pub async fn get_hmac_key(&self, req: &GetHmacKeyRequest) -> Result<HmacKeyMetadata, Error> {
792        let builder = hmac_keys::get::build(self.v1_endpoint.as_str(), &self.http, req);
793        self.send(builder).await
794    }
795
796    /// Creates the hmac key.
797    /// https://cloud.google.com/storage/docs/json_api/v1/projects/hmacKeys/create
798    ///
799    /// ```
800    /// use google_cloud_storage::client::Client;
801    /// use google_cloud_storage::http::hmac_keys::create::CreateHmacKeyRequest;
802    ///
803    ///
804    /// async fn run(client:Client) {
805    ///
806    ///     let result = client.create_hmac_key(&CreateHmacKeyRequest {
807    ///         service_account_email: "service_account_email".to_string(),
808    ///         project_id: "project".to_string(),
809    ///     }).await;
810    /// }
811    /// ```
812    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
813    pub async fn create_hmac_key(&self, req: &CreateHmacKeyRequest) -> Result<CreateHmacKeyResponse, Error> {
814        let builder = hmac_keys::create::build(self.v1_endpoint.as_str(), &self.http, req);
815        self.send(builder).await
816    }
817
818    /// Updates the hmac key.
819    /// https://cloud.google.com/storage/docs/json_api/v1/projects/hmacKeys/update
820    ///
821    /// ```
822    /// use google_cloud_storage::client::Client;
823    /// use google_cloud_storage::http::hmac_keys::HmacKeyMetadata;
824    /// use google_cloud_storage::http::hmac_keys::update::UpdateHmacKeyRequest;
825    ///
826    ///
827    /// async fn run(client:Client) {
828    ///
829    ///     let result = client.update_hmac_key(&UpdateHmacKeyRequest{
830    ///         access_id: "access_id".to_string(),
831    ///         project_id: "project_id".to_string(),
832    ///         metadata: HmacKeyMetadata {
833    ///             state: "INACTIVE".to_string(),
834    ///             ..Default::default()
835    ///         },
836    ///     }).await;
837    /// }
838    /// ```
839    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
840    pub async fn update_hmac_key(&self, req: &UpdateHmacKeyRequest) -> Result<HmacKeyMetadata, Error> {
841        let builder = hmac_keys::update::build(self.v1_endpoint.as_str(), &self.http, req);
842        self.send(builder).await
843    }
844
845    /// Deletes the hmac key.
846    /// https://cloud.google.com/storage/docs/json_api/v1/projects/hmacKeys/delete
847    ///
848    /// ```
849    /// use google_cloud_storage::client::Client;
850    /// use google_cloud_storage::http::hmac_keys::delete::DeleteHmacKeyRequest;
851    ///
852    ///
853    /// async fn run(client:Client) {
854    ///
855    ///     let result = client.delete_hmac_key(&DeleteHmacKeyRequest{
856    ///         access_id: "access_id".to_string(),
857    ///         project_id:"project_id".to_string(),
858    ///     }).await;
859    /// }
860    /// ```
861    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
862    pub async fn delete_hmac_key(&self, req: &DeleteHmacKeyRequest) -> Result<(), Error> {
863        let builder = hmac_keys::delete::build(self.v1_endpoint.as_str(), &self.http, req);
864        self.send_get_empty(builder).await
865    }
866
867    /// Lists the objects.
868    /// https://cloud.google.com/storage/docs/json_api/v1/objects/list
869    ///
870    /// ```
871    /// use google_cloud_storage::client::Client;
872    /// use google_cloud_storage::http::objects::list::ListObjectsRequest;
873    ///
874    ///
875    /// async fn run(client:Client) {
876    ///
877    ///     let result = client.list_objects(&ListObjectsRequest{
878    ///         bucket: "bucket".to_string(),
879    ///         ..Default::default()
880    ///     }).await;
881    /// }
882    /// ```
883    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
884    pub async fn list_objects(&self, req: &ListObjectsRequest) -> Result<ListObjectsResponse, Error> {
885        let builder = objects::list::build(self.v1_endpoint.as_str(), &self.http, req);
886        self.send(builder).await
887    }
888
889    /// Gets the object.
890    /// https://cloud.google.com/storage/docs/json_api/v1/objects/get
891    ///
892    /// ```
893    /// use google_cloud_storage::client::Client;
894    /// use google_cloud_storage::http::objects::get::GetObjectRequest;
895    ///
896    /// async fn run(client:Client) {
897    ///     let result = client.get_object(&GetObjectRequest{
898    ///         bucket: "bucket".to_string(),
899    ///         object: "object".to_string(),
900    ///         ..Default::default()
901    ///     }).await;
902    /// }
903    /// ```
904    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
905    pub async fn get_object(&self, req: &GetObjectRequest) -> Result<Object, Error> {
906        let builder = objects::get::build(self.v1_endpoint.as_str(), &self.http, req);
907        self.send(builder).await
908    }
909
910    /// Copy the object.
911    /// https://cloud.google.com/storage/docs/json_api/v1/objects/copy
912    ///
913    /// ```
914    /// use google_cloud_storage::client::Client;
915    /// use google_cloud_storage::http::objects::copy::CopyObjectRequest;
916    ///
917    /// async fn run(client:Client) {
918    ///     let result = client.copy_object(&CopyObjectRequest{
919    ///         source_bucket: "bucket".to_string(),
920    ///         destination_bucket: "bucket".to_string(),
921    ///         destination_object: "object".to_string(),
922    ///         source_object: "object".to_string(),
923    ///         ..Default::default()
924    ///     }).await;
925    /// }
926    /// ```
927    ///
928    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
929    pub async fn copy_object(&self, req: &CopyObjectRequest) -> Result<Object, Error> {
930        let builder = objects::copy::build(self.v1_endpoint.as_str(), &self.http, req);
931        self.send(builder).await
932    }
933
934    /// Move the object.
935    /// https://cloud.google.com/storage/docs/copying-renaming-moving-objects#rest-move-object
936    /// This function will first make a copy of the object, and then delete the original object.
937    ///
938    /// ```
939    /// use google_cloud_storage::client::Client;
940    /// use google_cloud_storage::http::objects::r#move::MoveObjectRequest;
941    ///
942    /// async fn run(client:Client) {
943    ///     let result = client.move_object(&MoveObjectRequest{
944    ///         source_bucket: "source_bucket".to_string(),
945    ///         source_object: "source_object".to_string(),
946    ///         destination_bucket: "destination_source".to_string(),
947    ///         destination_object: "destination_object".to_string(),
948    ///         ..Default::default()
949    ///     }).await;
950    /// }
951    /// ```
952    ///
953    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
954    pub async fn move_object(&self, req: &MoveObjectRequest) -> Result<Object, Error> {
955        let copy_req: CopyObjectRequest = req.clone().into();
956        let delete_req: DeleteObjectRequest = req.clone().into();
957        // Only result of the copy operations is of interest, as it contains details of destination.
958        let copy_result = self.copy_object(&copy_req).await?;
959        self.delete_object(&delete_req).await?;
960        Ok(copy_result)
961    }
962
963    /// Download the object.
964    /// https://cloud.google.com/storage/docs/json_api/v1/objects/get
965    /// alt is always media
966    ///
967    /// ```
968    /// use google_cloud_storage::client::Client;
969    /// use google_cloud_storage::http::objects::get::GetObjectRequest;
970    /// use google_cloud_storage::http::objects::download::Range;
971    ///
972    ///
973    /// async fn run(client:Client) {
974    ///
975    ///     let result = client.download_object(&GetObjectRequest{
976    ///         bucket: "bucket".to_string(),
977    ///         object: "object".to_string(),
978    ///         ..Default::default()
979    ///     }, &Range::default()).await;
980    /// }
981    /// ```
982    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
983    pub async fn download_object(&self, req: &GetObjectRequest, range: &Range) -> Result<Vec<u8>, Error> {
984        let builder = objects::download::build(self.v1_endpoint.as_str(), &self.http, req, range);
985        let request = self.with_headers(builder).await?;
986        let response = request.send().await?;
987        let response = check_response_status(response).await?;
988        Ok(response.bytes().await?.to_vec())
989    }
990
991    /// Download the object.
992    /// https://cloud.google.com/storage/docs/json_api/v1/objects/get
993    /// alt is always media
994    ///
995    /// ```
996    /// use google_cloud_storage::client::Client;
997    /// use google_cloud_storage::http::objects::get::GetObjectRequest;
998    /// use google_cloud_storage::http::objects::download::Range;
999    ///
1000    /// async fn run(client:Client) {
1001    ///     let result = client.download_streamed_object(&GetObjectRequest{
1002    ///         bucket: "bucket".to_string(),
1003    ///         object: "object".to_string(),
1004    ///         ..Default::default()
1005    ///     }, &Range::default()).await;
1006    ///
1007    ///     //  while let Some(v) = downloaded.next().await? {
1008    ///     //      let d: bytes::Bytes = v.unwrap();
1009    ///     //  }
1010    /// }
1011    /// ```
1012    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1013    pub async fn download_streamed_object(
1014        &self,
1015        req: &GetObjectRequest,
1016        range: &Range,
1017    ) -> Result<impl Stream<Item = Result<bytes::Bytes, Error>>, Error> {
1018        let builder = objects::download::build(self.v1_endpoint.as_str(), &self.http, req, range);
1019        let request = self.with_headers(builder).await?;
1020        let response = request.send().await?;
1021        let response = check_response_status(response).await?;
1022        Ok(response.bytes_stream().map_err(Error::from))
1023    }
1024
1025    /// Uploads the object.
1026    /// https://cloud.google.com/storage/docs/json_api/v1/objects/insert
1027    ///
1028    /// ```
1029    /// use std::collections::HashMap;
1030    /// use google_cloud_storage::client::Client;
1031    /// use google_cloud_storage::http::objects::Object;
1032    /// use google_cloud_storage::http::objects::upload::{Media, UploadObjectRequest, UploadType};
1033    ///
1034    /// async fn run_simple(client:Client) {
1035    ///     let upload_type = UploadType::Simple(Media::new("filename"));
1036    ///     let result = client.upload_object(&UploadObjectRequest{
1037    ///         bucket: "bucket".to_string(),
1038    ///         ..Default::default()
1039    ///     }, "hello world".as_bytes(), &upload_type).await;
1040    /// }
1041    ///
1042    /// async fn run_multipart(client:Client) {
1043    ///     let mut metadata = HashMap::<String, String>::new();
1044    ///     metadata.insert("key1".to_string(), "value1".to_string());
1045    ///     let upload_type = UploadType::Multipart(Box::new(Object {
1046    ///         name: "test1_meta".to_string(),
1047    ///         content_type: Some("text/plain".to_string()),
1048    ///         metadata: Some(metadata),
1049    ///         ..Default::default()
1050    ///     }));
1051    ///     let result = client.upload_object(&UploadObjectRequest{
1052    ///         bucket: "bucket".to_string(),
1053    ///         ..Default::default()
1054    ///     }, "hello world".as_bytes(), &upload_type).await;
1055    /// }
1056    /// ```
1057    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1058    pub async fn upload_object<T: Into<Body>>(
1059        &self,
1060        req: &UploadObjectRequest,
1061        data: T,
1062        upload_type: &UploadType,
1063    ) -> Result<Object, Error> {
1064        match upload_type {
1065            UploadType::Multipart(meta) => {
1066                let builder =
1067                    objects::upload::build_multipart(self.v1_upload_endpoint.as_str(), &self.http, req, meta, data)?;
1068                self.send(builder).await
1069            }
1070            UploadType::Simple(media) => {
1071                let builder = objects::upload::build(self.v1_upload_endpoint.as_str(), &self.http, req, media, data);
1072                let builder = self.with_headers(builder).await?;
1073                let mut request = builder.build()?;
1074                // In the case of not streamed and 0 bytes, Content-Length=0 must be explicitly specified.
1075                if !request.headers().contains_key(CONTENT_LENGTH) {
1076                    if let Some(Some(is_empty)) = request.body().map(|b| b.as_bytes().map(|b| b.is_empty())) {
1077                        if is_empty {
1078                            request
1079                                .headers_mut()
1080                                .insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
1081                        }
1082                    }
1083                }
1084                self.send_request(request).await
1085            }
1086        }
1087    }
1088
1089    /// Creates resumable upload from known URL.
1090    ///
1091    /// Assumes URL is correct, if not, `ResumableUploadClient` is not guaranteed to perform correctly.
1092    pub fn get_resumable_upload(&self, url: String) -> ResumableUploadClient {
1093        ResumableUploadClient::new(url, self.http.clone())
1094    }
1095
1096    /// Perform resumable uploads
1097    /// https://cloud.google.com/storage/docs/performing-resumable-uploads
1098    ///
1099    /// ```
1100    /// use std::collections::HashMap;
1101    /// use google_cloud_storage::client::Client;
1102    /// use google_cloud_storage::http::objects::Object;
1103    /// use google_cloud_storage::http::objects::upload::{Media, UploadObjectRequest, UploadType};
1104    /// use google_cloud_storage::http::resumable_upload_client::{ChunkSize, UploadedRange, UploadStatus};
1105    ///
1106    /// async fn run_simple(client:Client) {
1107    ///     let upload_type = UploadType::Simple(Media::new("filename"));
1108    ///     let uploader = client.prepare_resumable_upload(&UploadObjectRequest{
1109    ///         bucket: "bucket".to_string(),
1110    ///         ..Default::default()
1111    ///     }, &upload_type).await.unwrap();
1112    ///
1113    ///     // We can also use upload_multiple_chunk.
1114    ///     let data = [1,2,3,4,5];
1115    ///     let result = uploader.upload_single_chunk(Vec::from(data), data.len()).await;
1116    /// }
1117    ///
1118    /// async fn run_with_metadata(client:Client) {
1119    ///     let mut metadata = HashMap::<String, String>::new();
1120    ///     metadata.insert("key1".to_string(), "value1".to_string());
1121    ///     let upload_type = UploadType::Multipart(Box::new(Object {
1122    ///         name: "test1_meta".to_string(),
1123    ///         content_type: Some("text/plain".to_string()),
1124    ///         metadata: Some(metadata),
1125    ///         ..Default::default()
1126    ///     }));
1127    ///     let uploader = client.prepare_resumable_upload(&UploadObjectRequest{
1128    ///         bucket: "bucket".to_string(),
1129    ///         ..Default::default()
1130    ///     }, &upload_type).await.unwrap();
1131    ///
1132    ///     let chunk1_data : Vec<u8>= (0..256 * 1024).map(|i| (i % 256) as u8).collect();
1133    ///     let chunk2_data : Vec<u8>= (1..256 * 1024 + 50).map(|i| (i % 256) as u8).collect();
1134    ///     let chunk1_size = chunk1_data.len() as u64;
1135    ///     let chunk2_size = chunk2_data.len() as u64;
1136    ///     let total_size = Some(chunk1_size + chunk2_size);
1137    ///
1138    ///     // The chunk size should be multiple of 256KiB, unless it's the last chunk that completes the upload.
1139    ///     let chunk1 = ChunkSize::new(0, chunk1_size - 1, total_size.clone());
1140    ///     let status1 = uploader.upload_multiple_chunk(chunk1_data.clone(), &chunk1).await.unwrap();
1141    ///     assert_eq!(
1142    ///         status1,
1143    ///         UploadStatus::ResumeIncomplete(UploadedRange {
1144    ///             first_byte: 0,
1145    ///             last_byte: chunk1_data.len() as u64 - 1
1146    ///         })
1147    ///     );
1148    ///
1149    ///     let chunk2 = ChunkSize::new(chunk1_size, chunk1_size + chunk2_size - 1, total_size.clone());
1150    ///     let status2 = uploader.upload_multiple_chunk(chunk2_data.clone(), &chunk2).await.unwrap();
1151    ///     assert!(matches!(status2, UploadStatus::Ok(_)));
1152    /// }
1153    /// ```
1154    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1155    pub async fn prepare_resumable_upload(
1156        &self,
1157        req: &UploadObjectRequest,
1158        upload_type: &UploadType,
1159    ) -> Result<ResumableUploadClient, Error> {
1160        let request = match upload_type {
1161            UploadType::Multipart(meta) => objects::upload::build_resumable_session_metadata(
1162                self.v1_upload_endpoint.as_str(),
1163                &self.http,
1164                req,
1165                meta,
1166            ),
1167            UploadType::Simple(media) => objects::upload::build_resumable_session_simple(
1168                self.v1_upload_endpoint.as_str(),
1169                &self.http,
1170                req,
1171                media,
1172            ),
1173        };
1174        self.send_get_url(request)
1175            .await
1176            .map(|url| ResumableUploadClient::new(url, self.http.clone()))
1177    }
1178
1179    /// Uploads the streamed object.
1180    /// https://cloud.google.com/storage/docs/json_api/v1/objects/insert
1181    ///
1182    /// ```
1183    /// use google_cloud_storage::client::Client;
1184    /// use google_cloud_storage::http::objects::upload::{Media, UploadObjectRequest, UploadType};
1185    ///
1186    /// async fn run(client:Client) {
1187    ///     let source = vec!["hello", " ", "world"];
1188    ///     let size = source.iter().map(|x| x.len() as u64).sum();
1189    ///     let chunks: Vec<Result<_, ::std::io::Error>> = source.clone().into_iter().map(|x| Ok(x)).collect();
1190    ///     let stream = futures_util::stream::iter(chunks);
1191    ///     let mut media = Media::new("filename");
1192    ///     media.content_length = Some(size);
1193    ///     let mut upload_type = UploadType::Simple(media);
1194    ///     let result = client.upload_streamed_object(&UploadObjectRequest{
1195    ///         bucket: "bucket".to_string(),
1196    ///         ..Default::default()
1197    ///     }, stream, &upload_type).await;
1198    /// }
1199    /// ```
1200    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1201    pub async fn upload_streamed_object<S>(
1202        &self,
1203        req: &UploadObjectRequest,
1204        data: S,
1205        upload_type: &UploadType,
1206    ) -> Result<Object, Error>
1207    where
1208        S: TryStream + Send + Sync + 'static,
1209        S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
1210        bytes::Bytes: From<S::Ok>,
1211    {
1212        //TODO resumable upload
1213        self.upload_object(req, Body::wrap_stream(data), upload_type).await
1214    }
1215
1216    /// Patches the object.
1217    /// https://cloud.google.com/storage/docs/json_api/v1/objects/patch
1218    ///
1219    /// ```
1220    /// use google_cloud_storage::client::Client;
1221    /// use google_cloud_storage::http::objects::patch::PatchObjectRequest;
1222    ///
1223    ///
1224    /// async fn run(client:Client) {
1225    ///
1226    ///     let result = client.patch_object(&PatchObjectRequest{
1227    ///         bucket: "bucket".to_string(),
1228    ///         object: "object".to_string(),
1229    ///         ..Default::default()
1230    ///     }).await;
1231    /// }
1232    /// ```
1233    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1234    pub async fn patch_object(&self, req: &PatchObjectRequest) -> Result<Object, Error> {
1235        let builder = objects::patch::build(self.v1_endpoint.as_str(), &self.http, req);
1236        self.send(builder).await
1237    }
1238
1239    /// Deletes the object.
1240    /// https://cloud.google.com/storage/docs/json_api/v1/objects/delete
1241    ///
1242    /// ```
1243    /// use google_cloud_storage::client::Client;
1244    /// use google_cloud_storage::http::objects::delete::DeleteObjectRequest;
1245    ///
1246    ///
1247    /// async fn run(client:Client) {
1248    ///
1249    ///     let result = client.delete_object(&DeleteObjectRequest{
1250    ///         bucket: "bucket".to_string(),
1251    ///         object: "object".to_string(),
1252    ///         ..Default::default()
1253    ///     }).await;
1254    /// }
1255    /// ```
1256    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1257    pub async fn delete_object(&self, req: &DeleteObjectRequest) -> Result<(), Error> {
1258        let builder = objects::delete::build(self.v1_endpoint.as_str(), &self.http, req);
1259        self.send_get_empty(builder).await
1260    }
1261
1262    /// Rewrites the object.
1263    /// https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite
1264    ///
1265    /// ```
1266    /// use google_cloud_storage::client::Client;
1267    /// use google_cloud_storage::http::objects::rewrite::RewriteObjectRequest;
1268    ///
1269    ///
1270    /// async fn run(client:Client) {
1271    ///     let mut done = false;
1272    ///     let mut rewrite_token = None;
1273    ///
1274    ///     while !done {
1275    ///         let result = client.rewrite_object(&RewriteObjectRequest{
1276    ///             source_bucket: "bucket1".to_string(),
1277    ///             source_object: "object".to_string(),
1278    ///             destination_bucket: "bucket2".to_string(),
1279    ///             destination_object: "object1".to_string(),
1280    ///             rewrite_token: rewrite_token.clone(),
1281    ///             ..Default::default()
1282    ///         }).await.unwrap();
1283    ///
1284    ///         done = result.done;
1285    ///         rewrite_token = result.rewrite_token;
1286    ///     }
1287    /// }
1288    /// ```
1289    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1290    pub async fn rewrite_object(&self, req: &RewriteObjectRequest) -> Result<RewriteObjectResponse, Error> {
1291        let builder = objects::rewrite::build(self.v1_endpoint.as_str(), &self.http, req);
1292        self.send(builder).await
1293    }
1294
1295    /// Composes the object.
1296    /// https://cloud.google.com/storage/docs/json_api/v1/objects/compose
1297    ///
1298    /// ```
1299    /// use google_cloud_storage::client::Client;
1300    /// use google_cloud_storage::http::objects::compose::{ComposeObjectRequest, ComposingTargets};
1301    /// use google_cloud_storage::http::objects::rewrite::RewriteObjectRequest;
1302    /// use google_cloud_storage::http::objects::SourceObjects;
1303    ///
1304    /// async fn run(client:Client) {
1305    ///     let result = client.compose_object(&ComposeObjectRequest{
1306    ///         bucket: "bucket1".to_string(),
1307    ///         destination_object: "object1".to_string(),
1308    ///         composing_targets: ComposingTargets {
1309    ///             source_objects: vec![SourceObjects {
1310    ///                 name: "src".to_string(),
1311    ///                 ..Default::default()
1312    ///             }],
1313    ///             ..Default::default()
1314    ///         },
1315    ///         ..Default::default()
1316    ///     }).await;
1317    /// }
1318    /// ```
1319    #[cfg_attr(feature = "trace", tracing::instrument(skip_all))]
1320    pub async fn compose_object(&self, req: &ComposeObjectRequest) -> Result<Object, Error> {
1321        let builder = objects::compose::build(self.v1_endpoint.as_str(), &self.http, req);
1322        self.send(builder).await
1323    }
1324
1325    async fn with_headers(&self, builder: RequestBuilder) -> Result<RequestBuilder, Error> {
1326        let builder = builder
1327            .header("X-Goog-Api-Client", "rust")
1328            .header(reqwest::header::USER_AGENT, "google-cloud-storage");
1329        let builder = match &self.ts {
1330            Some(ts) => {
1331                let token = ts.token().await.map_err(Error::TokenSource)?;
1332                builder.header(reqwest::header::AUTHORIZATION, token)
1333            }
1334            None => builder,
1335        };
1336        Ok(builder)
1337    }
1338
1339    async fn send_request<T>(&self, request: Request) -> Result<T, Error>
1340    where
1341        T: serde::de::DeserializeOwned,
1342    {
1343        let response = self.http.execute(request).await?;
1344        let response = check_response_status(response).await?;
1345        Ok(response.json().await?)
1346    }
1347
1348    async fn send<T>(&self, builder: RequestBuilder) -> Result<T, Error>
1349    where
1350        T: serde::de::DeserializeOwned,
1351    {
1352        let builder = self.with_headers(builder).await?;
1353        let response = builder.send().await?;
1354        let response = check_response_status(response).await?;
1355        Ok(response.json().await?)
1356    }
1357
1358    async fn send_get_empty(&self, builder: RequestBuilder) -> Result<(), Error> {
1359        let builder = self.with_headers(builder).await?;
1360        let response = builder.send().await?;
1361        check_response_status(response).await?;
1362        Ok(())
1363    }
1364
1365    async fn send_get_url(&self, builder: RequestBuilder) -> Result<String, Error> {
1366        let builder = self.with_headers(builder).await?;
1367        let response = builder.send().await?;
1368        let response = check_response_status(response).await?;
1369        Ok(String::from_utf8_lossy(response.headers()[LOCATION].as_bytes()).into_owned())
1370    }
1371}
1372
1373#[cfg(test)]
1374pub(crate) mod test {
1375    use std::collections::HashMap;
1376
1377    use bytes::Buf;
1378    use futures_util::StreamExt;
1379    use serial_test::serial;
1380
1381    use google_cloud_auth::project::Config;
1382    use google_cloud_auth::token::DefaultTokenSourceProvider;
1383    use google_cloud_token::TokenSourceProvider;
1384
1385    use crate::http::bucket_access_controls::delete::DeleteBucketAccessControlRequest;
1386    use crate::http::bucket_access_controls::get::GetBucketAccessControlRequest;
1387    use crate::http::bucket_access_controls::insert::{
1388        BucketAccessControlCreationConfig, InsertBucketAccessControlRequest,
1389    };
1390    use crate::http::bucket_access_controls::list::ListBucketAccessControlsRequest;
1391    use crate::http::bucket_access_controls::BucketACLRole;
1392    use crate::http::buckets::delete::DeleteBucketRequest;
1393    use crate::http::buckets::get::GetBucketRequest;
1394    use crate::http::buckets::get_iam_policy::GetIamPolicyRequest;
1395    use crate::http::buckets::iam_configuration::{PublicAccessPrevention, UniformBucketLevelAccess};
1396    use crate::http::buckets::insert::{
1397        BucketCreationConfig, InsertBucketParam, InsertBucketRequest, RetentionPolicyCreationConfig,
1398    };
1399    use crate::http::buckets::list::ListBucketsRequest;
1400    use crate::http::buckets::patch::{BucketPatchConfig, PatchBucketRequest};
1401    use crate::http::buckets::set_iam_policy::SetIamPolicyRequest;
1402    use crate::http::buckets::test_iam_permissions::TestIamPermissionsRequest;
1403    use crate::http::buckets::{lifecycle, Billing, Binding, Cors, IamConfiguration, Lifecycle, Website};
1404    use crate::http::default_object_access_controls::delete::DeleteDefaultObjectAccessControlRequest;
1405    use crate::http::default_object_access_controls::get::GetDefaultObjectAccessControlRequest;
1406    use crate::http::default_object_access_controls::insert::InsertDefaultObjectAccessControlRequest;
1407    use crate::http::default_object_access_controls::list::ListDefaultObjectAccessControlsRequest;
1408    use crate::http::hmac_keys::create::CreateHmacKeyRequest;
1409    use crate::http::hmac_keys::delete::DeleteHmacKeyRequest;
1410    use crate::http::hmac_keys::get::GetHmacKeyRequest;
1411    use crate::http::hmac_keys::list::ListHmacKeysRequest;
1412    use crate::http::hmac_keys::update::UpdateHmacKeyRequest;
1413    use crate::http::hmac_keys::HmacKeyMetadata;
1414    use crate::http::notifications::delete::DeleteNotificationRequest;
1415    use crate::http::notifications::get::GetNotificationRequest;
1416    use crate::http::notifications::insert::{InsertNotificationRequest, NotificationCreationConfig};
1417    use crate::http::notifications::list::ListNotificationsRequest;
1418    use crate::http::notifications::EventType;
1419    use crate::http::object_access_controls::delete::DeleteObjectAccessControlRequest;
1420    use crate::http::object_access_controls::get::GetObjectAccessControlRequest;
1421    use crate::http::object_access_controls::insert::{
1422        InsertObjectAccessControlRequest, ObjectAccessControlCreationConfig,
1423    };
1424    use crate::http::object_access_controls::list::ListObjectAccessControlsRequest;
1425    use crate::http::object_access_controls::ObjectACLRole;
1426    use crate::http::objects::compose::{ComposeObjectRequest, ComposingTargets};
1427    use crate::http::objects::copy::CopyObjectRequest;
1428    use crate::http::objects::delete::DeleteObjectRequest;
1429    use crate::http::objects::download::Range;
1430    use crate::http::objects::get::GetObjectRequest;
1431    use crate::http::objects::list::ListObjectsRequest;
1432    use crate::http::objects::rewrite::RewriteObjectRequest;
1433    use crate::http::objects::upload::{Media, UploadObjectRequest, UploadType};
1434    use crate::http::objects::{Object, SourceObjects};
1435    use crate::http::resumable_upload_client::{ChunkSize, UploadStatus, UploadedRange};
1436    use crate::http::storage_client::{StorageClient, SCOPES};
1437
1438    #[ctor::ctor]
1439    fn init() {
1440        let filter = tracing_subscriber::filter::EnvFilter::from_default_env()
1441            .add_directive("google_cloud_storage=trace".parse().unwrap());
1442        let _ = tracing_subscriber::fmt().with_env_filter(filter).try_init();
1443    }
1444
1445    pub fn bucket_name(project: &str, name: &str) -> String {
1446        format!("{}_gcrgcs_{}", project, name)
1447    }
1448
1449    async fn client() -> (StorageClient, String, String) {
1450        let tsp = DefaultTokenSourceProvider::new(Config::default().with_scopes(&SCOPES))
1451            .await
1452            .unwrap();
1453        let cred = tsp.source_credentials.clone();
1454        let ts = tsp.token_source();
1455        let client = StorageClient::new(
1456            Some(ts),
1457            "https://storage.googleapis.com",
1458            reqwest_middleware::ClientBuilder::new(reqwest::Client::default()).build(),
1459        );
1460        let cred = cred.unwrap();
1461        (client, cred.project_id.unwrap(), cred.client_email.unwrap())
1462    }
1463
1464    #[tokio::test]
1465    #[serial]
1466    pub async fn list_buckets() {
1467        let (client, project, _) = client().await;
1468        let buckets = client
1469            .list_buckets(&ListBucketsRequest {
1470                project: project.clone(),
1471                max_results: None,
1472                page_token: None,
1473                prefix: Some(bucket_name(&project, "object")),
1474                projection: None,
1475                match_glob: None,
1476            })
1477            .await
1478            .unwrap();
1479        assert_eq!(2, buckets.items.len());
1480    }
1481
1482    #[tokio::test]
1483    #[serial]
1484    pub async fn crud_bucket() {
1485        let (client, project, email) = client().await;
1486        let name = bucket_name(
1487            &project,
1488            &format!("crud_bucket-{}", time::OffsetDateTime::now_utc().unix_timestamp()),
1489        );
1490        let mut labels = HashMap::new();
1491        labels.insert("labelkey".to_string(), "labelvalue".to_string());
1492
1493        let bucket = client
1494            .insert_bucket(&InsertBucketRequest {
1495                name,
1496                param: InsertBucketParam {
1497                    project,
1498                    ..Default::default()
1499                },
1500                bucket: BucketCreationConfig {
1501                    location: "ASIA-NORTHEAST1".to_string(),
1502                    storage_class: Some("STANDARD".to_string()),
1503                    default_event_based_hold: true,
1504                    labels: Some(labels),
1505                    website: Some(Website {
1506                        main_page_suffix: "_suffix".to_string(),
1507                        not_found_page: "notfound.html".to_string(),
1508                    }),
1509                    iam_configuration: Some(IamConfiguration {
1510                        uniform_bucket_level_access: Some(UniformBucketLevelAccess {
1511                            enabled: false,
1512                            locked_time: None,
1513                        }),
1514                        public_access_prevention: Some(PublicAccessPrevention::Enforced),
1515                    }),
1516                    billing: Some(Billing { requester_pays: false }),
1517                    retention_policy: Some(RetentionPolicyCreationConfig {
1518                        retention_period: 10000,
1519                    }),
1520                    cors: Some(vec![Cors {
1521                        origin: vec!["*".to_string()],
1522                        method: vec!["GET".to_string(), "HEAD".to_string()],
1523                        response_header: vec!["200".to_string()],
1524                        max_age_seconds: 100,
1525                    }]),
1526                    lifecycle: Some(Lifecycle {
1527                        rule: vec![lifecycle::Rule {
1528                            action: Some(lifecycle::rule::Action {
1529                                r#type: lifecycle::rule::ActionType::Delete,
1530                                storage_class: None,
1531                            }),
1532                            condition: Some(lifecycle::rule::Condition {
1533                                age: Some(365),
1534                                is_live: Some(true),
1535                                ..Default::default()
1536                            }),
1537                        }],
1538                    }),
1539                    rpo: None,
1540                    ..Default::default()
1541                },
1542            })
1543            .await
1544            .unwrap();
1545
1546        let found = client
1547            .get_bucket(&GetBucketRequest {
1548                bucket: bucket.name.to_string(),
1549                ..Default::default()
1550            })
1551            .await
1552            .unwrap();
1553
1554        assert_eq!(found.location.as_str(), "ASIA-NORTHEAST1");
1555
1556        let entity = format!("user-{}", email);
1557        let patched = client
1558            .patch_bucket(&PatchBucketRequest {
1559                bucket: bucket.name.to_string(),
1560                metadata: Some(BucketPatchConfig {
1561                    default_object_acl: Some(vec![ObjectAccessControlCreationConfig {
1562                        entity: entity.to_string(),
1563                        role: ObjectACLRole::READER,
1564                    }]),
1565                    ..Default::default()
1566                }),
1567                ..Default::default()
1568            })
1569            .await
1570            .unwrap();
1571
1572        let default_object_acl = patched.default_object_acl.unwrap();
1573        assert_eq!(default_object_acl.len(), 1);
1574        assert_eq!(default_object_acl[0].entity.as_str(), entity);
1575        assert_eq!(default_object_acl[0].role, ObjectACLRole::READER);
1576        assert_eq!(found.storage_class.as_str(), patched.storage_class.as_str());
1577        assert_eq!(found.location.as_str(), patched.location.as_str());
1578
1579        client
1580            .delete_bucket(&DeleteBucketRequest {
1581                bucket: bucket.name,
1582                param: Default::default(),
1583            })
1584            .await
1585            .unwrap();
1586    }
1587
1588    #[tokio::test]
1589    #[serial]
1590    async fn set_get_test_iam() {
1591        let (client, project, email) = client().await;
1592        let bucket_name = bucket_name(&project, "test_iam");
1593        let mut policy = client
1594            .get_iam_policy(&GetIamPolicyRequest {
1595                resource: bucket_name.to_string(),
1596                options_requested_policy_version: None,
1597            })
1598            .await
1599            .unwrap();
1600        policy.bindings.push(Binding {
1601            role: "roles/storage.objectViewer".to_string(),
1602            members: vec![format!("serviceAccount:{}", email)],
1603            condition: None,
1604        });
1605
1606        let mut result = client
1607            .set_iam_policy(&SetIamPolicyRequest {
1608                resource: bucket_name.to_string(),
1609                policy,
1610            })
1611            .await
1612            .unwrap();
1613        assert_eq!(result.bindings.len(), 5);
1614        assert_eq!(result.bindings.pop().unwrap().role, "roles/storage.objectViewer");
1615
1616        let permissions = client
1617            .test_iam_permissions(&TestIamPermissionsRequest {
1618                resource: bucket_name.to_string(),
1619                permissions: vec!["storage.buckets.get".to_string()],
1620            })
1621            .await
1622            .unwrap();
1623        assert_eq!(permissions.permissions[0], "storage.buckets.get");
1624    }
1625
1626    #[tokio::test]
1627    #[serial]
1628    pub async fn crud_default_object_controls() {
1629        let (client, project, email) = client().await;
1630        let bucket_name = bucket_name(&project, "default_object_acl");
1631        let entity = format!("user-{}", email);
1632
1633        client
1634            .insert_default_object_access_control(&InsertDefaultObjectAccessControlRequest {
1635                bucket: bucket_name.to_string(),
1636                object_access_control: ObjectAccessControlCreationConfig {
1637                    entity: entity.to_string(),
1638                    role: ObjectACLRole::READER,
1639                },
1640            })
1641            .await
1642            .unwrap();
1643
1644        let found = client
1645            .get_default_object_access_control(&GetDefaultObjectAccessControlRequest {
1646                bucket: bucket_name.to_string(),
1647                entity: entity.to_string(),
1648            })
1649            .await
1650            .unwrap();
1651        assert_eq!(found.entity, entity);
1652        assert_eq!(found.role, ObjectACLRole::READER);
1653
1654        let acls = client
1655            .list_default_object_access_controls(&ListDefaultObjectAccessControlsRequest {
1656                bucket: bucket_name.to_string(),
1657                ..Default::default()
1658            })
1659            .await
1660            .unwrap();
1661        assert!(acls.items.is_some());
1662        assert_eq!(4, acls.items.unwrap().len());
1663
1664        client
1665            .delete_default_object_access_control(&DeleteDefaultObjectAccessControlRequest {
1666                bucket: bucket_name.to_string(),
1667                entity: entity.to_string(),
1668            })
1669            .await
1670            .unwrap();
1671    }
1672
1673    #[tokio::test]
1674    #[serial]
1675    pub async fn crud_bucket_access_controls() {
1676        let (client, project, email) = client().await;
1677        let bucket_name = bucket_name(&project, "bucket_acl");
1678
1679        let entity = format!("user-{}", email);
1680        client
1681            .insert_bucket_access_control(&InsertBucketAccessControlRequest {
1682                bucket: bucket_name.to_string(),
1683                acl: BucketAccessControlCreationConfig {
1684                    entity: entity.to_string(),
1685                    role: BucketACLRole::READER,
1686                },
1687            })
1688            .await
1689            .unwrap();
1690
1691        let found = client
1692            .get_bucket_access_control(&GetBucketAccessControlRequest {
1693                bucket: bucket_name.to_string(),
1694                entity: entity.to_string(),
1695            })
1696            .await
1697            .unwrap();
1698        assert_eq!(found.entity, entity);
1699        assert_eq!(found.role, BucketACLRole::READER);
1700
1701        let acls = client
1702            .list_bucket_access_controls(&ListBucketAccessControlsRequest {
1703                bucket: bucket_name.to_string(),
1704            })
1705            .await
1706            .unwrap();
1707        assert_eq!(4, acls.items.len());
1708
1709        client
1710            .delete_bucket_access_control(&DeleteBucketAccessControlRequest {
1711                bucket: bucket_name.to_string(),
1712                entity: entity.to_string(),
1713            })
1714            .await
1715            .unwrap();
1716    }
1717
1718    #[tokio::test]
1719    #[serial]
1720    pub async fn crud_object_access_controls() {
1721        let (client, project, email) = client().await;
1722        let bucket_name = bucket_name(&project, "object_acl");
1723        let object_name = "test.txt";
1724
1725        let entity = format!("user-{}", email);
1726
1727        client
1728            .insert_object_access_control(&InsertObjectAccessControlRequest {
1729                bucket: bucket_name.to_string(),
1730                object: object_name.to_string(),
1731                generation: None,
1732                acl: ObjectAccessControlCreationConfig {
1733                    entity: entity.to_string(),
1734                    role: ObjectACLRole::READER,
1735                },
1736            })
1737            .await
1738            .unwrap();
1739
1740        let found = client
1741            .get_object_access_control(&GetObjectAccessControlRequest {
1742                bucket: bucket_name.to_string(),
1743                entity: entity.to_string(),
1744                object: object_name.to_string(),
1745                generation: None,
1746            })
1747            .await
1748            .unwrap();
1749        assert_eq!(found.entity, entity);
1750        assert_eq!(found.role, ObjectACLRole::READER);
1751
1752        let acls = client
1753            .list_object_access_controls(&ListObjectAccessControlsRequest {
1754                bucket: bucket_name.to_string(),
1755                object: object_name.to_string(),
1756                generation: None,
1757            })
1758            .await
1759            .unwrap();
1760        assert_eq!(5, acls.items.len());
1761
1762        client
1763            .delete_object_access_control(&DeleteObjectAccessControlRequest {
1764                bucket: bucket_name.to_string(),
1765                object: object_name.to_string(),
1766                entity: entity.to_string(),
1767                generation: None,
1768            })
1769            .await
1770            .unwrap();
1771    }
1772
1773    #[tokio::test]
1774    #[serial]
1775    pub async fn crud_notification() {
1776        let (client, project, _) = client().await;
1777        let bucket_name = bucket_name(&project, "notification");
1778
1779        let notifications = client
1780            .list_notifications(&ListNotificationsRequest {
1781                bucket: bucket_name.to_string(),
1782            })
1783            .await
1784            .unwrap();
1785
1786        for n in notifications.items.unwrap_or_default() {
1787            client
1788                .delete_notification(&DeleteNotificationRequest {
1789                    bucket: bucket_name.to_string(),
1790                    notification: n.id.to_string(),
1791                })
1792                .await
1793                .unwrap();
1794        }
1795
1796        let post = client
1797            .insert_notification(&InsertNotificationRequest {
1798                bucket: bucket_name.to_string(),
1799                notification: NotificationCreationConfig {
1800                    topic: format!("projects/{project}/topics/{bucket_name}"),
1801                    event_types: Some(vec![EventType::ObjectMetadataUpdate, EventType::ObjectDelete]),
1802                    object_name_prefix: Some("notification-test".to_string()),
1803                    ..Default::default()
1804                },
1805            })
1806            .await
1807            .unwrap();
1808
1809        let found = client
1810            .get_notification(&GetNotificationRequest {
1811                bucket: bucket_name.to_string(),
1812                notification: post.id.to_string(),
1813            })
1814            .await
1815            .unwrap();
1816        assert_eq!(found.id, post.id);
1817        assert_eq!(found.event_types.unwrap().len(), 2);
1818    }
1819
1820    #[tokio::test]
1821    #[serial]
1822    pub async fn crud_hmac_key() {
1823        let (client, project_id, email) = client().await;
1824
1825        let post = client
1826            .create_hmac_key(&CreateHmacKeyRequest {
1827                project_id: project_id.clone(),
1828                service_account_email: email,
1829            })
1830            .await
1831            .unwrap();
1832
1833        let found = client
1834            .get_hmac_key(&GetHmacKeyRequest {
1835                access_id: post.metadata.access_id.to_string(),
1836                project_id: project_id.clone(),
1837            })
1838            .await
1839            .unwrap();
1840        assert_eq!(found.id, post.metadata.id);
1841        assert_eq!(found.state, "ACTIVE");
1842
1843        let keys = client
1844            .list_hmac_keys(&ListHmacKeysRequest {
1845                project_id: project_id.clone(),
1846                ..Default::default()
1847            })
1848            .await
1849            .unwrap();
1850
1851        for n in keys.items.unwrap_or_default() {
1852            let result = client
1853                .update_hmac_key(&UpdateHmacKeyRequest {
1854                    access_id: n.access_id.to_string(),
1855                    project_id: n.project_id.to_string(),
1856                    metadata: HmacKeyMetadata {
1857                        state: "INACTIVE".to_string(),
1858                        ..n.clone()
1859                    },
1860                })
1861                .await
1862                .unwrap();
1863            assert_eq!(result.state, "INACTIVE");
1864
1865            client
1866                .delete_hmac_key(&DeleteHmacKeyRequest {
1867                    access_id: n.access_id.to_string(),
1868                    project_id: n.project_id.to_string(),
1869                })
1870                .await
1871                .unwrap();
1872        }
1873    }
1874
1875    #[tokio::test]
1876    #[serial]
1877    pub async fn crud_object_with_metadata() {
1878        let (client, project, _) = client().await;
1879        let bucket_name = bucket_name(&project, "object");
1880        let mut metadata = HashMap::<String, String>::new();
1881        metadata.insert("key1".to_string(), "value1".to_string());
1882        let uploaded = client
1883            .upload_object(
1884                &UploadObjectRequest {
1885                    bucket: bucket_name.to_string(),
1886                    ..Default::default()
1887                },
1888                vec![1, 2, 3, 4, 5, 6, 7],
1889                &UploadType::Multipart(Box::new(Object {
1890                    name: "test1_meta".to_string(),
1891                    content_type: Some("text/plain".to_string()),
1892                    content_language: Some("ja".to_string()),
1893                    metadata: Some(metadata),
1894                    ..Default::default()
1895                })),
1896            )
1897            .await
1898            .unwrap();
1899        assert_eq!(uploaded.content_type.unwrap(), "text/plain".to_string());
1900        assert_eq!(uploaded.content_language.unwrap(), "ja".to_string());
1901        assert_eq!(uploaded.metadata.unwrap().get("key1").unwrap().clone(), "value1".to_string());
1902
1903        let download = |range: Range| {
1904            let client = client.clone();
1905            let bucket_name = uploaded.bucket.clone();
1906            let object_name = uploaded.name.clone();
1907            async move {
1908                client
1909                    .download_object(
1910                        &GetObjectRequest {
1911                            bucket: bucket_name,
1912                            object: object_name,
1913                            ..Default::default()
1914                        },
1915                        &range,
1916                    )
1917                    .await
1918                    .unwrap()
1919            }
1920        };
1921
1922        let object = client
1923            .get_object(&GetObjectRequest {
1924                bucket: uploaded.bucket.clone(),
1925                object: uploaded.name.clone(),
1926                ..Default::default()
1927            })
1928            .await
1929            .unwrap();
1930
1931        assert_eq!(object.content_type.unwrap(), "text/plain".to_string());
1932        assert_eq!(object.content_language.unwrap(), "ja".to_string());
1933        assert_eq!(object.metadata.unwrap().get("key1").unwrap().clone(), "value1".to_string());
1934
1935        let downloaded = download(Range::default()).await;
1936        assert_eq!(downloaded, vec![1, 2, 3, 4, 5, 6, 7]);
1937    }
1938
1939    #[tokio::test]
1940    #[serial]
1941    pub async fn crud_object() {
1942        let (client, project, _) = client().await;
1943        let bucket_name = bucket_name(&project, "object");
1944
1945        let objects = client
1946            .list_objects(&ListObjectsRequest {
1947                bucket: bucket_name.to_string(),
1948                ..Default::default()
1949            })
1950            .await
1951            .unwrap()
1952            .items
1953            .unwrap_or_default();
1954        for o in objects {
1955            client
1956                .delete_object(&DeleteObjectRequest {
1957                    bucket: o.bucket.to_string(),
1958                    object: o.name.to_string(),
1959                    ..Default::default()
1960                })
1961                .await
1962                .unwrap();
1963        }
1964
1965        let mut media = Media::new("test1");
1966        media.content_type = "text/plain".into();
1967        let uploaded = client
1968            .upload_object(
1969                &UploadObjectRequest {
1970                    bucket: bucket_name.to_string(),
1971                    ..Default::default()
1972                },
1973                vec![1, 2, 3, 4, 5, 6],
1974                &UploadType::Simple(media.clone()),
1975            )
1976            .await
1977            .unwrap();
1978
1979        assert_eq!(uploaded.content_type.unwrap(), "text/plain".to_string());
1980
1981        let media = Media::new("test1_zero");
1982        let uploaded_empty = client
1983            .upload_object(
1984                &UploadObjectRequest {
1985                    bucket: bucket_name.to_string(),
1986                    ..Default::default()
1987                },
1988                vec![],
1989                &UploadType::Simple(media),
1990            )
1991            .await
1992            .unwrap();
1993
1994        let download = |name: &str, range: Range| {
1995            let client = client.clone();
1996            let bucket_name = uploaded.bucket.clone();
1997            let object_name = name.to_string();
1998            async move {
1999                client
2000                    .download_object(
2001                        &GetObjectRequest {
2002                            bucket: bucket_name,
2003                            object: object_name,
2004                            ..Default::default()
2005                        },
2006                        &range,
2007                    )
2008                    .await
2009                    .unwrap()
2010            }
2011        };
2012
2013        let downloaded = download(&uploaded.name, Range::default()).await;
2014        assert_eq!(downloaded, vec![1, 2, 3, 4, 5, 6]);
2015        let downloaded = download(&uploaded.name, Range(Some(1), None)).await;
2016        assert_eq!(downloaded, vec![2, 3, 4, 5, 6]);
2017        let downloaded = download(&uploaded.name, Range(Some(1), Some(2))).await;
2018        assert_eq!(downloaded, vec![2, 3]);
2019        let downloaded = download(&uploaded.name, Range(None, Some(2))).await;
2020        assert_eq!(downloaded, vec![5, 6]);
2021
2022        let downloaded = download(&uploaded_empty.name, Range::default()).await;
2023        assert!(downloaded.is_empty());
2024
2025        let _copied = client
2026            .copy_object(&CopyObjectRequest {
2027                destination_bucket: bucket_name.to_string(),
2028                destination_object: format!("{}_copy", uploaded.name),
2029                source_bucket: bucket_name.to_string(),
2030                source_object: uploaded.name.to_string(),
2031                ..Default::default()
2032            })
2033            .await
2034            .unwrap();
2035
2036        let _rewrited = client
2037            .rewrite_object(&RewriteObjectRequest {
2038                destination_bucket: bucket_name.to_string(),
2039                destination_object: format!("{}_rewrite", uploaded.name),
2040                source_bucket: bucket_name.to_string(),
2041                source_object: uploaded.name.to_string(),
2042                ..Default::default()
2043            })
2044            .await
2045            .unwrap();
2046
2047        let _composed = client
2048            .compose_object(&ComposeObjectRequest {
2049                bucket: bucket_name.to_string(),
2050                destination_object: format!("{}_composed", uploaded.name),
2051                destination_predefined_acl: None,
2052                composing_targets: ComposingTargets {
2053                    destination: Some(Object {
2054                        content_type: Some("image/jpeg".to_string()),
2055                        ..Default::default()
2056                    }),
2057                    source_objects: vec![SourceObjects {
2058                        name: format!("{}_rewrite", uploaded.name),
2059                        ..Default::default()
2060                    }],
2061                },
2062                ..Default::default()
2063            })
2064            .await
2065            .unwrap();
2066    }
2067
2068    #[tokio::test]
2069    #[serial]
2070    pub async fn streamed_object() {
2071        let (client, project, _) = client().await;
2072        let bucket_name = bucket_name(&project, "object");
2073        let file_name = format!("stream_{}", time::OffsetDateTime::now_utc().unix_timestamp());
2074
2075        // let stream= reqwest::Client::default().get("https://avatars.githubusercontent.com/u/958174?s=96&v=4").send().await.unwrap().bytes_stream();
2076        let source = vec!["hello", " ", "world"];
2077        let chunks: Vec<Result<_, ::std::io::Error>> = source.clone().into_iter().map(Ok).collect();
2078        let stream = futures_util::stream::iter(chunks);
2079        let media = Media::new(file_name);
2080        let upload_type = UploadType::Simple(media);
2081        let uploaded = client
2082            .upload_streamed_object(
2083                &UploadObjectRequest {
2084                    bucket: bucket_name.to_string(),
2085                    predefined_acl: None,
2086                    ..Default::default()
2087                },
2088                stream,
2089                &upload_type,
2090            )
2091            .await
2092            .unwrap();
2093
2094        let file_name = format!("stream_empty_{}", time::OffsetDateTime::now_utc().unix_timestamp());
2095        let source: Vec<&str> = vec![];
2096        let chunks: Vec<Result<_, ::std::io::Error>> = source.clone().into_iter().map(Ok).collect();
2097        let stream = futures_util::stream::iter(chunks);
2098        let media = Media::new(file_name);
2099        let upload_type = UploadType::Simple(media);
2100        let uploaded_empty = client
2101            .upload_streamed_object(
2102                &UploadObjectRequest {
2103                    bucket: bucket_name.to_string(),
2104                    predefined_acl: None,
2105                    ..Default::default()
2106                },
2107                stream,
2108                &upload_type,
2109            )
2110            .await
2111            .unwrap();
2112
2113        let download = |name: &str, range: Range| {
2114            let client = client.clone();
2115            let bucket_name = uploaded.bucket.clone();
2116            let object_name = name.to_string();
2117            async move {
2118                let mut downloaded = client
2119                    .download_streamed_object(
2120                        &GetObjectRequest {
2121                            bucket: bucket_name,
2122                            object: object_name,
2123                            ..Default::default()
2124                        },
2125                        &range,
2126                    )
2127                    .await
2128                    .unwrap();
2129                let mut data = Vec::with_capacity(10);
2130                while let Some(v) = downloaded.next().await {
2131                    let d: bytes::Bytes = v.unwrap();
2132                    data.extend_from_slice(d.chunk());
2133                }
2134                data
2135            }
2136        };
2137        let downloaded = download(&uploaded.name, Range::default()).await;
2138        assert_eq!("hello world", String::from_utf8_lossy(downloaded.as_slice()));
2139        let downloaded = download(&uploaded.name, Range(Some(1), None)).await;
2140        assert_eq!("ello world", String::from_utf8_lossy(downloaded.as_slice()));
2141        let downloaded = download(&uploaded.name, Range(Some(1), Some(2))).await;
2142        assert_eq!("el", String::from_utf8_lossy(downloaded.as_slice()));
2143        let downloaded = download(&uploaded.name, Range(None, Some(2))).await;
2144        assert_eq!("ld", String::from_utf8_lossy(downloaded.as_slice()));
2145
2146        let downloaded = download(&uploaded_empty.name, Range::default()).await;
2147        assert!(downloaded.is_empty());
2148    }
2149
2150    #[tokio::test]
2151    #[serial]
2152    pub async fn resumable_simple_upload() {
2153        let (client, project, _) = client().await;
2154        let bucket_name = bucket_name(&project, "object");
2155        let file_name = format!("resumable_{}", time::OffsetDateTime::now_utc().unix_timestamp());
2156
2157        let mut media = Media::new(file_name.clone());
2158        media.content_type = "text/plain".into();
2159        let upload_type = UploadType::Simple(media);
2160        let uploader = client
2161            .prepare_resumable_upload(
2162                &UploadObjectRequest {
2163                    bucket: bucket_name.to_string(),
2164                    ..Default::default()
2165                },
2166                &upload_type,
2167            )
2168            .await
2169            .unwrap();
2170        let data = vec![1, 2, 3, 4, 5];
2171        uploader.upload_single_chunk(data.clone(), 5).await.unwrap();
2172
2173        let get_request = &GetObjectRequest {
2174            bucket: bucket_name.to_string(),
2175            object: file_name.to_string(),
2176            ..Default::default()
2177        };
2178        let download = client.download_object(get_request, &Range::default()).await.unwrap();
2179        assert_eq!(data, download);
2180
2181        let object = client.get_object(get_request).await.unwrap();
2182        assert_eq!(object.content_type.unwrap(), "text/plain");
2183    }
2184
2185    #[tokio::test]
2186    #[serial]
2187    pub async fn resumable_multiple_chunk_upload() {
2188        let (client, project, _) = client().await;
2189        let bucket_name = bucket_name(&project, "object");
2190        let file_name = format!("resumable_multiple_chunk{}", time::OffsetDateTime::now_utc().unix_timestamp());
2191
2192        let metadata = Object {
2193            name: file_name.to_string(),
2194            content_type: Some("video/mp4".to_string()),
2195            ..Default::default()
2196        };
2197        let upload_type = UploadType::Multipart(Box::new(metadata));
2198        let uploader = client
2199            .prepare_resumable_upload(
2200                &UploadObjectRequest {
2201                    bucket: bucket_name.to_string(),
2202                    ..Default::default()
2203                },
2204                &upload_type,
2205            )
2206            .await
2207            .unwrap();
2208        let mut chunk1_data: Vec<u8> = (0..256 * 1024).map(|i| (i % 256) as u8).collect();
2209        let chunk2_data: Vec<u8> = (1..256 * 1024 + 50).map(|i| (i % 256) as u8).collect();
2210        let total_size = Some(chunk1_data.len() as u64 + chunk2_data.len() as u64);
2211
2212        tracing::info!("start upload chunk {}", uploader.url());
2213        let chunk1 = ChunkSize::new(0, chunk1_data.len() as u64 - 1, total_size);
2214        tracing::info!("upload chunk1 {:?}", chunk1);
2215        let status1 = uploader
2216            .upload_multiple_chunk(chunk1_data.clone(), &chunk1)
2217            .await
2218            .unwrap();
2219
2220        assert_eq!(
2221            status1,
2222            UploadStatus::ResumeIncomplete(UploadedRange {
2223                first_byte: 0,
2224                last_byte: chunk1_data.len() as u64 - 1,
2225            })
2226        );
2227
2228        tracing::info!("check status chunk1");
2229        let status_check = uploader.status(total_size).await.unwrap();
2230        assert_eq!(
2231            status_check,
2232            UploadStatus::ResumeIncomplete(UploadedRange {
2233                first_byte: 0,
2234                last_byte: chunk1_data.len() as u64 - 1,
2235            })
2236        );
2237
2238        let chunk2 = ChunkSize::new(
2239            chunk1_data.len() as u64,
2240            chunk1_data.len() as u64 + chunk2_data.len() as u64 - 1,
2241            total_size,
2242        );
2243        tracing::info!("upload chunk2 {:?}", chunk2);
2244        let status2 = uploader
2245            .upload_multiple_chunk(chunk2_data.clone(), &chunk2)
2246            .await
2247            .unwrap();
2248        assert!(matches!(status2, UploadStatus::Ok(_)));
2249
2250        tracing::info!("check status chunk2");
2251        let status_check2 = uploader.status(total_size).await.unwrap();
2252        assert!(matches!(status_check2, UploadStatus::Ok(_)));
2253
2254        let get_request = &GetObjectRequest {
2255            bucket: bucket_name.to_string(),
2256            object: file_name.to_string(),
2257            ..Default::default()
2258        };
2259
2260        let object = client.get_object(get_request).await.unwrap();
2261        assert_eq!(object.content_type.unwrap(), "video/mp4");
2262
2263        let download = client.download_object(get_request, &Range::default()).await.unwrap();
2264        chunk1_data.extend(chunk2_data);
2265        assert_eq!(chunk1_data, download);
2266    }
2267
2268    #[tokio::test]
2269    #[serial]
2270    pub async fn resumable_upload_cancel() {
2271        let (client, project, _) = client().await;
2272        let bucket_name = bucket_name(&project, "object");
2273        let file_name = format!("resumable_cancel{}", time::OffsetDateTime::now_utc().unix_timestamp());
2274
2275        let metadata = Object {
2276            name: file_name.to_string(),
2277            content_type: Some("video/mp4".to_string()),
2278            ..Default::default()
2279        };
2280        let upload_type = UploadType::Multipart(Box::new(metadata));
2281        let uploader = client
2282            .prepare_resumable_upload(
2283                &UploadObjectRequest {
2284                    bucket: bucket_name.to_string(),
2285                    ..Default::default()
2286                },
2287                &upload_type,
2288            )
2289            .await
2290            .unwrap();
2291        let cloned = uploader.clone();
2292        uploader.cancel().await.unwrap();
2293
2294        let result = cloned.upload_single_chunk(vec![1], 1).await;
2295        assert!(result.is_err());
2296    }
2297
2298    #[tokio::test]
2299    #[serial]
2300    pub async fn resumable_multiple_chunk_upload_unknown() {
2301        let (client, project, _) = client().await;
2302        let bucket_name = bucket_name(&project, "object");
2303        let file_name = format!(
2304            "resumable_multiple_chunk_unknown{}",
2305            time::OffsetDateTime::now_utc().unix_timestamp()
2306        );
2307
2308        let metadata = Object {
2309            name: file_name.to_string(),
2310            content_type: Some("video/mp4".to_string()),
2311            ..Default::default()
2312        };
2313        let upload_type = UploadType::Multipart(Box::new(metadata));
2314        let uploader = client
2315            .prepare_resumable_upload(
2316                &UploadObjectRequest {
2317                    bucket: bucket_name.to_string(),
2318                    ..Default::default()
2319                },
2320                &upload_type,
2321            )
2322            .await
2323            .unwrap();
2324        let mut chunk1_data: Vec<u8> = (0..256 * 1024).map(|i| (i % 256) as u8).collect();
2325        let chunk2_data: Vec<u8> = vec![10, 20, 30];
2326        let total_size = None;
2327
2328        tracing::info!("start upload chunk {}", uploader.url());
2329        let chunk1 = ChunkSize::new(0, chunk1_data.len() as u64 - 1, total_size);
2330        tracing::info!("upload chunk1 {:?}", chunk1);
2331        let status1 = uploader
2332            .upload_multiple_chunk(chunk1_data.clone(), &chunk1)
2333            .await
2334            .unwrap();
2335
2336        assert_eq!(
2337            status1,
2338            UploadStatus::ResumeIncomplete(UploadedRange {
2339                first_byte: 0,
2340                last_byte: chunk1_data.len() as u64 - 1,
2341            })
2342        );
2343
2344        tracing::info!("upload chunk1 resume {:?}", chunk1);
2345        let status1 = uploader
2346            .upload_multiple_chunk(chunk1_data.clone(), &chunk1)
2347            .await
2348            .unwrap();
2349
2350        assert_eq!(
2351            status1,
2352            UploadStatus::ResumeIncomplete(UploadedRange {
2353                first_byte: 0,
2354                last_byte: chunk1_data.len() as u64 - 1,
2355            })
2356        );
2357
2358        // total size is required for final chunk.
2359        let remaining = chunk1_data.len() as u64 + chunk2_data.len() as u64;
2360        let chunk2 = ChunkSize::new(chunk1_data.len() as u64, remaining - 1, Some(remaining));
2361        tracing::info!("upload chunk2 {:?}", chunk2);
2362        let status2 = uploader
2363            .upload_multiple_chunk(chunk2_data.clone(), &chunk2)
2364            .await
2365            .unwrap();
2366        assert!(matches!(status2, UploadStatus::Ok(_)));
2367
2368        let get_request = &GetObjectRequest {
2369            bucket: bucket_name.to_string(),
2370            object: file_name.to_string(),
2371            ..Default::default()
2372        };
2373
2374        let object = client.get_object(get_request).await.unwrap();
2375        assert_eq!(object.content_type.unwrap(), "video/mp4");
2376
2377        let download = client.download_object(get_request, &Range::default()).await.unwrap();
2378        chunk1_data.extend(chunk2_data);
2379        assert_eq!(chunk1_data, download);
2380    }
2381}