drive_v3/resources/files.rs
1use std::fs;
2use std::io::Read;
3use std::path::PathBuf;
4use std::collections::HashMap;
5use reqwest::{Url, Method, header};
6use reqwest::blocking::{Client, multipart, RequestBuilder};
7use drive_v3_macros::{DriveRequestBuilder, request};
8
9use super::DriveRequestBuilder;
10use crate::utils::upload::FileUploader;
11use crate::{Credentials, Error, ErrorKind, objects};
12
13#[request(
14 method=Method::POST,
15 url="https://www.googleapis.com/drive/v3/files/{file_id}/copy",
16 returns=objects::File,
17)]
18#[derive(DriveRequestBuilder)]
19/// A request builder to create a copy of a file and apply any requested
20/// updates with patch semantics.
21pub struct CopyRequest {
22 /// Whether to ignore the domain's default visibility settings for the
23 /// created file.
24 ///
25 /// Domain administrators can choose to make all uploaded files visible
26 /// to the domain by default; this parameter bypasses that behavior for
27 /// the request. Permissions are still inherited from parent folders.
28 #[drive_v3(parameter)]
29 ignore_default_visibility: Option<bool>,
30
31 /// Whether to set the `keepForever` field in the new head revision.
32 ///
33 /// This is only applicable to files with binary content in a Drive.
34 ///
35 /// Only 200 revisions for the file can be kept forever, if the limit is
36 /// reached, try deleting pinned revisions.
37 #[drive_v3(parameter)]
38 keep_revision_forever: Option<bool>,
39
40 /// A language hint for OCR processing during image import
41 /// (ISO 639-1 code).
42 #[drive_v3(parameter)]
43 ocr_language: Option<String>,
44
45 /// Whether the requesting application supports both My Drives and
46 /// shared drives.
47 #[drive_v3(parameter)]
48 supports_all_drives: Option<bool>,
49
50 /// Specifies which additional view's permissions to include in the
51 /// response.
52 ///
53 /// Only `published` is supported.
54 #[drive_v3(parameter)]
55 include_permissions_for_view: Option<String>,
56
57 /// A comma-separated list of IDs of labels to include in the
58 /// [`label_info`](objects::File::label_info) part of the response.
59 #[drive_v3(parameter)]
60 include_labels: Option<String>,
61
62 /// Sets the metadata that the copied file will have.
63 #[drive_v3(body)]
64 metadata: Option<objects::File>,
65}
66
67#[request(
68 method=Method::POST,
69 url="https://www.googleapis.com/upload/drive/v3/files",
70)]
71#[derive(DriveRequestBuilder)]
72/// A request builder to create a new file in a Drive.
73pub struct CreateRequest {
74 /// The type of upload request to the /upload URI.
75 #[drive_v3(parameter)]
76 upload_type: Option<objects::UploadType>,
77
78 /// Whether to ignore the domain's default visibility settings for the
79 /// created file.
80 ///
81 /// Domain administrators can choose to make all uploaded files visible
82 /// to the domain by default; this parameter bypasses that behavior for
83 /// the request. Permissions are still inherited from parent folders.
84 #[drive_v3(parameter)]
85 ignore_default_visibility: Option<bool>,
86
87 /// Whether to set the `keepForever` field in the new head revision.
88 ///
89 /// This is only applicable to files with binary content in a Drive.
90 ///
91 /// Only 200 revisions for the file can be kept forever, if the limit is
92 /// reached, try deleting pinned revisions.
93 #[drive_v3(parameter)]
94 keep_revision_forever: Option<bool>,
95
96 /// A language hint for OCR processing during image import
97 /// (ISO 639-1 code).
98 #[drive_v3(parameter)]
99 ocr_language: Option<String>,
100
101 /// Whether the requesting application supports both My Drives and
102 /// shared drives.
103 #[drive_v3(parameter)]
104 supports_all_drives: Option<bool>,
105
106 /// Whether to use the uploaded content as indexable text.
107 #[drive_v3(parameter)]
108 use_content_as_indexable_text: Option<bool>,
109
110 /// Specifies which additional view's permissions to include in the
111 /// response.
112 ///
113 /// Only `published` is supported.
114 #[drive_v3(parameter)]
115 include_permissions_for_view: Option<String>,
116
117 /// A comma-separated list of IDs of labels to include in the
118 /// [`label_info`](objects::File::label_info) part of the response.
119 #[drive_v3(parameter)]
120 include_labels: Option<String>,
121
122 /// Sets the metadata that the created file will have in Google Drive.
123 #[drive_v3(body)]
124 metadata: Option<objects::File>,
125
126 /// The path of the source file to be uploaded.
127 content_source: Option<PathBuf>,
128
129 /// Use this string as the content of the created file.
130 content_string: Option<String>,
131
132 /// A callback to receive updates on a resumable upload.
133 #[drive_v3(callback)]
134 callback: Option<fn(usize, usize)>,
135}
136
137impl CreateRequest {
138 /// Gets a media request.
139 fn get_media_request( &self ) -> crate::Result<RequestBuilder> {
140 let mut content_bytes = Vec::new();
141
142 if self.content_source.is_some() && self.content_string.is_some() {
143 return Err( Error::new(
144 ErrorKind::Request,
145 "a create request can only use one of 'content_source' or 'content_string'"
146 ) )
147 }
148
149 if let Some(source) = &self.content_source {
150 let mut file = fs::File::open(source)?;
151 file.read_to_end(&mut content_bytes)?;
152 }
153
154 if let Some(string) = &self.content_string {
155 content_bytes = string.as_bytes().to_vec();
156 }
157
158 let mut request = self.build()?
159 .header( "Content-Length", content_bytes.len().to_string() )
160 .body(content_bytes);
161
162 let metadata = self.metadata.clone().unwrap_or_default();
163 if let Some(mime_type) = metadata.mime_type {
164 request = request.header("Content-Type", mime_type);
165 }
166
167 Ok(request)
168 }
169
170 fn get_metadata_form_part( &self ) -> crate::Result<multipart::Part> {
171 let metadata_string = serde_json::to_string(&self.metadata)?;
172
173 let mut metadata_headers = header::HeaderMap::new();
174
175 metadata_headers.insert(
176 header::CONTENT_TYPE, "application/json; charset=UTF-8".parse()?
177 );
178
179 metadata_headers.insert(
180 header::CONTENT_DISPOSITION, "form-data; name=\"metadata\"".parse()?
181 );
182
183 Ok( multipart::Part::text(metadata_string)
184 .headers(metadata_headers) )
185 }
186
187 fn get_file_form_part( &self ) -> crate::Result<multipart::Part> {
188 let metadata = self.metadata.clone().unwrap_or_default();
189 let content_mime_type = metadata.mime_type.unwrap_or( "*/*".into() );
190
191 let mut file_headers = reqwest::header::HeaderMap::new();
192
193 file_headers.insert(
194 header::CONTENT_TYPE, content_mime_type.parse()?
195 );
196
197 file_headers.insert(
198 header::CONTENT_DISPOSITION, "form-data; name=\"file\"".parse()?
199 );
200
201 let mut file_part = multipart::Part::text("");
202
203 if let Some(source) = &self.content_source {
204 file_part = multipart::Part::file(source)?;
205 }
206
207 if let Some(string) = &self.content_string {
208 file_part = multipart::Part::text(string.clone());
209 }
210
211 Ok( file_part.headers(file_headers) )
212 }
213
214 /// Gets a multipart request.
215 fn get_multipart_request( &self ) -> crate::Result<RequestBuilder> {
216 let metadata_part = self.get_metadata_form_part()?;
217 let file_part = self.get_file_form_part()?;
218
219 let form = multipart::Form::new()
220 .part("metadata", metadata_part)
221 .part("file", file_part);
222
223 Ok( self.build()?
224 .multipart(form) )
225 }
226
227 /// Performs a resumable upload.
228 fn perform_resumable_upload( &self ) -> crate::Result<objects::File> {
229 let metadata = self.metadata.clone().unwrap_or_default();
230 let metadata_string = serde_json::to_string(&metadata)?;
231 let metadata_size = metadata_string.as_bytes().len();
232 let content_mime_type = metadata.mime_type.unwrap_or( "*/*".into() );
233
234 if self.content_string.is_some() {
235 return Err( Error::new(
236 ErrorKind::Request,
237 "A resumable upload cannot be created from a string, it must be a file",
238 ) )
239 }
240
241 let mut file = match &self.content_source {
242 Some(source) => fs::File::open(source)?,
243 None => {
244 return Err( Error::new(
245 ErrorKind::Request,
246 "A resumable request must include a source file"
247 ) )
248 }
249 };
250
251 let file_size = file.metadata()?.len();
252
253 let request = self.build()?
254 .header( "X-Upload-Content-Type", &content_mime_type )
255 .header( "X-Upload-Content-Length", &file_size.to_string() )
256 .header( "Content-Type", "application/json; charset=UTF-8" )
257 .header( "Content-Length", &metadata_size.to_string() )
258 .body(metadata_string);
259
260 let response = request.send()?;
261
262 if !response.status().is_success() {
263 return Err( response.into() );
264 }
265
266 let upload_uri = match response.headers().get("location") {
267 Some(header) => header.to_str()?,
268 #[cfg(not(tarpaulin_include))]
269 None => {
270 return Err( Error::new(
271 ErrorKind::Request,
272 "unable to get the resumable upload location",
273 ) )
274 }
275 };
276
277 let mut file_uploader = FileUploader::from_uri(upload_uri);
278
279 if let Some(callback) = self.callback {
280 file_uploader = file_uploader.with_callback(callback);
281 }
282
283 file_uploader.upload_file(&mut file)
284 }
285
286 /// Executes this request.
287 ///
288 /// # Errors
289 ///
290 /// - an [`IO`](crate::ErrorKind::IO) error, if the source file does not exist.
291 /// - a [`UrlParsing`](crate::ErrorKind::UrlParsing) error, if the creation of the request URL failed.
292 /// - a [`Json`](crate::ErrorKind::Json) error, if unable to parse the destination file to JSON.
293 /// - a [`Request`](crate::ErrorKind::Request) error, if unable to send the request or get a body from the response.
294 /// - a [`Response`](crate::ErrorKind::Response) error, if the request returned an error response.
295 pub fn execute( &self ) -> crate::Result<objects::File> {
296 let upload_type = self.upload_type.unwrap_or_default();
297
298 let request = match upload_type {
299 objects::UploadType::Media => self.get_media_request()?,
300 objects::UploadType::Multipart => self.get_multipart_request()?,
301 objects::UploadType::Resumable => {
302 return self.perform_resumable_upload()
303 },
304 };
305
306 let response = request.send()?;
307
308 if !response.status().is_success() {
309 return Err( response.into() );
310 }
311
312 Ok( serde_json::from_str( &response.text()? )? )
313 }
314}
315
316#[request(
317 method=Method::DELETE,
318 url="https://www.googleapis.com/drive/v3/files/{file_id}",
319 returns=(),
320)]
321#[derive(DriveRequestBuilder)]
322/// A request builder to permanently delete a file without moving it into
323/// the trash.
324pub struct DeleteRequest {
325 /// Whether the requesting application supports both My Drives and
326 /// shared drives.
327 #[drive_v3(parameter)]
328 supports_all_drives: Option<bool>,
329}
330
331#[request(
332 method=Method::DELETE,
333 url="https://www.googleapis.com/drive/v3/files/trash",
334 returns=(),
335)]
336#[derive(DriveRequestBuilder)]
337/// A request builder to permanently delete all of the user's trashed files.
338pub struct EmptyTrashRequest {
339 /// If set, empties the trash of the provided shared drive.
340 drive_id: Option<String>,
341}
342
343#[request(
344 method=Method::GET,
345 url="https://www.googleapis.com/drive/v3/files/{file_id}/export",
346 returns=Vec::<u8>,
347 returns_bytes=true,
348)]
349#[derive(DriveRequestBuilder)]
350/// A request builder to export a Google Workspace document.
351pub struct ExportRequest {
352 /// The MIME type of the format requested for this export.
353 #[drive_v3(parameter)]
354 mime_type: Option<String>,
355}
356
357#[request(
358 method=Method::GET,
359 url="https://www.googleapis.com/drive/v3/files/generateIds",
360 returns=objects::GeneratedIDs,
361)]
362#[derive(DriveRequestBuilder)]
363/// A request builder to generate IDs.
364pub struct GenerateIDsRequest {
365 /// The number of IDs to return.
366 #[drive_v3(parameter)]
367 count: Option<i64>,
368
369 /// The space in which the IDs can be used to create new files.
370 #[drive_v3(parameter)]
371 space: Option<objects::Space>,
372
373 /// The type of items which the IDs can be used for.
374 #[drive_v3(parameter)]
375 kind: Option<objects::IDKind>,
376}
377
378#[request(
379 method=Method::GET,
380 url="https://www.googleapis.com/drive/v3/files/{file_id}",
381 returns=objects::File,
382)]
383#[derive(DriveRequestBuilder)]
384/// A request builder to get a file’s metadata by ID.
385pub struct GetRequest {
386 /// Whether the requesting application supports both My Drives and
387 /// shared drives.
388 #[drive_v3(parameter)]
389 supports_all_drives: Option<bool>,
390
391 /// Specifies which additional view's permissions to include in the
392 /// response.
393 ///
394 /// Only `published` is supported.
395 #[drive_v3(parameter)]
396 include_permissions_for_view: Option<String>,
397
398 /// A comma-separated list of IDs of labels to include in the
399 /// [`label_info`](objects::File::label_info) part of the response.
400 #[drive_v3(parameter)]
401 include_labels: Option<String>,
402}
403
404#[request(
405 method=Method::GET,
406 url="https://www.googleapis.com/drive/v3/files/{file_id}",
407)]
408#[derive(DriveRequestBuilder)]
409/// A request builder to get a file’s content by ID.
410pub struct GetMediaRequest {
411 /// Whether the user is acknowledging the risk of downloading known
412 /// malware or other abusive files.
413 #[drive_v3(parameter)]
414 acknowledge_abuse: Option<bool>,
415
416 /// Whether the requesting application supports both My Drives and
417 /// shared drives.
418 #[drive_v3(parameter)]
419 supports_all_drives: Option<bool>,
420
421 /// Specifies which additional view's permissions to include in the
422 /// response.
423 ///
424 /// Only `published` is supported.
425 #[drive_v3(parameter)]
426 include_permissions_for_view: Option<String>,
427
428 /// A comma-separated list of IDs of labels to include in the
429 /// [`label_info`](objects::File::label_info) part of the response.
430 #[drive_v3(parameter)]
431 include_labels: Option<String>,
432
433 /// Path to save the contents to.
434 save_to: Option<PathBuf>,
435}
436
437impl GetMediaRequest {
438 /// Executes this request.
439 ///
440 /// # Errors
441 ///
442 /// - a [`UrlParsing`](ErrorKind::UrlParsing) error, if the creation of the request URL failed.
443 /// - a [`Json`](ErrorKind::Json) error, if unable to parse the destination file to JSON.
444 /// - a [`Request`](ErrorKind::Request) error, if unable to send the request or get a body from the response.
445 /// - a [`Response`](ErrorKind::Response) error, if the request returned an error response.
446 pub fn execute( &self ) -> crate::Result< Vec<u8> > {
447 let mut parameters = self.get_parameters();
448 parameters.push(( "alt".into(), objects::Alt::Media.to_string() ));
449
450 let url = Url::parse_with_params(&self.url, parameters)?;
451 let request = Client::new()
452 .request( self.method.clone(), url )
453 .bearer_auth( self.credentials.get_access_token() );
454
455 let response = request.send()?;
456
457 if !response.status().is_success() {
458 return Err( response.into() );
459 }
460
461 let file_bytes: Vec<u8> = response.bytes()?.into();
462
463 if let Some(path) = &self.save_to {
464 use std::io::Write;
465
466 let mut file = fs::File::create(path)?;
467 file.write_all(&file_bytes)?;
468 }
469
470 Ok(file_bytes)
471 }
472}
473
474#[request(
475 method=Method::GET,
476 url="https://www.googleapis.com/drive/v3/files",
477 returns=objects::FileList,
478)]
479#[derive(DriveRequestBuilder)]
480/// A request builder to list the user's files.
481pub struct ListRequest {
482 /// Bodies of items (files/documents) to which the query applies.
483 ///
484 /// Supported bodies are `user`, `domain`, `drive`, and `allDrives`.
485 ///
486 /// Prefer `user` or `drive` to `allDrives` for efficiency. By default,
487 /// corpora is set to `user`. However, this can change depending on the
488 /// filter set through the [`q`](ListRequest::q) parameter.
489 #[drive_v3(parameter)]
490 corpora: Option<String>,
491
492 /// ID of the shared drive to search.
493 #[drive_v3(parameter)]
494 drive_id: Option<String>,
495
496 /// Whether both My Drive and shared drive items should be included in
497 /// results.
498 #[drive_v3(parameter)]
499 include_items_from_all_drives: Option<bool>,
500
501 /// A comma-separated list of sort keys.
502 ///
503 /// Valid keys are `createdTime`, `folder`, `modifiedByMeTime`,
504 /// `modifiedTime`, `name`, `name_natural`, `quotaBytesUsed`,
505 /// `recency`, `sharedWithMeTime`, `starred`, and `viewedByMeTime`.
506 ///
507 /// Each key sorts ascending by default, but can be reversed by adding
508 /// the `desc` to the end of the key.
509 #[drive_v3(parameter)]
510 order_by: Option<String>,
511
512 /// The maximum number of files to return per page.
513 ///
514 /// Partial or empty result pages are possible even before the end of
515 /// the files list has been reached.
516 #[drive_v3(parameter)]
517 page_size: Option<i64>,
518
519 /// The token for continuing a previous list request on the next page.
520 ///
521 /// This should be set to the value of
522 /// [`nest_page_token`](objects::FileList::next_page_token) from the
523 /// previous response.
524 #[drive_v3(parameter)]
525 page_token: Option<String>,
526
527 /// A query for filtering the file results.
528 ///
529 /// For more information, see Google's
530 /// [Search for files & folders](https://developers.google.com/drive/api/guides/search-files)
531 /// guide.
532 #[drive_v3(parameter)]
533 q: Option<String>,
534
535 /// A comma-separated list of spaces to query within the corpora.
536 ///
537 /// Supported values are `drive` and `appDataFolder`.
538 #[drive_v3(parameter)]
539 spaces: Option<String>,
540
541 /// Whether the requesting application supports both My Drives and
542 /// shared drives.
543 #[drive_v3(parameter)]
544 supports_all_drives: Option<bool>,
545
546 /// Specifies which additional view's permissions to include in the
547 /// response.
548 ///
549 /// Only `published` is supported.
550 #[drive_v3(parameter)]
551 include_permissions_for_view: Option<String>,
552
553 /// A comma-separated list of IDs of labels to include in the
554 /// [`label_info`](objects::File::label_info) part of the response.
555 #[drive_v3(parameter)]
556 include_labels: Option<String>,
557}
558
559#[request(
560 method=Method::GET,
561 url="https://www.googleapis.com/drive/v3/files/{file_id}/listLabels",
562 returns=objects::LabelList,
563)]
564#[derive(DriveRequestBuilder)]
565/// A request builder to list the labels in a file.
566pub struct ListLabelsRequest {
567 /// The maximum number of labels to return per page.
568 ///
569 /// When not set, defaults to 100.
570 #[drive_v3(parameter)]
571 max_results: Option<i64>,
572
573 /// The token for continuing a previous list request on the next page.
574 ///
575 /// This should be set to the value of
576 /// [`next_page_token`](objects::LabelList::next_page_token) from the
577 /// previous response.
578 #[drive_v3(parameter)]
579 page_token: Option<String>,
580}
581
582#[request(
583 method=Method::POST,
584 url="https://www.googleapis.com/drive/v3/files/{file_id}/modifyLabels",
585)]
586#[derive(DriveRequestBuilder)]
587/// A request builder to modify the labels in a file.
588pub struct ModifyLabelsRequest {
589 /// Sets the metadata that the updated file will have in Google Drive.
590 #[drive_v3(body)]
591 modifications: Option< Vec<objects::LabelModification> >,
592}
593
594#[cfg(not(tarpaulin_include))] // Requires a business account
595impl ModifyLabelsRequest {
596 /// Executes this request.
597 ///
598 /// # Errors
599 ///
600 /// - a [`UrlParsing`](ErrorKind::UrlParsing) error, if the creation of the
601 /// request URL failed.
602 /// - a [`Json`](ErrorKind::Json) error, if unable to parse the destination
603 /// file to JSON.
604 /// - a [`Request`](ErrorKind::Request) error, if unable to send the request
605 /// or get a body from the response.
606 /// - a [`Response`](ErrorKind::Response) error, if the request returned an
607 /// error response.
608 pub fn execute( &self ) -> crate::Result< Vec<objects::Label> > {
609 let mut modify_request = objects::ModifyLabelsRequest::new();
610
611 if let Some(modifications) = &self.modifications {
612 modify_request.label_modifications = Some( modifications.to_vec() );
613 }
614
615 let request = self.build()?
616 .body( serde_json::to_string(&modify_request)? );
617
618 let response = request.send()?;
619
620 if !response.status().is_success() {
621 return Err( response.into() )
622 }
623
624 let response_text = response.text()?;
625 let parsed_json = serde_json::from_str::<HashMap<&str, serde_json::Value>> (&response_text)?;
626
627 match parsed_json.get("modifiedLabels") {
628 Some(labels) => Ok( serde_json::from_value(labels.clone())? ),
629 None => Err( Error::new(
630 ErrorKind::Json,
631 "the response did not contain the modified labels",
632 ) )
633 }
634 }
635}
636
637#[request(
638 method=Method::PATCH,
639 url="https://www.googleapis.com/upload/drive/v3/files/{file_id}",
640)]
641#[derive(DriveRequestBuilder)]
642/// A request builder to list the labels in a file.
643pub struct UpdateRequest {
644 /// The type of upload request to the /upload URI.
645 #[drive_v3(parameter)]
646 upload_type: Option<objects::UploadType>,
647
648 /// A comma-separated list of parent IDs to add.
649 #[drive_v3(parameter)]
650 add_parents: Option<String>,
651
652 /// Whether to set the `keepForever` field in the new head revision.
653 ///
654 /// This is only applicable to files with binary content in a Drive.
655 ///
656 /// Only 200 revisions for the file can be kept forever, if the limit is
657 /// reached, try deleting pinned revisions.
658 #[drive_v3(parameter)]
659 keep_revision_forever: Option<bool>,
660
661 /// A language hint for OCR processing during image import
662 /// (ISO 639-1 code).
663 #[drive_v3(parameter)]
664 ocr_language: Option<String>,
665
666 /// Whether the requesting application supports both My Drives and
667 /// shared drives.
668 #[drive_v3(parameter)]
669 supports_all_drives: Option<bool>,
670
671 /// Whether to use the uploaded content as indexable text.
672 #[drive_v3(parameter)]
673 use_content_as_indexable_text: Option<bool>,
674
675 /// Specifies which additional view's permissions to include in the
676 /// response.
677 ///
678 /// Only 'published' is supported.
679 #[drive_v3(parameter)]
680 include_permissions_for_view: Option<String>,
681
682 /// A comma-separated list of IDs of labels to include in the
683 /// [`label_info`](objects::File::label_info) part of the response.
684 #[drive_v3(parameter)]
685 include_labels: Option<String>,
686
687 /// Sets the metadata that the updated file will have in Google Drive.
688 #[drive_v3(body)]
689 metadata: Option<objects::File>,
690
691 /// The path of the source file to be uploaded.
692 content_source: Option<PathBuf>,
693
694 /// Use this string as the content of the created file.
695 content_string: Option<String>,
696
697 /// A callback to receive updates on a resumable upload.
698 #[drive_v3(callback)]
699 callback: Option<fn(usize, usize)>,
700}
701
702impl UpdateRequest {
703 /// Gets a media request.
704 fn get_media_request( &self ) -> crate::Result<RequestBuilder> {
705 let mut content_bytes = Vec::new();
706
707 if self.content_source.is_some() && self.content_string.is_some() {
708 return Err( Error::new(
709 ErrorKind::Request,
710 "an update request can only use one of 'content_source' or 'content_string'"
711 ) )
712 }
713
714 if let Some(source) = &self.content_source {
715 let mut file = fs::File::open(source)?;
716 file.read_to_end(&mut content_bytes)?;
717 }
718
719 if let Some(string) = &self.content_string {
720 content_bytes = string.as_bytes().to_vec();
721 }
722
723 let mut request = self.build()?
724 .header( "Content-Length", content_bytes.len().to_string() )
725 .body(content_bytes);
726
727 let metadata = self.metadata.clone().unwrap_or_default();
728 if let Some(mime_type) = metadata.mime_type {
729 request = request.header("Content-Type", mime_type);
730 }
731
732 Ok(request)
733 }
734
735 fn get_metadata_form_part( &self ) -> crate::Result<multipart::Part> {
736 let metadata_string = serde_json::to_string(&self.metadata)?;
737
738 let mut metadata_headers = header::HeaderMap::new();
739
740 metadata_headers.insert(
741 header::CONTENT_TYPE, "application/json; charset=UTF-8".parse()?
742 );
743
744 metadata_headers.insert(
745 header::CONTENT_DISPOSITION, "form-data; name=\"metadata\"".parse()?
746 );
747
748 Ok( multipart::Part::text(metadata_string)
749 .headers(metadata_headers) )
750 }
751
752 fn get_file_form_part( &self ) -> crate::Result<multipart::Part> {
753 let metadata = self.metadata.clone().unwrap_or_default();
754 let content_mime_type = metadata.mime_type.unwrap_or( "*/*".into() );
755
756 let mut file_headers = reqwest::header::HeaderMap::new();
757
758 file_headers.insert(
759 header::CONTENT_TYPE, content_mime_type.parse()?
760 );
761
762 file_headers.insert(
763 header::CONTENT_DISPOSITION, "form-data; name=\"file\"".parse()?
764 );
765
766 let mut file_part = multipart::Part::text("");
767
768 if let Some(source) = &self.content_source {
769 file_part = multipart::Part::file(source)?;
770 }
771
772 if let Some(string) = &self.content_string {
773 file_part = multipart::Part::text(string.clone());
774 }
775
776 Ok( file_part.headers(file_headers) )
777 }
778
779 /// Gets a multipart request.
780 fn get_multipart_request( &self ) -> crate::Result<RequestBuilder> {
781 let metadata_part = self.get_metadata_form_part()?;
782 let file_part = self.get_file_form_part()?;
783
784 let form = multipart::Form::new()
785 .part("metadata", metadata_part)
786 .part("file", file_part);
787
788 Ok( self.build()?
789 .multipart(form) )
790 }
791
792 /// Performs a resumable upload.
793 fn perform_resumable_upload( &self ) -> crate::Result<objects::File> {
794 let metadata = self.metadata.clone().unwrap_or_default();
795 let metadata_string = serde_json::to_string(&metadata)?;
796 let metadata_size = metadata_string.as_bytes().len();
797 let content_mime_type = metadata.mime_type.unwrap_or( "*/*".into() );
798
799 if self.content_string.is_some() {
800 return Err( Error::new(
801 ErrorKind::Request,
802 "A resumable upload cannot be created from a string, it must be a file",
803 ) )
804 }
805
806 let mut file = match &self.content_source {
807 Some(source) => fs::File::open(source)?,
808 None => {
809 return Err( Error::new(
810 ErrorKind::Request,
811 "A resumable request must include a source file",
812 ) )
813 }
814 };
815
816 let file_size = file.metadata()?.len();
817
818 let request = self.build()?
819 .header( "X-Upload-Content-Type", &content_mime_type )
820 .header( "X-Upload-Content-Length", &file_size.to_string() )
821 .header( "Content-Type", "application/json; charset=UTF-8" )
822 .header( "Content-Length", &metadata_size.to_string() )
823 .body(metadata_string);
824
825 let response = request.send()?;
826
827 if !response.status().is_success() {
828 return Err( response.into() );
829 }
830
831 let upload_uri = match response.headers().get("location") {
832 Some(header) => header.to_str()?,
833 #[cfg(not(tarpaulin_include))]
834 None => {
835 return Err( Error::new(
836 ErrorKind::Request,
837 "unable to get the resumable upload location",
838 ) )
839 }
840 };
841
842 let mut file_uploader = FileUploader::from_uri(upload_uri);
843
844 if let Some(callback) = self.callback {
845 file_uploader = file_uploader.with_callback(callback);
846 }
847
848 file_uploader.upload_file(&mut file)
849 }
850
851 /// Executes this request.
852 ///
853 /// # Errors
854 ///
855 /// - an [`IO`](crate::ErrorKind::IO) error, if the source file does not exist.
856 /// - a [`UrlParsing`](crate::ErrorKind::UrlParsing) error, if the creation of the request URL failed.
857 /// - a [`Json`](crate::ErrorKind::Json) error, if unable to parse the destination file to JSON.
858 /// - a [`Request`](crate::ErrorKind::Request) error, if unable to send the request or get a body from the response.
859 /// - a [`Response`](crate::ErrorKind::Response) error, if the request returned an error response.
860 pub fn execute( &self ) -> crate::Result<objects::File> {
861 let upload_type = self.upload_type.unwrap_or_default();
862
863 let request = match upload_type {
864 objects::UploadType::Media => self.get_media_request()?,
865 objects::UploadType::Multipart => self.get_multipart_request()?,
866 objects::UploadType::Resumable => {
867 return self.perform_resumable_upload()
868 },
869 };
870
871 let response = request.send()?;
872
873 if !response.status().is_success() {
874 return Err( response.into() );
875 }
876
877 Ok( serde_json::from_str( &response.text()? )? )
878 }
879}
880
881#[request(
882 method=Method::POST,
883 url="https://www.googleapis.com/drive/v3/files/{file_id}/watch",
884 returns=objects::Channel,
885)]
886#[derive(DriveRequestBuilder)]
887/// A request builder to modify the labels in a file.
888pub struct WatchRequest {
889 /// Whether the requesting application supports both My Drives and
890 /// shared drives.
891 #[drive_v3(parameter)]
892 supports_all_drives: Option<bool>,
893
894 /// Whether the user is acknowledging the risk of downloading known
895 /// malware or other abusive files.
896 #[drive_v3(parameter)]
897 acknowledge_abuse: Option<bool>,
898
899 /// Specifies which additional view's permissions to include in the
900 /// response.
901 ///
902 /// Only `published` is supported.
903 #[drive_v3(parameter)]
904 include_permissions_for_view: Option<String>,
905
906 /// A comma-separated list of IDs of labels to include in the
907 /// [`label_info`](objects::File::label_info) part of the response.
908 #[drive_v3(parameter)]
909 include_labels: Option<String>,
910
911 /// Sets the metadata that the channel will have.
912 #[drive_v3(body)]
913 channel: Option<objects::Channel>,
914}
915
916/// Information related to a user's files.
917///
918/// # Examples:
919///
920/// List the files in a drive
921///
922/// ```no_run
923/// # use drive_v3::Error;
924/// use drive_v3::{Credentials, Drive};
925///
926/// let credentials_path = "my_credentials.json";
927/// let scopes = ["https://www.googleapis.com/auth/drive.metadata.readonly"];
928///
929/// let credentials = Credentials::from_file(&credentials_path, &scopes)?;
930/// let drive = Drive::new(&credentials);
931///
932/// let file_list = drive.files.list()
933/// .fields("files(name, id, mimeType)") // Set what fields will be returned
934/// .q("name = 'file_im_looking_for' and not trashed") // search for specific files
935/// .execute()?;
936///
937/// if let Some(files) = file_list.files {
938/// for file in &files {
939/// println!("{}", file);
940/// }
941/// }
942/// # Ok::<(), Error>(())
943/// ```
944#[derive(Debug, Clone, PartialEq, Eq)]
945pub struct Files {
946 /// Credentials used to authenticate a user's access to this resource.
947 credentials: Credentials,
948}
949
950impl Files {
951 /// Creates a new [`Files`] resource with the given [`Credentials`].
952 pub fn new( credentials: &Credentials ) -> Self {
953 Self {
954 credentials: credentials.clone(),
955 }
956 }
957
958 /// Creates a copy of a file and applies any requested updates with patch
959 /// semantics.
960 ///
961 /// See Google's
962 /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/files/copy)
963 /// for more information.
964 ///
965 /// # Requires one of the following OAuth scopes:
966 ///
967 /// - `https://www.googleapis.com/auth/drive`
968 /// - `https://www.googleapis.com/auth/drive.appdata`
969 /// - `https://www.googleapis.com/auth/drive.file`
970 /// - `https://www.googleapis.com/auth/drive.photos.readonly`
971 ///
972 /// # Examples:
973 ///
974 /// ```no_run
975 /// use drive_v3::objects::File;
976 /// # use drive_v3::{Error, Credentials, Drive};
977 /// #
978 /// # let drive = Drive::new( &Credentials::from_file(
979 /// # "../.secure-files/google_drive_credentials.json",
980 /// # &["https://www.googleapis.com/auth/drive.file"],
981 /// # )? );
982 ///
983 /// // Set the metadata that the copied file will have
984 /// let metadata = File {
985 /// name: Some( "my-copy.txt".to_string() ),
986 /// description: Some( "I copied this using drive_v3!".to_string() ),
987 /// ..Default::default()
988 /// };
989 ///
990 /// // Set the ID of the file you want to copy,
991 /// // you can get this using files.list()
992 /// let source_file_id = "some-file-id";
993 ///
994 /// let copied_file = drive.files.copy(&source_file_id)
995 /// .metadata(&metadata)
996 /// .execute()?;
997 ///
998 /// assert_eq!(copied_file.name, metadata.name);
999 /// assert_eq!(copied_file.description, metadata.description);
1000 /// # Ok::<(), Error>(())
1001 /// ```
1002 pub fn copy<T: AsRef<str>> ( &self, file_id: T ) -> CopyRequest {
1003 CopyRequest::new(&self.credentials, &file_id)
1004 }
1005
1006 /// Creates a new file.
1007 ///
1008 /// See Google's
1009 /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/files/create)
1010 /// for more information.
1011 ///
1012 /// # Note:
1013 ///
1014 /// You can use any of the [`UploadTypes`](objects::UploadType), but for
1015 /// most uploads I recommend using the
1016 /// [`Resumable`](objects::UploadType::Resumable) type, which will allow you
1017 /// to set a callback to monitor the upload progress.
1018 ///
1019 /// # Requires one of the following OAuth scopes:
1020 ///
1021 /// - `https://www.googleapis.com/auth/drive`
1022 /// - `https://www.googleapis.com/auth/drive.appdata`
1023 /// - `https://www.googleapis.com/auth/drive.file`
1024 ///
1025 /// # Examples:
1026 ///
1027 /// Perform a simple upload to create a small media file (5 MB or less)
1028 /// without supplying metadata:
1029 ///
1030 /// ```no_run
1031 /// use drive_v3::objects::{File, UploadType};
1032 /// # use drive_v3::{Error, Credentials, Drive};
1033 /// #
1034 /// # let drive = Drive::new( &Credentials::from_file(
1035 /// # "../.secure-files/google_drive_credentials.json",
1036 /// # &["https://www.googleapis.com/auth/drive.file"],
1037 /// # )? );
1038 ///
1039 /// // A simple upload does not support metadata, however you can use it in
1040 /// // this request to set the MIME type of your new file, any other fields
1041 /// // you set will be ignored.
1042 /// let metadata = File {
1043 /// mime_type: Some( "text/plain".to_string() ),
1044 /// ..Default::default()
1045 /// };
1046 ///
1047 /// let my_new_file = drive.files.create()
1048 /// .upload_type(UploadType::Media)
1049 /// .metadata(&metadata)
1050 /// .content_string("This is the content of my new file!")
1051 /// // .content_source("path/to/file.txt") // You can also load a file from the system
1052 /// .execute()?;
1053 ///
1054 /// assert_eq!(my_new_file.mime_type, metadata.mime_type);
1055 /// # Ok::<(), Error>(())
1056 /// ```
1057 ///
1058 /// Perform a multipart upload to create a small media file (5 MB or less)
1059 /// along with metadata that describes the file, in a single request:
1060 ///
1061 /// ```no_run
1062 /// use drive_v3::objects::{File, UploadType};
1063 /// # use drive_v3::{Error, Credentials, Drive};
1064 /// #
1065 /// # let drive = Drive::new( &Credentials::from_file(
1066 /// # "../.secure-files/google_drive_credentials.json",
1067 /// # &["https://www.googleapis.com/auth/drive.file"],
1068 /// # )? );
1069 ///
1070 /// // Set what information the uploaded file wil have
1071 /// let metadata = File {
1072 /// name: Some( "my-new-file.txt".to_string() ),
1073 /// mime_type: Some( "text/plain".to_string() ),
1074 /// ..Default::default()
1075 /// };
1076 ///
1077 /// let my_new_file = drive.files.create()
1078 /// .upload_type(UploadType::Multipart)
1079 /// .metadata(&metadata)
1080 /// .content_source("path/to/file.txt")
1081 /// // .content_string("This is the content of my new file!") // You can use a string
1082 /// .execute()?;
1083 ///
1084 /// assert_eq!(my_new_file.name, metadata.name);
1085 /// assert_eq!(my_new_file.mime_type, metadata.mime_type);
1086 /// # Ok::<(), Error>(())
1087 /// ```
1088 ///
1089 /// Perform a resumable upload to create a large file (greater than 5 MB):
1090 ///
1091 /// ```no_run
1092 /// use drive_v3::objects::{File, UploadType};
1093 /// # use drive_v3::{Error, Credentials, Drive};
1094 /// #
1095 /// # let drive = Drive::new( &Credentials::from_file(
1096 /// # "../.secure-files/google_drive_credentials.json",
1097 /// # &["https://www.googleapis.com/auth/drive.file"],
1098 /// # )? );
1099 ///
1100 /// // Set what information the uploaded file wil have
1101 /// let metadata = File {
1102 /// name: Some( "my-new-file.txt".to_string() ),
1103 /// mime_type: Some( "text/plain".to_string() ),
1104 /// ..Default::default()
1105 /// };
1106 ///
1107 /// // You can set a callback that will be called when a resumable upload
1108 /// // progresses
1109 /// fn progress_callback( total_bytes: usize, uploaded_bytes: usize ) {
1110 /// println!("Uploaded {} bytes, out of a total of {}.", uploaded_bytes, total_bytes);
1111 /// }
1112 ///
1113 /// let my_new_file = drive.files.create()
1114 /// .upload_type(UploadType::Resumable)
1115 /// .callback(progress_callback)
1116 /// .metadata(&metadata)
1117 /// .content_source("path/to/file.txt")
1118 /// .execute()?;
1119 ///
1120 /// assert_eq!(my_new_file.name, metadata.name);
1121 /// assert_eq!(my_new_file.mime_type, metadata.mime_type);
1122 /// # Ok::<(), Error>(())
1123 /// ```
1124 pub fn create( &self ) -> CreateRequest {
1125 CreateRequest::new(&self.credentials)
1126 }
1127
1128 /// Permanently deletes a file owned by the user without moving it to the
1129 /// trash.
1130 ///
1131 /// If the file belongs to a shared drive, the user must be an organizer on
1132 /// the parent folder. If the target is a folder, all descendants owned by
1133 /// the user are also deleted.
1134 ///
1135 /// # Requires one of the following OAuth scopes:
1136 ///
1137 /// - `https://www.googleapis.com/auth/drive`
1138 /// - `https://www.googleapis.com/auth/drive.appdata`
1139 /// - `https://www.googleapis.com/auth/drive.file`
1140 ///
1141 /// # Examples:
1142 ///
1143 /// ```no_run
1144 /// # use drive_v3::{Error, Credentials, Drive};
1145 /// #
1146 /// # let drive = Drive::new( &Credentials::from_file(
1147 /// # "../.secure-files/google_drive_credentials.json",
1148 /// # &["https://www.googleapis.com/auth/drive.file"],
1149 /// # )? );
1150 /// #
1151 /// let my_file_id = "example-id";
1152 ///
1153 /// drive.files.delete(&my_file_id).execute()?;
1154 ///
1155 /// # Ok::<(), Error>(())
1156 /// ```
1157 pub fn delete<T: AsRef<str>> ( &self, file_id: T ) -> DeleteRequest {
1158 DeleteRequest::new(&self.credentials, &file_id)
1159 }
1160
1161 /// Permanently deletes all of the user's trashed files.
1162 ///
1163 /// # Note:
1164 ///
1165 /// The emptying of the trash may take some time to be reflected in a user's
1166 /// Drive.
1167 ///
1168 /// # Requires the following OAuth scope:
1169 ///
1170 /// - `https://www.googleapis.com/auth/drive`
1171 ///
1172 /// # Examples:
1173 ///
1174 /// ```no_run
1175 /// # use drive_v3::{Error, Credentials, Drive};
1176 /// #
1177 /// # let drive = Drive::new( &Credentials::from_file(
1178 /// # "../.secure-files/google_drive_credentials.json",
1179 /// # &["https://www.googleapis.com/auth/drive"],
1180 /// # )? );
1181 /// #
1182 /// drive.files.empty_trash()
1183 /// // .drive_id("my-drive-id") // You can specify which drive to empty the trash from
1184 /// .execute()?;
1185 ///
1186 /// # Ok::<(), Error>(())
1187 /// ```
1188 #[cfg(not(tarpaulin_include))] // Requires higher permissions
1189 pub fn empty_trash( &self ) -> EmptyTrashRequest {
1190 EmptyTrashRequest::new(&self.credentials)
1191 }
1192
1193 /// Exports a Google Workspace document to the requested MIME type and
1194 /// returns exported byte content (limited to 10MB).
1195 ///
1196 /// For more information on the supported export MIME types, check Google's
1197 /// [documentation](https://developers.google.com/drive/api/guides/ref-export-formats).
1198 ///
1199 /// # Note:
1200 ///
1201 /// This request requires you to set the
1202 /// [`mime_type`](ExportRequest::mime_type) of the file to export.
1203 ///
1204 /// # Requires one of the following OAuth scopes:
1205 ///
1206 /// - `https://www.googleapis.com/auth/drive`
1207 /// - `https://www.googleapis.com/auth/drive.file`
1208 /// - `https://www.googleapis.com/auth/drive.readonly`
1209 ///
1210 /// # Examples:
1211 ///
1212 /// ```no_run
1213 /// use std::fs;
1214 /// use std::io::Write;
1215 /// # use drive_v3::{Error, Credentials, Drive};
1216 /// #
1217 /// # let drive = Drive::new( &Credentials::from_file(
1218 /// # "../.secure-files/google_drive_credentials.json",
1219 /// # &["https://www.googleapis.com/auth/drive.file"],
1220 /// # )? );
1221 /// #
1222 /// let my_file_id = "file-id";
1223 /// let my_exported_mime_type = "application/pdf";
1224 ///
1225 /// let exported_bytes = drive.files.export(&my_file_id)
1226 /// .mime_type(my_exported_mime_type)
1227 /// .execute()?;
1228 ///
1229 /// // Write the bytes to a file
1230 /// let mut file = fs::File::create("exported-file.pdf")?;
1231 /// file.write_all(&exported_bytes)?;
1232 ///
1233 /// # Ok::<(), Error>(())
1234 /// ```
1235 #[cfg(not(tarpaulin_include))] // Requires higher permissions
1236 pub fn export<T: AsRef<str>> ( &self, file_id: T ) -> ExportRequest {
1237 ExportRequest::new(&self.credentials, &file_id)
1238 }
1239
1240 /// Generates a set of file IDs which can be provided in create or copy
1241 /// requests.
1242 ///
1243 /// # Requires one of the following OAuth scopes:
1244 ///
1245 /// - `https://www.googleapis.com/auth/drive`
1246 /// - `https://www.googleapis.com/auth/drive.appdata`
1247 /// - `https://www.googleapis.com/auth/drive.file`
1248 ///
1249 /// # Examples:
1250 ///
1251 /// ```no_run
1252 /// use drive_v3::objects::{Space, IDKind};
1253 /// # use drive_v3::{Error, Credentials, Drive};
1254 /// #
1255 /// # let drive = Drive::new( &Credentials::from_file(
1256 /// # "../.secure-files/google_drive_credentials.json",
1257 /// # &["https://www.googleapis.com/auth/drive.file"],
1258 /// # )? );
1259 ///
1260 /// let my_generated_ids = drive.files.generate_ids()
1261 /// .count(5) // How many IDs to generate
1262 /// .space(Space::Drive) // Set the space in which the IDs can be used
1263 /// .kind(IDKind::Files) // Set the type of the IDs
1264 /// .execute()?;
1265 ///
1266 /// for id in &my_generated_ids.ids {
1267 /// println!("Generated this ID: {}", id);
1268 /// }
1269 /// # Ok::<(), Error>(())
1270 /// ```
1271 pub fn generate_ids( &self ) -> GenerateIDsRequest {
1272 GenerateIDsRequest::new(&self.credentials)
1273 }
1274
1275 /// Gets a file's metadata by ID.
1276 ///
1277 /// # Note:
1278 ///
1279 /// To get the content of a file you can use [`get_media`](Files::get_media)
1280 /// (only works if the file is stored in Drive).
1281 ///
1282 /// To download Google Docs, Sheets, and Slides use
1283 /// [`export`](Files::export) instead.
1284 ///
1285 /// # Requires one of the following OAuth scopes:
1286 ///
1287 /// - `https://www.googleapis.com/auth/drive`
1288 /// - `https://www.googleapis.com/auth/drive.appdata`
1289 /// - `https://www.googleapis.com/auth/drive.file`
1290 /// - `https://www.googleapis.com/auth/drive.metadata`
1291 /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
1292 /// - `https://www.googleapis.com/auth/drive.photos.readonly`
1293 /// - `https://www.googleapis.com/auth/drive.readonly`
1294 ///
1295 /// # Examples:
1296 ///
1297 /// ```no_run
1298 /// # use drive_v3::{Error, Credentials, Drive};
1299 /// #
1300 /// # let drive = Drive::new( &Credentials::from_file(
1301 /// # "../.secure-files/google_drive_credentials.json",
1302 /// # &["https://www.googleapis.com/auth/drive.file"],
1303 /// # )? );
1304 /// #
1305 /// let my_file_id = "file-id";
1306 ///
1307 /// let file_metadata = drive.files.get(&my_file_id).execute()?;
1308 ///
1309 /// println!("Look at this metadata:\n{}", file_metadata);
1310 /// # Ok::<(), Error>(())
1311 /// ```
1312 pub fn get<T: AsRef<str>> ( &self, file_id: T ) -> GetRequest {
1313 GetRequest::new(&self.credentials, &file_id)
1314 }
1315
1316 /// Gets a file's content by ID.
1317 ///
1318 /// # Requires one of the following OAuth scopes:
1319 ///
1320 /// - `https://www.googleapis.com/auth/drive`
1321 /// - `https://www.googleapis.com/auth/drive.appdata`
1322 /// - `https://www.googleapis.com/auth/drive.file`
1323 /// - `https://www.googleapis.com/auth/drive.metadata`
1324 /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
1325 /// - `https://www.googleapis.com/auth/drive.photos.readonly`
1326 /// - `https://www.googleapis.com/auth/drive.readonly`
1327 ///
1328 /// # Examples:
1329 ///
1330 /// ```no_run
1331 /// # use drive_v3::{Error, Credentials, Drive};
1332 /// #
1333 /// # let drive = Drive::new( &Credentials::from_file(
1334 /// # "../.secure-files/google_drive_credentials.json",
1335 /// # &["https://www.googleapis.com/auth/drive.file"],
1336 /// # )? );
1337 /// #
1338 /// let my_text_file_id = "file-id";
1339 ///
1340 /// let file_bytes = drive.files.get_media(&my_text_file_id)
1341 /// // .save_to("my_downloaded_file.txt") // Save the contents to a path
1342 /// .execute()?;
1343 ///
1344 /// let content = String::from_utf8_lossy(&file_bytes);
1345 ///
1346 /// println!("content: {}", content);
1347 /// # Ok::<(), Error>(())
1348 /// ```
1349 pub fn get_media<T: AsRef<str>> ( &self, file_id: T ) -> GetMediaRequest {
1350 GetMediaRequest::new(&self.credentials, &file_id)
1351 }
1352
1353 /// Lists the user's files.
1354 ///
1355 /// This method accepts the [`q`](ListRequest::q) parameter, which is a
1356 /// search query combining one or more search terms.
1357 ///
1358 /// For more information, see Google's
1359 /// [Search for files & folders](https://developers.google.com/drive/api/guides/search-files)
1360 /// guide.
1361 ///
1362 /// # Note
1363 ///
1364 /// This method returns all files by default, including trashed files. If
1365 /// you don't want trashed files to appear in the list, use the
1366 /// `trashed=false` or `not trashed` in the [`q`](ListRequest::q) parameter
1367 /// to remove trashed files from the results.
1368 ///
1369 /// # Requires one of the following OAuth scopes:
1370 ///
1371 /// - `https://www.googleapis.com/auth/drive`
1372 /// - `https://www.googleapis.com/auth/drive.appdata`
1373 /// - `https://www.googleapis.com/auth/drive.file`
1374 /// - `https://www.googleapis.com/auth/drive.metadata`
1375 /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
1376 /// - `https://www.googleapis.com/auth/drive.photos.readonly`
1377 /// - `https://www.googleapis.com/auth/drive.readonly`
1378 ///
1379 /// # Examples:
1380 ///
1381 /// ```no_run
1382 /// # use drive_v3::{Error, Credentials, Drive};
1383 /// #
1384 /// # let drive = Drive::new( &Credentials::from_file(
1385 /// # "../.secure-files/google_drive_credentials.json",
1386 /// # &["https://www.googleapis.com/auth/drive.file"],
1387 /// # )? );
1388 /// #
1389 /// let file_list = drive.files.list()
1390 /// .fields("files(name, id, mimeType)") // Set what fields will be returned
1391 /// .q("name = 'file_im_looking_for' and not trashed") // search for specific files
1392 /// .execute()?;
1393 ///
1394 /// if let Some(files) = file_list.files {
1395 /// for file in &files {
1396 /// println!("{}", file);
1397 /// }
1398 /// }
1399 /// # Ok::<(), Error>(())
1400 /// ```
1401 pub fn list( &self ) -> ListRequest {
1402 ListRequest::new(&self.credentials)
1403 }
1404
1405 /// Lists the labels on a file.
1406 ///
1407 /// # Requires one of the following OAuth scopes:
1408 ///
1409 /// - `https://www.googleapis.com/auth/drive`
1410 /// - `https://www.googleapis.com/auth/drive.file`
1411 /// - `https://www.googleapis.com/auth/drive.metadata`
1412 /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
1413 /// - `https://www.googleapis.com/auth/drive.readonly`
1414 ///
1415 /// # Examples:
1416 ///
1417 /// ```no_run
1418 /// # use drive_v3::{Error, Credentials, Drive};
1419 /// #
1420 /// # let drive = Drive::new( &Credentials::from_file(
1421 /// # "../.secure-files/google_drive_credentials.json",
1422 /// # &["https://www.googleapis.com/auth/drive.file"],
1423 /// # )? );
1424 /// #
1425 /// let my_file_id = "file-id";
1426 ///
1427 /// let label_list = drive.files.list_labels(&my_file_id)
1428 /// .max_results(10)
1429 /// .execute()?;
1430 ///
1431 /// if let Some(labels) = label_list.labels {
1432 /// for label in &labels {
1433 /// println!("{}", label);
1434 /// }
1435 /// }
1436 /// # Ok::<(), Error>(())
1437 /// ```
1438 pub fn list_labels<T: AsRef<str>> ( &self, file_id: T ) -> ListLabelsRequest {
1439 ListLabelsRequest::new(&self.credentials, &file_id)
1440 }
1441
1442 /// Modifies the set of labels applied to a file.
1443 ///
1444 /// Returns a list of the labels that were added or modified.
1445 ///
1446 /// # Note
1447 ///
1448 /// As of now, labels on files are not supported on personal (free) Google
1449 /// Drive accounts.
1450 ///
1451 /// # Requires one of the following OAuth scopes:
1452 ///
1453 /// - `https://www.googleapis.com/auth/drive`
1454 /// - `https://www.googleapis.com/auth/drive.file`
1455 /// - `https://www.googleapis.com/auth/drive.metadata`
1456 ///
1457 /// # Examples:
1458 ///
1459 /// ```no_run
1460 /// use drive_v3::objects::{LabelModification, FieldModification};
1461 /// # use drive_v3::{Error, Credentials, Drive};
1462 /// #
1463 /// # let drive = Drive::new( &Credentials::from_file(
1464 /// # "../.secure-files/google_drive_credentials.json",
1465 /// # &["https://www.googleapis.com/auth/drive.file"],
1466 /// # )? );
1467 ///
1468 /// let label_modifications = vec![
1469 /// LabelModification::from(
1470 /// "label-id",
1471 /// &vec![
1472 /// FieldModification {
1473 /// set_text_values: Some( vec!["text".into(), "other_text".into()] ),
1474 /// ..Default::default()
1475 /// }
1476 /// ]
1477 /// )
1478 /// ];
1479 ///
1480 /// let my_file_id = "file-id";
1481 ///
1482 /// let modified_labels = drive.files.modify_labels(&my_file_id)
1483 /// .modifications(label_modifications)
1484 /// .execute()?;
1485 ///
1486 /// for label in &modified_labels {
1487 /// println!("this label was modified:\n{}", label);
1488 /// }
1489 /// # Ok::<(), Error>(())
1490 /// ```
1491 #[cfg(not(tarpaulin_include))] // Requires a business account
1492 pub fn modify_labels<T: AsRef<str>> ( &self, file_id: T ) -> ModifyLabelsRequest {
1493 ModifyLabelsRequest::new(&self.credentials, &file_id)
1494 }
1495
1496 /// Updates a file's metadata and/or content.
1497 ///
1498 /// When calling this method, only populate fields in the request that you
1499 /// want to modify. When updating fields, some fields might be changed
1500 /// automatically, such as [`modified_time`](objects::File::modified_time).
1501 ///
1502 /// This method supports patch semantics.
1503 ///
1504 /// # Requires one of the following OAuth scopes:
1505 ///
1506 /// - `https://www.googleapis.com/auth/drive`
1507 /// - `https://www.googleapis.com/auth/drive.appdata`
1508 /// - `https://www.googleapis.com/auth/drive.file`
1509 ///
1510 /// # Examples:
1511 ///
1512 /// Perform a simple upload to create a small media file (5 MB or less)
1513 /// without supplying metadata:
1514 ///
1515 /// ```no_run
1516 /// use drive_v3::objects::{File, UploadType};
1517 /// # use drive_v3::{Error, Credentials, Drive};
1518 /// #
1519 /// # let drive = Drive::new( &Credentials::from_file(
1520 /// # "../.secure-files/google_drive_credentials.json",
1521 /// # &["https://www.googleapis.com/auth/drive.file"],
1522 /// # )? );
1523 /// #
1524 /// // A simple upload does not support metadata, however you can use it in
1525 /// // this request to set the MIME type of your new file, any other fields
1526 /// // you set will be ignored.
1527 /// let metadata = File {
1528 /// mime_type: Some( "text/plain".to_string() ),
1529 /// ..Default::default()
1530 /// };
1531 ///
1532 /// let my_file_id = "file-id";
1533 ///
1534 /// let my_new_file = drive.files.update(&my_file_id)
1535 /// .upload_type(UploadType::Media)
1536 /// .metadata(&metadata)
1537 /// .content_string("This is the content of my new file!")
1538 /// // .content_source("path/to/file.txt") // You can also load a file from the system
1539 /// .execute()?;
1540 ///
1541 /// # Ok::<(), Error>(())
1542 /// ```
1543 ///
1544 /// Perform a multipart upload to create a small media file (5 MB or less)
1545 /// along with metadata that describes the file, in a single request:
1546 ///
1547 /// ```no_run
1548 /// use drive_v3::objects::{File, UploadType};
1549 /// # use drive_v3::{Error, Credentials, Drive};
1550 /// #
1551 /// # let drive = Drive::new( &Credentials::from_file(
1552 /// # "../.secure-files/google_drive_credentials.json",
1553 /// # &["https://www.googleapis.com/auth/drive.file"],
1554 /// # )? );
1555 /// #
1556 /// // Set what information the uploaded file wil have
1557 /// let metadata = File {
1558 /// name: Some( "my-new-file.txt".to_string() ),
1559 /// mime_type: Some( "text/plain".to_string() ),
1560 /// ..Default::default()
1561 /// };
1562 ///
1563 /// let my_file_id = "file-id";
1564 ///
1565 /// let my_new_file = drive.files.update(&my_file_id)
1566 /// .upload_type(UploadType::Multipart)
1567 /// .metadata(&metadata)
1568 /// .content_source("path/to/file.txt")
1569 /// // .content_string("This is the content of my new file!") // You can use a string
1570 /// .execute()?;
1571 ///
1572 /// # Ok::<(), Error>(())
1573 /// ```
1574 ///
1575 /// Perform a resumable upload to create a large file (greater than 5 MB):
1576 ///
1577 /// ```no_run
1578 /// use drive_v3::objects::{File, UploadType};
1579 /// # use drive_v3::{Error, Credentials, Drive};
1580 /// #
1581 /// # let drive = Drive::new( &Credentials::from_file(
1582 /// # "../.secure-files/google_drive_credentials.json",
1583 /// # &["https://www.googleapis.com/auth/drive.file"],
1584 /// # )? );
1585 /// #
1586 /// // Set what information the uploaded file wil have
1587 /// let metadata = File {
1588 /// name: Some( "my-new-file.txt".to_string() ),
1589 /// mime_type: Some( "text/plain".to_string() ),
1590 /// ..Default::default()
1591 /// };
1592 ///
1593 /// // You can set a callback that will be called when a resumable upload
1594 /// // progress, that way you can monitor and display how far along your
1595 /// // file upload is
1596 /// fn progress_callback( total_bytes: usize, uploaded_bytes: usize ) {
1597 /// println!("Uploaded {} bytes, out of a total of {}.", uploaded_bytes, total_bytes);
1598 /// }
1599 ///
1600 /// let my_file_id = "file-id";
1601 ///
1602 /// let my_new_file = drive.files.update(&my_file_id)
1603 /// .upload_type(UploadType::Resumable)
1604 /// .callback(progress_callback)
1605 /// .metadata(&metadata)
1606 /// .content_source("path/to/file.txt")
1607 /// .execute()?;
1608 ///
1609 /// # Ok::<(), Error>(())
1610 /// ```
1611 pub fn update<T: AsRef<str>> ( &self, file_id: T ) -> UpdateRequest {
1612 UpdateRequest::new(&self.credentials, &file_id)
1613 }
1614
1615 /// Subscribes to changes to a file.
1616 ///
1617 /// # Note
1618 ///
1619 /// In order to subscribe to a file's changes, you must provide a
1620 /// [`Channel`](objects::Channel) with an `id` and an `address` which is the
1621 /// one that will receive the notifications. This can be done by creating a
1622 /// channel using [`from`](objects::Channel::from).
1623 ///
1624 /// For more information on channels, see Google's
1625 /// [documentation](https://developers.google.com/drive/api/guides/push).
1626 ///
1627 /// # Requires one of the following OAuth scopes:
1628 ///
1629 /// - `https://www.googleapis.com/auth/drive`
1630 /// - `https://www.googleapis.com/auth/drive.appdata`
1631 /// - `https://www.googleapis.com/auth/drive.file`
1632 /// - `https://www.googleapis.com/auth/drive.metadata`
1633 /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
1634 /// - `https://www.googleapis.com/auth/drive.photos.readonly`
1635 /// - `https://www.googleapis.com/auth/drive.readonly`
1636 ///
1637 /// # Examples:
1638 ///
1639 /// ```no_run
1640 /// use drive_v3::objects::Channel;
1641 /// # use drive_v3::{Error, Credentials, Drive};
1642 /// #
1643 /// # let drive = Drive::new( &Credentials::from_file(
1644 /// # "../.secure-files/google_drive_credentials.json",
1645 /// # &["https://www.googleapis.com/auth/drive.file"],
1646 /// # )? );
1647 ///
1648 /// let channel_id = "my-channel-id";
1649 /// let channel_address = "https://mydomain.com/channel-notifications";
1650 /// let channel = Channel::from(&channel_id, &channel_address);
1651 ///
1652 /// let my_file_id = "file-id";
1653 ///
1654 /// let created_channel = drive.files.watch(&my_file_id)
1655 /// .channel(&channel)
1656 /// .execute()?;
1657 ///
1658 /// println!("this is the created channel:\n{}", created_channel);
1659 /// # Ok::<(), Error>(())
1660 /// ```
1661 pub fn watch<T: AsRef<str>> ( &self, file_id: T ) -> WatchRequest {
1662 WatchRequest::new(&self.credentials, &file_id)
1663 }
1664}
1665
1666#[cfg(test)]
1667mod tests {
1668 use std::fs;
1669 use super::Files;
1670 use std::io::Write;
1671 use std::path::PathBuf;
1672 use crate::{objects, ErrorKind};
1673 use crate::utils::test::{INVALID_CREDENTIALS, LOCAL_STORAGE_IN_USE, VALID_CREDENTIALS};
1674
1675 fn get_resource() -> Files {
1676 Files::new(&VALID_CREDENTIALS)
1677 }
1678
1679 fn get_invalid_resource() -> Files {
1680 Files::new(&INVALID_CREDENTIALS)
1681 }
1682
1683 fn delete_file( file: &objects::File ) -> crate::Result<()> {
1684 get_resource().delete( file.clone().id.unwrap() ).execute()
1685 }
1686
1687 fn get_test_metadata() -> objects::File {
1688 objects::File {
1689 name: Some( "test.txt".to_string() ),
1690 description: Some( "a test file".to_string() ),
1691 mime_type: Some( "text/plain".to_string() ),
1692 ..Default::default()
1693 }
1694 }
1695
1696 fn get_test_file() -> (fs::File, PathBuf) {
1697 let path = PathBuf::from("test-file.txt");
1698
1699 let mut test_file = fs::File::create(&path).unwrap();
1700 test_file.write_all( "content".as_bytes() ).unwrap();
1701
1702 (test_file, path)
1703 }
1704
1705 fn get_test_drive_file() -> crate::Result<objects::File> {
1706 let metadata = get_test_metadata();
1707
1708 get_resource().create()
1709 .upload_type(objects::UploadType::Multipart)
1710 .metadata(&metadata)
1711 .content_string("content")
1712 .execute()
1713 }
1714
1715 #[test]
1716 fn new_test() {
1717 let valid_resource = get_resource();
1718 let invalid_resource = get_invalid_resource();
1719
1720 assert_eq!( valid_resource.credentials, VALID_CREDENTIALS.clone() );
1721 assert_eq!( invalid_resource.credentials, INVALID_CREDENTIALS.clone() );
1722 }
1723
1724 #[test]
1725 fn copy_test() {
1726 let metadata = get_test_metadata();
1727 let test_drive_file = get_test_drive_file().unwrap();
1728
1729 let response = get_resource().copy( &test_drive_file.clone().id.unwrap() )
1730 .fields("*")
1731 .metadata(&metadata)
1732 .execute().unwrap();
1733
1734 assert_eq!(response.name, metadata.name);
1735 assert_eq!(response.description, metadata.description);
1736 assert_eq!(response.mime_type, metadata.mime_type);
1737
1738 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
1739 delete_file(&response).expect("Failed to cleanup a created file");
1740 }
1741
1742 #[test]
1743 fn copy_invalid_response_test() {
1744 let response = get_resource().copy("invalid-id")
1745 .execute();
1746
1747 assert!( response.is_err() );
1748 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
1749 }
1750
1751 #[test]
1752 fn create_media_test() {
1753 // Only run if no other tests are using the local storage
1754 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
1755
1756 let metadata = get_test_metadata();
1757 let (_, test_file_path) = get_test_file();
1758
1759 let response = get_resource().create()
1760 .upload_type(objects::UploadType::Media)
1761 .metadata(&metadata)
1762 .content_source(&test_file_path)
1763 .execute()
1764 .unwrap();
1765
1766 assert_eq!(response.mime_type, metadata.mime_type);
1767
1768 delete_file(&response).expect("Failed to cleanup a created file");
1769 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
1770 }
1771
1772 #[test]
1773 fn create_multipart_test() {
1774 // Only run if no other tests are using the local storage
1775 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
1776
1777 let metadata = get_test_metadata();
1778 let (_, test_file_path) = get_test_file();
1779
1780 let response = get_resource().create()
1781 .fields("*")
1782 .upload_type(objects::UploadType::Multipart)
1783 .metadata(&metadata)
1784 .content_string("content")
1785 .execute()
1786 .unwrap();
1787
1788 assert_eq!(response.name, metadata.name);
1789 assert_eq!(response.description, metadata.description);
1790 assert_eq!(response.mime_type, metadata.mime_type);
1791
1792 delete_file(&response).expect("Failed to cleanup a created file");
1793
1794 let response = get_resource().create()
1795 .fields("*")
1796 .upload_type(objects::UploadType::Multipart)
1797 .metadata(&metadata)
1798 .content_source(&test_file_path)
1799 .execute()
1800 .unwrap();
1801
1802 assert_eq!(response.name, metadata.name);
1803 assert_eq!(response.description, metadata.description);
1804 assert_eq!(response.mime_type, metadata.mime_type);
1805
1806 delete_file(&response).expect("Failed to cleanup a created file");
1807 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
1808 }
1809
1810 #[test]
1811 fn create_resumable_test() {
1812 // Only run if no other tests are using the local storage
1813 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
1814
1815 fn callback( _: usize, _: usize ) {}
1816
1817 let metadata = get_test_metadata();
1818 let (_, test_file_path) = get_test_file();
1819
1820 let response = get_resource().create()
1821 .fields("*")
1822 .upload_type(objects::UploadType::Resumable)
1823 .metadata(&metadata)
1824 .content_source(&test_file_path)
1825 .callback(callback)
1826 .execute()
1827 .unwrap();
1828
1829 assert_eq!(response.name, metadata.name);
1830 assert_eq!(response.description, metadata.description);
1831 assert_eq!(response.mime_type, metadata.mime_type);
1832
1833 delete_file(&response).expect("Failed to cleanup a created file");
1834 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
1835 }
1836
1837 #[test]
1838 fn create_resumable_invalid_response_test() {
1839 // Only run if no other tests are using the local storage
1840 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
1841
1842 let (_, test_file_path) = get_test_file();
1843
1844 let response = get_invalid_resource().create()
1845 .upload_type(objects::UploadType::Resumable)
1846 .content_source(&test_file_path)
1847 .execute();
1848
1849 assert!( response.is_err() );
1850 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
1851
1852 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
1853 }
1854
1855 #[test]
1856 fn create_multiple_sources_test() {
1857 // Only run if no other tests are using the local storage
1858 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
1859
1860 let (_, test_file_path) = get_test_file();
1861
1862 let response = get_resource().create()
1863 .upload_type(objects::UploadType::Media)
1864 .content_string("content")
1865 .content_source(&test_file_path)
1866 .execute();
1867
1868 assert!( response.is_err() );
1869 assert_eq!( response.unwrap_err().kind, ErrorKind::Request );
1870
1871 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
1872 }
1873
1874 #[test]
1875 fn create_invalid_resumable_test() {
1876 let response = get_resource().create()
1877 .upload_type(objects::UploadType::Resumable)
1878 .content_string("content")
1879 .execute();
1880
1881 assert!( response.is_err() );
1882 assert_eq!( response.unwrap_err().kind, ErrorKind::Request );
1883 }
1884
1885 #[test]
1886 fn create_no_source_resumable_test() {
1887 let response = get_resource().create()
1888 .upload_type(objects::UploadType::Resumable)
1889 .execute();
1890
1891 assert!( response.is_err() );
1892 assert_eq!( response.unwrap_err().kind, ErrorKind::Request );
1893 }
1894
1895 #[test]
1896 fn create_invalid_response_test() {
1897 let response = get_invalid_resource().create()
1898 .content_string("content")
1899 .execute();
1900
1901 assert!( response.is_err() );
1902 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
1903 }
1904
1905 #[test]
1906 fn generate_ids_test() {
1907 let response = get_resource().generate_ids()
1908 .count(8)
1909 .space(objects::Space::Drive)
1910 .kind(objects::IDKind::Files)
1911 .execute().unwrap();
1912
1913 assert_eq!( response.ids.len(), 8 );
1914 assert_eq!( response.space, objects::Space::Drive );
1915 }
1916
1917 #[test]
1918 fn get_test() {
1919 let metadata = get_test_metadata();
1920 let test_drive_file = get_test_drive_file().unwrap();
1921
1922 let response = get_resource().get( test_drive_file.clone().id.unwrap() )
1923 .fields("*")
1924 .execute().unwrap();
1925
1926 assert_eq!(response.name, metadata.name);
1927 assert_eq!(response.description, metadata.description);
1928 assert_eq!(response.mime_type, metadata.mime_type);
1929
1930 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
1931 }
1932
1933 #[test]
1934 fn get_media_test() {
1935 // Only run if no other tests are using the local storage
1936 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
1937
1938 let test_drive_file = get_test_drive_file().unwrap();
1939 let save_path = PathBuf::from("saved.txt");
1940
1941 let response = get_resource().get_media( test_drive_file.clone().id.unwrap() )
1942 .save_to(&save_path)
1943 .execute().unwrap();
1944
1945 let content = String::from_utf8(response).unwrap();
1946 let saved_content = fs::read_to_string(&save_path).unwrap();
1947
1948 assert_eq!(&content, "content");
1949 assert_eq!(&saved_content, "content");
1950
1951 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
1952 fs::remove_file(&save_path).expect("Failed to cleanup a created file");
1953 }
1954
1955 #[test]
1956 fn get_media_invalid_response_test() {
1957 let response = get_invalid_resource().get_media("invalid-id")
1958 .execute();
1959
1960 assert!( response.is_err() );
1961 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
1962 }
1963
1964 #[test]
1965 fn list_test() {
1966 let response = get_resource().list()
1967 .execute();
1968
1969 assert!( response.is_ok() );
1970 }
1971
1972 #[test]
1973 fn list_labels_test() {
1974 let test_drive_file = get_test_drive_file().unwrap();
1975
1976 let response = get_resource().list_labels( test_drive_file.clone().id.unwrap() )
1977 .execute();
1978
1979 assert!( response.is_ok() );
1980
1981 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
1982 }
1983
1984 #[test]
1985 fn update_test() {
1986 // Only run if no other tests are using the local storage
1987 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
1988
1989 let metadata = get_test_metadata();
1990 let test_drive_file = get_test_drive_file().unwrap();
1991 let (_, test_file_path) = get_test_file();
1992
1993 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
1994 .upload_type(objects::UploadType::Media)
1995 .metadata(&metadata)
1996 .content_source(&test_file_path)
1997 .execute()
1998 .unwrap();
1999
2000 assert_eq!(response.id, test_drive_file.id);
2001 assert_eq!(response.mime_type, metadata.mime_type);
2002
2003 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2004 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
2005 }
2006
2007 #[test]
2008 fn update_string_test() {
2009 // Only run if no other tests are using the local storage
2010 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
2011
2012 let metadata = get_test_metadata();
2013 let test_drive_file = get_test_drive_file().unwrap();
2014 let (_, test_file_path) = get_test_file();
2015
2016 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
2017 .upload_type(objects::UploadType::Media)
2018 .metadata(&metadata)
2019 .content_string("new content")
2020 .execute()
2021 .unwrap();
2022
2023 assert_eq!(response.id, test_drive_file.id);
2024 assert_eq!(response.mime_type, metadata.mime_type);
2025
2026 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2027 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
2028 }
2029
2030 #[test]
2031 fn update_multipart_test() {
2032 // Only run if no other tests are using the local storage
2033 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
2034
2035 let metadata = get_test_metadata();
2036 let test_drive_file = get_test_drive_file().unwrap();
2037 let (_, test_file_path) = get_test_file();
2038
2039 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
2040 .fields("*")
2041 .upload_type(objects::UploadType::Multipart)
2042 .metadata(&metadata)
2043 .content_string("content")
2044 .execute()
2045 .unwrap();
2046
2047 assert_eq!(response.id, test_drive_file.id);
2048 assert_eq!(response.name, metadata.name);
2049 assert_eq!(response.description, metadata.description);
2050 assert_eq!(response.mime_type, metadata.mime_type);
2051
2052 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
2053 .fields("*")
2054 .upload_type(objects::UploadType::Multipart)
2055 .metadata(&metadata)
2056 .content_source(&test_file_path)
2057 .execute()
2058 .unwrap();
2059
2060 assert_eq!(response.id, test_drive_file.id);
2061 assert_eq!(response.name, metadata.name);
2062 assert_eq!(response.description, metadata.description);
2063 assert_eq!(response.mime_type, metadata.mime_type);
2064
2065 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2066 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
2067 }
2068
2069 #[test]
2070 fn update_resumable_test() {
2071 // Only run if no other tests are using the local storage
2072 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
2073
2074 fn callback( _: usize, _: usize ) {}
2075
2076 let metadata = get_test_metadata();
2077 let test_drive_file = get_test_drive_file().unwrap();
2078 let (_, test_file_path) = get_test_file();
2079
2080 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
2081 .fields("*")
2082 .upload_type(objects::UploadType::Resumable)
2083 .metadata(&metadata)
2084 .content_source(&test_file_path)
2085 .callback(callback)
2086 .execute()
2087 .unwrap();
2088
2089 assert_eq!(response.id, test_drive_file.id);
2090 assert_eq!(response.name, metadata.name);
2091 assert_eq!(response.description, metadata.description);
2092 assert_eq!(response.mime_type, metadata.mime_type);
2093
2094 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2095 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
2096 }
2097
2098 #[test]
2099 fn update_invalid_id_test() {
2100 let response = get_resource().update("invalid-id")
2101 .execute();
2102
2103 assert!( response.is_err() );
2104 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
2105 }
2106
2107 #[test]
2108 fn update_resumable_invalid_response_test() {
2109 // Only run if no other tests are using the local storage
2110 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
2111
2112 let (_, test_file_path) = get_test_file();
2113
2114 let response = get_invalid_resource().update("invalid-id")
2115 .upload_type(objects::UploadType::Resumable)
2116 .content_source(&test_file_path)
2117 .execute();
2118
2119 assert!( response.is_err() );
2120 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
2121
2122 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
2123 }
2124
2125 #[test]
2126 fn update_invalid_response_test() {
2127 let response = get_invalid_resource().update("invalid-id")
2128 .content_string("content")
2129 .execute();
2130
2131 assert!( response.is_err() );
2132 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
2133 }
2134
2135 #[test]
2136 fn update_multiple_sources_test() {
2137 // Only run if no other tests are using the local storage
2138 let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
2139
2140 let test_drive_file = get_test_drive_file().unwrap();
2141 let (_, test_file_path) = get_test_file();
2142
2143 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
2144 .upload_type(objects::UploadType::Media)
2145 .content_string("content")
2146 .content_source(&test_file_path)
2147 .execute();
2148
2149 assert!( response.is_err() );
2150 assert_eq!( response.unwrap_err().kind, ErrorKind::Request );
2151
2152 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2153 fs::remove_file(&test_file_path).expect("Failed to cleanup a created file");
2154 }
2155
2156 #[test]
2157 fn update_invalid_resumable_test() {
2158 let test_drive_file = get_test_drive_file().unwrap();
2159
2160 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
2161 .upload_type(objects::UploadType::Resumable)
2162 .content_string("content")
2163 .execute();
2164
2165 assert!( response.is_err() );
2166 assert_eq!( response.unwrap_err().kind, ErrorKind::Request );
2167
2168 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2169 }
2170
2171 #[test]
2172 fn update_no_source_resumable_test() {
2173 let test_drive_file = get_test_drive_file().unwrap();
2174
2175 let response = get_resource().update( test_drive_file.clone().id.unwrap() )
2176 .upload_type(objects::UploadType::Resumable)
2177 .execute();
2178
2179 assert!( response.is_err() );
2180 assert_eq!( response.unwrap_err().kind, ErrorKind::Request );
2181
2182 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2183 }
2184
2185 #[test]
2186 fn watch_test() {
2187 let test_drive_file = get_test_drive_file().unwrap();
2188
2189 let channel_id = "channel_id".to_string();
2190 let channel_address = "https://wwwgoogle.com".to_string();
2191
2192 let channel = objects::Channel::from(&channel_id, &channel_address);
2193
2194 let response = get_resource().watch( test_drive_file.clone().id.unwrap() )
2195 .channel(&channel)
2196 .execute();
2197
2198 assert!( response.is_ok() );
2199 assert_eq!( response.unwrap().id, Some(channel_id) );
2200
2201 delete_file(&test_drive_file).expect("Failed to cleanup a created file");
2202 }
2203
2204 #[test]
2205 fn watch_invalid_response_test() {
2206 let response = get_invalid_resource().watch("invalid-id")
2207 .execute();
2208
2209 assert!( response.is_err() );
2210 assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
2211 }
2212}