openzeppelin_relayer/bootstrap/
initialize_app_state.rs1use 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
31pub 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
95pub 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 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 let relayer = create_mock_relayer("test-relayer".to_string(), false);
165 let signer = create_mock_signer();
166 let network = create_mock_network();
167
168 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 assert!(result.is_err());
203 let error = result.unwrap_err();
204 assert!(error.to_string().contains("Redis") || error.to_string().contains("connection"));
205 }
206}