drive_v3/resources/
changes.rs

1use reqwest::Method;
2use std::collections::HashMap;
3use drive_v3_macros::{DriveRequestBuilder, request};
4
5use super::DriveRequestBuilder;
6use crate::{objects, Credentials, Error, ErrorKind};
7
8#[request(
9    method=Method::GET,
10    url="https://www.googleapis.com/drive/v3/changes/startPageToken",
11)]
12#[derive(DriveRequestBuilder)]
13/// A request builder to get the starting `pageToken` for listing future
14/// changes.
15pub struct GetStartPageTokenRequest {
16    /// The ID of the shared drive for which the starting pageToken for
17    /// listing future changes from that shared drive will be returned.
18    #[drive_v3(parameter)]
19    drive_id: Option<String>,
20
21    /// Whether the requesting application supports both My Drives and
22    /// shared drives.
23    #[drive_v3(parameter)]
24    supports_all_drives: Option<bool>,
25}
26
27impl GetStartPageTokenRequest {
28    /// Executes this request.
29    ///
30    /// # Errors:
31    ///
32    /// - a [`UrlParsing`](crate::ErrorKind::UrlParsing) error, if the creation
33    /// of the request's URL failed.
34    /// - a [`Request`](crate::ErrorKind::Request) error, if unable to send the
35    /// request or get a body from the response.
36    /// - a [`Response`](crate::ErrorKind::Response) error, if the request
37    /// returned an error response.
38    /// - a [`Json`](crate::ErrorKind::Json) error, if unable to parse the
39    /// response's body.
40    pub fn execute( &self ) -> crate::Result<String> {
41        let response = self.send()?;
42        let response_text = response.text()?;
43        let parsed_json = serde_json::from_str::<HashMap<&str, String>> (&response_text)?;
44
45        match parsed_json.get("startPageToken") {
46            Some(token) => Ok( token.clone() ),
47            #[cfg(not(tarpaulin_include))]
48            None => Err( Error::new(
49                ErrorKind::Json,
50                "the response did not contain the 'startPageToken' field",
51            ) )
52        }
53    }
54}
55
56#[request(
57    method=Method::GET,
58    url="https://www.googleapis.com/drive/v3/changes",
59    returns=objects::ChangeList,
60)]
61#[derive(DriveRequestBuilder)]
62/// A request builder to list the changes for a user or drive.
63pub struct ListRequest {
64    /// The shared drive from which changes will be returned.
65    ///
66    /// If specified the change IDs will be reflective of the shared drive;
67    /// use the combined drive ID and change ID as an identifier.
68    #[drive_v3(parameter)]
69    drive_id: Option<String>,
70
71    /// Whether changes should include the file resource if the file is
72    /// still accessible by the user at the time of the request, even when
73    /// a file was removed from the list of changes and there will be no
74    /// further change entries for this file.
75    #[drive_v3(parameter)]
76    include_corpus_removals: Option<bool>,
77
78    /// Whether both My Drive and shared drive items should be included in
79    /// results.
80    #[drive_v3(parameter)]
81    include_items_from_all_drives: Option<bool>,
82
83    /// Whether to include changes indicating that items have been removed
84    /// from the list of changes, for example by deletion or loss of access.
85    #[drive_v3(parameter)]
86    include_removed: Option<bool>,
87
88    /// The maximum number of changes to return per page.
89    #[drive_v3(parameter)]
90    page_size: Option<i64>,
91
92    /// The token for continuing a previous list request on the next page.
93    ///
94    /// This should be set to the value of
95    /// [`next_page_token`](objects::ChangeList::next_page_token) from the
96    /// previous response or to the response from the
97    /// [`get_start_page_token`](Changes::get_start_page_token) method.
98    #[drive_v3(parameter)]
99    page_token: Option<String>,
100
101    /// Whether to restrict the results to changes inside the My Drive
102    /// hierarchy.
103    ///
104    /// This omits changes to files such as those in the Application Data
105    /// folder or shared files which have not been added to My Drive.
106    #[drive_v3(parameter)]
107    restrict_to_my_drive: Option<bool>,
108
109    /// A comma-separated list of spaces to query within the corpora.
110    ///
111    /// Supported values are `drive` and `appDataFolder`.
112    #[drive_v3(parameter)]
113    spaces: Option<String>,
114
115    /// Whether the requesting application supports both My Drives and
116    /// shared drives.
117    #[drive_v3(parameter)]
118    supports_all_drives: Option<bool>,
119
120    /// Specifies which additional view's permissions to include in the
121    /// response.
122    ///
123    /// Only `published` is supported.
124    #[drive_v3(parameter)]
125    include_permissions_for_view: Option<String>,
126
127    /// A comma-separated list of IDs of labels to include in the
128    /// [`label_info`](objects::File::label_info) part of the response.
129    #[drive_v3(parameter)]
130    include_labels: Option<String>,
131}
132
133#[request(
134    method=Method::POST,
135    url="https://www.googleapis.com/drive/v3/changes/watch",
136    returns=objects::Channel,
137)]
138#[derive(DriveRequestBuilder)]
139/// A request builder for subscribing to changes for a user.
140pub struct WatchRequest {
141    /// The shared drive from which changes will be returned.
142    ///
143    /// If specified the change IDs will be reflective of the shared drive;
144    /// use the combined drive ID and change ID as an identifier.
145    #[drive_v3(parameter)]
146    drive_id: Option<String>,
147
148    /// Whether changes should include the file resource if the file is
149    /// still accessible by the user at the time of the request, even when
150    /// a file was removed from the list of changes and there will be no
151    /// further change entries for this file.
152    #[drive_v3(parameter)]
153    include_corpus_removals: Option<bool>,
154
155    /// Whether both My Drive and shared drive items should be included in
156    /// results.
157    #[drive_v3(parameter)]
158    include_items_from_all_drives: Option<bool>,
159
160    /// Whether to include changes indicating that items have been removed
161    /// from the list of changes, for example by deletion or loss of access.
162    #[drive_v3(parameter)]
163    include_removed: Option<bool>,
164
165    /// The maximum number of changes to return per page.
166    #[drive_v3(parameter)]
167    page_size: Option<i64>,
168
169    /// The token for continuing a previous list request on the next page.
170    ///
171    /// This should be set to the value of
172    /// [`next_page_token`](objects::ChangeList::next_page_token) from the
173    /// previous response or to the response from the
174    /// [`get_start_page_token`](Changes::get_start_page_token) method.
175    #[drive_v3(parameter)]
176    page_token: Option<String>,
177
178    /// Whether to restrict the results to changes inside the My Drive
179    /// hierarchy.
180    ///
181    /// This omits changes to files such as those in the Application Data
182    /// folder or shared files which have not been added to My Drive.
183    #[drive_v3(parameter)]
184    restrict_to_my_drive: Option<bool>,
185
186    /// A comma-separated list of spaces to query within the corpora.
187    ///
188    /// Supported values are `drive` and `appDataFolder`.
189    #[drive_v3(parameter)]
190    spaces: Option<String>,
191
192    /// Whether the requesting application supports both My Drives and
193    /// shared drives.
194    #[drive_v3(parameter)]
195    supports_all_drives: Option<bool>,
196
197    /// Specifies which additional view's permissions to include in the
198    /// response.
199    ///
200    /// Only `published` is supported.
201    #[drive_v3(parameter)]
202    include_permissions_for_view: Option<String>,
203
204    /// A comma-separated list of IDs of labels to include in the
205    /// [`label_info`](objects::File::label_info) part of the response.
206    #[drive_v3(parameter)]
207    include_labels: Option<String>,
208
209    /// Sets the metadata that the channel will have.
210    #[drive_v3(body)]
211    channel: Option<objects::Channel>
212}
213
214/// A change to a file or shared drive.
215///
216/// # Examples:
217///
218/// List changes in a drive
219///
220/// ```no_run
221/// # use drive_v3::{Error, Credentials, Drive};
222/// #
223/// # let drive = Drive::new( &Credentials::from_file(
224/// #     "../.secure-files/google_drive_credentials.json",
225/// #     &["https://www.googleapis.com/auth/drive.file"],
226/// # )? );
227/// #
228/// let drive_id = "some-drive-id";
229/// let page_token = "some-page-token";
230///
231/// let change_list = drive.changes.list()
232///     .page_size(10)
233///     .drive_id(drive_id)
234///     .page_token(page_token)
235///     .execute()?;
236///
237/// if let Some(changes) = change_list.changes {
238///     for change in changes {
239///         println!("{}", change);
240///     }
241/// }
242/// # Ok::<(), Error>(())
243/// ```
244#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct Changes {
246    /// Credentials used to authenticate a user's access to this resource.
247    credentials: Credentials,
248}
249
250impl Changes {
251    /// Creates a new [`Changes`] resource with the given [`Credentials`].
252    pub fn new( credentials: &Credentials ) -> Self {
253        Self { credentials: credentials.clone() }
254    }
255
256    /// Gets the starting `pageToken` for listing future changes.
257    ///
258    /// The starting page token is used for listing future changes. The page
259    /// token doesn't expire.
260    ///
261    /// See Google's
262    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/changes/getStartPageToken)
263    /// for more information.
264    ///
265    /// # Note:
266    ///
267    /// This request requires you to set the
268    /// [`drive_id`](GetStartPageTokenRequest::drive_id) and
269    /// [`supports_all_drives`](GetStartPageTokenRequest::supports_all_drives)
270    /// parameters.
271    ///
272    /// # Requires one of the following OAuth scopes:
273    ///
274    /// - `https://www.googleapis.com/auth/drive`
275    /// - `https://www.googleapis.com/auth/drive.appdata`
276    /// - `https://www.googleapis.com/auth/drive.file`
277    /// - `https://www.googleapis.com/auth/drive.metadata`
278    /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
279    /// - `https://www.googleapis.com/auth/drive.photos.readonly`
280    /// - `https://www.googleapis.com/auth/drive.readonly`
281    ///
282    /// # Examples:
283    ///
284    /// ```no_run
285    /// # use drive_v3::{Error, Credentials, Drive};
286    /// #
287    /// # let drive = Drive::new( &Credentials::from_file(
288    /// #     "../.secure-files/google_drive_credentials.json",
289    /// #     &["https://www.googleapis.com/auth/drive.file"],
290    /// # )? );
291    /// #
292    /// let drive_id = "some-drive-id";
293    ///
294    /// let start_page_token = drive.changes.get_start_page_token()
295    ///     .drive_id(drive_id)
296    ///     .supports_all_drives(true)
297    ///     .execute()?;
298    ///
299    /// println!("Your start page token is: {}", start_page_token);
300    /// # Ok::<(), Error>(())
301    /// ```
302    pub fn get_start_page_token( &self ) -> GetStartPageTokenRequest {
303        GetStartPageTokenRequest::new(&self.credentials)
304    }
305
306    /// Lists the changes for a user or shared drive.
307    ///
308    /// See Google's
309    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/changes/list)
310    /// for more information.
311    ///
312    /// # Note:
313    ///
314    /// This request requires you to set the
315    /// [`drive_id`](ListRequest::drive_id) and
316    /// [`page_token`](ListRequest::page_token)
317    /// parameters.
318    ///
319    /// # Requires one of the following OAuth scopes:
320    ///
321    /// - `https://www.googleapis.com/auth/drive`
322    /// - `https://www.googleapis.com/auth/drive.appdata`
323    /// - `https://www.googleapis.com/auth/drive.file`
324    /// - `https://www.googleapis.com/auth/drive.metadata`
325    /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
326    /// - `https://www.googleapis.com/auth/drive.photos.readonly`
327    /// - `https://www.googleapis.com/auth/drive.readonly`
328    ///
329    /// # Examples:
330    ///
331    /// ```no_run
332    /// # use drive_v3::{Error, Credentials, Drive};
333    /// #
334    /// # let drive = Drive::new( &Credentials::from_file(
335    /// #     "../.secure-files/google_drive_credentials.json",
336    /// #     &["https://www.googleapis.com/auth/drive.file"],
337    /// # )? );
338    /// #
339    /// let drive_id = "some-drive-id";
340    /// let page_token = "some-page-token";
341    ///
342    /// let change_list = drive.changes.list()
343    ///     .page_size(10)
344    ///     .drive_id(drive_id)
345    ///     .page_token(page_token)
346    ///     .execute()?;
347    ///
348    /// if let Some(changes) = change_list.changes {
349    ///     for change in changes {
350    ///         println!("{}", change);
351    ///     }
352    /// }
353    /// # Ok::<(), Error>(())
354    /// ```
355    pub fn list( &self ) -> ListRequest {
356        ListRequest::new(&self.credentials)
357    }
358
359    /// Subscribes to changes for a user.
360    ///
361    /// See Google's
362    /// [documentation](https://developers.google.com/drive/api/reference/rest/v3/changes/watch)
363    /// for more information.
364    ///
365    /// # Note:
366    ///
367    /// This request requires you to set the
368    /// [`drive_id`](ListRequest::drive_id) and
369    /// [`page_token`](ListRequest::page_token)
370    /// parameters.
371    ///
372    /// # Note
373    ///
374    /// In order to subscribe to changes, you must provide a
375    /// [`Channel`](objects::Channel) with an `id` and an `address` which is the
376    /// one that will receive the notifications. This can be done by creating a
377    /// channel using [`from`](objects::Channel::from).
378    ///
379    /// For more informChangesation on channels, see Google's
380    /// [documentation](https://developers.google.com/drive/api/guides/push).
381    ///
382    /// # Requires one of the following OAuth scopes:
383    ///
384    /// - `https://www.googleapis.com/auth/drive`
385    /// - `https://www.googleapis.com/auth/drive.appdata`
386    /// - `https://www.googleapis.com/auth/drive.file`
387    /// - `https://www.googleapis.com/auth/drive.metadata`
388    /// - `https://www.googleapis.com/auth/drive.metadata.readonly`
389    /// - `https://www.googleapis.com/auth/drive.photos.readonly`
390    /// - `https://www.googleapis.com/auth/drive.readonly`
391    ///
392    /// # Examples:
393    ///
394    /// ```no_run
395    /// use drive_v3::objects::Channel;
396    /// # use drive_v3::{Error, Credentials, Drive};
397    /// #
398    /// # let drive = Drive::new( &Credentials::from_file(
399    /// #     "../.secure-files/google_drive_credentials.json",
400    /// #     &["https://www.googleapis.com/auth/drive.file"],
401    /// # )? );
402    ///
403    /// let channel_id = "my-channel-id";
404    /// let channel_address = "https://mydomain.com/channel-notifications";
405    /// let channel = Channel::from(&channel_id, &channel_address);
406    ///
407    /// let drive_id = "some-drive-id";
408    /// let page_token = "some-page-token";
409    ///
410    /// let created_channel = drive.changes.watch()
411    ///     .drive_id(drive_id)
412    ///     .page_token(page_token)
413    ///     .channel(&channel)
414    ///     .execute()?;
415    ///
416    /// println!("this is the created channel:\n{}", created_channel);
417    /// # Ok::<(), Error>(())
418    /// ```
419    pub fn watch( &self ) -> WatchRequest {
420        WatchRequest::new(&self.credentials)
421    }
422}
423
424#[cfg(test)]
425mod tests {
426    use super::Changes;
427    use crate::{objects, ErrorKind};
428    use crate::utils::test::{INVALID_CREDENTIALS, VALID_CREDENTIALS};
429
430    fn get_resource() -> Changes {
431        Changes::new(&VALID_CREDENTIALS)
432    }
433
434    fn get_invalid_resource() -> Changes {
435        Changes::new(&INVALID_CREDENTIALS)
436    }
437
438    #[test]
439    fn new_test() {
440        let valid_resource = get_resource();
441        let invalid_resource = get_invalid_resource();
442
443        assert_eq!( valid_resource.credentials, VALID_CREDENTIALS.clone() );
444        assert_eq!( invalid_resource.credentials, INVALID_CREDENTIALS.clone() );
445    }
446
447    #[test]
448    fn get_start_page_token_invalid_response_test() {
449        let response = get_invalid_resource().get_start_page_token()
450            .drive_id("test-drive-id")
451            .supports_all_drives(true)
452            .execute();
453
454        assert!( response.is_err() );
455        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
456    }
457
458    #[test]
459    fn list_invalid_response_test() {
460        let response = get_invalid_resource().list()
461            .drive_id("test-drive-id")
462            .page_token("test-page-token")
463            .execute();
464
465        assert!( response.is_err() );
466        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
467    }
468
469    #[test]
470    fn watch_invalid_response_test() {
471        let channel = objects::Channel::from("test-channel-id", "test-address");
472
473        let response = get_invalid_resource().watch()
474            .drive_id("test-drive-id")
475            .page_token("test-page-token")
476            .channel(&channel)
477            .execute();
478
479        assert!( response.is_err() );
480        assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
481    }
482}