pub mod handler; use std::{str::FromStr, sync::Arc}; use futures_util::TryFutureExt; use giterated_models::{ error::OperationError, model::{instance::Instance, repository::Repository, user::User}, operation::{GiteratedObject, GiteratedOperation, Object, ObjectBackend, ObjectRequestError}, }; use std::fmt::Debug; use tokio::sync::Mutex; use crate::backend::{RepositoryBackend, UserBackend}; use self::handler::{ repository_get_setting, repository_get_value, repository_set_setting, user_get_setting, user_get_value, user_set_setting, OperationHandlers, }; #[derive(Clone, Debug)] pub struct Foobackend {} #[async_trait::async_trait] impl ObjectBackend for Foobackend { async fn object_operation + Debug>( &self, _object: O, _operation: D, ) -> Result> { // We don't handle operations with this backend Err(OperationError::Unhandled) } async fn get_object( &self, _object_str: &str, ) -> Result, OperationError> { Err(OperationError::Unhandled) } } /// A backend implementation which attempts to resolve data from the instance's database. #[derive(Clone)] pub struct DatabaseBackend { pub(self) our_instance: Instance, pub(self) user_backend: Arc>, pub(self) repository_backend: Arc>, } impl Debug for DatabaseBackend { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DatabaseBackend").finish() } } #[async_trait::async_trait] impl ObjectBackend for DatabaseBackend { async fn object_operation + Debug>( &self, object: O, operation: D, ) -> Result> { let serialized = serde_json::to_value(operation).map_err(|e| OperationError::Internal(e.to_string()))?; let object_name = object.to_string(); if let Ok(user) = User::from_str(&object_name) { let mut handler = OperationHandlers::default(); handler .insert(user_get_value) .insert(user_get_setting) .insert(user_set_setting); match handler .handle( &user, D::operation_name(), serde_json::from_value(serialized.clone()).unwrap(), self.clone(), ) .await { Ok(result) => Ok(serde_json::from_slice(&result) .map_err(|e| OperationError::Internal(e.to_string()))?), Err(err) => match err { OperationError::Internal(internal) => Err(OperationError::Internal(internal)), OperationError::Unhandled => Err(OperationError::Unhandled), OperationError::Operation(err) => Err(OperationError::Operation( serde_json::from_slice(&err) .map_err(|e| OperationError::Internal(e.to_string()))?, )), }, } } else if let Ok(_repository) = Repository::from_str(&object_name) { let mut handler = OperationHandlers::default(); handler .insert(repository_get_value) .insert(repository_get_setting) .insert(repository_set_setting); // handler.handle(&repository, D::operation_name(), bincode::deserialize(&serialized).unwrap(), DatabaseBackendState).await; todo!() } else if Instance::from_str(&object_name).is_ok() { Err(OperationError::Unhandled) } else { Err(OperationError::Unhandled) } } async fn get_object( &self, object_str: &str, ) -> Result, OperationError> { if let Ok(user) = User::from_str(object_str) { let mut user_backend = self.user_backend.lock().await; if user_backend .exists(&user) .await .map_err(|e| OperationError::Internal(e.to_string()))? { Ok(unsafe { Object::new_unchecked( O::from_object_str(object_str) .map_err(|e| ObjectRequestError::Deserialization(e.to_string()))?, self.clone(), ) }) } else { return Err(OperationError::Unhandled); } } else if let Ok(repository) = Repository::from_str(object_str) { let mut repository_backend = self.repository_backend.lock().await; if repository_backend .exists(&repository) .await .map_err(|e| OperationError::Internal(e.to_string()))? { Ok(unsafe { Object::new_unchecked( O::from_object_str(object_str) .map_err(|e| ObjectRequestError::Deserialization(e.to_string()))?, self.clone(), ) }) } else { return Err(OperationError::Unhandled); } } else if Instance::from_str(object_str).is_ok() { return Err(OperationError::Unhandled); } else { // Invalid object type return Err(OperationError::Unhandled); } } } mod test { use std::{str::FromStr, sync::Arc}; use anyhow::Error; use giterated_models::{ model::{ authenticated::UserAuthenticationToken, instance::Instance, repository::{Repository, RepositorySummary, RepositoryTreeEntry}, settings::AnySetting, user::User, }, operation::{ instance::{ AuthenticationTokenRequest, RegisterAccountRequest, RepositoryCreateRequest, }, repository::RepositoryFileInspectRequest, GiteratedObjectValue, ObjectBackend, }, values::{user::DisplayName, AnyValue}, }; use serde_json::Value; use tokio::sync::Mutex; use crate::backend::{git::GitBackendError, AuthBackend, RepositoryBackend, UserBackend}; use super::DatabaseBackend; pub struct TestUserDatabaseBackend; #[async_trait::async_trait] impl UserBackend for TestUserDatabaseBackend { async fn get_value(&mut self, _user: &User, name: &str) -> Result, Error> { assert_eq!(name, DisplayName::value_name()); Ok(serde_json::from_slice( &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(), ) .unwrap()) } async fn get_setting(&mut self, _user: &User, _name: &str) -> Result { todo!() } async fn write_setting( &mut self, _user: &User, _name: &str, _setting: &Value, ) -> Result<(), Error> { todo!() } async fn exists(&mut self, user: &User) -> Result { Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap()) } } #[async_trait::async_trait] impl AuthBackend for TestUserDatabaseBackend { async fn register( &mut self, _request: RegisterAccountRequest, ) -> Result { todo!() } async fn login( &mut self, _source: &Instance, _request: AuthenticationTokenRequest, ) -> Result { todo!() } } pub struct TestUserRepositoryBackend; #[async_trait::async_trait] impl RepositoryBackend for TestUserRepositoryBackend { async fn create_repository( &mut self, _user: &User, _request: &RepositoryCreateRequest, ) -> Result { todo!() } async fn repository_file_inspect( &mut self, _requester: Option<&User>, _request: &RepositoryFileInspectRequest, ) -> Result, Error> { todo!() } async fn repositories_for_user( &mut self, _requester: Option<&User>, _user: &User, ) -> Result, Error> { todo!() } async fn exists(&mut self, _repository: &Repository) -> Result { todo!() } } fn test_backend() -> DatabaseBackend { DatabaseBackend { our_instance: Instance::from_str("testing.giterated.dev").unwrap(), user_backend: Arc::new(Mutex::new(TestUserDatabaseBackend)) as _, repository_backend: Arc::new(Mutex::new(TestUserRepositoryBackend)) as _, } } #[tokio::test] async fn test_user_get() { let backend = test_backend(); let mut user = backend .get_object::("test_user:test.giterated.dev") .await .expect("object should have been returned"); user.get::() .await .expect("object value should have been returned"); } }