Skip to main content

tsuzuri_dynamodb/store/
error.rs

1use ::serde::de::StdError;
2use aws_sdk_dynamodb::{
3    error::SdkError,
4    operation::{query::QueryError, scan::ScanError, transact_write_items::TransactWriteItemsError},
5};
6use tsuzuri::{error::AggregateError, persist::PersistenceError};
7
8#[derive(Debug, thiserror::Error)]
9pub enum DynamoAggregateError {
10    #[error("optimistic lock error")]
11    OptimisticLock,
12    #[error("Too many operations: {0}, DynamoDb supports only up to 25 operations per transactions")]
13    TransactionListTooLong(usize),
14    #[error("missing attribute: {0}")]
15    MissingAttribute(String),
16    #[error("builder error: {0}")]
17    BuilderError(String),
18    #[error(transparent)]
19    UnknownError(Box<dyn std::error::Error + Send + Sync + 'static>),
20}
21
22impl<T: std::error::Error> From<DynamoAggregateError> for AggregateError<T> {
23    fn from(error: DynamoAggregateError) -> Self {
24        match error {
25            DynamoAggregateError::OptimisticLock => Self::AggregateConflict,
26            // DynamoAggregateError::ConnectionError(err) => Self::DatabaseConnectionError(err),
27            // DynamoAggregateError::DeserializationError(err) => Self::DeserializationError(err),
28            DynamoAggregateError::TransactionListTooLong(_) => Self::UnexpectedError(Box::new(error)),
29            DynamoAggregateError::MissingAttribute(err) => {
30                Self::UnexpectedError(Box::new(DynamoAggregateError::MissingAttribute(err)))
31            }
32            DynamoAggregateError::BuilderError(err) => {
33                Self::UnexpectedError(Box::new(DynamoAggregateError::BuilderError(err)))
34            }
35            DynamoAggregateError::UnknownError(err) => Self::UnexpectedError(err),
36        }
37    }
38}
39
40impl From<serde_json::Error> for DynamoAggregateError {
41    fn from(err: serde_json::Error) -> Self {
42        Self::UnknownError(Box::new(err))
43    }
44}
45
46impl From<SdkError<TransactWriteItemsError>> for DynamoAggregateError {
47    fn from(error: SdkError<TransactWriteItemsError>) -> Self {
48        if let SdkError::ServiceError(err) = &error {
49            if let TransactWriteItemsError::TransactionCanceledException(cancellation) = err.err() {
50                for reason in cancellation.cancellation_reasons() {
51                    if reason.code() == Some("ConditionalCheckFailed") {
52                        return Self::OptimisticLock;
53                    }
54                }
55            }
56        }
57        Self::UnknownError(Box::new(error))
58    }
59}
60
61impl From<SdkError<QueryError>> for DynamoAggregateError {
62    fn from(error: SdkError<QueryError>) -> Self {
63        unknown_error(error)
64    }
65}
66
67impl From<SdkError<ScanError>> for DynamoAggregateError {
68    fn from(error: SdkError<ScanError>) -> Self {
69        unknown_error(error)
70    }
71}
72
73fn unknown_error<T: StdError + Send + Sync + 'static>(error: SdkError<T>) -> DynamoAggregateError {
74    DynamoAggregateError::UnknownError(Box::new(error))
75}
76
77impl From<DynamoAggregateError> for PersistenceError {
78    fn from(error: DynamoAggregateError) -> Self {
79        match error {
80            DynamoAggregateError::OptimisticLock => Self::OptimisticLockError,
81            // DynamoAggregateError::ConnectionError(err) => Self::ConnectionError(err),
82            // DynamoAggregateError::DeserializationError(err) => Self::DeserializationError(err),
83            DynamoAggregateError::TransactionListTooLong(_) => Self::UnknownError(Box::new(error)),
84            DynamoAggregateError::MissingAttribute(err) => {
85                Self::UnknownError(Box::new(DynamoAggregateError::MissingAttribute(err)))
86            }
87            DynamoAggregateError::BuilderError(err) => {
88                Self::UnknownError(Box::new(DynamoAggregateError::BuilderError(err)))
89            }
90            DynamoAggregateError::UnknownError(err) => Self::UnknownError(err),
91        }
92    }
93}