use std::{fs, path::PathBuf};
use reqwest::{blocking::Client, Method, Url};
use crate::{objects, request_builder, Credentials, Error};
request_builder!(
pub DeleteRequest {},
Method::DELETE,
("https://www.googleapis.com/drive/v3/files/{file_id}/revisions/{revision_id}", file_id, revision_id),
);
impl DeleteRequest {
pub fn execute( &self ) -> Result<(), Error> {
self.send()?;
Ok(())
}
}
request_builder!(
pub GetRequest {},
Method::GET,
("https://www.googleapis.com/drive/v3/files/{file_id}/revisions/{revision_id}", file_id, revision_id),
);
impl GetRequest {
pub fn execute( &self ) -> Result<objects::Revision, Error> {
let response = self.send()?;
Ok( serde_json::from_str( &response.text()? )? )
}
}
request_builder!(
pub GetMediaRequest {
acknowledge_abuse: Option<bool>
},
Method::GET,
("https://www.googleapis.com/drive/v3/files/{file_id}/revisions/{revision_id}", file_id, revision_id),
save_to: Option<PathBuf>
);
impl GetMediaRequest {
pub fn execute( &self ) -> Result<Vec<u8>, Error> {
let mut parameters = self.get_parameters();
parameters.push(( "alt".into(), objects::Alt::Media.to_string() ));
let url = Url::parse_with_params(&self.url, parameters)?;
let request = Client::new()
.request( self.method.clone(), url )
.bearer_auth( self.credentials.get_access_token() );
let response = request.send()?;
if !response.status().is_success() {
return Err( response.into() );
}
let file_bytes: Vec<u8> = response.bytes()?.into();
if let Some(path) = &self.save_to {
use std::io::Write;
let mut file = fs::File::create(path)?;
file.write_all(&file_bytes)?;
}
Ok(file_bytes)
}
}
request_builder!(
pub ListRequest {
page_size: Option<i64>,
page_token: Option<String>,
},
Method::GET,
("https://www.googleapis.com/drive/v3/files/{file_id}/revisions", file_id),
);
impl ListRequest {
pub fn execute( &self ) -> Result<objects::RevisionList, Error> {
let response = self.send()?;
Ok( serde_json::from_str( &response.text()? )? )
}
}
request_builder!(
pub UpdateRequest {},
Method::PATCH,
("https://www.googleapis.com/drive/v3/files/{file_id}/revisions/{revision_id}", file_id, revision_id),
revision: Option<objects::Revision>
);
impl UpdateRequest {
pub fn execute( &self ) -> Result<objects::Revision, Error> {
let revision = self.revision.clone().unwrap_or_default();
let revision_string = serde_json::to_string(&revision)?;
let content_length = revision_string.as_bytes().len();
let request = self.build()?
.header( "Content-Length", content_length.to_string() )
.body(revision_string);
let response = request.send()?;
if !response.status().is_success() {
return Err( response.into() )
}
Ok( serde_json::from_str( &response.text()? )? )
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Revisions {
credentials: Credentials,
}
impl Revisions {
pub fn new( credentials: &Credentials ) -> Self {
Self { credentials: credentials.clone() }
}
pub fn delete<T, U> ( &self, file_id: T, revision_id: U ) -> DeleteRequest
where
T: AsRef<str>,
U: AsRef<str>,
{
DeleteRequest::new(&self.credentials, file_id, revision_id)
}
pub fn get<T, U> ( &self, file_id: T, revision_id: U ) -> GetRequest
where
T: AsRef<str>,
U: AsRef<str>,
{
GetRequest::new(&self.credentials, file_id, revision_id)
}
pub fn get_media<T, U> ( &self, file_id: T, revision_id: U ) -> GetMediaRequest
where
T: AsRef<str>,
U: AsRef<str>,
{
GetMediaRequest::new(&self.credentials, file_id, revision_id)
}
pub fn list<T: AsRef<str>> ( &self, file_id: T ) -> ListRequest {
ListRequest::new(&self.credentials, file_id)
}
pub fn update<T, U> ( &self, file_id: T, revision_id: U ) -> UpdateRequest
where
T: AsRef<str>,
U: AsRef<str>,
{
UpdateRequest::new(&self.credentials, file_id, revision_id)
}
}
#[cfg(test)]
mod tests {
use std::fs;
use super::Revisions;
use std::path::PathBuf;
use crate::{Error, ErrorKind, objects, resources};
use crate::utils::test::{INVALID_CREDENTIALS, LOCAL_STORAGE_IN_USE, VALID_CREDENTIALS};
fn get_resource() -> Revisions {
Revisions::new(&VALID_CREDENTIALS)
}
fn get_invalid_resource() -> Revisions {
Revisions::new(&INVALID_CREDENTIALS)
}
fn get_files_resource() -> resources::Files {
resources::Files::new(&VALID_CREDENTIALS)
}
fn delete_file( file: &objects::File ) -> Result<(), Error> {
get_files_resource().delete( file.clone().id.unwrap() ).execute()
}
fn get_test_file_metadata() -> objects::File {
objects::File {
name: Some( "test.txt".to_string() ),
description: Some( "a test file".to_string() ),
mime_type: Some( "text/plain".to_string() ),
..Default::default()
}
}
fn get_test_drive_file() -> Result<objects::File, Error> {
let metadata = get_test_file_metadata();
let file = get_files_resource().create()
.upload_type(objects::UploadType::Multipart)
.metadata(&metadata)
.content_string("content")
.execute()?;
get_files_resource().update( &file.clone().id.unwrap() )
.upload_type(objects::UploadType::Multipart)
.metadata(&metadata)
.content_string("content")
.execute()
}
fn get_oldest_file_revision( file: &objects::File ) -> Result<objects::Revision, Error> {
let revision_list = get_resource().list( &file.clone().id.unwrap() )
.fields("*")
.execute()?;
Ok( revision_list.revisions.unwrap().last().unwrap().clone() )
}
#[test]
fn new_test() {
let valid_resource = get_resource();
let invalid_resource = get_invalid_resource();
assert_eq!( valid_resource.credentials, VALID_CREDENTIALS.clone() );
assert_eq!( invalid_resource.credentials, INVALID_CREDENTIALS.clone() );
}
#[test]
fn delete_test() {
let test_drive_file = get_test_drive_file().unwrap();
let oldest_file_revision = get_oldest_file_revision(&test_drive_file).unwrap();
let response = get_resource().delete(
&test_drive_file.clone().id.unwrap(),
&oldest_file_revision.clone().id.unwrap(),
)
.execute();
assert!( response.is_ok() );
delete_file(&test_drive_file).expect("Failed to cleanup created file");
}
#[test]
fn delete_invalid_test() {
let response = get_invalid_resource().delete("invalid-id", "invalid-id")
.execute();
assert!( response.is_err() );
assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
}
#[test]
fn get_test() {
let test_drive_file = get_test_drive_file().unwrap();
let oldest_file_revision = get_oldest_file_revision(&test_drive_file).unwrap();
let response = get_resource().get(
&test_drive_file.clone().id.unwrap(),
&oldest_file_revision.clone().id.unwrap(),
)
.fields("*")
.execute();
assert!( response.is_ok() );
let revision = response.unwrap();
assert_eq!(revision, oldest_file_revision);
delete_file(&test_drive_file).expect("Failed to cleanup created file");
}
#[test]
fn get_invalid_test() {
let response = get_invalid_resource().get("invalid-id", "invalid-id")
.execute();
assert!( response.is_err() );
assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
}
#[test]
fn get_media_test() {
let _unused = LOCAL_STORAGE_IN_USE.lock().unwrap();
let test_drive_file = get_test_drive_file().unwrap();
let oldest_file_revision = get_oldest_file_revision(&test_drive_file).unwrap();
let save_path = PathBuf::from("saved.txt");
let response = get_resource().get_media(
&test_drive_file.clone().id.unwrap(),
&oldest_file_revision.clone().id.unwrap(),
)
.save_to(&save_path)
.execute();
assert!( response.is_ok() );
let content = String::from_utf8( response.unwrap() ).unwrap();
let saved_content = fs::read_to_string(&save_path).unwrap();
assert_eq!(&content, "content");
assert_eq!(&saved_content, "content");
delete_file(&test_drive_file).expect("Failed to cleanup a created file");
fs::remove_file(&save_path).expect("Failed to cleanup a created file");
}
#[test]
fn get_media_invalid_test() {
let response = get_invalid_resource().get_media("invalid-id", "invalid-id")
.execute();
assert!( response.is_err() );
assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
}
#[test]
fn list_test() {
let test_drive_file = get_test_drive_file().unwrap();
let response = get_resource().list( &test_drive_file.clone().id.unwrap() )
.execute();
assert!( response.is_ok() );
delete_file(&test_drive_file).expect("Failed to cleanup created file");
}
#[test]
fn list_invalid_test() {
let response = get_invalid_resource().list("invalid-id")
.execute();
assert!( response.is_err() );
assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
}
#[test]
fn update_test() {
let test_drive_file = get_test_drive_file().unwrap();
let oldest_file_revision = get_oldest_file_revision(&test_drive_file).unwrap();
let updated_revision = objects::Revision {
published: Some(false),
..Default::default()
};
let response = get_resource().update(
&test_drive_file.clone().id.unwrap(),
&oldest_file_revision.clone().id.unwrap(),
)
.fields("*")
.revision(&updated_revision)
.execute();
assert!( response.is_ok() );
let revision = response.unwrap();
assert_eq!(revision.published, updated_revision.published);
delete_file(&test_drive_file).expect("Failed to cleanup created file");
}
#[test]
fn update_invalid_test() {
let response = get_invalid_resource().update("invalid-id", "invalid-id")
.execute();
assert!( response.is_err() );
assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
}
}