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

ambee/giterated

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

Fixes

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨73a5af5

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