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

ambee/giterated

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

Beginning of `stack-next` refactor

-Refactoring the protocol stack into something similar to a runtime. -Handles merging handler builders which is placing the ground work for plugins in. - Increased metadata generation during compilation enables less ser/de during execution. - Goal is to have an O(1) time from incoming operation to calling handlers. - Decreased penalty for using the statically typed API from within your code, now avoids some allocation. # Changes - Added `GiteratedRuntime` which is to replace the current unified stack - Added `RuntimeBuilder` which does what the current `OperationHandlers` struct does, but much better. - Added `RuntimeMetadata` to store type metadata for new `Any` based internals - Refactored serde_json out of the internal operation handling

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨708dea4

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