drive_v3/resources/
replies.rs

1use reqwest::Method;
2use drive_v3_macros::{DriveRequestBuilder, request};
3
4use super::DriveRequestBuilder;
5use crate::{objects, Credentials};
6
7#[request(
8    method=Method::POST,
9    url="https://www.googleapis.com/drive/v3/files/{file_id}/comments/{comment_id}/replies",
10    returns=objects::Reply,
11)]
12#[derive(DriveRequestBuilder)]
13/// A request builder to create a reply to a comment.
14pub struct CreateRequest {
15    /// The reply which will be created.
16    #[drive_v3(body)]
17    reply: Option<objects::Reply>,
18}
19
20#[request(
21    method=Method::DELETE,
22    url="https://www.googleapis.com/drive/v3/files/{file_id}/comments/{comment_id}/replies/{reply_id}",
23    returns=(),
24)]
25#[derive(DriveRequestBuilder)]
26/// A request builder to delete a reply.
27pub struct DeleteRequest {}
28
29#[request(
30    method=Method::GET,
31    url="https://www.googleapis.com/drive/v3/files/{file_id}/comments/{comment_id}/replies/{reply_id}",
32    returns=objects::Reply,
33)]
34#[derive(DriveRequestBuilder)]
35/// A request builder to get a reply by ID.
36pub struct GetRequest {
37    /// Whether to return deleted replies.
38    ///
39    /// Deleted replies will not include their original content.
40    #[drive_v3(parameter)]
41    include_deleted: Option<bool>,
42}
43
44#[request(
45    method=Method::GET,
46    url="https://www.googleapis.com/drive/v3/files/{file_id}/comments/{comment_id}/replies",
47    returns=objects::ReplyList,
48)]
49#[derive(DriveRequestBuilder)]
50/// A request builder to list a comments's replies.
51pub struct ListRequest {
52    /// Whether to return deleted replies.
53    ///
54    /// Deleted replies will not include their original content.
55    #[drive_v3(parameter)]
56    include_deleted: Option<bool>,
57
58    /// The maximum number of permissions to return per page.
59    ///
60    /// When not set for files in a shared drive, at most 100 results will
61    /// be returned. When not set for files that are not in a shared drive,
62    /// the entire list will be returned.
63    #[drive_v3(parameter)]
64    page_size: Option<i64>,
65
66    /// The token for continuing a previous list request on the next page.
67    ///
68    /// This should be set to the value of
69    /// [`next_page_token`](objects::ReplyList::next_page_token) from
70    /// the previous response.
71    #[drive_v3(parameter)]
72    page_token: Option<String>,
73}
74
75#[request(
76    method=Method::PATCH,
77    url="https://www.googleapis.com/drive/v3/files/{file_id}/comments/{comment_id}/replies/{reply_id}",
78    returns=objects::Reply,
79)]
80#[derive(DriveRequestBuilder)]
81/// A request builder to update a permission for a file or shared drive.
82pub struct UpdateRequest {
83    /// Whether to remove the expiration date.
84    #[drive_v3(parameter)]
85    remove_expiration: Option<bool>,
86
87    /// Whether the requesting application supports both My Drives and
88    /// shared drives.
89    #[drive_v3(parameter)]
90    supports_all_drives: Option<bool>,
91
92    /// Whether to transfer ownership to the specified user and downgrade
93    /// the current owner to a writer.
94    ///
95    /// This parameter is required as an acknowledgement of the side effect.
96    #[drive_v3(parameter)]
97    transfer_ownership: Option<bool>,
98
99    /// Issue the request as a domain administrator.
100    ///
101    /// if set to `true`, then the requester will be granted access if they
102    /// are an administrator of the domain to which the shared drive
103    /// belongs.
104    #[drive_v3(parameter)]
105    use_domain_admin_access: Option<bool>,
106
107    /// The updated reply.
108    #[drive_v3(body)]
109    reply: Option<objects::Reply>,
110}
111
112/// A reply to a comment on a file.
113///
114/// Some resource methods (such as [`replies.update`](Replies::update))
115/// require a `permission_id`. Use the [`replies.list`](Replies::list)
116/// method to retrieve the ID for a file, folder, or shared drive.
117///
118/// # Examples:
119///
120/// List the permission in a file
121///
122/// ```no_run
123/// # use drive_v3::{Error, Credentials, Drive};
124/// #
125/// # let drive = Drive::new( &Credentials::from_file(
126/// #     "../.secure-files/google_drive_credentials.json",
127/// #     &["https://www.googleapis.com/auth/drive.file"],
128/// # )? );
129/// #
130/// let file_id = "some-file-id";
131/// let permission_list = drive.permissions.list(&file_id).execute()?;
132///
133/// if let Some(permissions) = permission_list.permissions {
134///     for permission in permissions {
135///         println!("{}", permission);
136///     }
137/// }
138/// # Ok::<(), Error>(())
139/// ```
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct Replies {
142    /// Credentials used to authenticate a user's access to this resource.
143    credentials: Credentials,
144}
145
146impl Replies {
147    /// Creates a new [`Replies`] resource with the given [`Credentials`].
148    pub fn new( credentials: &Credentials ) -> Self {
149        Self { credentials: credentials.clone() }
150    }
151
152    /// Creates a reply to a comment.
153    ///
154    /// See Google's
155    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/replies/create)
156    /// for more information.
157    ///
158    ///
159    /// # Requires one of the following OAuth scopes:
160    ///
161    /// - `https://www.googleapis.com/auth/drive`
162    /// - `https://www.googleapis.com/auth/drive.file`
163    ///
164    /// # Examples:
165    ///
166    /// ```no_run
167    /// use drive_v3::objects::Reply;
168    /// # use drive_v3::{Error, Credentials, Drive};
169    /// #
170    /// # let drive = Drive::new( &Credentials::from_file(
171    /// #     "../.secure-files/google_drive_credentials.json",
172    /// #     &["https://www.googleapis.com/auth/drive.file"],
173    /// # )? );
174    ///
175    /// let reply = Reply {
176    ///     content: Some( "my reply content".to_string() ),
177    ///     ..Default::default()
178    /// };
179    ///
180    /// let file_id = "some-file-id";
181    /// let comment_id = "some-comment-id";
182    ///
183    /// let created_reply = drive.replies.create(&file_id, &comment_id)
184    ///     .reply(&reply)
185    ///     .execute()?;
186    ///
187    /// assert_eq!(created_reply.content, reply.content);
188    /// # Ok::<(), Error>(())
189    /// ```
190    pub fn create<T, U> ( &self, file_id: T, comment_id: U ) -> CreateRequest
191        where
192            T: AsRef<str>,
193            U: AsRef<str>
194    {
195        CreateRequest::new(&self.credentials, file_id, comment_id)
196    }
197
198    /// Deletes a reply.
199    ///
200    /// See Google's
201    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/replies/delete)
202    /// for more information.
203    ///
204    /// # Requires one of the following OAuth scopes:
205    ///
206    /// - `https://www.googleapis.com/auth/drive`
207    /// - `https://www.googleapis.com/auth/drive.file`
208    ///
209    /// # Examples:
210    ///
211    /// ```no_run
212    /// # use drive_v3::{Error, Credentials, Drive};
213    /// #
214    /// # let drive = Drive::new( &Credentials::from_file(
215    /// #     "../.secure-files/google_drive_credentials.json",
216    /// #     &["https://www.googleapis.com/auth/drive.file"],
217    /// # )? );
218    /// #
219    /// let file_id = "some-file-id";
220    /// let comment_id = "some-comment-id";
221    /// let reply_id = "some-reply-id";
222    ///
223    /// let response = drive.replies.delete(&file_id, &comment_id, &reply_id).execute();
224    ///
225    /// assert!( response.is_ok() );
226    /// # Ok::<(), Error>(())
227    /// ```
228    pub fn delete<T, U, V> ( &self, file_id: T, comment_id: U, reply_id: V ) -> DeleteRequest
229        where
230            T: AsRef<str>,
231            U: AsRef<str>,
232            V: AsRef<str>,
233    {
234        DeleteRequest::new(&self.credentials, file_id, comment_id, reply_id)
235    }
236
237    /// Gets a reply by ID.
238    ///
239    /// See Google's
240    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/replies/get)
241    /// for more information.
242    ///
243    /// # Note:
244    ///
245    /// This request requires you to set the [`fields`](GetRequest::fields)
246    /// parameter.
247    ///
248    /// # Requires one of the following OAuth scopes:
249    ///
250    /// - `https://www.googleapis.com/auth/drive`
251    /// - `https://www.googleapis.com/auth/drive.file`
252    /// - `https://www.googleapis.com/auth/drive.readonly`
253    ///
254    /// # Examples:
255    ///
256    /// ```no_run
257    /// # use drive_v3::{Error, Credentials, Drive};
258    /// #
259    /// # let drive = Drive::new( &Credentials::from_file(
260    /// #     "../.secure-files/google_drive_credentials.json",
261    /// #     &["https://www.googleapis.com/auth/drive.file"],
262    /// # )? );
263    /// #
264    /// let file_id = "some-file-id";
265    /// let comment_id = "some-comment-id";
266    /// let reply_id = "some-reply-id";
267    ///
268    /// let reply = drive.replies.get(&file_id, &comment_id, &reply_id)
269    ///     .fields("*")
270    ///     .execute()?;
271    ///
272    /// println!("This is the comment's reply:\n{}", reply);
273    /// # Ok::<(), Error>(())
274    /// ```
275    pub fn get<T, U, V> ( &self, file_id: T, comment_id: U, reply_id: V ) -> GetRequest
276        where
277            T: AsRef<str>,
278            U: AsRef<str>,
279            V: AsRef<str>,
280    {
281        GetRequest::new(&self.credentials, file_id, comment_id, reply_id)
282    }
283
284    /// Lists a comment's replies.
285    ///
286    /// See Google's
287    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/replies/list)
288    /// for more information.
289    ///
290    /// # Note:
291    ///
292    /// This request requires you to set the [`fields`](ListRequest::fields)
293    /// parameter.
294    ///
295    /// # Requires one of the following OAuth scopes:
296    ///
297    /// - `https://www.googleapis.com/auth/drive`
298    /// - `https://www.googleapis.com/auth/drive.file`
299    /// - `https://www.googleapis.com/auth/drive.readonly`
300    ///
301    /// # Examples:
302    ///
303    /// ```no_run
304    /// # use drive_v3::{Error, Credentials, Drive};
305    /// #
306    /// # let drive = Drive::new( &Credentials::from_file(
307    /// #     "../.secure-files/google_drive_credentials.json",
308    /// #     &["https://www.googleapis.com/auth/drive.file"],
309    /// # )? );
310    /// #
311    /// let file_id = "some-file-id";
312    /// let comment_id = "some-comment-id";
313    ///
314    /// let reply_list = drive.replies.list(&file_id, &comment_id)
315    ///     .fields("*")
316    ///     .execute()?;
317    ///
318    /// if let Some(replies) = reply_list.replies {
319    ///     for reply in replies {
320    ///         println!("{}", reply);
321    ///     }
322    /// }
323    /// # Ok::<(), Error>(())
324    /// ```
325    pub fn list<T, U> ( &self, file_id: T, comment_id: U ) -> ListRequest
326        where
327            T: AsRef<str>,
328            U: AsRef<str>,
329    {
330        ListRequest::new(&self.credentials, file_id, comment_id)
331    }
332
333    /// Updates a reply with patch semantics.
334    ///
335    /// See Google's
336    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/replies/update)
337    /// for more information.
338    ///
339    /// # Note:
340    ///
341    /// This request requires you to set the [`fields`](UpdateRequest::fields)
342    /// parameter.
343    ///
344    /// # Requires one of the following OAuth scopes:
345    ///
346    /// - `https://www.googleapis.com/auth/drive`
347    /// - `https://www.googleapis.com/auth/drive.file`
348    ///
349    /// # Examples:
350    ///
351    /// ```no_run
352    /// use drive_v3::objects::Reply;
353    /// # use drive_v3::{Error, Credentials, Drive};
354    /// #
355    /// # let drive = Drive::new( &Credentials::from_file(
356    /// #     "../.secure-files/google_drive_credentials.json",
357    /// #     &["https://www.googleapis.com/auth/drive.file"],
358    /// # )? );
359    ///
360    /// let updated_reply = Reply {
361    ///     content: Some( "updated content".to_string() ),
362    ///     ..Default::default()
363    /// };
364    ///
365    /// let file_id = "some-file-id";
366    /// let comment_id = "some-comment-id";
367    /// let reply_id = "some-reply-id";
368    ///
369    /// let reply = drive.replies.update(&file_id, &comment_id, &reply_id)
370    ///     .fields("*")
371    ///     .reply(&updated_reply)
372    ///     .execute()?;
373    ///
374    /// assert_eq!(reply.content, updated_reply.content);
375    /// # Ok::<(), Error>(())
376    /// ```
377    pub fn update<T, U, V> ( &self, file_id: T, comment_id: U, reply_id: V ) -> UpdateRequest
378        where
379            T: AsRef<str>,
380            U: AsRef<str>,
381            V: AsRef<str>,
382    {
383        UpdateRequest::new(&self.credentials, file_id, comment_id, reply_id)
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    use super::Replies;
390    use crate::{ErrorKind, objects, resources};
391    use crate::utils::test::{INVALID_CREDENTIALS, VALID_CREDENTIALS};
392
393    fn get_resource() -> Replies {
394        Replies::new(&VALID_CREDENTIALS)
395    }
396
397    fn get_invalid_resource() -> Replies {
398        Replies::new(&INVALID_CREDENTIALS)
399    }
400
401    fn get_files_resource() -> resources::Files {
402        resources::Files::new(&VALID_CREDENTIALS)
403    }
404
405    fn get_comments_resource() -> resources::Comments {
406        resources::Comments::new(&VALID_CREDENTIALS)
407    }
408
409    fn delete_file( file: &objects::File ) -> crate::Result<()> {
410        get_files_resource().delete( file.clone().id.unwrap() ).execute()
411    }
412
413    fn get_test_file_metadata() -> objects::File {
414        objects::File {
415            name: Some( "test.txt".to_string() ),
416            description: Some( "a test file".to_string() ),
417            mime_type: Some( "text/plain".to_string() ),
418            ..Default::default()
419        }
420    }
421
422    fn get_test_reply() -> objects::Reply {
423        objects::Reply {
424            content: Some( "test reply".to_string() ),
425            ..Default::default()
426        }
427    }
428
429    fn get_test_comment() -> objects::Comment {
430        objects::Comment {
431            content: Some( "test comment".to_string() ),
432            ..Default::default()
433        }
434    }
435
436    fn get_test_drive_file() -> crate::Result<objects::File> {
437        let metadata = get_test_file_metadata();
438
439        get_files_resource().create()
440            .upload_type(objects::UploadType::Multipart)
441            .metadata(&metadata)
442            .content_string("content")
443            .execute()
444    }
445
446    fn get_test_drive_comment( file: &objects::File ) -> crate::Result<objects::Comment> {
447        let comment = get_test_comment();
448
449        get_comments_resource().create( &file.clone().id.unwrap() )
450            .fields("*")
451            .comment(&comment)
452            .execute()
453    }
454
455    fn get_test_drive_reply( file: &objects::File, comment: &objects::Comment ) -> crate::Result<objects::Reply> {
456        let test_reply = get_test_reply();
457
458        get_resource().create(
459                &file.clone().id.unwrap(),
460                &comment.clone().id.unwrap(),
461            )
462            .fields("*")
463            .reply(&test_reply)
464            .execute()
465    }
466
467    #[test]
468    fn new_test() {
469        let valid_resource = get_resource();
470        let invalid_resource = get_invalid_resource();
471
472        assert_eq!( valid_resource.credentials, VALID_CREDENTIALS.clone() );
473        assert_eq!( invalid_resource.credentials, INVALID_CREDENTIALS.clone() );
474    }
475
476    #[test]
477    fn create_test() {
478        let test_drive_file = get_test_drive_file().unwrap();
479        let test_drive_comment = get_test_drive_comment(&test_drive_file).unwrap();
480        let test_reply = get_test_reply();
481
482        let response = get_resource().create(
483                &test_drive_file.clone().id.unwrap(),
484                &test_drive_comment.clone().id.unwrap(),
485            )
486            .fields("content")
487            .reply(&test_reply)
488            .execute();
489
490        assert!( response.is_ok() );
491
492        let reply = response.unwrap();
493        assert_eq!(reply.content, test_reply.content);
494
495        delete_file(&test_drive_file).expect("Failed to cleanup created file");
496    }
497
498    #[test]
499    fn create_invalid_test() {
500        let response = get_invalid_resource().create("invalid-id", "invalid-id")
501            .execute();
502
503        assert!( response.is_err() );
504        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
505    }
506
507    #[test]
508    fn delete_test() {
509        let test_drive_file = get_test_drive_file().unwrap();
510        let test_drive_comment = get_test_drive_comment(&test_drive_file).unwrap();
511        let test_drive_reply = get_test_drive_reply(&test_drive_file, &test_drive_comment).unwrap();
512
513        let response = get_resource().delete(
514            &test_drive_file.clone().id.unwrap(),
515            &test_drive_comment.clone().id.unwrap(),
516            &test_drive_reply.clone().id.unwrap(),
517            )
518            .execute();
519
520        assert!( response.is_ok() );
521
522        delete_file(&test_drive_file).expect("Failed to cleanup created file");
523    }
524
525    #[test]
526    fn delete_invalid_test() {
527        let response = get_invalid_resource()
528            .delete("invalid-id", "invalid-id", "invalid-id")
529            .execute();
530
531        assert!( response.is_err() );
532        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
533    }
534
535    #[test]
536    fn get_test() {
537        let test_drive_file = get_test_drive_file().unwrap();
538        let test_drive_comment = get_test_drive_comment(&test_drive_file).unwrap();
539        let test_drive_reply = get_test_drive_reply(&test_drive_file, &test_drive_comment).unwrap();
540
541        let response = get_resource().get(
542            &test_drive_file.clone().id.unwrap(),
543            &test_drive_comment.clone().id.unwrap(),
544            &test_drive_reply.clone().id.unwrap(),
545            )
546            .fields("*")
547            .execute();
548
549        assert!( response.is_ok() );
550
551        let reply = response.unwrap();
552        assert_eq!(reply, test_drive_reply);
553
554        delete_file(&test_drive_file).expect("Failed to cleanup created file");
555    }
556
557    #[test]
558    fn get_invalid_test() {
559        let response = get_invalid_resource()
560            .get("invalid-id", "invalid-id", "invalid-id")
561            .execute();
562
563        assert!( response.is_err() );
564        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
565    }
566
567    #[test]
568    fn list_test() {
569        let test_drive_file = get_test_drive_file().unwrap();
570        let test_drive_comment = get_test_drive_comment(&test_drive_file).unwrap();
571        let test_drive_reply = get_test_drive_reply(&test_drive_file, &test_drive_comment).unwrap();
572
573        let response = get_resource().list(
574                &test_drive_file.clone().id.unwrap(),
575                &test_drive_comment.clone().id.unwrap(),
576            )
577            .fields("*")
578            .execute();
579
580        assert!( response.is_ok() );
581
582        let replies = response.unwrap().replies.unwrap();
583        assert!( replies.contains(&test_drive_reply) );
584
585        delete_file(&test_drive_file).expect("Failed to cleanup created file");
586    }
587
588    #[test]
589    fn list_invalid_test() {
590        let response = get_invalid_resource().list("invalid-id", "invalid-id")
591            .execute();
592
593        assert!( response.is_err() );
594        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
595    }
596
597    #[test]
598    fn update_test() {
599        let test_drive_file = get_test_drive_file().unwrap();
600        let test_drive_comment = get_test_drive_comment(&test_drive_file).unwrap();
601        let test_drive_reply = get_test_drive_reply(&test_drive_file, &test_drive_comment).unwrap();
602
603        let updated_reply = objects::Reply {
604            content: Some( "updated reply".to_string() ),
605            ..Default::default()
606        };
607
608        let response = get_resource().update(
609                &test_drive_file.clone().id.unwrap(),
610                &test_drive_comment.clone().id.unwrap(),
611                &test_drive_reply.clone().id.unwrap(),
612            )
613            .fields("*")
614            .reply(&updated_reply)
615            .execute();
616
617        assert!( response.is_ok() );
618
619        let reply = response.unwrap();
620        assert_eq!(reply.content, updated_reply.content);
621
622        delete_file(&test_drive_file).expect("Failed to cleanup created file");
623    }
624
625    #[test]
626    fn update_invalid_test() {
627        let response = get_invalid_resource()
628            .update("invalid-id", "invalid-id", "invalid-id")
629            .execute();
630
631        assert!( response.is_err() );
632        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
633    }
634}