mirror of
https://github.com/OMGeeky/gdriver2.git
synced 2026-02-23 15:38:32 +01:00
first basic implementation of functionality
(mounting, & getting changes)
This commit is contained in:
@@ -1,18 +1,26 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::drive::google_drive::GoogleDrive;
|
||||
use crate::path_resolver::PathResolver;
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
mod google_drive;
|
||||
pub struct Drive {
|
||||
tracked_files: HashMap<DriveId, DateTime<Utc>>,
|
||||
pub path_resolver: PathResolver,
|
||||
google_drive: GoogleDrive,
|
||||
}
|
||||
impl Drive {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
#[instrument()]
|
||||
pub async fn new() -> Result<Self> {
|
||||
Ok(Self {
|
||||
tracked_files: HashMap::new(),
|
||||
}
|
||||
path_resolver: PathResolver::new(),
|
||||
google_drive: GoogleDrive::new().await?,
|
||||
})
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
pub fn get_file_tracking_state(&self, id: &DriveId) -> TrackingState {
|
||||
let file = self.tracked_files.get(id);
|
||||
match file {
|
||||
@@ -20,6 +28,24 @@ impl Drive {
|
||||
None => TrackingState::Untracked,
|
||||
}
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
pub async fn update(&mut self) -> Result<()> {
|
||||
let changes = self.google_drive.get_changes().await?;
|
||||
if changes.is_empty() {
|
||||
info!("No changes");
|
||||
return Ok(());
|
||||
}
|
||||
for change in changes {
|
||||
dbg!(change);
|
||||
}
|
||||
Err("Not implemented".into())
|
||||
|
||||
// Ok(()) //TODO: implement this
|
||||
}
|
||||
#[instrument(skip(self))]
|
||||
pub async fn ping(&self) -> Result<()> {
|
||||
self.google_drive.ping().await
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TrackingState {
|
||||
|
||||
164
gdriver-backend/src/drive/google_drive.rs
Normal file
164
gdriver-backend/src/drive/google_drive.rs
Normal file
@@ -0,0 +1,164 @@
|
||||
use crate::prelude::*;
|
||||
use const_format::formatcp;
|
||||
use gdriver_common::ipc::gdriver_service::SETTINGS;
|
||||
use gdriver_common::prelude::*;
|
||||
use google_drive3::api::{Change, File, Scope, StartPageToken};
|
||||
use google_drive3::hyper::client::HttpConnector;
|
||||
use google_drive3::hyper::{Body, Client, Response};
|
||||
use google_drive3::hyper_rustls::HttpsConnector;
|
||||
use google_drive3::DriveHub;
|
||||
use google_drive3::{hyper_rustls, oauth2};
|
||||
use std::any::type_name;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use tokio::fs;
|
||||
|
||||
const FIELDS_FILE: &'static str = "id, name, size, mimeType, kind, md5Checksum, parents, trashed, createdTime, modifiedTime, viewedByMeTime";
|
||||
const FIELDS_CHANGE: &str = formatcp!("changes(removed, fileId, changeType, file({FIELDS_FILE}))");
|
||||
#[derive(Clone)]
|
||||
pub struct GoogleDrive {
|
||||
hub: DriveHub<HttpsConnector<HttpConnector>>,
|
||||
changes_start_page_token: Option<String>,
|
||||
}
|
||||
|
||||
impl GoogleDrive {
|
||||
#[instrument]
|
||||
pub(crate) async fn new() -> Result<Self> {
|
||||
trace!("Initializing GoogleDrive client.");
|
||||
let auth = oauth2::read_application_secret("auth/client_secret.json").await?;
|
||||
|
||||
let auth = oauth2::InstalledFlowAuthenticator::builder(
|
||||
auth,
|
||||
oauth2::InstalledFlowReturnMethod::HTTPRedirect,
|
||||
)
|
||||
.persist_tokens_to_disk("auth/tokens.json")
|
||||
.build()
|
||||
.await?;
|
||||
let http_client = Client::builder().build(
|
||||
hyper_rustls::HttpsConnectorBuilder::new()
|
||||
.with_native_roots()
|
||||
.https_or_http()
|
||||
.enable_http1()
|
||||
// .enable_http2()
|
||||
.build(),
|
||||
);
|
||||
let hub = DriveHub::new(http_client, auth);
|
||||
|
||||
let drive = GoogleDrive {
|
||||
hub,
|
||||
changes_start_page_token: None,
|
||||
};
|
||||
trace!("Successfully initialized {}", drive);
|
||||
Ok(drive)
|
||||
}
|
||||
#[instrument]
|
||||
pub(crate) async fn ping(&self) -> Result<()> {
|
||||
let (response, body) = self
|
||||
.hub
|
||||
.about()
|
||||
.get()
|
||||
.param("fields", "user(emailAddress)")
|
||||
.add_scope(Scope::Readonly)
|
||||
.doit()
|
||||
.await?;
|
||||
let status_code = response.status();
|
||||
let email = body
|
||||
.user
|
||||
.unwrap_or_default()
|
||||
.email_address
|
||||
.unwrap_or_default();
|
||||
trace!("response status: {}, email: '{}'", status_code, email);
|
||||
if status_code.is_success() {
|
||||
return Ok(());
|
||||
}
|
||||
error!(
|
||||
"Did not get expected result on ping: {} {:?}",
|
||||
status_code, response
|
||||
);
|
||||
|
||||
Err("Did not get expected result on ping".into())
|
||||
}
|
||||
//region changes
|
||||
#[instrument]
|
||||
pub async fn get_changes(&mut self) -> Result<Vec<Change>> {
|
||||
let mut page_token = Some(self.change_start_token().await?);
|
||||
let mut changes = Vec::new();
|
||||
while let Some(current_page_token) = page_token {
|
||||
info!("Getting changes with page token: {}", current_page_token);
|
||||
let (response, body) = self
|
||||
.hub
|
||||
.changes()
|
||||
.list(current_page_token.as_str())
|
||||
.param("fields", FIELDS_CHANGE)
|
||||
.page_size(2) //TODO: Change this to a more reasonable value
|
||||
.include_corpus_removals(true) //TODO3: Check if this is useful
|
||||
.supports_all_drives(false)
|
||||
.restrict_to_my_drive(true)
|
||||
.include_removed(true)
|
||||
.include_items_from_all_drives(false)
|
||||
.doit()
|
||||
.await?;
|
||||
self.changes_start_page_token = body.new_start_page_token;
|
||||
if response.status().is_success() {
|
||||
changes.extend(body.changes.unwrap_or_default());
|
||||
page_token = body.next_page_token;
|
||||
} else {
|
||||
error!("Could not get changes: {:?}", response);
|
||||
return Err("Could not get changes".into());
|
||||
}
|
||||
}
|
||||
trace!("Got {} changes", changes.len());
|
||||
Ok(changes)
|
||||
}
|
||||
|
||||
async fn change_start_token(&mut self) -> Result<String> {
|
||||
Ok(match &self.changes_start_page_token {
|
||||
None => {
|
||||
//
|
||||
let token = fs::read_to_string(SETTINGS.get_changes_file_path())
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
self.changes_start_page_token = if !token.is_empty() {
|
||||
Some(token)
|
||||
} else {
|
||||
Some(self.get_changes_start_token_from_api().await?)
|
||||
};
|
||||
self.changes_start_page_token
|
||||
.clone()
|
||||
.expect("We just set it")
|
||||
}
|
||||
Some(start_token) => start_token.clone(),
|
||||
})
|
||||
}
|
||||
async fn get_changes_start_token_from_api(&self) -> Result<String> {
|
||||
let (response, body) = self
|
||||
.hub
|
||||
.changes()
|
||||
.get_start_page_token()
|
||||
.supports_all_drives(false)
|
||||
.doit()
|
||||
.await?;
|
||||
if response.status().is_success() {
|
||||
let start_page_token = body.start_page_token.unwrap_or_default();
|
||||
Ok(start_page_token)
|
||||
} else {
|
||||
Err("Could not get start page token".into())
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
|
||||
//region debug & display traits
|
||||
impl Debug for GoogleDrive {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct(type_name::<GoogleDrive>())
|
||||
.field("changes_start_page_token", &self.changes_start_page_token)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for GoogleDrive {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", type_name::<GoogleDrive>())
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
@@ -8,6 +8,7 @@ use tarpc::{
|
||||
};
|
||||
|
||||
mod drive;
|
||||
mod path_resolver;
|
||||
mod prelude;
|
||||
mod sample;
|
||||
mod service;
|
||||
|
||||
61
gdriver-backend/src/path_resolver.rs
Normal file
61
gdriver-backend/src/path_resolver.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use crate::drive::Drive;
|
||||
use crate::prelude::*;
|
||||
use gdriver_common::ipc::gdriver_service::ReadDirResult;
|
||||
use gdriver_common::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||
pub struct PathResolver {
|
||||
parents: HashMap<DriveId, Vec<DriveId>>,
|
||||
children: HashMap<DriveId, Vec<ReadDirResult>>,
|
||||
}
|
||||
|
||||
impl PathResolver {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
parents: HashMap::new(),
|
||||
children: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub async fn get_id_from_path(&mut self, path: &Path, drive: &Drive) -> Result<DriveId> {
|
||||
let segments: Vec<_> = path
|
||||
.to_str()
|
||||
.ok_or(PathResolveError::InvalidPath)?
|
||||
.split('/')
|
||||
.collect();
|
||||
let mut current = ROOT_ID.clone();
|
||||
self.update_from_drive(drive).await?;
|
||||
for segment in segments {
|
||||
current = self
|
||||
.get_id_from_parent_and_name(segment, ¤t)
|
||||
.ok_or("path-segment not found")?;
|
||||
}
|
||||
return Ok(current);
|
||||
}
|
||||
pub fn get_id_from_parent_and_name(&self, name: &str, parent: &DriveId) -> Option<DriveId> {
|
||||
if let Some(children) = self.children.get(parent) {
|
||||
if let Some(x) = children.into_iter().find(|x| x.name.eq(name)) {
|
||||
return Some(x.id.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
async fn update_from_drive(&mut self, drive: &Drive) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
pub(crate) fn add_relationship(&mut self, parent: DriveId, entry: ReadDirResult) {
|
||||
todo!()
|
||||
}
|
||||
pub(crate) fn remove_relationship(&mut self, parent: DriveId, entry: ReadDirResult) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
|
||||
pub enum PathResolveError {
|
||||
#[error("The path provided was invalid")]
|
||||
InvalidPath,
|
||||
}
|
||||
@@ -20,8 +20,21 @@ impl GDriverService for GdriverServer {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
async fn get_file_by_name(self, context: Context, name: OsString, parent: DriveId) -> StdResult<DriveId, GetFileByPathError> {
|
||||
todo!()
|
||||
async fn get_file_by_name(
|
||||
self,
|
||||
context: Context,
|
||||
name: OsString,
|
||||
parent: DriveId,
|
||||
) -> StdResult<DriveId, GetFileByPathError> {
|
||||
let mut drive_lock = self.drive.lock().await;
|
||||
let x = drive_lock.path_resolver.get_id_from_parent_and_name(
|
||||
name.to_str().ok_or(GetFileByPathError::InvalidName)?,
|
||||
&parent,
|
||||
);
|
||||
match x {
|
||||
None => Err(GetFileByPathError::NotFound),
|
||||
Some(id) => Ok(id),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_file_by_path(
|
||||
@@ -29,7 +42,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
path: PathBuf,
|
||||
) -> StdResult<DriveId, GetFileByPathError> {
|
||||
todo!()
|
||||
Err(GetFileByPathError::Other)
|
||||
}
|
||||
|
||||
async fn write_local_change(
|
||||
@@ -37,7 +50,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<(), WriteLocalChangeError> {
|
||||
todo!()
|
||||
Err(WriteLocalChangeError::Other)
|
||||
}
|
||||
|
||||
async fn get_metadata_for_file(
|
||||
@@ -45,7 +58,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<(), GetMetadataError> {
|
||||
todo!()
|
||||
Err(GetMetadataError::Other)
|
||||
}
|
||||
|
||||
async fn download_content_for_file(
|
||||
@@ -53,7 +66,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<(), GetContentError> {
|
||||
todo!()
|
||||
Err(GetContentError::Other)
|
||||
}
|
||||
|
||||
async fn list_files_in_directory(
|
||||
@@ -78,7 +91,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<(), MarkFileAsDeletedError> {
|
||||
todo!()
|
||||
Err(MarkFileAsDeletedError::Other)
|
||||
}
|
||||
|
||||
async fn mark_file_for_keeping_local(
|
||||
@@ -86,7 +99,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<(), MarkFileForKeepingLocalError> {
|
||||
todo!()
|
||||
Err(MarkFileForKeepingLocalError::Other)
|
||||
}
|
||||
|
||||
async fn unmark_file_for_keeping_local(
|
||||
@@ -94,7 +107,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<(), UnmarkFileForKeepingLocalError> {
|
||||
todo!()
|
||||
Err(UnmarkFileForKeepingLocalError::Other)
|
||||
}
|
||||
|
||||
#[doc = " Returns true if the file was had remote changes and was updadet"]
|
||||
@@ -103,7 +116,7 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<bool, UpdateChangesError> {
|
||||
todo!()
|
||||
Err(UpdateChangesError::Other)
|
||||
}
|
||||
|
||||
async fn update_changes(self, context: Context) -> StdResult<(), UpdateChangesError> {
|
||||
@@ -174,7 +187,16 @@ pub async fn start() -> Result<()> {
|
||||
let config = &CONFIGURATION;
|
||||
info!("Config: {:?}", **config);
|
||||
|
||||
let drive = Drive::new();
|
||||
let drive = Drive::new().await?;
|
||||
match drive.ping().await {
|
||||
Ok(_) => {
|
||||
info!("Can reach google drive api.");
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Cannot reach google drive api.");
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
let m = Arc::new(Mutex::new(drive));
|
||||
|
||||
let server_addr = (config.ip, config.port);
|
||||
|
||||
Reference in New Issue
Block a user