pub mod handler; use std::{str::FromStr, sync::Arc}; use giterated_models::error::OperationError; use giterated_models::instance::Instance; use giterated_models::object::{ AnyObject, GiteratedObject, Object, ObjectRequest, ObjectRequestError, }; use giterated_models::object_backend::ObjectBackend; use giterated_models::operation::GiteratedOperation; use giterated_models::repository::Repository; use giterated_models::user::User; use std::fmt::Debug; use tokio::sync::Mutex; use crate::backend::{RepositoryBackend, UserBackend}; use self::handler::{ repository_commit_before, repository_diff, repository_file_from_id, repository_get_setting, repository_get_value, repository_info, repository_set_setting, user_get_repositories, 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: &str, _payload: 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 DatabaseBackend { pub fn new( instance: Instance, user_backend: Arc>, repository_backend: Arc>, ) -> Self { Self { our_instance: instance, user_backend, repository_backend, } } } 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: &str, payload: D, ) -> Result> { let serialized = serde_json::to_value(payload).map_err(|e| OperationError::Internal(e.to_string()))?; let object = object.to_string(); if let Ok(user) = User::from_str(&object) { let mut handler = OperationHandlers::default(); handler .insert(user_get_repositories) .insert(user_get_value) .insert(user_get_setting) .insert(user_set_setting); match handler .handle( &user, operation, 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) => { warn!( "Internal Error: {:?}", OperationError::<()>::Internal(internal.clone()) ); 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) { let mut handler = OperationHandlers::default(); handler .insert(repository_info) .insert(repository_file_from_id) .insert(repository_diff) .insert(repository_commit_before) .insert(repository_get_value) .insert(repository_get_setting) .insert(repository_set_setting); match handler .handle( &repository, operation, 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) => { warn!( "Internal Error: {:?}", OperationError::<()>::Internal(internal.clone()) ); 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(instance) = Instance::from_str(&object) { if instance != self.our_instance { // We don't handle other objects currently return Err(OperationError::Unhandled); } if let Ok(object_request) = serde_json::from_value::(serialized.clone()) { let result = self.get_object::(&object_request.0).await; let result = result .map(|success| serde_json::to_vec(&success.object()).unwrap()) .map_err(|err| match err { OperationError::Operation(err) => { OperationError::Operation(serde_json::to_vec(&err).unwrap()) } OperationError::Internal(internal) => OperationError::Internal(internal), OperationError::Unhandled => OperationError::Unhandled, }); match result { Ok(result) => Ok(serde_json::from_slice(&result) .map_err(|e| OperationError::Internal(e.to_string()))?), Err(err) => match err { OperationError::Internal(internal) => { warn!( "Internal Error: {:?}", OperationError::<()>::Internal(internal.clone()) ); 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 { 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 let Ok(instance) = Instance::from_str(object_str) { if instance != self.our_instance { // We don't handle other objects currently return Err(OperationError::Unhandled); } return Err(OperationError::Unhandled); } else { // Invalid object type return Err(OperationError::Unhandled); } } } // These tests verify that the essential handling of the database backend is // functional and correct. #[cfg(test)] mod test { use std::{str::FromStr, sync::Arc}; use anyhow::Error; use giterated_models::authenticated::UserAuthenticationToken; use giterated_models::instance::{ AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest, }; use giterated_models::object_backend::ObjectBackend; use giterated_models::repository::{ Commit, Description, Repository, RepositoryCommitBeforeRequest, RepositoryDiff, RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, RepositoryFileInspectRequest, RepositorySummary, RepositoryTreeEntry, }; use giterated_models::settings::AnySetting; use giterated_models::user::{DisplayName, User}; use giterated_models::value::{AnyValue, GiteratedObjectValue}; 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 { Ok(serde_json::from_slice( &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(), ) .unwrap()) } async fn write_setting( &mut self, _user: &User, _name: &str, _setting: &Value, ) -> Result<(), Error> { Ok(()) } async fn exists(&mut self, user: &User) -> Result { Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap()) } async fn repositories_for_user( &mut self, _requester: Option<&User>, _user: &User, ) -> Result, Error> { todo!() } } #[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>, _repository: &Repository, _request: &RepositoryFileInspectRequest, ) -> Result, Error> { todo!() } async fn repository_file_from_id( &mut self, _requester: Option<&User>, _repository: &Repository, _request: &RepositoryFileFromIdRequest, ) -> Result { todo!() } async fn repository_diff( &mut self, _requester: Option<&User>, _repository: &Repository, _request: &RepositoryDiffRequest, ) -> Result { todo!() } async fn repository_commit_before( &mut self, _requester: Option<&User>, _repository: &Repository, _request: &RepositoryCommitBeforeRequest, ) -> Result { todo!() } async fn get_value( &mut self, _repository: &Repository, _name: &str, ) -> Result, Error> { Ok(serde_json::from_slice( &serde_json::to_vec(&Description(String::from("test"))).unwrap(), ) .unwrap()) } async fn get_setting( &mut self, _repository: &Repository, _name: &str, ) -> Result { Ok(serde_json::from_slice( &serde_json::to_vec(&Description(String::from("test"))).unwrap(), ) .unwrap()) } async fn write_setting( &mut self, _repository: &Repository, _name: &str, _setting: &Value, ) -> Result<(), Error> { Ok(()) } async fn exists(&mut self, repository: &Repository) -> Result { // Ok(true) Ok(repository == &Repository::from_str( "test_user:test.giterated.dev/repository@test.giterated.dev", ) .unwrap()) } } 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"); } #[tokio::test] async fn test_user_get_setting() { let backend = test_backend(); let mut user = backend .get_object::("test_user:test.giterated.dev") .await .expect("object should have been returned"); user.get_setting::() .await .expect("object value should have been returned"); } #[tokio::test] async fn test_user_set_setting() { let backend = test_backend(); let mut user = backend .get_object::("test_user:test.giterated.dev") .await .expect("object should have been returned"); user.set_setting::(DisplayName(String::from("test"))) .await .expect("object value should have been returned"); } #[tokio::test] async fn test_respository_get() { let backend = test_backend(); let mut repository = backend .get_object::("test_user:test.giterated.dev/repository@test.giterated.dev") .await .expect("object should have been returned"); repository .get::() .await .expect("object value should have been returned"); } #[tokio::test] async fn test_repository_get_setting() { let backend = test_backend(); let mut repository = backend .get_object::("test_user:test.giterated.dev/repository@test.giterated.dev") .await .expect("object should have been returned"); repository .get_setting::() .await .expect("object value should have been returned"); } #[tokio::test] async fn test_repository_set_setting() { let backend = test_backend(); let mut repository = backend .get_object::("test_user:test.giterated.dev/repository@test.giterated.dev") .await .expect("object should have been returned"); repository .set_setting::(Description(String::from("test"))) .await .expect("object value should have been returned"); } }