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

ambee/giterated

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

Finish unified stack refactor.

Adds support for operation state, which will be used to pass authentication information around. Added generic backend that uses a channel to communicate with a typed backend.

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨d15581c

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