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

ambee/giterated

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

Repository commit before request and handler

Type: Feature

erremilia - ⁨2⁩ years ago

parent: tbd commit: ⁨144b159

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