openzeppelin_relayer/models/
app_state.rs

1//! This module defines the `AppState` struct, which encapsulates the application's state.
2//! It includes various repositories and services necessary for the application's operation.
3//! The `AppState` provides methods to access these components in a thread-safe manner.
4use std::sync::Arc;
5
6use actix_web::web::ThinData;
7
8use crate::{
9    jobs::{JobProducer, JobProducerTrait},
10    models::{
11        NetworkRepoModel, NotificationRepoModel, RelayerRepoModel, SignerRepoModel,
12        TransactionRepoModel,
13    },
14    repositories::{
15        ApiKeyRepositoryStorage, ApiKeyRepositoryTrait, NetworkRepository,
16        NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage,
17        PluginRepositoryTrait, RelayerRepository, RelayerRepositoryStorage, Repository,
18        SignerRepositoryStorage, TransactionCounterRepositoryStorage, TransactionCounterTrait,
19        TransactionRepository, TransactionRepositoryStorage,
20    },
21};
22
23/// Represents the application state, holding various repositories and services
24/// required for the application's operation.
25#[derive(Debug)]
26pub struct AppState<
27    J: JobProducerTrait + Send + Sync + 'static,
28    RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
29    TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
30    NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
31    NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
32    SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
33    TCR: TransactionCounterTrait + Send + Sync + 'static,
34    PR: PluginRepositoryTrait + Send + Sync + 'static,
35    AKR: ApiKeyRepositoryTrait + Send + Sync + 'static,
36> {
37    /// Repository for managing relayer data.
38    pub relayer_repository: Arc<RR>,
39    /// Repository for managing transaction data.
40    pub transaction_repository: Arc<TR>,
41    /// Repository for managing signer data.
42    pub signer_repository: Arc<SR>,
43    /// Repository for managing notification data.
44    pub notification_repository: Arc<NFR>,
45    /// Repository for managing network data.
46    pub network_repository: Arc<NR>,
47    /// Store for managing transaction counters.
48    pub transaction_counter_store: Arc<TCR>,
49    /// Producer for managing job creation and execution.
50    pub job_producer: Arc<J>,
51    /// Repository for managing plugins.
52    pub plugin_repository: Arc<PR>,
53    /// Repository for managing api keys.
54    pub api_key_repository: Arc<AKR>,
55}
56
57// Manual Clone implementation since all fields are Arc (cheap to clone)
58impl<J, RR, TR, NR, NFR, SR, TCR, PR, AKR> Clone for AppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>
59where
60    J: JobProducerTrait + Send + Sync + 'static,
61    RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
62    TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
63    NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
64    NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
65    SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
66    TCR: TransactionCounterTrait + Send + Sync + 'static,
67    PR: PluginRepositoryTrait + Send + Sync + 'static,
68    AKR: ApiKeyRepositoryTrait + Send + Sync + 'static,
69{
70    fn clone(&self) -> Self {
71        Self {
72            job_producer: Arc::clone(&self.job_producer),
73            relayer_repository: Arc::clone(&self.relayer_repository),
74            transaction_repository: Arc::clone(&self.transaction_repository),
75            signer_repository: Arc::clone(&self.signer_repository),
76            notification_repository: Arc::clone(&self.notification_repository),
77            network_repository: Arc::clone(&self.network_repository),
78            transaction_counter_store: Arc::clone(&self.transaction_counter_store),
79            plugin_repository: Arc::clone(&self.plugin_repository),
80            api_key_repository: Arc::clone(&self.api_key_repository),
81        }
82    }
83}
84
85/// type alias for the app state wrapped in a ThinData to avoid clippy warnings
86pub type ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR> =
87    ThinData<AppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>>;
88
89pub type DefaultAppState = AppState<
90    JobProducer,
91    RelayerRepositoryStorage,
92    TransactionRepositoryStorage,
93    NetworkRepositoryStorage,
94    NotificationRepositoryStorage,
95    SignerRepositoryStorage,
96    TransactionCounterRepositoryStorage,
97    PluginRepositoryStorage,
98    ApiKeyRepositoryStorage,
99>;
100
101impl<
102        J: JobProducerTrait,
103        RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
104        TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
105        NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
106        NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
107        SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
108        TCR: TransactionCounterTrait + Send + Sync + 'static,
109        PR: PluginRepositoryTrait + Send + Sync + 'static,
110        AKR: ApiKeyRepositoryTrait + Send + Sync + 'static,
111    > AppState<J, RR, TR, NR, NFR, SR, TCR, PR, AKR>
112{
113    /// Returns a clone of the relayer repository.
114    ///
115    /// # Returns
116    ///
117    /// An `Arc` pointing to the `RelayerRepositoryStorage`.
118    pub fn relayer_repository(&self) -> Arc<RR> {
119        self.relayer_repository.clone()
120    }
121
122    /// Returns a clone of the transaction repository.
123    ///
124    /// # Returns
125    ///
126    /// An `Arc` pointing to the `Arc<TransactionRepositoryStorage>`.
127    pub fn transaction_repository(&self) -> Arc<TR> {
128        Arc::clone(&self.transaction_repository)
129    }
130
131    /// Returns a clone of the signer repository.
132    ///
133    /// # Returns
134    ///
135    /// An `Arc` pointing to the `InMemorySignerRepository`.
136    pub fn signer_repository(&self) -> Arc<SR> {
137        Arc::clone(&self.signer_repository)
138    }
139
140    /// Returns a clone of the notification repository.
141    ///
142    /// # Returns
143    ///
144    /// An `Arc` pointing to the `InMemoryNotificationRepository`.
145    pub fn notification_repository(&self) -> Arc<NFR> {
146        Arc::clone(&self.notification_repository)
147    }
148
149    /// Returns a clone of the network repository.
150    ///
151    /// # Returns
152    ///
153    /// An `Arc` pointing to the `InMemoryNetworkRepository`.
154    pub fn network_repository(&self) -> Arc<NR> {
155        Arc::clone(&self.network_repository)
156    }
157
158    /// Returns a clone of the transaction counter store.
159    ///
160    /// # Returns
161    ///
162    /// An `Arc` pointing to the `InMemoryTransactionCounter`.
163    pub fn transaction_counter_store(&self) -> Arc<TCR> {
164        Arc::clone(&self.transaction_counter_store)
165    }
166
167    /// Returns a clone of the job producer.
168    ///
169    /// # Returns
170    ///
171    /// An `Arc` pointing to the `JobProducer`.
172    pub fn job_producer(&self) -> Arc<J> {
173        Arc::clone(&self.job_producer)
174    }
175
176    /// Returns a clone of the plugin repository.
177    ///
178    /// # Returns
179    ///
180    /// An `Arc` pointing to the `InMemoryPluginRepository`.
181    pub fn plugin_repository(&self) -> Arc<PR> {
182        Arc::clone(&self.plugin_repository)
183    }
184
185    /// Returns a clone of the api key repository.
186    ///
187    /// # Returns
188    ///
189    /// An `Arc` pointing to the `InMemoryApiKeyRepository`.
190    pub fn api_key_repository(&self) -> Arc<AKR> {
191        Arc::clone(&self.api_key_repository)
192    }
193}
194
195#[cfg(test)]
196mod tests {
197    use crate::{jobs::MockJobProducerTrait, repositories::TransactionRepositoryStorage};
198
199    use super::*;
200    use std::sync::Arc;
201
202    fn create_test_app_state() -> AppState<
203        MockJobProducerTrait,
204        RelayerRepositoryStorage,
205        TransactionRepositoryStorage,
206        NetworkRepositoryStorage,
207        NotificationRepositoryStorage,
208        SignerRepositoryStorage,
209        TransactionCounterRepositoryStorage,
210        PluginRepositoryStorage,
211        ApiKeyRepositoryStorage,
212    > {
213        // Create a mock job producer
214        let mut mock_job_producer = MockJobProducerTrait::new();
215
216        // Set up expectations for the mock
217        mock_job_producer
218            .expect_produce_transaction_request_job()
219            .returning(|_, _| Box::pin(async { Ok(()) }));
220
221        mock_job_producer
222            .expect_produce_submit_transaction_job()
223            .returning(|_, _| Box::pin(async { Ok(()) }));
224
225        mock_job_producer
226            .expect_produce_check_transaction_status_job()
227            .returning(|_, _| Box::pin(async { Ok(()) }));
228
229        mock_job_producer
230            .expect_produce_send_notification_job()
231            .returning(|_, _| Box::pin(async { Ok(()) }));
232
233        AppState {
234            relayer_repository: Arc::new(RelayerRepositoryStorage::new_in_memory()),
235            transaction_repository: Arc::new(TransactionRepositoryStorage::new_in_memory()),
236            signer_repository: Arc::new(SignerRepositoryStorage::new_in_memory()),
237            notification_repository: Arc::new(NotificationRepositoryStorage::new_in_memory()),
238            network_repository: Arc::new(NetworkRepositoryStorage::new_in_memory()),
239            transaction_counter_store: Arc::new(
240                TransactionCounterRepositoryStorage::new_in_memory(),
241            ),
242            job_producer: Arc::new(mock_job_producer),
243            plugin_repository: Arc::new(PluginRepositoryStorage::new_in_memory()),
244            api_key_repository: Arc::new(ApiKeyRepositoryStorage::new_in_memory()),
245        }
246    }
247
248    #[test]
249    fn test_relayer_repository_getter() {
250        let app_state = create_test_app_state();
251        let repo1 = app_state.relayer_repository();
252        let repo2 = app_state.relayer_repository();
253
254        // Verify that we get a new Arc pointing to the same underlying data
255        assert!(Arc::ptr_eq(&repo1, &repo2));
256        assert!(Arc::ptr_eq(&repo1, &app_state.relayer_repository));
257    }
258
259    #[test]
260    fn test_transaction_repository_getter() {
261        let app_state = create_test_app_state();
262        let repo1 = app_state.transaction_repository();
263        let repo2 = app_state.transaction_repository();
264
265        assert!(Arc::ptr_eq(&repo1, &repo2));
266        assert!(Arc::ptr_eq(&repo1, &app_state.transaction_repository));
267    }
268
269    #[test]
270    fn test_signer_repository_getter() {
271        let app_state = create_test_app_state();
272        let repo1 = app_state.signer_repository();
273        let repo2 = app_state.signer_repository();
274
275        assert!(Arc::ptr_eq(&repo1, &repo2));
276        assert!(Arc::ptr_eq(&repo1, &app_state.signer_repository));
277    }
278
279    #[test]
280    fn test_notification_repository_getter() {
281        let app_state = create_test_app_state();
282        let repo1 = app_state.notification_repository();
283        let repo2 = app_state.notification_repository();
284
285        assert!(Arc::ptr_eq(&repo1, &repo2));
286        assert!(Arc::ptr_eq(&repo1, &app_state.notification_repository));
287    }
288
289    #[test]
290    fn test_transaction_counter_store_getter() {
291        let app_state = create_test_app_state();
292        let store1 = app_state.transaction_counter_store();
293        let store2 = app_state.transaction_counter_store();
294
295        assert!(Arc::ptr_eq(&store1, &store2));
296        assert!(Arc::ptr_eq(&store1, &app_state.transaction_counter_store));
297    }
298
299    #[test]
300    fn test_job_producer_getter() {
301        let app_state = create_test_app_state();
302        let producer1 = app_state.job_producer();
303        let producer2 = app_state.job_producer();
304
305        assert!(Arc::ptr_eq(&producer1, &producer2));
306        assert!(Arc::ptr_eq(&producer1, &app_state.job_producer));
307    }
308
309    #[test]
310    fn test_plugin_repository_getter() {
311        let app_state = create_test_app_state();
312        let store1 = app_state.plugin_repository();
313        let store2 = app_state.plugin_repository();
314
315        assert!(Arc::ptr_eq(&store1, &store2));
316        assert!(Arc::ptr_eq(&store1, &app_state.plugin_repository));
317    }
318
319    #[test]
320    fn test_api_key_repository_getter() {
321        let app_state = create_test_app_state();
322        let repo1 = app_state.api_key_repository();
323        let repo2 = app_state.api_key_repository();
324
325        assert!(Arc::ptr_eq(&repo1, &repo2));
326        assert!(Arc::ptr_eq(&repo1, &app_state.api_key_repository));
327    }
328}