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

ambee/giterated

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

Error handling refactor

This refactor aims to improve error handling throughout the project by refining the overarching error types and increasing usage of proper error handling. Replaced existing networked operation error with `NetworkOperationError`. `NetworkOperationError` does not forward any internal error details, which allows `OperationError` to grow into a better error type. `OperationError` now has support for storing real typed errors inside of it for better debugging. `IntoInternalError` is a trait which allows for easy conversion of error types into `OperationError::internal`.

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨e02c03d

⁨giterated-daemon/src/database_backend/handler.rs⁩ - ⁨20233⁩ bytes
Raw
1 use std::sync::Arc;
2
3 use futures_util::{future::LocalBoxFuture, FutureExt};
4 use giterated_models::{
5 authenticated::UserAuthenticationToken,
6 error::{
7 GetValueError, InstanceError, IntoInternalError, OperationError, RepositoryError, UserError,
8 },
9 instance::{
10 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
11 },
12 object_backend::ObjectBackend,
13 repository::{
14 Commit, DefaultBranch, Description, LatestCommit, Repository, RepositoryBranch,
15 RepositoryBranchesRequest, RepositoryCommitBeforeRequest, RepositoryCommitFromIdRequest,
16 RepositoryDiff, RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile,
17 RepositoryFileFromIdRequest, RepositoryFileFromPathRequest, RepositoryFileInspectRequest,
18 RepositoryInfoRequest, RepositoryLastCommitOfFileRequest, RepositoryStatistics,
19 RepositoryStatisticsRequest, RepositorySummary, RepositoryView, Visibility,
20 },
21 settings::{GetSetting, GetSettingError},
22 user::{Bio, DisplayName, User, UserRepositoriesRequest},
23 value::{AnyValue, GetValueTyped},
24 };
25 use giterated_stack::{AuthenticatedUser, AuthorizedInstance, GiteratedStack, StackOperationState};
26 use serde_json::Value;
27
28 use super::DatabaseBackend;
29
30 pub fn user_get_repositories(
31 object: &User,
32 _operation: UserRepositoriesRequest,
33 state: DatabaseBackend,
34 _operation_state: StackOperationState,
35 requester: Option<AuthenticatedUser>,
36 ) -> LocalBoxFuture<'static, Result<Vec<RepositorySummary>, OperationError<UserError>>> {
37 let object = object.clone();
38
39 async move {
40 let mut user_backend = state.user_backend.lock().await;
41 let repositories_response = user_backend
42 .repositories_for_user(&requester, &object)
43 .await
44 .as_internal_error()?;
45 drop(user_backend);
46 let mut repositories_backend = state.repository_backend.lock().await;
47
48 let mut repositories = vec![];
49
50 for repository in repositories_response {
51 if repositories_backend
52 .exists(&requester, &repository.repository)
53 .await
54 .as_internal_error()?
55 {
56 repositories.push(repository);
57 }
58 }
59
60 Ok(repositories)
61 }
62 .boxed_local()
63 }
64
65 pub fn user_get_value(
66 object: &User,
67 operation: GetValueTyped<AnyValue<User>>,
68 state: DatabaseBackend,
69 ) -> LocalBoxFuture<'static, Result<AnyValue<User>, OperationError<GetValueError>>> {
70 let object = object.clone();
71
72 async move {
73 let mut user_backend = state.user_backend.lock().await;
74 let value = user_backend
75 .get_value(&object, &operation.value_name)
76 .await
77 .as_internal_error()?;
78
79 Ok(value)
80 }
81 .boxed_local()
82 }
83
84 pub fn user_get_setting(
85 object: &User,
86 operation: GetSetting,
87 state: DatabaseBackend,
88 ) -> LocalBoxFuture<'static, Result<Value, OperationError<GetSettingError>>> {
89 let object = object.clone();
90
91 async move {
92 let mut user_backend = state.user_backend.lock().await;
93 let value = user_backend
94 .get_setting(&object, &operation.setting_name)
95 .await
96 .as_internal_error()?;
97
98 Ok(value.0)
99 }
100 .boxed_local()
101 }
102
103 pub fn repository_info(
104 object: &Repository,
105 operation: RepositoryInfoRequest,
106 state: DatabaseBackend,
107 operation_state: StackOperationState,
108 backend: Arc<GiteratedStack>,
109 requester: Option<AuthenticatedUser>,
110 ) -> LocalBoxFuture<'static, Result<RepositoryView, OperationError<RepositoryError>>> {
111 let object = object.clone();
112
113 async move {
114 let mut object = backend
115 .get_object::<Repository>(&object.to_string(), &operation_state)
116 .await
117 .unwrap();
118 let mut repository_backend = state.repository_backend.lock().await;
119 let tree = repository_backend
120 .repository_file_inspect(
121 &requester,
122 object.object(),
123 &RepositoryFileInspectRequest {
124 extra_metadata: operation.extra_metadata,
125 path: operation.path,
126 rev: operation.rev.clone(),
127 },
128 )
129 .await
130 .as_internal_error()?;
131
132 let statistics = repository_backend
133 .repository_get_statistics(
134 &requester,
135 object.object(),
136 &RepositoryStatisticsRequest {
137 rev: operation.rev.clone(),
138 },
139 )
140 .await
141 .as_internal_error()?;
142 drop(repository_backend);
143
144 let info = RepositoryView {
145 name: object.object().name.clone(),
146 owner: object.object().owner.clone(),
147 description: object.get::<Description>(&operation_state).await.ok(),
148 visibility: object
149 .get::<Visibility>(&operation_state)
150 .await
151 .as_internal_error()?,
152 default_branch: object
153 .get::<DefaultBranch>(&operation_state)
154 .await
155 .as_internal_error()?,
156 // TODO: Can't be a simple get function, this needs to be returned alongside the tree as this differs depending on the rev and path.
157 latest_commit: object.get::<LatestCommit>(&operation_state).await.ok(),
158 stats: statistics,
159 tree_rev: operation.rev,
160 tree,
161 };
162
163 Ok(info)
164 }
165 .boxed_local()
166 }
167
168 pub fn repository_get_statistics(
169 object: &Repository,
170 operation: RepositoryStatisticsRequest,
171 state: DatabaseBackend,
172 operation_state: StackOperationState,
173 backend: Arc<GiteratedStack>,
174 requester: Option<AuthenticatedUser>,
175 ) -> LocalBoxFuture<'static, Result<RepositoryStatistics, OperationError<RepositoryError>>> {
176 let object = object.clone();
177
178 async move {
179 let object = backend
180 .get_object::<Repository>(&object.to_string(), &operation_state)
181 .await
182 .unwrap();
183
184 let mut repository_backend = state.repository_backend.lock().await;
185 let statistics = repository_backend
186 .repository_get_statistics(
187 &requester,
188 object.object(),
189 &RepositoryStatisticsRequest { rev: operation.rev },
190 )
191 .await
192 .as_internal_error()?;
193 drop(repository_backend);
194
195 Ok(statistics)
196 }
197 .boxed_local()
198 }
199
200 pub fn repository_get_branches(
201 object: &Repository,
202 operation: RepositoryBranchesRequest,
203 state: DatabaseBackend,
204 operation_state: StackOperationState,
205 backend: Arc<GiteratedStack>,
206 requester: Option<AuthenticatedUser>,
207 ) -> LocalBoxFuture<'static, Result<Vec<RepositoryBranch>, OperationError<RepositoryError>>> {
208 let object = object.clone();
209
210 async move {
211 let object = backend
212 .get_object::<Repository>(&object.to_string(), &operation_state)
213 .await
214 .unwrap();
215
216 let mut repository_backend = state.repository_backend.lock().await;
217 let branches = repository_backend
218 .repository_get_branches(&requester, object.object(), &operation)
219 .await
220 .as_internal_error()?;
221 drop(repository_backend);
222
223 Ok(branches)
224 }
225 .boxed_local()
226 }
227
228 pub fn repository_file_from_id(
229 object: &Repository,
230 operation: RepositoryFileFromIdRequest,
231 state: DatabaseBackend,
232 operation_state: StackOperationState,
233 backend: Arc<GiteratedStack>,
234
235 requester: Option<AuthenticatedUser>,
236 ) -> LocalBoxFuture<'static, Result<RepositoryFile, OperationError<RepositoryError>>> {
237 let object = object.clone();
238
239 async move {
240 let object = backend
241 .get_object::<Repository>(&object.to_string(), &operation_state)
242 .await
243 .unwrap();
244
245 let mut repository_backend = state.repository_backend.lock().await;
246 let file = repository_backend
247 .repository_file_from_id(
248 &requester,
249 object.object(),
250 &RepositoryFileFromIdRequest(operation.0),
251 )
252 .await
253 .as_internal_error()?;
254 drop(repository_backend);
255
256 Ok(file)
257 }
258 .boxed_local()
259 }
260
261 pub fn repository_file_from_path(
262 object: &Repository,
263 operation: RepositoryFileFromPathRequest,
264 state: DatabaseBackend,
265 operation_state: StackOperationState,
266 backend: Arc<GiteratedStack>,
267 requester: Option<AuthenticatedUser>,
268 ) -> LocalBoxFuture<'static, Result<(RepositoryFile, String), OperationError<RepositoryError>>> {
269 let object = object.clone();
270
271 async move {
272 let object = backend
273 .get_object::<Repository>(&object.to_string(), &operation_state)
274 .await
275 .unwrap();
276
277 let mut repository_backend = state.repository_backend.lock().await;
278 let file = repository_backend
279 .repository_file_from_path(
280 &requester,
281 object.object(),
282 &RepositoryFileFromPathRequest {
283 rev: operation.rev,
284 path: operation.path,
285 },
286 )
287 .await
288 .as_internal_error()?;
289 drop(repository_backend);
290
291 Ok(file)
292 }
293 .boxed_local()
294 }
295
296 pub fn repository_last_commit_of_file(
297 object: &Repository,
298 operation: RepositoryLastCommitOfFileRequest,
299 state: DatabaseBackend,
300 operation_state: StackOperationState,
301 backend: Arc<GiteratedStack>,
302 requester: Option<AuthenticatedUser>,
303 ) -> LocalBoxFuture<'static, Result<Commit, OperationError<RepositoryError>>> {
304 let object = object.clone();
305
306 async move {
307 let object = backend
308 .get_object::<Repository>(&object.to_string(), &operation_state)
309 .await
310 .unwrap();
311
312 let mut repository_backend = state.repository_backend.lock().await;
313 let commit = repository_backend
314 .repository_last_commit_of_file(
315 &requester,
316 object.object(),
317 &RepositoryLastCommitOfFileRequest {
318 start_commit: operation.start_commit,
319 path: operation.path,
320 },
321 )
322 .await
323 .as_internal_error()?;
324 drop(repository_backend);
325
326 Ok(commit)
327 }
328 .boxed_local()
329 }
330
331 pub fn repository_commit_by_id(
332 object: &Repository,
333 operation: RepositoryCommitFromIdRequest,
334 state: DatabaseBackend,
335 operation_state: StackOperationState,
336 backend: Arc<GiteratedStack>,
337 requester: Option<AuthenticatedUser>,
338 ) -> LocalBoxFuture<'static, Result<Commit, OperationError<RepositoryError>>> {
339 let object = object.clone();
340
341 async move {
342 let object = backend
343 .get_object::<Repository>(&object.to_string(), &operation_state)
344 .await
345 .unwrap();
346
347 let mut repository_backend = state.repository_backend.lock().await;
348 let commit = repository_backend
349 .repository_commit_from_id(
350 &requester,
351 object.object(),
352 &RepositoryCommitFromIdRequest(operation.0),
353 )
354 .await
355 .as_internal_error()?;
356 drop(repository_backend);
357
358 Ok(commit)
359 }
360 .boxed_local()
361 }
362
363 pub fn repository_diff(
364 object: &Repository,
365 operation: RepositoryDiffRequest,
366 state: DatabaseBackend,
367 operation_state: StackOperationState,
368 backend: Arc<GiteratedStack>,
369 requester: Option<AuthenticatedUser>,
370 ) -> LocalBoxFuture<'static, Result<RepositoryDiff, OperationError<RepositoryError>>> {
371 let object = object.clone();
372
373 async move {
374 let object = backend
375 .get_object::<Repository>(&object.to_string(), &operation_state)
376 .await
377 .unwrap();
378
379 let mut repository_backend = state.repository_backend.lock().await;
380 let diff = repository_backend
381 .repository_diff(&requester, object.object(), &operation)
382 .await
383 .as_internal_error()?;
384 drop(repository_backend);
385
386 Ok(diff)
387 }
388 .boxed_local()
389 }
390
391 pub fn repository_diff_patch(
392 object: &Repository,
393 operation: RepositoryDiffPatchRequest,
394 state: DatabaseBackend,
395 operation_state: StackOperationState,
396 backend: Arc<GiteratedStack>,
397 requester: Option<AuthenticatedUser>,
398 ) -> LocalBoxFuture<'static, Result<String, OperationError<RepositoryError>>> {
399 let object = object.clone();
400
401 async move {
402 let object = backend
403 .get_object::<Repository>(&object.to_string(), &operation_state)
404 .await
405 .unwrap();
406
407 let mut repository_backend = state.repository_backend.lock().await;
408 let patch = repository_backend
409 .repository_diff_patch(&requester, object.object(), &operation)
410 .await
411 .as_internal_error()?;
412 drop(repository_backend);
413
414 Ok(patch)
415 }
416 .boxed_local()
417 }
418
419 pub fn repository_commit_before(
420 object: &Repository,
421 operation: RepositoryCommitBeforeRequest,
422 state: DatabaseBackend,
423 operation_state: StackOperationState,
424 backend: Arc<GiteratedStack>,
425 requester: Option<AuthenticatedUser>,
426 ) -> LocalBoxFuture<'static, Result<Commit, OperationError<RepositoryError>>> {
427 let object = object.clone();
428
429 async move {
430 let object = backend
431 .get_object::<Repository>(&object.to_string(), &operation_state)
432 .await
433 .unwrap();
434
435 let mut repository_backend = state.repository_backend.lock().await;
436 let file = repository_backend
437 .repository_commit_before(&requester, object.object(), &operation)
438 .await
439 .as_internal_error()?;
440 drop(repository_backend);
441
442 Ok(file)
443 }
444 .boxed_local()
445 }
446
447 pub fn repository_get_value(
448 object: &Repository,
449 operation: GetValueTyped<AnyValue<Repository>>,
450 state: DatabaseBackend,
451 ) -> LocalBoxFuture<'static, Result<AnyValue<Repository>, OperationError<GetValueError>>> {
452 let object = object.clone();
453
454 async move {
455 let mut repository_backend = state.repository_backend.lock().await;
456 let value = repository_backend
457 .get_value(&object, &operation.value_name)
458 .await
459 .as_internal_error()?;
460
461 Ok(value)
462 }
463 .boxed_local()
464 }
465
466 pub fn repository_get_setting(
467 object: &Repository,
468 operation: GetSetting,
469 state: DatabaseBackend,
470 ) -> LocalBoxFuture<'static, Result<Value, OperationError<GetSettingError>>> {
471 let object = object.clone();
472
473 async move {
474 let mut repository_backend = state.repository_backend.lock().await;
475 let value = repository_backend
476 .get_setting(&object, &operation.setting_name)
477 .await
478 .as_internal_error()?;
479
480 Ok(value.0)
481 }
482 .boxed_local()
483 }
484
485 pub fn instance_authentication_request(
486 object: &Instance,
487 operation: AuthenticationTokenRequest,
488 state: DatabaseBackend,
489 // Authorizes the request for SAME-INSTANCE
490 _authorized_instance: AuthorizedInstance,
491 ) -> LocalBoxFuture<'static, Result<UserAuthenticationToken, OperationError<InstanceError>>> {
492 let object = object.clone();
493 async move {
494 let mut backend = state.user_backend.lock().await;
495
496 backend.login(&object, operation).await.as_internal_error()
497 }
498 .boxed_local()
499 }
500
501 pub fn instance_registration_request(
502 _object: &Instance,
503 operation: RegisterAccountRequest,
504 state: DatabaseBackend,
505 // Authorizes the request for SAME-INSTANCE
506 _authorized_instance: AuthorizedInstance,
507 ) -> LocalBoxFuture<'static, Result<UserAuthenticationToken, OperationError<InstanceError>>> {
508 async move {
509 let mut backend = state.user_backend.lock().await;
510
511 backend.register(operation).await.as_internal_error()
512 }
513 .boxed_local()
514 }
515
516 pub fn instance_create_repository_request(
517 _object: &Instance,
518 operation: RepositoryCreateRequest,
519 state: DatabaseBackend,
520 requester: AuthenticatedUser,
521 // Authorizes the request for SAME-INSTANCE
522 _authorized_instance: AuthorizedInstance,
523 ) -> LocalBoxFuture<'static, Result<Repository, OperationError<InstanceError>>> {
524 async move {
525 let mut backend = state.repository_backend.lock().await;
526
527 backend
528 .create_repository(&requester, &operation)
529 .await
530 .as_internal_error()
531 }
532 .boxed_local()
533 }
534
535 pub fn user_get_value_display_name(
536 object: &User,
537 operation: GetValueTyped<DisplayName>,
538 state: DatabaseBackend,
539 // _requester: AuthorizedUser,
540 ) -> LocalBoxFuture<'static, Result<DisplayName, OperationError<GetValueError>>> {
541 let object = object.clone();
542
543 async move {
544 let mut backend = state.user_backend.lock().await;
545
546 let raw_value = backend
547 .get_value(&object, &operation.value_name)
548 .await
549 .as_internal_error()?;
550
551 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
552 }
553 .boxed_local()
554 }
555
556 pub fn user_get_value_bio(
557 object: &User,
558 operation: GetValueTyped<Bio>,
559 state: DatabaseBackend,
560 ) -> LocalBoxFuture<'static, Result<Bio, OperationError<GetValueError>>> {
561 let object = object.clone();
562
563 async move {
564 let mut backend = state.user_backend.lock().await;
565
566 let raw_value = backend
567 .get_value(&object, &operation.value_name)
568 .await
569 .as_internal_error()?;
570
571 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
572 }
573 .boxed_local()
574 }
575
576 pub fn repository_get_value_description(
577 object: &Repository,
578 operation: GetValueTyped<Description>,
579 state: DatabaseBackend,
580 ) -> LocalBoxFuture<'static, Result<Description, OperationError<GetValueError>>> {
581 let object = object.clone();
582
583 async move {
584 let mut backend = state.repository_backend.lock().await;
585
586 let raw_value = backend
587 .get_value(&object, &operation.value_name)
588 .await
589 .as_internal_error()?;
590
591 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
592 }
593 .boxed_local()
594 }
595
596 pub fn repository_get_value_visibility(
597 object: &Repository,
598 operation: GetValueTyped<Visibility>,
599 state: DatabaseBackend,
600 ) -> LocalBoxFuture<'static, Result<Visibility, OperationError<GetValueError>>> {
601 let object = object.clone();
602
603 async move {
604 let mut backend = state.repository_backend.lock().await;
605
606 let raw_value = backend
607 .get_value(&object, &operation.value_name)
608 .await
609 .as_internal_error()?;
610
611 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
612 }
613 .boxed_local()
614 }
615
616 pub fn repository_get_default_branch(
617 object: &Repository,
618 operation: GetValueTyped<DefaultBranch>,
619 state: DatabaseBackend,
620 ) -> LocalBoxFuture<'static, Result<DefaultBranch, OperationError<GetValueError>>> {
621 let object = object.clone();
622
623 async move {
624 let mut backend = state.repository_backend.lock().await;
625
626 let raw_value = backend
627 .get_value(&object, &operation.value_name)
628 .await
629 .as_internal_error()?;
630
631 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
632 }
633 .boxed_local()
634 }
635
636 pub fn repository_get_latest_commit(
637 object: &Repository,
638 operation: GetValueTyped<LatestCommit>,
639 state: DatabaseBackend,
640 ) -> LocalBoxFuture<'static, Result<LatestCommit, OperationError<GetValueError>>> {
641 let object = object.clone();
642
643 async move {
644 let mut backend = state.repository_backend.lock().await;
645
646 let raw_value = backend
647 .get_value(&object, &operation.value_name)
648 .await
649 .as_internal_error()?;
650
651 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
652 }
653 .boxed_local()
654 }
655