openzeppelin_relayer/bootstrap/
initialize_app_state.rs1use crate::{
6 config::{RepositoryStorageType, ServerConfig},
7 jobs::{self, Queue},
8 models::{AppState, DefaultAppState},
9 repositories::{
10 ApiKeyRepositoryStorage, NetworkRepositoryStorage, NotificationRepositoryStorage,
11 PluginRepositoryStorage, RelayerRepositoryStorage, SignerRepositoryStorage,
12 TransactionCounterRepositoryStorage, TransactionRepositoryStorage,
13 },
14 utils::{initialize_redis_connections, RedisConnections},
15};
16use actix_web::web;
17use color_eyre::Result;
18use std::sync::Arc;
19use tracing::warn;
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 pub api_key: Arc<ApiKeyRepositoryStorage>,
30}
31
32pub async fn initialize_repositories(
45 config: &ServerConfig,
46 connections: Option<Arc<RedisConnections>>,
47) -> eyre::Result<RepositoryCollection> {
48 let repositories = match config.repository_storage_type {
49 RepositoryStorageType::InMemory => RepositoryCollection {
50 relayer: Arc::new(RelayerRepositoryStorage::new_in_memory()),
51 transaction: Arc::new(TransactionRepositoryStorage::new_in_memory()),
52 signer: Arc::new(SignerRepositoryStorage::new_in_memory()),
53 notification: Arc::new(NotificationRepositoryStorage::new_in_memory()),
54 network: Arc::new(NetworkRepositoryStorage::new_in_memory()),
55 transaction_counter: Arc::new(TransactionCounterRepositoryStorage::new_in_memory()),
56 plugin: Arc::new(PluginRepositoryStorage::new_in_memory()),
57 api_key: Arc::new(ApiKeyRepositoryStorage::new_in_memory()),
58 },
59 RepositoryStorageType::Redis => {
60 if config.storage_encryption_key.is_none() {
61 warn!("⚠️ Storage encryption key is not set. Please set the STORAGE_ENCRYPTION_KEY environment variable.");
62 return Err(eyre::eyre!("Storage encryption key is not set. Please set the STORAGE_ENCRYPTION_KEY environment variable."));
63 }
64
65 let connections = connections
66 .ok_or_else(|| eyre::eyre!("Redis connections required for Redis storage type"))?;
67
68 RepositoryCollection {
70 relayer: Arc::new(RelayerRepositoryStorage::new_redis(
71 connections.clone(),
72 config.redis_key_prefix.clone(),
73 )?),
74 transaction: Arc::new(TransactionRepositoryStorage::new_redis(
75 connections.clone(),
76 config.redis_key_prefix.clone(),
77 )?),
78 signer: Arc::new(SignerRepositoryStorage::new_redis(
79 connections.clone(),
80 config.redis_key_prefix.clone(),
81 )?),
82 notification: Arc::new(NotificationRepositoryStorage::new_redis(
83 connections.clone(),
84 config.redis_key_prefix.clone(),
85 )?),
86 network: Arc::new(NetworkRepositoryStorage::new_redis(
87 connections.clone(),
88 config.redis_key_prefix.clone(),
89 )?),
90 transaction_counter: Arc::new(TransactionCounterRepositoryStorage::new_redis(
91 connections.clone(),
92 config.redis_key_prefix.clone(),
93 )?),
94 plugin: Arc::new(PluginRepositoryStorage::new_redis(
95 connections.clone(),
96 config.redis_key_prefix.clone(),
97 )?),
98 api_key: Arc::new(ApiKeyRepositoryStorage::new_redis(
99 connections,
100 config.redis_key_prefix.clone(),
101 )?),
102 }
103 }
104 };
105
106 Ok(repositories)
107}
108
109pub async fn initialize_app_state(
121 server_config: Arc<ServerConfig>,
122) -> Result<web::ThinData<DefaultAppState>> {
123 let redis_connections = initialize_redis_connections(&server_config).await?;
127
128 let repo_connections = match server_config.repository_storage_type {
130 RepositoryStorageType::Redis => Some(redis_connections.clone()),
131 RepositoryStorageType::InMemory => None,
132 };
133
134 let repositories = initialize_repositories(&server_config, repo_connections).await?;
135
136 let queue = Queue::setup(redis_connections).await?;
138 let job_producer = Arc::new(jobs::JobProducer::new(queue.clone()));
139
140 let app_state = web::ThinData(AppState {
141 relayer_repository: repositories.relayer,
142 transaction_repository: repositories.transaction,
143 signer_repository: repositories.signer,
144 network_repository: repositories.network,
145 notification_repository: repositories.notification,
146 transaction_counter_store: repositories.transaction_counter,
147 job_producer,
148 plugin_repository: repositories.plugin,
149 api_key_repository: repositories.api_key,
150 });
151
152 Ok(app_state)
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::{
159 config::RepositoryStorageType,
160 models::SecretString,
161 repositories::{ApiKeyRepositoryTrait, Repository},
162 utils::mocks::mockutils::{
163 create_mock_api_key, create_mock_network, create_mock_relayer, create_mock_signer,
164 create_test_server_config,
165 },
166 };
167 use std::sync::Arc;
168
169 #[tokio::test]
170 async fn test_initialize_repositories_in_memory() {
171 let config = create_test_server_config(RepositoryStorageType::InMemory);
172 let result = initialize_repositories(&config, None).await;
174
175 assert!(result.is_ok());
176 let repositories = result.unwrap();
177
178 assert!(Arc::strong_count(&repositories.relayer) >= 1);
180 assert!(Arc::strong_count(&repositories.transaction) >= 1);
181 assert!(Arc::strong_count(&repositories.signer) >= 1);
182 assert!(Arc::strong_count(&repositories.notification) >= 1);
183 assert!(Arc::strong_count(&repositories.network) >= 1);
184 assert!(Arc::strong_count(&repositories.transaction_counter) >= 1);
185 assert!(Arc::strong_count(&repositories.plugin) >= 1);
186 assert!(Arc::strong_count(&repositories.api_key) >= 1);
187 }
188
189 #[tokio::test]
190 async fn test_repository_collection_functionality() {
191 let config = create_test_server_config(RepositoryStorageType::InMemory);
192 let repositories = initialize_repositories(&config, None).await.unwrap();
194
195 let relayer = create_mock_relayer("test-relayer".to_string(), false);
197 let signer = create_mock_signer();
198 let network = create_mock_network();
199 let api_key = create_mock_api_key();
200
201 repositories.relayer.create(relayer.clone()).await.unwrap();
203 repositories.signer.create(signer.clone()).await.unwrap();
204 repositories.network.create(network.clone()).await.unwrap();
205 repositories.api_key.create(api_key.clone()).await.unwrap();
206
207 let retrieved_relayer = repositories
208 .relayer
209 .get_by_id("test-relayer".to_string())
210 .await
211 .unwrap();
212 let retrieved_signer = repositories
213 .signer
214 .get_by_id("test".to_string())
215 .await
216 .unwrap();
217 let retrieved_network = repositories
218 .network
219 .get_by_id("test".to_string())
220 .await
221 .unwrap();
222 let retrieved_api_key = repositories
223 .api_key
224 .get_by_id("test-api-key")
225 .await
226 .unwrap();
227
228 assert_eq!(retrieved_relayer.id, "test-relayer");
229 assert_eq!(retrieved_signer.id, "test");
230 assert_eq!(retrieved_network.id, "test");
231 assert_eq!(retrieved_api_key.unwrap().id, "test-api-key");
232 }
233
234 #[tokio::test]
235 async fn test_initialize_app_state_repository_error() {
236 let mut config = create_test_server_config(RepositoryStorageType::Redis);
237 config.redis_url = "redis://invalid_url".to_string();
238
239 let result = initialize_app_state(Arc::new(config)).await;
240
241 assert!(result.is_err());
243 let error = result.unwrap_err();
244 assert!(error.to_string().contains("Redis") || error.to_string().contains("connection"));
245 }
246
247 #[tokio::test]
248 async fn test_initialize_repositories_redis_without_encryption_key() {
249 let mut config = create_test_server_config(RepositoryStorageType::Redis);
250 config.storage_encryption_key = None;
252
253 let result = initialize_repositories(&config, None).await;
256
257 assert!(result.is_err());
258 let error = match result {
259 Err(e) => e,
260 Ok(_) => panic!("Expected error for missing encryption key"),
261 };
262 assert!(
263 error.to_string().contains("encryption key"),
264 "Expected error about encryption key, got: {}",
265 error
266 );
267 }
268
269 #[tokio::test]
270 async fn test_initialize_repositories_redis_without_pool() {
271 let mut config = create_test_server_config(RepositoryStorageType::Redis);
272 config.storage_encryption_key = Some(SecretString::new("test-encryption-key-32-bytes!!!"));
274
275 let result = initialize_repositories(&config, None).await;
277
278 assert!(result.is_err());
279 let error = match result {
280 Err(e) => e,
281 Ok(_) => panic!("Expected error for missing pool"),
282 };
283 assert!(
284 error
285 .to_string()
286 .contains("Redis connections required for Redis storage type"),
287 "Expected error about Redis pool being required, got: {}",
288 error
289 );
290 }
291
292 #[tokio::test]
293 async fn test_initialize_repositories_in_memory_ignores_pool() {
294 let config = create_test_server_config(RepositoryStorageType::InMemory);
297
298 let result = initialize_repositories(&config, None).await;
300 assert!(result.is_ok());
301
302 let repositories = result.unwrap();
304 let relayer = create_mock_relayer("test-relayer".to_string(), false);
305 repositories.relayer.create(relayer).await.unwrap();
306 let retrieved = repositories
307 .relayer
308 .get_by_id("test-relayer".to_string())
309 .await
310 .unwrap();
311 assert_eq!(retrieved.id, "test-relayer");
312 }
313
314 #[tokio::test]
315 async fn test_repository_collection_all_eight_repositories() {
316 let config = create_test_server_config(RepositoryStorageType::InMemory);
318 let repositories = initialize_repositories(&config, None).await.unwrap();
319
320 let repo_refs = vec![
323 Arc::strong_count(&repositories.relayer),
324 Arc::strong_count(&repositories.transaction),
325 Arc::strong_count(&repositories.signer),
326 Arc::strong_count(&repositories.notification),
327 Arc::strong_count(&repositories.network),
328 Arc::strong_count(&repositories.transaction_counter),
329 Arc::strong_count(&repositories.plugin),
330 Arc::strong_count(&repositories.api_key),
331 ];
332
333 assert_eq!(repo_refs.len(), 8, "Expected exactly 8 repositories");
334 for (i, count) in repo_refs.iter().enumerate() {
335 assert!(
336 *count >= 1,
337 "Repository {} has invalid Arc count: {}",
338 i,
339 count
340 );
341 }
342 }
343
344 #[tokio::test]
345 async fn test_repository_delete_operations() {
346 let config = create_test_server_config(RepositoryStorageType::InMemory);
347 let repositories = initialize_repositories(&config, None).await.unwrap();
348
349 let relayer = create_mock_relayer("delete-test".to_string(), false);
351 repositories.relayer.create(relayer).await.unwrap();
352
353 let exists = repositories
355 .relayer
356 .get_by_id("delete-test".to_string())
357 .await;
358 assert!(exists.is_ok());
359
360 let delete_result = repositories
362 .relayer
363 .delete_by_id("delete-test".to_string())
364 .await;
365 assert!(delete_result.is_ok());
366
367 let after_delete = repositories
369 .relayer
370 .get_by_id("delete-test".to_string())
371 .await;
372 assert!(after_delete.is_err() || after_delete.unwrap().id != "delete-test");
373 }
374
375 #[tokio::test]
376 async fn test_repository_update_operations() {
377 let config = create_test_server_config(RepositoryStorageType::InMemory);
378 let repositories = initialize_repositories(&config, None).await.unwrap();
379
380 let relayer = create_mock_relayer("update-test".to_string(), false);
382 repositories.relayer.create(relayer.clone()).await.unwrap();
383
384 let mut updated_relayer = relayer.clone();
386 updated_relayer.system_disabled = true;
387
388 let update_result = repositories
389 .relayer
390 .update("update-test".to_string(), updated_relayer)
391 .await;
392 assert!(update_result.is_ok());
393
394 let retrieved = repositories
396 .relayer
397 .get_by_id("update-test".to_string())
398 .await
399 .unwrap();
400 assert!(retrieved.system_disabled);
401 }
402
403 #[tokio::test]
404 async fn test_repository_list_operations() {
405 let config = create_test_server_config(RepositoryStorageType::InMemory);
406 let repositories = initialize_repositories(&config, None).await.unwrap();
407
408 for i in 0..5 {
410 let relayer = create_mock_relayer(format!("list-test-{}", i), false);
411 repositories.relayer.create(relayer).await.unwrap();
412 }
413
414 let all_relayers = repositories.relayer.list_all().await.unwrap();
416 assert_eq!(all_relayers.len(), 5);
417
418 for i in 0..5 {
420 let found = all_relayers
421 .iter()
422 .any(|r| r.id == format!("list-test-{}", i));
423 assert!(found, "Expected to find relayer list-test-{}", i);
424 }
425 }
426
427 #[tokio::test]
428 async fn test_repository_collection_struct_fields() {
429 let config = create_test_server_config(RepositoryStorageType::InMemory);
431 let repos = initialize_repositories(&config, None).await.unwrap();
432
433 let _ = &repos.relayer;
435 let _ = &repos.transaction;
436 let _ = &repos.signer;
437 let _ = &repos.notification;
438 let _ = &repos.network;
439 let _ = &repos.transaction_counter;
440 let _ = &repos.plugin;
441 let _ = &repos.api_key;
442
443 assert!(true);
445 }
446}