pub mod handler; use std::sync::Arc; use giterated_models::error::OperationError; use giterated_models::instance::Instance; use giterated_models::object::{GiteratedObject, Object, ObjectRequestError}; use giterated_models::object_backend::ObjectBackend; use giterated_models::operation::GiteratedOperation; use giterated_models::repository::Repository; use giterated_models::user::User; use giterated_stack::handler::GiteratedBackend; use giterated_stack::OperationHandlers; 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, }; #[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, } } pub fn into_backend(&self) -> GiteratedBackend { let mut handlers = OperationHandlers::default(); handlers .insert(user_get_repositories) .insert(user_get_value) .insert(user_get_setting) .insert(user_set_setting) .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) .register_object::() .register_object::() .register_object::(); GiteratedBackend::new(self.clone(), handlers) } } impl Debug for DatabaseBackend { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("DatabaseBackend").finish() } } // TODO: These should be on the stack // 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 giterated_stack::handler::GiteratedBackend; 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() -> GiteratedBackend { 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 _, } .into_backend() } #[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"); } }