openzeppelin_relayer/bootstrap/
initialize_app_state.rs

1//! Application state initialization
2//!
3//! This module contains functions for initializing the application state,
4//! including setting up repositories, job queues, and other necessary components.
5use crate::{
6    config::{RepositoryStorageType, ServerConfig},
7    jobs::{self, Queue},
8    models::{AppState, DefaultAppState},
9    repositories::{
10        NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage,
11        RelayerRepositoryStorage, SignerRepositoryStorage, TransactionCounterRepositoryStorage,
12        TransactionRepositoryStorage,
13    },
14    utils::initialize_redis_connection,
15};
16use actix_web::web;
17use color_eyre::Result;
18use log::warn;
19use std::sync::Arc;
20
21pub struct RepositoryCollection {
22    pub relayer: Arc<RelayerRepositoryStorage>,
23    pub transaction: Arc<TransactionRepositoryStorage>,
24    pub signer: Arc<SignerRepositoryStorage>,
25    pub notification: Arc<NotificationRepositoryStorage>,
26    pub network: Arc<NetworkRepositoryStorage>,
27    pub transaction_counter: Arc<TransactionCounterRepositoryStorage>,
28    pub plugin: Arc<PluginRepositoryStorage>,
29}
30
31/// Initializes repositories based on the server configuration
32///
33/// # Returns
34///
35/// * `Result<RepositoryCollection>` - Initialized repositories
36///
37/// # Errors
38pub async fn initialize_repositories(config: &ServerConfig) -> eyre::Result<RepositoryCollection> {
39    let repositories = match config.repository_storage_type {
40        RepositoryStorageType::InMemory => RepositoryCollection {
41            relayer: Arc::new(RelayerRepositoryStorage::new_in_memory()),
42            transaction: Arc::new(TransactionRepositoryStorage::new_in_memory()),
43            signer: Arc::new(SignerRepositoryStorage::new_in_memory()),
44            notification: Arc::new(NotificationRepositoryStorage::new_in_memory()),
45            network: Arc::new(NetworkRepositoryStorage::new_in_memory()),
46            transaction_counter: Arc::new(TransactionCounterRepositoryStorage::new_in_memory()),
47            plugin: Arc::new(PluginRepositoryStorage::new_in_memory()),
48        },
49        RepositoryStorageType::Redis => {
50            warn!("⚠️ Redis repository storage support is experimental. Use with caution.");
51
52            if config.storage_encryption_key.is_none() {
53                warn!("⚠️ Storage encryption key is not set. Please set the STORAGE_ENCRYPTION_KEY environment variable.");
54                return Err(eyre::eyre!("Storage encryption key is not set. Please set the STORAGE_ENCRYPTION_KEY environment variable."));
55            }
56
57            let connection_manager = initialize_redis_connection(config).await?;
58
59            RepositoryCollection {
60                relayer: Arc::new(RelayerRepositoryStorage::new_redis(
61                    connection_manager.clone(),
62                    config.redis_key_prefix.clone(),
63                )?),
64                transaction: Arc::new(TransactionRepositoryStorage::new_redis(
65                    connection_manager.clone(),
66                    config.redis_key_prefix.clone(),
67                )?),
68                signer: Arc::new(SignerRepositoryStorage::new_redis(
69                    connection_manager.clone(),
70                    config.redis_key_prefix.clone(),
71                )?),
72                notification: Arc::new(NotificationRepositoryStorage::new_redis(
73                    connection_manager.clone(),
74                    config.redis_key_prefix.clone(),
75                )?),
76                network: Arc::new(NetworkRepositoryStorage::new_redis(
77                    connection_manager.clone(),
78                    config.redis_key_prefix.clone(),
79                )?),
80                transaction_counter: Arc::new(TransactionCounterRepositoryStorage::new_redis(
81                    connection_manager.clone(),
82                    config.redis_key_prefix.clone(),
83                )?),
84                plugin: Arc::new(PluginRepositoryStorage::new_redis(
85                    connection_manager,
86                    config.redis_key_prefix.clone(),
87                )?),
88            }
89        }
90    };
91
92    Ok(repositories)
93}
94
95/// Initializes application state
96///
97/// # Returns
98///
99/// * `Result<web::Data<AppState>>` - Initialized application state
100///
101/// # Errors
102///
103/// Returns error if:
104/// - Repository initialization fails
105/// - Configuration loading fails
106pub async fn initialize_app_state(
107    server_config: Arc<ServerConfig>,
108) -> Result<web::ThinData<DefaultAppState>> {
109    let repositories = initialize_repositories(&server_config).await?;
110
111    let queue = Queue::setup().await?;
112    let job_producer = Arc::new(jobs::JobProducer::new(queue.clone()));
113
114    let app_state = web::ThinData(AppState {
115        relayer_repository: repositories.relayer,
116        transaction_repository: repositories.transaction,
117        signer_repository: repositories.signer,
118        network_repository: repositories.network,
119        notification_repository: repositories.notification,
120        transaction_counter_store: repositories.transaction_counter,
121        job_producer,
122        plugin_repository: repositories.plugin,
123    });
124
125    Ok(app_state)
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use crate::{
132        config::RepositoryStorageType,
133        repositories::Repository,
134        utils::mocks::mockutils::{
135            create_mock_network, create_mock_relayer, create_mock_signer, create_test_server_config,
136        },
137    };
138    use std::sync::Arc;
139
140    #[tokio::test]
141    async fn test_initialize_repositories_in_memory() {
142        let config = create_test_server_config(RepositoryStorageType::InMemory);
143        let result = initialize_repositories(&config).await;
144
145        assert!(result.is_ok());
146        let repositories = result.unwrap();
147
148        // Verify all repositories are created
149        assert!(Arc::strong_count(&repositories.relayer) >= 1);
150        assert!(Arc::strong_count(&repositories.transaction) >= 1);
151        assert!(Arc::strong_count(&repositories.signer) >= 1);
152        assert!(Arc::strong_count(&repositories.notification) >= 1);
153        assert!(Arc::strong_count(&repositories.network) >= 1);
154        assert!(Arc::strong_count(&repositories.transaction_counter) >= 1);
155        assert!(Arc::strong_count(&repositories.plugin) >= 1);
156    }
157
158    #[tokio::test]
159    async fn test_repository_collection_functionality() {
160        let config = create_test_server_config(RepositoryStorageType::InMemory);
161        let repositories = initialize_repositories(&config).await.unwrap();
162
163        // Test basic repository operations
164        let relayer = create_mock_relayer("test-relayer".to_string(), false);
165        let signer = create_mock_signer();
166        let network = create_mock_network();
167
168        // Test creating and retrieving items
169        repositories.relayer.create(relayer.clone()).await.unwrap();
170        repositories.signer.create(signer.clone()).await.unwrap();
171        repositories.network.create(network.clone()).await.unwrap();
172
173        let retrieved_relayer = repositories
174            .relayer
175            .get_by_id("test-relayer".to_string())
176            .await
177            .unwrap();
178        let retrieved_signer = repositories
179            .signer
180            .get_by_id("test".to_string())
181            .await
182            .unwrap();
183        let retrieved_network = repositories
184            .network
185            .get_by_id("test".to_string())
186            .await
187            .unwrap();
188
189        assert_eq!(retrieved_relayer.id, "test-relayer");
190        assert_eq!(retrieved_signer.id, "test");
191        assert_eq!(retrieved_network.id, "test");
192    }
193
194    #[tokio::test]
195    async fn test_initialize_app_state_repository_error() {
196        let mut config = create_test_server_config(RepositoryStorageType::Redis);
197        config.redis_url = "redis://invalid_url".to_string();
198
199        let result = initialize_app_state(Arc::new(config)).await;
200
201        // Should fail during repository initialization
202        assert!(result.is_err());
203        let error = result.unwrap_err();
204        assert!(error.to_string().contains("Redis") || error.to_string().contains("connection"));
205    }
206}