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

ambee/giterated

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

Repository diff request and handler

Type: Feature

erremilia - ⁨2⁩ years ago

parent: tbd commit: ⁨502a11c

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨17335⁩ 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,
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_get_value)
123 .insert(repository_get_setting)
124 .insert(repository_set_setting);
125
126 match handler
127 .handle(
128 &repository,
129 operation,
130 serde_json::from_value(serialized.clone()).unwrap(),
131 self.clone(),
132 )
133 .await
134 {
135 Ok(result) => Ok(serde_json::from_slice(&result)
136 .map_err(|e| OperationError::Internal(e.to_string()))?),
137 Err(err) => match err {
138 OperationError::Internal(internal) => Err(OperationError::Internal(internal)),
139 OperationError::Unhandled => Err(OperationError::Unhandled),
140 OperationError::Operation(err) => Err(OperationError::Operation(
141 serde_json::from_slice(&err)
142 .map_err(|e| OperationError::Internal(e.to_string()))?,
143 )),
144 },
145 }
146 } else if let Ok(instance) = Instance::from_str(&object) {
147 if instance != self.our_instance {
148 // We don't handle other objects currently
149 return Err(OperationError::Unhandled);
150 }
151
152 if let Ok(object_request) = serde_json::from_value::<ObjectRequest>(serialized.clone())
153 {
154 let result = self.get_object::<AnyObject>(&object_request.0).await;
155
156 let result = result
157 .map(|success| serde_json::to_vec(&success.object()).unwrap())
158 .map_err(|err| match err {
159 OperationError::Operation(err) => {
160 OperationError::Operation(serde_json::to_vec(&err).unwrap())
161 }
162 OperationError::Internal(internal) => OperationError::Internal(internal),
163 OperationError::Unhandled => OperationError::Unhandled,
164 });
165
166 match result {
167 Ok(result) => Ok(serde_json::from_slice(&result)
168 .map_err(|e| OperationError::Internal(e.to_string()))?),
169 Err(err) => match err {
170 OperationError::Internal(internal) => {
171 Err(OperationError::Internal(internal))
172 }
173 OperationError::Unhandled => Err(OperationError::Unhandled),
174 OperationError::Operation(err) => Err(OperationError::Operation(
175 serde_json::from_slice(&err)
176 .map_err(|e| OperationError::Internal(e.to_string()))?,
177 )),
178 },
179 }
180 } else {
181 Err(OperationError::Unhandled)
182 }
183 } else {
184 Err(OperationError::Unhandled)
185 }
186 }
187
188 async fn get_object<O: GiteratedObject + Debug>(
189 &self,
190 object_str: &str,
191 ) -> Result<Object<O, Self>, OperationError<ObjectRequestError>> {
192 if let Ok(user) = User::from_str(object_str) {
193 let mut user_backend = self.user_backend.lock().await;
194
195 if user_backend
196 .exists(&user)
197 .await
198 .map_err(|e| OperationError::Internal(e.to_string()))?
199 {
200 Ok(unsafe {
201 Object::new_unchecked(
202 O::from_object_str(object_str)
203 .map_err(|e| ObjectRequestError::Deserialization(e.to_string()))?,
204 self.clone(),
205 )
206 })
207 } else {
208 return Err(OperationError::Unhandled);
209 }
210 } else if let Ok(repository) = Repository::from_str(object_str) {
211 let mut repository_backend = self.repository_backend.lock().await;
212
213 if repository_backend
214 .exists(&repository)
215 .await
216 .map_err(|e| OperationError::Internal(e.to_string()))?
217 {
218 Ok(unsafe {
219 Object::new_unchecked(
220 O::from_object_str(object_str)
221 .map_err(|e| ObjectRequestError::Deserialization(e.to_string()))?,
222 self.clone(),
223 )
224 })
225 } else {
226 return Err(OperationError::Unhandled);
227 }
228 } else if let Ok(instance) = Instance::from_str(object_str) {
229 if instance != self.our_instance {
230 // We don't handle other objects currently
231 return Err(OperationError::Unhandled);
232 }
233
234 return Err(OperationError::Unhandled);
235 } else {
236 // Invalid object type
237 return Err(OperationError::Unhandled);
238 }
239 }
240 }
241
242 // These tests verify that the essential handling of the database backend is
243 // functional and correct.
244 #[cfg(test)]
245 mod test {
246 use std::{str::FromStr, sync::Arc};
247
248 use anyhow::Error;
249
250 use giterated_models::authenticated::UserAuthenticationToken;
251
252 use giterated_models::instance::{
253 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
254 };
255
256 use giterated_models::object_backend::ObjectBackend;
257
258 use giterated_models::repository::{
259 Description, Repository, RepositoryFileInspectRequest, RepositorySummary,
260 RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, RepositoryDiff, RepositoryDiffRequest
261 };
262 use giterated_models::settings::AnySetting;
263 use giterated_models::user::{DisplayName, User};
264 use giterated_models::value::{AnyValue, GiteratedObjectValue};
265 use serde_json::Value;
266 use tokio::sync::Mutex;
267
268 use crate::backend::{git::GitBackendError, AuthBackend, RepositoryBackend, UserBackend};
269
270 use super::DatabaseBackend;
271 pub struct TestUserDatabaseBackend;
272
273 #[async_trait::async_trait]
274 impl UserBackend for TestUserDatabaseBackend {
275 async fn get_value(&mut self, _user: &User, name: &str) -> Result<AnyValue<User>, Error> {
276 assert_eq!(name, DisplayName::value_name());
277
278 Ok(serde_json::from_slice(
279 &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(),
280 )
281 .unwrap())
282 }
283 async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
284 Ok(serde_json::from_slice(
285 &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(),
286 )
287 .unwrap())
288 }
289 async fn write_setting(
290 &mut self,
291 _user: &User,
292 _name: &str,
293 _setting: &Value,
294 ) -> Result<(), Error> {
295 Ok(())
296 }
297 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
298 Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap())
299 }
300 async fn repositories_for_user(
301 &mut self,
302 requester: Option<&User>,
303 user: &User,
304 ) -> Result<Vec<RepositorySummary>, Error> {
305 todo!()
306 }
307 }
308
309 #[async_trait::async_trait]
310 impl AuthBackend for TestUserDatabaseBackend {
311 async fn register(
312 &mut self,
313 _request: RegisterAccountRequest,
314 ) -> Result<UserAuthenticationToken, Error> {
315 todo!()
316 }
317
318 async fn login(
319 &mut self,
320 _source: &Instance,
321 _request: AuthenticationTokenRequest,
322 ) -> Result<UserAuthenticationToken, Error> {
323 todo!()
324 }
325 }
326
327 pub struct TestUserRepositoryBackend;
328
329 #[async_trait::async_trait]
330 impl RepositoryBackend for TestUserRepositoryBackend {
331 async fn create_repository(
332 &mut self,
333 _user: &User,
334 _request: &RepositoryCreateRequest,
335 ) -> Result<Repository, GitBackendError> {
336 todo!()
337 }
338 async fn repository_file_inspect(
339 &mut self,
340 requester: Option<&User>,
341 repository: &Repository,
342 request: &RepositoryFileInspectRequest,
343 ) -> Result<Vec<RepositoryTreeEntry>, Error> {
344 todo!()
345 }
346 async fn repository_file_from_id(
347 &mut self,
348 requester: Option<&User>,
349 repository: &Repository,
350 request: &RepositoryFileFromIdRequest,
351 ) -> Result<RepositoryFile, Error> {
352 todo!()
353 }
354 async fn repository_diff(
355 &mut self,
356 requester: Option<&User>,
357 repository: &Repository,
358 request: &RepositoryDiffRequest,
359 ) -> Result<RepositoryDiff, Error> {
360 todo!()
361 }
362 async fn get_value(
363 &mut self,
364 _repository: &Repository,
365 _name: &str,
366 ) -> Result<AnyValue<Repository>, Error> {
367 Ok(serde_json::from_slice(
368 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
369 )
370 .unwrap())
371 }
372 async fn get_setting(
373 &mut self,
374 _repository: &Repository,
375 _name: &str,
376 ) -> Result<AnySetting, Error> {
377 Ok(serde_json::from_slice(
378 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
379 )
380 .unwrap())
381 }
382 async fn write_setting(
383 &mut self,
384 _repository: &Repository,
385 _name: &str,
386 _setting: &Value,
387 ) -> Result<(), Error> {
388 Ok(())
389 }
390
391 async fn exists(&mut self, repository: &Repository) -> Result<bool, Error> {
392 // Ok(true)
393 Ok(repository
394 == &Repository::from_str(
395 "test_user:test.giterated.dev/[email protected]",
396 )
397 .unwrap())
398 }
399 }
400
401 fn test_backend() -> DatabaseBackend {
402 DatabaseBackend {
403 our_instance: Instance::from_str("testing.giterated.dev").unwrap(),
404 user_backend: Arc::new(Mutex::new(TestUserDatabaseBackend)) as _,
405 repository_backend: Arc::new(Mutex::new(TestUserRepositoryBackend)) as _,
406 }
407 }
408
409 #[tokio::test]
410 async fn test_user_get() {
411 let backend = test_backend();
412
413 let mut user = backend
414 .get_object::<User>("test_user:test.giterated.dev")
415 .await
416 .expect("object should have been returned");
417
418 user.get::<DisplayName>()
419 .await
420 .expect("object value should have been returned");
421 }
422
423 #[tokio::test]
424 async fn test_user_get_setting() {
425 let backend = test_backend();
426
427 let mut user = backend
428 .get_object::<User>("test_user:test.giterated.dev")
429 .await
430 .expect("object should have been returned");
431
432 user.get_setting::<DisplayName>()
433 .await
434 .expect("object value should have been returned");
435 }
436
437 #[tokio::test]
438 async fn test_user_set_setting() {
439 let backend = test_backend();
440
441 let mut user = backend
442 .get_object::<User>("test_user:test.giterated.dev")
443 .await
444 .expect("object should have been returned");
445
446 user.set_setting::<DisplayName>(DisplayName(String::from("test")))
447 .await
448 .expect("object value should have been returned");
449 }
450
451 #[tokio::test]
452 async fn test_respository_get() {
453 let backend = test_backend();
454
455 let mut repository = backend
456 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
457 .await
458 .expect("object should have been returned");
459
460 repository
461 .get::<Description>()
462 .await
463 .expect("object value should have been returned");
464 }
465
466 #[tokio::test]
467 async fn test_repository_get_setting() {
468 let backend = test_backend();
469
470 let mut repository = backend
471 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
472 .await
473 .expect("object should have been returned");
474
475 repository
476 .get_setting::<Description>()
477 .await
478 .expect("object value should have been returned");
479 }
480
481 #[tokio::test]
482 async fn test_repository_set_setting() {
483 let backend = test_backend();
484
485 let mut repository = backend
486 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
487 .await
488 .expect("object should have been returned");
489
490 repository
491 .set_setting::<Description>(Description(String::from("test")))
492 .await
493 .expect("object value should have been returned");
494 }
495 }
496