# drive-v3 ![pipeline](https://gitlab.com/mderr/drive-v3/badges/main/pipeline.svg) ![coverage](https://gitlab.com/mderr/drive-v3/badges/main/coverage.svg) A Rust library to send request to the Google Drive API v3. This library includes functions to call all Google Drive API v3 present in Google's [documentation](https://developers.google.com/drive/api/reference/rest/v3) as of the date of the latest release. ## Usage ### Setup Before using Google Drive's API you need to enable it on a Google Cloud project. This is free and you can follow these [steps](https://developers.google.com/drive/api/quickstart/python#set_up_your_environment) to enable the API. After you complete the [Authorize credentials for a desktop application](https://developers.google.com/drive/api/quickstart/python#authorize_credentials_for_a_desktop_application) section, you can us the saved `client_secrets.json` file to authorize this library. ### Getting credentials Before you start making calls to Google's API, you first need to get some valid `Credentials`. For this you will need your `client_secrets.json` file and the scopes that you plan on using. For help on selecting scopes you can read Google's [choose scopes](https://developers.google.com/workspace/guides/configure-oauth-consent#choose-scopes) guide. With these elements you can call `Credentials::from_client_secrets_file` to prompt you to authorize your app via the browser: ```rust,ignore use drive_v3::Credentials; // This is the file downloaded in the Setup section let client_secrets_path = "my_client_secrets.json"; // The OAuth scopes you need let scopes: [&'static str; 2] = [ "https://www.googleapis.com/auth/drive.metadata.readonly", "https://www.googleapis.com/auth/drive.file", ]; let credentials = Credentials::from_client_secrets_file(&client_secrets_path, &scopes)?; ``` Great! now you have valid credentials to make requests to Google's API, however these credentials will expire after some time, at which point you will have to request them again. To avoid having to authorize using the browser every time they expire, you should save them to your system: ```rust,ignore credentials.store("very/secure/path/to/my/credentials.json")?; ``` Now you don't need to worry about requesting new credentials every time, instead you can simply check if they have expired and refresh them if necessary: ```rust,ignore use drive_v3::Credentials; // Path where the credentials were stored let credentials_storage_path = "very/secure/path/to/my/credentials.json"; // The OAuth scopes you need let scopes: [&'static str; 2] = [ "https://www.googleapis.com/auth/drive.metadata.readonly", "https://www.googleapis.com/auth/drive.file", ]; let mut stored_credentials = Credentials::from_file(&credentials_storage_path, &scopes)?; // Refresh the credentials if they have expired if !stored_credentials.are_valid() { stored_credentials.refresh()?; // Save them so we don't have to refresh them every time stored_credentials.store(&credentials_storage_path)?; } ``` ### Making requests Now that we have valid `Credentials`, we can start making requests. For this we need to create a `Drive`. A `Drive` is the representation of the API itself, with it we can make request to any endpoint in Google's API. To create it we simply need to pass it the `Credentials`: ```rust,ignore use drive_v3::Drive; let drive = Drive::new(&credentials); ``` And then make the requests you want, for example list the files in a Drive: ```rust,ignore let file_list = drive.files.list() .fields("files(name, id, mimeType)") // Set what fields will be returned .q("name = 'file_im_looking_for' and not trashed") // search for specific files .execute()?; if let Some(files) = file_list.files { for file in &files { println!("{}", file); } } ``` As you can see the `files.list` function uses the [builder](https://rust-unofficial.github.io/patterns/patterns/creational/builder.html) pattern, this allows you to specify the query parameters that you need in your request. Thanks to this pattern you can search the documentation of a specific resource in Google's [reference](https://developers.google.com/drive/api/reference/rest/v3) and use any of its query parameters which will have corresponding functions with the same name (the only difference being that the functions will use `snake_case` instead of `camelCase`). ### Request responses As seen in the previous section to get a response from one of the `Drive`'s resources you have to call `.execute()` this will return a `Result` which will either be an `Error` or the response from Google's API. The type of the response will depend on the request, it could return nothing, an object like a `File` or a `Comment` or a vector of bytes. You can check the return types in each request's documentation. ### Objects Some request will either return an `object` or need one for the request, an `object` is a representation of the types in Google's API. All `objects` in `drive-v3` have the same structure, a struct with fields that are all an `Option`. The reason for this is that not all of the fields of an `object` will be populated by a request or needed to make one. For example the `File` object has 60 different fields, it would be impractical to have to specify all fields when you want to create a new file. So instead you can just populate the fields that you actually want to set and leave the rest as `None`. This is easy since all objects implement the `default` trait: ```rust,ignore use drive_v3::objects::File; let file = File { name: Some( "my-file.txt".to_string() ), mime_type: Some( "text/plain".to_string() ), description: Some( "this is a text file".to_string() ) ..Default::default() }; ``` And when it comes to responses, all requests have the function `fields()` which you can set to return the exact fields you need, improving performance in your method call. Check Google's [documentation](https://developers.google.com/drive/api/guides/fields-parameter) for more info on the fields parameter. ### More examples Upload a new file to a Drive: ```rust,ignore use drive_v3::objects::{File, UploadType}; // Set what information the uploaded file wil have let metadata = File { name: Some( "my-new-file.txt".to_string() ), mime_type: Some( "text/plain".to_string() ), ..Default::default() }; // You can set a callback that will be called when a resumable upload progresses fn progress_callback( total_bytes: usize, uploaded_bytes: usize ) { println!("Uploaded {} bytes, out of a total of {}.", uploaded_bytes, total_bytes); } let my_new_file = drive.files.create() .upload_type(UploadType::Resumable) .callback(progress_callback) .metadata(&metadata) .content_source("path/to/file.txt") .execute()?; assert_eq!(my_new_file.name, metadata.name); assert_eq!(my_new_file.mime_type, metadata.mime_type); ``` Download a file from Drive: ```rust,ignore let file_id = "some-file-id"; let file_bytes = drive.files.get_media(&file_id) // .save_to("my_downloaded_file.txt") // Save the contents to a path .execute()?; let content = String::from_utf8_lossy(&file_bytes); println!("file content: {}", content); ``` Get information about a Drive: ```rust,ignore let information = drive.about.get() .fields("storageQuota, canCreateDrives, appInstalled") .execute()?; println!("look at all this information:\n{}", information); ``` ## Contributing Pull requests are welcome. For major changes, please open an issue first. ## License MIT