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

Showing ⁨⁨8⁩ changed files⁩ with ⁨⁨279⁩ insertions⁩ and ⁨⁨141⁩ deletions⁩

Cargo.lock

View file
@@ -809,12 +809,14 @@ dependencies = [
809 809 name = "giterated-stack"
810 810 version = "0.1.0"
811 811 dependencies = [
812 "anyhow",
812 813 "async-trait",
813 814 "bincode",
814 815 "futures-util",
815 816 "giterated-models",
816 817 "serde",
817 818 "serde_json",
819 "thiserror",
818 820 "tokio",
819 821 "tracing",
820 822 ]

giterated-daemon/src/authorization.rs

View file
@@ -14,6 +14,11 @@ use giterated_models::user::User;
14 14
15 15 use giterated_models::value::GetValueTyped;
16 16 use giterated_models::{object::ObjectRequest, settings::SetSetting, value::GiteratedObjectValue};
17
18 #[derive(Debug, thiserror::Error)]
19 #[error("unauthorized")]
20 pub struct UnauthorizedError;
21
17 22 #[async_trait::async_trait]
18 23 pub trait AuthorizedOperation<O: GiteratedObject, S> {
19 24 /// Authorizes the operation, returning whether the operation was
@@ -23,7 +28,7 @@ pub trait AuthorizedOperation<O: GiteratedObject, S> {
23 28 authenticating_user: Option<&User>,
24 29 object: &O,
25 30 state: &mut S,
26 ) -> Result<bool, OperationError<()>>;
31 ) -> Result<bool, OperationError<UnauthorizedError>>;
27 32 }
28 33
29 34 #[async_trait::async_trait]
@@ -33,7 +38,7 @@ impl AuthorizedOperation<User, ConnectionState> for SetSetting {
33 38 _authenticating_user: Option<&User>,
34 39 _object: &User,
35 40 _state: &mut ConnectionState,
36 ) -> Result<bool, OperationError<()>> {
41 ) -> Result<bool, OperationError<UnauthorizedError>> {
37 42 // TODO
38 43 Ok(true)
39 44 }
@@ -46,7 +51,7 @@ impl AuthorizedOperation<Repository, ConnectionState> for SetSetting {
46 51 _authenticating_user: Option<&User>,
47 52 _object: &Repository,
48 53 _state: &mut ConnectionState,
49 ) -> Result<bool, OperationError<()>> {
54 ) -> Result<bool, OperationError<UnauthorizedError>> {
50 55 // TODO
51 56 Ok(true)
52 57 }
@@ -61,7 +66,7 @@ impl<V: GiteratedObjectValue + Send + Sync + Debug + Clone>
61 66 _authenticating_user: Option<&User>,
62 67 _object: &Repository,
63 68 _state: &mut ConnectionState,
64 ) -> Result<bool, OperationError<()>> {
69 ) -> Result<bool, OperationError<UnauthorizedError>> {
65 70 // TODO
66 71 Ok(true)
67 72 }
@@ -74,7 +79,7 @@ impl AuthorizedOperation<Repository, ConnectionState> for ObjectRequest {
74 79 _authenticating_user: Option<&User>,
75 80 _object: &Repository,
76 81 _state: &mut ConnectionState,
77 ) -> Result<bool, OperationError<()>> {
82 ) -> Result<bool, OperationError<UnauthorizedError>> {
78 83 // TODO
79 84 Ok(true)
80 85 }
@@ -87,7 +92,7 @@ impl AuthorizedOperation<Repository, ConnectionState> for RepositoryFileInspectR
87 92 _authenticating_user: Option<&User>,
88 93 _object: &Repository,
89 94 _state: &mut ConnectionState,
90 ) -> Result<bool, OperationError<()>> {
95 ) -> Result<bool, OperationError<UnauthorizedError>> {
91 96 // TODO
92 97 Ok(true)
93 98 }
@@ -100,7 +105,7 @@ impl AuthorizedOperation<Repository, ConnectionState> for RepositoryIssuesReques
100 105 _authenticating_user: Option<&User>,
101 106 _object: &Repository,
102 107 _state: &mut ConnectionState,
103 ) -> Result<bool, OperationError<()>> {
108 ) -> Result<bool, OperationError<UnauthorizedError>> {
104 109 // TODO
105 110 Ok(true)
106 111 }
@@ -113,7 +118,7 @@ impl AuthorizedOperation<Repository, ConnectionState> for RepositoryIssueLabelsR
113 118 _authenticating_user: Option<&User>,
114 119 _object: &Repository,
115 120 _state: &mut ConnectionState,
116 ) -> Result<bool, OperationError<()>> {
121 ) -> Result<bool, OperationError<UnauthorizedError>> {
117 122 // TODO
118 123 Ok(true)
119 124 }
@@ -126,7 +131,7 @@ impl AuthorizedOperation<Repository, ConnectionState> for RepositoryIssuesCountR
126 131 _authenticating_user: Option<&User>,
127 132 _object: &Repository,
128 133 _state: &mut ConnectionState,
129 ) -> Result<bool, OperationError<()>> {
134 ) -> Result<bool, OperationError<UnauthorizedError>> {
130 135 // TODO
131 136 Ok(true)
132 137 }

giterated-daemon/src/connection/wrapper.rs

View file
@@ -170,9 +170,12 @@ pub async fn connection_wrapper(
170 170 operation_state.instance = None;
171 171
172 172 if let Err(OperationError::Internal(internal_error)) = &result {
173 error!("An internal error has occured: {}", internal_error);
173 error!("An internal error has occurred:\n{:?}", internal_error);
174 174 }
175 175
176 // Map error to the network variant
177 let result = result.map_err(|e| e.into_network());
178
176 179 let mut socket = connection_state.socket.lock().await;
177 180 let _ = socket
178 181 .send(Message::Binary(bincode::serialize(&result).unwrap()))

giterated-daemon/src/database_backend/handler.rs

View file
@@ -1,9 +1,11 @@
1 use std::{error::Error, sync::Arc};
1 use std::sync::Arc;
2 2
3 3 use futures_util::{future::LocalBoxFuture, FutureExt};
4 4 use giterated_models::{
5 5 authenticated::UserAuthenticationToken,
6 error::{GetValueError, InstanceError, OperationError, RepositoryError, UserError},
6 error::{
7 GetValueError, InstanceError, IntoInternalError, OperationError, RepositoryError, UserError,
8 },
7 9 instance::{
8 10 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
9 11 },
@@ -39,7 +41,7 @@ pub fn user_get_repositories(
39 41 let repositories_response = user_backend
40 42 .repositories_for_user(&requester, &object)
41 43 .await
42 .map_err(|e| OperationError::Internal(e.to_string()))?;
44 .as_internal_error()?;
43 45 drop(user_backend);
44 46 let mut repositories_backend = state.repository_backend.lock().await;
45 47
@@ -49,7 +51,7 @@ pub fn user_get_repositories(
49 51 if repositories_backend
50 52 .exists(&requester, &repository.repository)
51 53 .await
52 .map_err(|e| OperationError::Internal(e.to_string()))?
54 .as_internal_error()?
53 55 {
54 56 repositories.push(repository);
55 57 }
@@ -72,7 +74,7 @@ pub fn user_get_value(
72 74 let value = user_backend
73 75 .get_value(&object, &operation.value_name)
74 76 .await
75 .map_err(|e| OperationError::Internal(e.to_string()))?;
77 .as_internal_error()?;
76 78
77 79 Ok(value)
78 80 }
@@ -91,7 +93,7 @@ pub fn user_get_setting(
91 93 let value = user_backend
92 94 .get_setting(&object, &operation.setting_name)
93 95 .await
94 .map_err(|e| OperationError::Internal(e.to_string()))?;
96 .as_internal_error()?;
95 97
96 98 Ok(value.0)
97 99 }
@@ -125,7 +127,7 @@ pub fn repository_info(
125 127 },
126 128 )
127 129 .await
128 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
130 .as_internal_error()?;
129 131
130 132 let statistics = repository_backend
131 133 .repository_get_statistics(
@@ -136,7 +138,7 @@ pub fn repository_info(
136 138 },
137 139 )
138 140 .await
139 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
141 .as_internal_error()?;
140 142 drop(repository_backend);
141 143
142 144 let info = RepositoryView {
@@ -146,11 +148,11 @@ pub fn repository_info(
146 148 visibility: object
147 149 .get::<Visibility>(&operation_state)
148 150 .await
149 .map_err(|e| OperationError::Internal(format!("{:?}: {}", e.source(), e)))?,
151 .as_internal_error()?,
150 152 default_branch: object
151 153 .get::<DefaultBranch>(&operation_state)
152 154 .await
153 .map_err(|e| OperationError::Internal(format!("{:?}: {}", e.source(), e)))?,
155 .as_internal_error()?,
154 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.
155 157 latest_commit: object.get::<LatestCommit>(&operation_state).await.ok(),
156 158 stats: statistics,
@@ -187,7 +189,7 @@ pub fn repository_get_statistics(
187 189 &RepositoryStatisticsRequest { rev: operation.rev },
188 190 )
189 191 .await
190 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
192 .as_internal_error()?;
191 193 drop(repository_backend);
192 194
193 195 Ok(statistics)
@@ -215,7 +217,7 @@ pub fn repository_get_branches(
215 217 let branches = repository_backend
216 218 .repository_get_branches(&requester, object.object(), &operation)
217 219 .await
218 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
220 .as_internal_error()?;
219 221 drop(repository_backend);
220 222
221 223 Ok(branches)
@@ -248,7 +250,7 @@ pub fn repository_file_from_id(
248 250 &RepositoryFileFromIdRequest(operation.0),
249 251 )
250 252 .await
251 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
253 .as_internal_error()?;
252 254 drop(repository_backend);
253 255
254 256 Ok(file)
@@ -283,7 +285,7 @@ pub fn repository_file_from_path(
283 285 },
284 286 )
285 287 .await
286 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
288 .as_internal_error()?;
287 289 drop(repository_backend);
288 290
289 291 Ok(file)
@@ -318,7 +320,7 @@ pub fn repository_last_commit_of_file(
318 320 },
319 321 )
320 322 .await
321 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
323 .as_internal_error()?;
322 324 drop(repository_backend);
323 325
324 326 Ok(commit)
@@ -350,7 +352,7 @@ pub fn repository_commit_by_id(
350 352 &RepositoryCommitFromIdRequest(operation.0),
351 353 )
352 354 .await
353 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
355 .as_internal_error()?;
354 356 drop(repository_backend);
355 357
356 358 Ok(commit)
@@ -378,7 +380,7 @@ pub fn repository_diff(
378 380 let diff = repository_backend
379 381 .repository_diff(&requester, object.object(), &operation)
380 382 .await
381 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
383 .as_internal_error()?;
382 384 drop(repository_backend);
383 385
384 386 Ok(diff)
@@ -406,7 +408,7 @@ pub fn repository_diff_patch(
406 408 let patch = repository_backend
407 409 .repository_diff_patch(&requester, object.object(), &operation)
408 410 .await
409 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
411 .as_internal_error()?;
410 412 drop(repository_backend);
411 413
412 414 Ok(patch)
@@ -434,7 +436,7 @@ pub fn repository_commit_before(
434 436 let file = repository_backend
435 437 .repository_commit_before(&requester, object.object(), &operation)
436 438 .await
437 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
439 .as_internal_error()?;
438 440 drop(repository_backend);
439 441
440 442 Ok(file)
@@ -454,9 +456,7 @@ pub fn repository_get_value(
454 456 let value = repository_backend
455 457 .get_value(&object, &operation.value_name)
456 458 .await
457 .map_err(|e| {
458 OperationError::Internal(format!("error getting value: {}", e.to_string()))
459 })?;
459 .as_internal_error()?;
460 460
461 461 Ok(value)
462 462 }
@@ -475,7 +475,7 @@ pub fn repository_get_setting(
475 475 let value = repository_backend
476 476 .get_setting(&object, &operation.setting_name)
477 477 .await
478 .map_err(|e| OperationError::Internal(e.to_string()))?;
478 .as_internal_error()?;
479 479
480 480 Ok(value.0)
481 481 }
@@ -493,10 +493,7 @@ pub fn instance_authentication_request(
493 493 async move {
494 494 let mut backend = state.user_backend.lock().await;
495 495
496 backend
497 .login(&object, operation)
498 .await
499 .map_err(|e| OperationError::Internal(e.to_string()))
496 backend.login(&object, operation).await.as_internal_error()
500 497 }
501 498 .boxed_local()
502 499 }
@@ -511,10 +508,7 @@ pub fn instance_registration_request(
511 508 async move {
512 509 let mut backend = state.user_backend.lock().await;
513 510
514 backend
515 .register(operation)
516 .await
517 .map_err(|e| OperationError::Internal(e.to_string()))
511 backend.register(operation).await.as_internal_error()
518 512 }
519 513 .boxed_local()
520 514 }
@@ -533,7 +527,7 @@ pub fn instance_create_repository_request(
533 527 backend
534 528 .create_repository(&requester, &operation)
535 529 .await
536 .map_err(|e| OperationError::Internal(e.to_string()))
530 .as_internal_error()
537 531 }
538 532 .boxed_local()
539 533 }
@@ -552,10 +546,9 @@ pub fn user_get_value_display_name(
552 546 let raw_value = backend
553 547 .get_value(&object, &operation.value_name)
554 548 .await
555 .map_err(|e| OperationError::Internal(e.to_string()))?;
549 .as_internal_error()?;
556 550
557 Ok(serde_json::from_value(raw_value.into_inner())
558 .map_err(|e| OperationError::Internal(e.to_string()))?)
551 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
559 552 }
560 553 .boxed_local()
561 554 }
@@ -573,10 +566,9 @@ pub fn user_get_value_bio(
573 566 let raw_value = backend
574 567 .get_value(&object, &operation.value_name)
575 568 .await
576 .map_err(|e| OperationError::Internal(e.to_string()))?;
569 .as_internal_error()?;
577 570
578 Ok(serde_json::from_value(raw_value.into_inner())
579 .map_err(|e| OperationError::Internal(e.to_string()))?)
571 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
580 572 }
581 573 .boxed_local()
582 574 }
@@ -594,10 +586,9 @@ pub fn repository_get_value_description(
594 586 let raw_value = backend
595 587 .get_value(&object, &operation.value_name)
596 588 .await
597 .map_err(|e| OperationError::Internal(e.to_string()))?;
589 .as_internal_error()?;
598 590
599 Ok(serde_json::from_value(raw_value.into_inner())
600 .map_err(|e| OperationError::Internal(e.to_string()))?)
591 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
601 592 }
602 593 .boxed_local()
603 594 }
@@ -615,10 +606,9 @@ pub fn repository_get_value_visibility(
615 606 let raw_value = backend
616 607 .get_value(&object, &operation.value_name)
617 608 .await
618 .map_err(|e| OperationError::Internal(e.to_string()))?;
609 .as_internal_error()?;
619 610
620 Ok(serde_json::from_value(raw_value.into_inner())
621 .map_err(|e| OperationError::Internal(e.to_string()))?)
611 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
622 612 }
623 613 .boxed_local()
624 614 }
@@ -636,10 +626,9 @@ pub fn repository_get_default_branch(
636 626 let raw_value = backend
637 627 .get_value(&object, &operation.value_name)
638 628 .await
639 .map_err(|e| OperationError::Internal(e.to_string()))?;
629 .as_internal_error()?;
640 630
641 Ok(serde_json::from_value(raw_value.into_inner())
642 .map_err(|e| OperationError::Internal(e.to_string()))?)
631 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
643 632 }
644 633 .boxed_local()
645 634 }
@@ -657,10 +646,9 @@ pub fn repository_get_latest_commit(
657 646 let raw_value = backend
658 647 .get_value(&object, &operation.value_name)
659 648 .await
660 .map_err(|e| OperationError::Internal(e.to_string()))?;
649 .as_internal_error()?;
661 650
662 Ok(serde_json::from_value(raw_value.into_inner())
663 .map_err(|e| OperationError::Internal(e.to_string()))?)
651 Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?)
664 652 }
665 653 .boxed_local()
666 654 }

giterated-models/src/error.rs

View file
@@ -1,3 +1,5 @@
1 use std::fmt::Display;
2
1 3 use serde::{Deserialize, Serialize};
2 4
3 5 #[derive(Debug, thiserror::Error, Deserialize, Serialize)]
@@ -20,12 +22,76 @@ pub enum GetValueError {
20 22 InvalidObject,
21 23 }
22 24
23 #[derive(Serialize, Deserialize, Debug, thiserror::Error)]
25 #[derive(Debug, thiserror::Error)]
26 #[error("unauthorized")]
27 pub struct UnauthorizedError;
28
29 #[derive(Debug, thiserror::Error)]
24 30 pub enum OperationError<B> {
25 #[error("the operation was handled but an error occured")]
31 #[error("the operation was handled but an error occurred")]
32 Operation(#[from] B),
33 #[error("an internal error occurred: {0:?}")]
34 Internal(anyhow::Error),
35 #[error("the operation was unhandled or unrecognized")]
36 Unhandled,
37 }
38
39 pub trait IntoInternalError<T>: Sized {
40 fn as_internal_error<B>(self) -> Result<T, OperationError<B>>;
41
42 fn as_internal_error_with_context<B, C>(self, context: C) -> Result<T, OperationError<B>>
43 where
44 C: Display + Send + Sync + 'static,
45 {
46 let internal_error = self.as_internal_error::<B>();
47
48 match internal_error {
49 Ok(success) => Ok(success),
50 Err(OperationError::Internal(internal)) => {
51 Err(OperationError::Internal(internal.context(context)))
52 }
53 _ => unreachable!(),
54 }
55 }
56 }
57
58 impl<E: Into<anyhow::Error>, T> IntoInternalError<T> for Result<T, E> {
59 fn as_internal_error<B>(self) -> Result<T, OperationError<B>> {
60 match self {
61 Ok(success) => Ok(success),
62 Err(err) => Err(OperationError::Internal(err.into())),
63 }
64 }
65 }
66
67 impl<B> OperationError<B> {
68 pub fn into_network(self) -> NetworkOperationError<B> {
69 match self {
70 OperationError::Operation(operation_error) => {
71 NetworkOperationError::Operation(operation_error)
72 }
73 OperationError::Internal(_) => NetworkOperationError::Internal,
74 OperationError::Unhandled => NetworkOperationError::Unhandled,
75 }
76 }
77 }
78
79 #[derive(Debug, thiserror::Error)]
80 #[error("an extractor failed with an error {0}")]
81 pub struct ExtractorError<E: Into<anyhow::Error>>(#[from] pub E);
82
83 impl<T, E: Into<anyhow::Error>> IntoInternalError<T> for ExtractorError<E> {
84 fn as_internal_error<B>(self) -> Result<T, OperationError<B>> {
85 todo!()
86 }
87 }
88
89 #[derive(Serialize, Deserialize, Debug, thiserror::Error)]
90 pub enum NetworkOperationError<B> {
91 #[error("the operation was handled but an error occurred")]
26 92 Operation(#[from] B),
27 #[error("an internal error occured: {0}")]
28 Internal(String),
93 #[error("internal error")]
94 Internal,
29 95 #[error("the operation was unhandled or unrecognized")]
30 96 Unhandled,
31 97 }

giterated-stack/Cargo.toml

View file
@@ -13,4 +13,6 @@ serde_json = "1.0"
13 13 bincode = "1.3"
14 14 futures-util = "0.3"
15 15 tracing = "0.1"
16 tokio = { version = "1.32", features = [ "full" ] }
16 \ No newline at end of file
16 tokio = { version = "1.32", features = [ "full" ] }
17 anyhow = "1"
18 thiserror = "1"
18 \ No newline at end of file

giterated-stack/src/handler.rs

View file
@@ -1,9 +1,10 @@
1 1 use std::{any::Any, collections::HashMap, sync::Arc};
2 2
3 use anyhow::Context;
3 4 use futures_util::FutureExt;
4 5 use giterated_models::{
5 6 authenticated::AuthenticatedPayload,
6 error::{GetValueError, OperationError},
7 error::{GetValueError, IntoInternalError, OperationError},
7 8 instance::Instance,
8 9 message::GiteratedMessage,
9 10 object::{
@@ -451,9 +452,28 @@ impl GiteratedStack {
451 452 // Special case
452 453 let operation: GetValue = serde_json::from_slice(&message.payload.0).unwrap();
453 454
454 return self
455 .network_get_value(object, object_type.clone(), operation, operation_state)
455 let result = self
456 .network_get_value(
457 object,
458 object_type.clone(),
459 operation.clone(),
460 operation_state,
461 )
456 462 .await;
463
464 // In the case of internal errors, attach context
465 let result = result.map_err(|err| match err {
466 OperationError::Operation(operation) => OperationError::Operation(operation),
467 OperationError::Internal(internal) => {
468 OperationError::Internal(internal.context(format!(
469 "{}::get_value::<{}> outcome",
470 object_type, operation.value_name
471 )))
472 }
473 OperationError::Unhandled => OperationError::Unhandled,
474 });
475
476 return result;
457 477 } else if message.operation == "get_setting" {
458 478 let operation: GetSetting = serde_json::from_slice(&message.payload.0).unwrap();
459 479 let setting_meta = self
@@ -478,7 +498,12 @@ impl GiteratedStack {
478 498
479 499 OperationError::Operation(serde_json::to_vec(&failure).unwrap())
480 500 }
481 OperationError::Internal(internal) => OperationError::Internal(internal),
501 OperationError::Internal(internal) => {
502 OperationError::Internal(internal.context(format!(
503 "{}::get_setting::<{}> handler outcome",
504 object_type, setting_meta.name
505 )))
506 }
482 507 OperationError::Unhandled => OperationError::Unhandled,
483 508 }),
484 509 };
@@ -508,8 +533,11 @@ impl GiteratedStack {
508 533 .get(&target)
509 534 .ok_or_else(|| OperationError::Unhandled)?;
510 535
511 let operation = (meta.deserialize)(&message.payload.0)
512 .map_err(|e| OperationError::Internal(e.to_string()))?;
536 let operation =
537 (meta.deserialize)(&message.payload.0).as_internal_error_with_context(format!(
538 "deserializing operation {}::{}",
539 target.object_name, target.operation_name
540 ))?;
513 541
514 542 trace!(
515 543 "Deserialized operation for network message {}::<{}>",
@@ -534,14 +562,17 @@ impl GiteratedStack {
534 562
535 563 // Deserialize the raw result for the network
536 564 match raw_result {
537 Ok(success) => Ok((meta.serialize_success)(success)
538 .map_err(|e| OperationError::Internal(e.to_string()))?),
565 Ok(success) => Ok((meta.serialize_success)(success).as_internal_error()?),
539 566 Err(err) => Err(match err {
540 OperationError::Operation(failure) => OperationError::Operation(
541 (meta.serialize_error)(failure)
542 .map_err(|e| OperationError::Internal(e.to_string()))?,
543 ),
544 OperationError::Internal(internal) => OperationError::Internal(internal),
567 OperationError::Operation(failure) => {
568 OperationError::Operation((meta.serialize_error)(failure).as_internal_error()?)
569 }
570 OperationError::Internal(internal) => {
571 OperationError::Internal(internal.context(format!(
572 "operation {}::{} handler outcome",
573 target.object_name, target.operation_name
574 )))
575 }
545 576 OperationError::Unhandled => OperationError::Unhandled,
546 577 }),
547 578 }
@@ -580,8 +611,7 @@ impl GiteratedStack {
580 611 {
581 612 Ok(success) => {
582 613 // Serialize success, which is the value type itself
583 let serialized = (value_meta.serialize)(success)
584 .map_err(|e| OperationError::Internal(e.to_string()))?;
614 let serialized = (value_meta.serialize)(success).as_internal_error()?;
585 615
586 616 Ok(serialized)
587 617 }
@@ -590,10 +620,7 @@ impl GiteratedStack {
590 620 // Failure is sourced from GetValue operation, but this is hardcoded for now
591 621 let failure: GetValueError = *failure.downcast().unwrap();
592 622
593 OperationError::Operation(
594 serde_json::to_vec(&failure)
595 .map_err(|e| OperationError::Internal(e.to_string()))?,
596 )
623 OperationError::Operation(serde_json::to_vec(&failure).as_internal_error()?)
597 624 }
598 625 OperationError::Internal(internal) => OperationError::Internal(internal),
599 626 OperationError::Unhandled => OperationError::Unhandled,
@@ -693,7 +720,13 @@ impl ObjectBackend<StackOperationState> for Arc<GiteratedStack> {
693 720 OperationError::Operation(failure) => {
694 721 OperationError::Operation(*failure.downcast::<D::Failure>().unwrap())
695 722 }
696 OperationError::Internal(internal) => OperationError::Internal(internal),
723 OperationError::Internal(internal) => {
724 OperationError::Internal(internal.context(format!(
725 "{}::get_value::<{}> handler outcome",
726 O::object_name(),
727 value_name
728 )))
729 }
697 730 OperationError::Unhandled => OperationError::Unhandled,
698 731 }),
699 732 };
@@ -702,6 +735,7 @@ impl ObjectBackend<StackOperationState> for Arc<GiteratedStack> {
702 735 return Err(OperationError::Unhandled);
703 736 } else if operation.is::<GetSetting>() {
704 737 let get_setting: Box<GetSetting> = operation.downcast().unwrap();
738 let setting_name = get_setting.setting_name.clone();
705 739
706 740 let raw_result = self
707 741 .get_setting(
@@ -725,7 +759,13 @@ impl ObjectBackend<StackOperationState> for Arc<GiteratedStack> {
725 759 // We know this is the right type
726 760 OperationError::Operation(*failure.downcast().unwrap())
727 761 }
728 OperationError::Internal(internal) => OperationError::Internal(internal),
762 OperationError::Internal(internal) => {
763 OperationError::Internal(internal.context(format!(
764 "{}::get_setting::<{}> handler outcome",
765 O::object_name(),
766 setting_name
767 )))
768 }
729 769 OperationError::Unhandled => OperationError::Unhandled,
730 770 }),
731 771 };
@@ -773,7 +813,12 @@ impl ObjectBackend<StackOperationState> for Arc<GiteratedStack> {
773 813 match raw_result {
774 814 Ok(result) => Ok(*result.downcast::<D::Success>().unwrap()),
775 815 Err(err) => Err(match err {
776 OperationError::Internal(internal) => OperationError::Internal(internal),
816 OperationError::Internal(internal) => {
817 OperationError::Internal(internal.context(format!(
818 "operation {}::{} handler outcome",
819 operation_type.object_name, operation_type.operation_name
820 )))
821 }
777 822 OperationError::Operation(boxed_error) => {
778 823 OperationError::Operation(*boxed_error.downcast::<D::Failure>().unwrap())
779 824 }

giterated-stack/src/lib.rs

View file
@@ -5,12 +5,12 @@ pub use meta::*;
5 5 pub mod state;
6 6 pub mod update;
7 7
8 use std::{any::Any, future::Future, ops::Deref, pin::Pin, sync::Arc};
8 use std::{any::Any, convert::Infallible, future::Future, ops::Deref, pin::Pin, sync::Arc};
9 9
10 10 use core::fmt::Debug;
11 11 use futures_util::FutureExt;
12 12 use giterated_models::{
13 error::OperationError,
13 error::{ExtractorError, IntoInternalError, OperationError, UnauthorizedError},
14 14 instance::{
15 15 AuthenticationTokenRequest, Instance, RegisterAccountRequest, RepositoryCreateRequest,
16 16 },
@@ -22,7 +22,6 @@ use giterated_models::{
22 22 user::User,
23 23 value::GetValue,
24 24 };
25 use serde::{de::DeserializeOwned, Serialize};
26 25
27 26 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
28 27 struct ObjectOperationPair {
@@ -110,6 +109,7 @@ where
110 109 <D as GiteratedOperation<O>>::Failure: Send,
111 110 S: Send + Sync + Clone + 'static,
112 111 O1: FromOperationState<O, D>,
112 ExtractorError<<O1 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
113 113 {
114 114 fn operation_name(&self) -> &str {
115 115 D::operation_name()
@@ -128,7 +128,7 @@ where
128 128 ) -> Result<D::Success, OperationError<D::Failure>> {
129 129 let o1 = O1::from_state(object, &operation, operation_state)
130 130 .await
131 .map_err(|e| OperationError::Internal(e.to_string()))?;
131 .as_internal_error()?;
132 132 self.clone()(object, operation, state, o1).await
133 133 }
134 134 }
@@ -152,7 +152,9 @@ where
152 152 <D as GiteratedOperation<O>>::Failure: Send,
153 153 S: Send + Sync + Clone + 'static,
154 154 O1: FromOperationState<O, D>,
155 ExtractorError<<O1 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
155 156 O2: FromOperationState<O, D>,
157 ExtractorError<<O2 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
156 158 {
157 159 fn operation_name(&self) -> &str {
158 160 D::operation_name()
@@ -171,10 +173,10 @@ where
171 173 ) -> Result<D::Success, OperationError<D::Failure>> {
172 174 let o1 = O1::from_state(object, &operation, operation_state)
173 175 .await
174 .map_err(|e| OperationError::Internal(e.to_string()))?;
176 .as_internal_error()?;
175 177 let o2 = O2::from_state(object, &operation, operation_state)
176 178 .await
177 .map_err(|e| OperationError::Internal(e.to_string()))?;
179 .as_internal_error()?;
178 180 self.clone()(object, operation, state, o1, o2).await
179 181 }
180 182 }
@@ -199,8 +201,11 @@ where
199 201 <D as GiteratedOperation<O>>::Failure: Send,
200 202 S: Send + Sync + Clone + 'static,
201 203 O1: FromOperationState<O, D>,
204 ExtractorError<<O1 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
202 205 O2: FromOperationState<O, D>,
206 ExtractorError<<O2 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
203 207 O3: FromOperationState<O, D>,
208 ExtractorError<<O3 as FromOperationState<O, D>>::Error>: Into<anyhow::Error>,
204 209 {
205 210 fn operation_name(&self) -> &str {
206 211 D::operation_name()
@@ -219,13 +224,13 @@ where
219 224 ) -> Result<D::Success, OperationError<D::Failure>> {
220 225 let o1 = O1::from_state(object, &operation, operation_state)
221 226 .await
222 .map_err(|e| OperationError::Internal(e.to_string()))?;
227 .as_internal_error()?;
223 228 let o2 = O2::from_state(object, &operation, operation_state)
224 229 .await
225 .map_err(|e| OperationError::Internal(e.to_string()))?;
230 .as_internal_error()?;
226 231 let o3 = O3::from_state(object, &operation, operation_state)
227 232 .await
228 .map_err(|e| OperationError::Internal(e.to_string()))?;
233 .as_internal_error()?;
229 234 self.clone()(object, operation, state, o1, o2, o3).await
230 235 }
231 236 }
@@ -315,26 +320,26 @@ impl OperationWrapper {
315 320
316 321 #[async_trait::async_trait(?Send)]
317 322 pub trait FromOperationState<O: GiteratedObject, D: GiteratedOperation<O>>: Sized + Clone {
318 type Error: Serialize + DeserializeOwned;
323 type Error: Into<anyhow::Error>;
319 324
320 325 async fn from_state(
321 326 object: &O,
322 327 operation: &D,
323 328 state: &StackOperationState,
324 ) -> Result<Self, OperationError<Self::Error>>;
329 ) -> Result<Self, ExtractorError<Self::Error>>;
325 330 }
326 331
327 332 #[async_trait::async_trait(?Send)]
328 333 impl<O: GiteratedObject, D: GiteratedOperation<O>> FromOperationState<O, D>
329 334 for Arc<GiteratedStack>
330 335 {
331 type Error = ();
336 type Error = Infallible;
332 337
333 338 async fn from_state(
334 339 _object: &O,
335 340 _operation: &D,
336 341 state: &StackOperationState,
337 ) -> Result<Self, OperationError<()>> {
342 ) -> Result<Self, ExtractorError<Infallible>> {
338 343 Ok(state.runtime.clone())
339 344 }
340 345 }
@@ -343,32 +348,36 @@ impl<O: GiteratedObject, D: GiteratedOperation<O>> FromOperationState<O, D>
343 348 impl<O: GiteratedObject, D: GiteratedOperation<O>> FromOperationState<O, D>
344 349 for StackOperationState
345 350 {
346 type Error = ();
351 type Error = Infallible;
347 352
348 353 async fn from_state(
349 354 _object: &O,
350 355 _operation: &D,
351 356 state: &StackOperationState,
352 ) -> Result<StackOperationState, OperationError<()>> {
357 ) -> Result<StackOperationState, ExtractorError<Infallible>> {
353 358 Ok(state.clone())
354 359 }
355 360 }
356 361
362 #[derive(Debug, thiserror::Error)]
363 #[error("missing value")]
364 pub struct MissingValue;
365
357 366 #[async_trait::async_trait(?Send)]
358 367 impl<O: GiteratedObject, D: GiteratedOperation<O> + Send + Sync> FromOperationState<O, D>
359 368 for AuthenticatedUser
360 369 {
361 type Error = ();
370 type Error = MissingValue;
362 371
363 372 async fn from_state(
364 373 _object: &O,
365 374 _operation: &D,
366 375 state: &StackOperationState,
367 ) -> Result<AuthenticatedUser, OperationError<()>> {
376 ) -> Result<AuthenticatedUser, ExtractorError<MissingValue>> {
368 377 state
369 378 .user
370 379 .clone()
371 .ok_or_else(|| OperationError::Operation(()))
380 .ok_or_else(|| ExtractorError(MissingValue))
372 381 }
373 382 }
374 383
@@ -376,17 +385,17 @@ impl<O: GiteratedObject, D: GiteratedOperation<O> + Send + Sync> FromOperationSt
376 385 impl<O: GiteratedObject, D: GiteratedOperation<O> + Send + Sync> FromOperationState<O, D>
377 386 for AuthenticatedInstance
378 387 {
379 type Error = ();
388 type Error = MissingValue;
380 389
381 390 async fn from_state(
382 391 _object: &O,
383 392 _operation: &D,
384 393 state: &StackOperationState,
385 ) -> Result<AuthenticatedInstance, OperationError<()>> {
394 ) -> Result<AuthenticatedInstance, ExtractorError<MissingValue>> {
386 395 state
387 396 .instance
388 397 .clone()
389 .ok_or_else(|| OperationError::Operation(()))
398 .ok_or_else(|| ExtractorError(MissingValue))
390 399 }
391 400 }
392 401
@@ -397,13 +406,13 @@ impl<
397 406 D: GiteratedOperation<O> + Send + Sync,
398 407 > FromOperationState<O, D> for Option<T>
399 408 {
400 type Error = ();
409 type Error = Infallible;
401 410
402 411 async fn from_state(
403 412 object: &O,
404 413 operation: &D,
405 414 state: &StackOperationState,
406 ) -> Result<Option<T>, OperationError<()>> {
415 ) -> Result<Option<T>, ExtractorError<Infallible>> {
407 416 Ok(T::from_state(object, operation, state).await.ok())
408 417 }
409 418 }
@@ -416,20 +425,24 @@ pub struct AuthorizedInstance(AuthenticatedInstance);
416 425
417 426 #[async_trait::async_trait(?Send)]
418 427 pub trait AuthorizedOperation<O: GiteratedObject>: GiteratedOperation<O> {
428 type Error: Into<anyhow::Error>;
429
419 430 async fn authorize(
420 431 &self,
421 432 authorize_for: &O,
422 433 state: &StackOperationState,
423 ) -> Result<bool, OperationError<()>>;
434 ) -> Result<bool, ExtractorError<Self::Error>>;
424 435 }
425 436
426 437 #[async_trait::async_trait(?Send)]
427 438 impl<O: GiteratedObject + Send + Sync + Debug + 'static> AuthorizedOperation<O> for GetValue {
439 type Error = anyhow::Error;
440
428 441 async fn authorize(
429 442 &self,
430 443 authorize_for: &O,
431 444 operation_state: &StackOperationState,
432 ) -> Result<bool, OperationError<()>> {
445 ) -> Result<bool, ExtractorError<anyhow::Error>> {
433 446 Ok(operation_state
434 447 .runtime
435 448 .get_object::<O>(&authorize_for.to_string(), operation_state)
@@ -440,15 +453,14 @@ impl<O: GiteratedObject + Send + Sync + Debug + 'static> AuthorizedOperation<O>
440 453
441 454 #[async_trait::async_trait(?Send)]
442 455 impl AuthorizedOperation<User> for SetSetting {
456 type Error = MissingValue;
457
443 458 async fn authorize(
444 459 &self,
445 460 authorize_for: &User,
446 461 operation_state: &StackOperationState,
447 ) -> Result<bool, OperationError<()>> {
448 let authenticated_user = operation_state
449 .user
450 .as_ref()
451 .ok_or_else(|| OperationError::Operation(()))?;
462 ) -> Result<bool, ExtractorError<MissingValue>> {
463 let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?;
452 464
453 465 Ok(authorize_for == authenticated_user.deref())
454 466 }
@@ -456,15 +468,14 @@ impl AuthorizedOperation<User> for SetSetting {
456 468
457 469 #[async_trait::async_trait(?Send)]
458 470 impl AuthorizedOperation<User> for GetSetting {
471 type Error = MissingValue;
472
459 473 async fn authorize(
460 474 &self,
461 475 authorize_for: &User,
462 476 operation_state: &StackOperationState,
463 ) -> Result<bool, OperationError<()>> {
464 let authenticated_user = operation_state
465 .user
466 .as_ref()
467 .ok_or_else(|| OperationError::Operation(()))?;
477 ) -> Result<bool, ExtractorError<MissingValue>> {
478 let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?;
468 479
469 480 Ok(authorize_for == authenticated_user.deref())
470 481 }
@@ -472,26 +483,28 @@ impl AuthorizedOperation<User> for GetSetting {
472 483
473 484 #[async_trait::async_trait(?Send)]
474 485 impl AuthorizedOperation<Repository> for SetSetting {
486 type Error = anyhow::Error;
487
475 488 async fn authorize(
476 489 &self,
477 490 authorize_for: &Repository,
478 491 operation_state: &StackOperationState,
479 ) -> Result<bool, OperationError<()>> {
492 ) -> Result<bool, ExtractorError<anyhow::Error>> {
480 493 let authenticated_user = operation_state
481 494 .user
482 495 .as_ref()
483 .ok_or_else(|| OperationError::Operation(()))?;
496 .ok_or_else(|| anyhow::Error::from(MissingValue))?;
484 497
485 498 let mut object = operation_state
486 499 .runtime
487 500 .get_object::<Repository>(&authorize_for.to_string(), operation_state)
488 501 .await
489 .map_err(|e| OperationError::Internal(e.to_string()))?;
502 .map_err(|err| anyhow::Error::from(err))?;
490 503
491 504 let access_list = object
492 505 .get_setting::<AccessList>(operation_state)
493 506 .await
494 .map_err(|e| OperationError::Internal(e.to_string()))?;
507 .map_err(|err| anyhow::Error::from(err))?;
495 508
496 509 if access_list
497 510 .0
@@ -508,26 +521,28 @@ impl AuthorizedOperation<Repository> for SetSetting {
508 521
509 522 #[async_trait::async_trait(?Send)]
510 523 impl AuthorizedOperation<Repository> for GetSetting {
524 type Error = anyhow::Error;
525
511 526 async fn authorize(
512 527 &self,
513 528 authorize_for: &Repository,
514 529 operation_state: &StackOperationState,
515 ) -> Result<bool, OperationError<()>> {
530 ) -> Result<bool, ExtractorError<anyhow::Error>> {
516 531 let authenticated_user = operation_state
517 532 .user
518 533 .as_ref()
519 .ok_or_else(|| OperationError::Operation(()))?;
534 .ok_or_else(|| anyhow::Error::from(MissingValue))?;
520 535
521 536 let mut object = operation_state
522 537 .runtime
523 538 .get_object::<Repository>(&authorize_for.to_string(), operation_state)
524 539 .await
525 .map_err(|e| OperationError::Internal(e.to_string()))?;
540 .map_err(|err| anyhow::Error::from(err))?;
526 541
527 542 let access_list = object
528 543 .get_setting::<AccessList>(operation_state)
529 544 .await
530 .map_err(|e| OperationError::Internal(e.to_string()))?;
545 .map_err(|err| anyhow::Error::from(err))?;
531 546
532 547 if access_list
533 548 .0
@@ -544,11 +559,13 @@ impl AuthorizedOperation<Repository> for GetSetting {
544 559
545 560 #[async_trait::async_trait(?Send)]
546 561 impl AuthorizedOperation<Instance> for RegisterAccountRequest {
562 type Error = Infallible;
563
547 564 async fn authorize(
548 565 &self,
549 566 authorize_for: &Instance,
550 567 state: &StackOperationState,
551 ) -> Result<bool, OperationError<()>> {
568 ) -> Result<bool, ExtractorError<Infallible>> {
552 569 if state.our_instance == *authorize_for {
553 570 Ok(true)
554 571 } else {
@@ -559,11 +576,13 @@ impl AuthorizedOperation<Instance> for RegisterAccountRequest {
559 576
560 577 #[async_trait::async_trait(?Send)]
561 578 impl AuthorizedOperation<Instance> for AuthenticationTokenRequest {
579 type Error = Infallible;
580
562 581 async fn authorize(
563 582 &self,
564 583 authorize_for: &Instance,
565 584 state: &StackOperationState,
566 ) -> Result<bool, OperationError<()>> {
585 ) -> Result<bool, ExtractorError<Infallible>> {
567 586 if state.our_instance == *authorize_for {
568 587 Ok(true)
569 588 } else {
@@ -574,11 +593,13 @@ impl AuthorizedOperation<Instance> for AuthenticationTokenRequest {
574 593
575 594 #[async_trait::async_trait(?Send)]
576 595 impl AuthorizedOperation<Instance> for RepositoryCreateRequest {
596 type Error = Infallible;
597
577 598 async fn authorize(
578 599 &self,
579 600 authorize_for: &Instance,
580 601 state: &StackOperationState,
581 ) -> Result<bool, OperationError<()>> {
602 ) -> Result<bool, ExtractorError<Infallible>> {
582 603 if state.our_instance == *authorize_for {
583 604 Ok(true)
584 605 } else {
@@ -589,20 +610,23 @@ impl AuthorizedOperation<Instance> for RepositoryCreateRequest {
589 610
590 611 #[async_trait::async_trait(?Send)]
591 612 impl<A: AuthorizedOperation<User> + Send + Sync> FromOperationState<User, A> for AuthorizedUser {
592 type Error = ();
613 type Error = UnauthorizedError;
593 614
594 615 async fn from_state(
595 616 object: &User,
596 617 operation: &A,
597 618 state: &StackOperationState,
598 ) -> Result<AuthorizedUser, OperationError<()>> {
599 let authenticated = AuthenticatedUser::from_state(object, operation, state).await?;
619 ) -> Result<AuthorizedUser, ExtractorError<UnauthorizedError>> {
620 // TODO
621 let authenticated = AuthenticatedUser::from_state(object, operation, state)
622 .await
623 .map_err(|_| ExtractorError(UnauthorizedError))?;
600 624
601 625 match operation.authorize(object, state).await {
602 626 Ok(authorized) => {
603 627 assert!(authorized);
604 628 }
605 Err(err) => return Err(OperationError::Internal(err.to_string())),
629 Err(_err) => return Err(ExtractorError(UnauthorizedError)),
606 630 };
607 631
608 632 Ok(AuthorizedUser(authenticated))
@@ -613,20 +637,23 @@ impl<A: AuthorizedOperation<User> + Send + Sync> FromOperationState<User, A> for
613 637 impl<A: AuthorizedOperation<Instance> + Send + Sync> FromOperationState<Instance, A>
614 638 for AuthorizedInstance
615 639 {
616 type Error = ();
640 type Error = UnauthorizedError;
617 641
618 642 async fn from_state(
619 643 object: &Instance,
620 644 operation: &A,
621 645 state: &StackOperationState,
622 ) -> Result<AuthorizedInstance, OperationError<()>> {
623 let authenticated = AuthenticatedInstance::from_state(object, operation, state).await?;
646 ) -> Result<AuthorizedInstance, ExtractorError<UnauthorizedError>> {
647 //TODO
648 let authenticated = AuthenticatedInstance::from_state(object, operation, state)
649 .await
650 .map_err(|_| ExtractorError(UnauthorizedError))?;
624 651
625 652 match operation.authorize(object, state).await {
626 653 Ok(authorized) => {
627 654 assert!(authorized);
628 655 }
629 Err(err) => return Err(OperationError::Internal(err.to_string())),
656 Err(_err) => return Err(ExtractorError(UnauthorizedError)),
630 657 };
631 658
632 659 Ok(AuthorizedInstance(authenticated))