JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Base protocol refactor complete

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨079d544

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨14175⁩ bytes
Raw
1 pub mod handler;
2
3 use std::{str::FromStr, sync::Arc};
4
5 use giterated_models::{
6 error::OperationError,
7 model::{instance::Instance, repository::Repository, user::User},
8 operation::{GiteratedObject, GiteratedOperation, Object, ObjectBackend, ObjectRequestError},
9 };
10 use std::fmt::Debug;
11 use tokio::sync::Mutex;
12
13 use crate::backend::{RepositoryBackend, UserBackend};
14
15 use self::handler::{
16 repository_get_setting, repository_get_value, repository_set_setting, user_get_setting,
17 user_get_value, user_set_setting, OperationHandlers,
18 };
19
20 #[derive(Clone, Debug)]
21 pub struct Foobackend {}
22
23 #[async_trait::async_trait]
24 impl ObjectBackend for Foobackend {
25 async fn object_operation<O: GiteratedObject + Debug, D: GiteratedOperation<O> + Debug>(
26 &self,
27 _object: O,
28 _operation: D,
29 ) -> Result<D::Success, OperationError<D::Failure>> {
30 // We don't handle operations with this backend
31 Err(OperationError::Unhandled)
32 }
33
34 async fn get_object<O: GiteratedObject + Debug>(
35 &self,
36 _object_str: &str,
37 ) -> Result<Object<O, Self>, OperationError<ObjectRequestError>> {
38 Err(OperationError::Unhandled)
39 }
40 }
41
42 /// A backend implementation which attempts to resolve data from the instance's database.
43 #[derive(Clone)]
44 pub struct DatabaseBackend {
45 pub(self) our_instance: Instance,
46 pub(self) user_backend: Arc<Mutex<dyn UserBackend + Send>>,
47 pub(self) repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
48 }
49
50 impl Debug for DatabaseBackend {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.debug_struct("DatabaseBackend").finish()
53 }
54 }
55
56 #[async_trait::async_trait]
57 impl ObjectBackend for DatabaseBackend {
58 async fn object_operation<O: GiteratedObject + Debug, D: GiteratedOperation<O> + Debug>(
59 &self,
60 object: O,
61 operation: D,
62 ) -> Result<D::Success, OperationError<D::Failure>> {
63 let serialized =
64 serde_json::to_value(operation).map_err(|e| OperationError::Internal(e.to_string()))?;
65 let object = object.to_string();
66 if let Ok(user) = User::from_str(&object) {
67 let mut handler = OperationHandlers::default();
68
69 handler
70 .insert(user_get_value)
71 .insert(user_get_setting)
72 .insert(user_set_setting);
73
74 match handler
75 .handle(
76 &user,
77 D::operation_name(),
78 serde_json::from_value(serialized.clone()).unwrap(),
79 self.clone(),
80 )
81 .await
82 {
83 Ok(result) => Ok(serde_json::from_slice(&result)
84 .map_err(|e| OperationError::Internal(e.to_string()))?),
85 Err(err) => match err {
86 OperationError::Internal(internal) => Err(OperationError::Internal(internal)),
87 OperationError::Unhandled => Err(OperationError::Unhandled),
88 OperationError::Operation(err) => Err(OperationError::Operation(
89 serde_json::from_slice(&err)
90 .map_err(|e| OperationError::Internal(e.to_string()))?,
91 )),
92 },
93 }
94 } else if let Ok(repository) = Repository::from_str(&object) {
95 let mut handler = OperationHandlers::default();
96
97 handler
98 .insert(repository_get_value)
99 .insert(repository_get_setting)
100 .insert(repository_set_setting);
101
102 match handler
103 .handle(
104 &repository,
105 D::operation_name(),
106 serde_json::from_value(serialized.clone()).unwrap(),
107 self.clone(),
108 )
109 .await
110 {
111 Ok(result) => Ok(serde_json::from_slice(&result)
112 .map_err(|e| OperationError::Internal(e.to_string()))?),
113 Err(err) => match err {
114 OperationError::Internal(internal) => Err(OperationError::Internal(internal)),
115 OperationError::Unhandled => Err(OperationError::Unhandled),
116 OperationError::Operation(err) => Err(OperationError::Operation(
117 serde_json::from_slice(&err)
118 .map_err(|e| OperationError::Internal(e.to_string()))?,
119 )),
120 },
121 }
122 } else if serde_json::from_str::<Instance>(&object).is_ok() {
123 Err(OperationError::Unhandled)
124 } else {
125 Err(OperationError::Unhandled)
126 }
127 }
128
129 async fn get_object<O: GiteratedObject + Debug>(
130 &self,
131 object_str: &str,
132 ) -> Result<Object<O, Self>, OperationError<ObjectRequestError>> {
133 if let Ok(user) = User::from_str(object_str) {
134 let mut user_backend = self.user_backend.lock().await;
135
136 if user_backend
137 .exists(&user)
138 .await
139 .map_err(|e| OperationError::Internal(e.to_string()))?
140 {
141 Ok(unsafe {
142 Object::new_unchecked(
143 O::from_object_str(object_str)
144 .map_err(|e| ObjectRequestError::Deserialization(e.to_string()))?,
145 self.clone(),
146 )
147 })
148 } else {
149 return Err(OperationError::Unhandled);
150 }
151 } else if let Ok(repository) = Repository::from_str(object_str) {
152 let mut repository_backend = self.repository_backend.lock().await;
153
154 if repository_backend
155 .exists(&repository)
156 .await
157 .map_err(|e| OperationError::Internal(e.to_string()))?
158 {
159 Ok(unsafe {
160 Object::new_unchecked(
161 O::from_object_str(object_str)
162 .map_err(|e| ObjectRequestError::Deserialization(e.to_string()))?,
163 self.clone(),
164 )
165 })
166 } else {
167 return Err(OperationError::Unhandled);
168 }
169 } else if Instance::from_str(object_str).is_ok() {
170 return Err(OperationError::Unhandled);
171 } else {
172 // Invalid object type
173 return Err(OperationError::Unhandled);
174 }
175 }
176 }
177
178 // These tests verify that the essential handling of the database backend is
179 // functional and correct.
180 #[cfg(test)]
181 mod test {
182 use std::{str::FromStr, sync::Arc};
183
184 use anyhow::Error;
185 use giterated_models::model::settings::UserDisplayName;
186 use giterated_models::operation::ObjectBackend;
187 use giterated_models::values::repository::Description;
188 use giterated_models::{
189 model::{
190 authenticated::UserAuthenticationToken,
191 instance::Instance,
192 repository::{Repository, RepositorySummary, RepositoryTreeEntry},
193 settings::AnySetting,
194 user::User,
195 },
196 operation::{
197 instance::{
198 AuthenticationTokenRequest, RegisterAccountRequest, RepositoryCreateRequest,
199 },
200 repository::RepositoryFileInspectRequest,
201 GiteratedObjectValue,
202 },
203 values::{user::DisplayName, AnyValue},
204 };
205
206 use serde_json::Value;
207 use tokio::sync::Mutex;
208
209 use crate::backend::{git::GitBackendError, AuthBackend, RepositoryBackend, UserBackend};
210
211 use super::DatabaseBackend;
212 pub struct TestUserDatabaseBackend;
213
214 #[async_trait::async_trait]
215 impl UserBackend for TestUserDatabaseBackend {
216 async fn get_value(&mut self, _user: &User, name: &str) -> Result<AnyValue<User>, Error> {
217 assert_eq!(name, DisplayName::value_name());
218
219 Ok(serde_json::from_slice(
220 &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(),
221 )
222 .unwrap())
223 }
224 async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
225 Ok(serde_json::from_slice(
226 &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(),
227 )
228 .unwrap())
229 }
230 async fn write_setting(
231 &mut self,
232 _user: &User,
233 _name: &str,
234 _setting: &Value,
235 ) -> Result<(), Error> {
236 Ok(())
237 }
238 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
239 Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap())
240 }
241 }
242
243 #[async_trait::async_trait]
244 impl AuthBackend for TestUserDatabaseBackend {
245 async fn register(
246 &mut self,
247 _request: RegisterAccountRequest,
248 ) -> Result<UserAuthenticationToken, Error> {
249 todo!()
250 }
251
252 async fn login(
253 &mut self,
254 _source: &Instance,
255 _request: AuthenticationTokenRequest,
256 ) -> Result<UserAuthenticationToken, Error> {
257 todo!()
258 }
259 }
260
261 pub struct TestUserRepositoryBackend;
262
263 #[async_trait::async_trait]
264 impl RepositoryBackend for TestUserRepositoryBackend {
265 async fn create_repository(
266 &mut self,
267 _user: &User,
268 _request: &RepositoryCreateRequest,
269 ) -> Result<Repository, GitBackendError> {
270 todo!()
271 }
272 async fn repository_file_inspect(
273 &mut self,
274 _requester: Option<&User>,
275 _request: &RepositoryFileInspectRequest,
276 ) -> Result<Vec<RepositoryTreeEntry>, Error> {
277 todo!()
278 }
279 async fn repositories_for_user(
280 &mut self,
281 _requester: Option<&User>,
282 _user: &User,
283 ) -> Result<Vec<RepositorySummary>, Error> {
284 todo!()
285 }
286
287 async fn get_value(
288 &mut self,
289 _repository: &Repository,
290 _name: &str,
291 ) -> Result<AnyValue<Repository>, Error> {
292 Ok(serde_json::from_slice(
293 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
294 )
295 .unwrap())
296 }
297 async fn get_setting(
298 &mut self,
299 _repository: &Repository,
300 _name: &str,
301 ) -> Result<AnySetting, Error> {
302 Ok(serde_json::from_slice(
303 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
304 )
305 .unwrap())
306 }
307 async fn write_setting(
308 &mut self,
309 _repository: &Repository,
310 _name: &str,
311 _setting: &Value,
312 ) -> Result<(), Error> {
313 Ok(())
314 }
315
316 async fn exists(&mut self, repository: &Repository) -> Result<bool, Error> {
317 // Ok(true)
318 Ok(repository
319 == &Repository::from_str(
320 "test_user:test.giterated.dev/[email protected]",
321 )
322 .unwrap())
323 }
324 }
325
326 fn test_backend() -> DatabaseBackend {
327 DatabaseBackend {
328 our_instance: Instance::from_str("testing.giterated.dev").unwrap(),
329 user_backend: Arc::new(Mutex::new(TestUserDatabaseBackend)) as _,
330 repository_backend: Arc::new(Mutex::new(TestUserRepositoryBackend)) as _,
331 }
332 }
333
334 #[tokio::test]
335 async fn test_user_get() {
336 let backend = test_backend();
337
338 let mut user = backend
339 .get_object::<User>("test_user:test.giterated.dev")
340 .await
341 .expect("object should have been returned");
342
343 user.get::<DisplayName>()
344 .await
345 .expect("object value should have been returned");
346 }
347
348 #[tokio::test]
349 async fn test_user_get_setting() {
350 let backend = test_backend();
351
352 let mut user = backend
353 .get_object::<User>("test_user:test.giterated.dev")
354 .await
355 .expect("object should have been returned");
356
357 user.get_setting::<UserDisplayName>()
358 .await
359 .expect("object value should have been returned");
360 }
361
362 #[tokio::test]
363 async fn test_user_set_setting() {
364 let backend = test_backend();
365
366 let mut user = backend
367 .get_object::<User>("test_user:test.giterated.dev")
368 .await
369 .expect("object should have been returned");
370
371 user.set_setting::<UserDisplayName>(UserDisplayName(String::from("test")))
372 .await
373 .expect("object value should have been returned");
374 }
375
376 #[tokio::test]
377 async fn test_respository_get() {
378 let backend = test_backend();
379
380 let mut repository = backend
381 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
382 .await
383 .expect("object should have been returned");
384
385 repository
386 .get::<Description>()
387 .await
388 .expect("object value should have been returned");
389 }
390
391 #[tokio::test]
392 async fn test_repository_get_setting() {
393 let backend = test_backend();
394
395 let mut repository = backend
396 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
397 .await
398 .expect("object should have been returned");
399
400 repository
401 .get_setting::<Description>()
402 .await
403 .expect("object value should have been returned");
404 }
405
406 #[tokio::test]
407 async fn test_repository_set_setting() {
408 let backend = test_backend();
409
410 let mut repository = backend
411 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
412 .await
413 .expect("object should have been returned");
414
415 repository
416 .set_setting::<Description>(Description(String::from("test")))
417 .await
418 .expect("object value should have been returned");
419 }
420 }
421