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_connection,
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(config: &ServerConfig) -> eyre::Result<RepositoryCollection> {
40 let repositories = match config.repository_storage_type {
41 RepositoryStorageType::InMemory => RepositoryCollection {
42 relayer: Arc::new(RelayerRepositoryStorage::new_in_memory()),
43 transaction: Arc::new(TransactionRepositoryStorage::new_in_memory()),
44 signer: Arc::new(SignerRepositoryStorage::new_in_memory()),
45 notification: Arc::new(NotificationRepositoryStorage::new_in_memory()),
46 network: Arc::new(NetworkRepositoryStorage::new_in_memory()),
47 transaction_counter: Arc::new(TransactionCounterRepositoryStorage::new_in_memory()),
48 plugin: Arc::new(PluginRepositoryStorage::new_in_memory()),
49 api_key: Arc::new(ApiKeyRepositoryStorage::new_in_memory()),
50 },
51 RepositoryStorageType::Redis => {
52 warn!("⚠️ Redis repository storage support is experimental. Use with caution.");
53
54 if config.storage_encryption_key.is_none() {
55 warn!("⚠️ Storage encryption key is not set. Please set the STORAGE_ENCRYPTION_KEY environment variable.");
56 return Err(eyre::eyre!("Storage encryption key is not set. Please set the STORAGE_ENCRYPTION_KEY environment variable."));
57 }
58
59 let connection_manager = initialize_redis_connection(config).await?;
60
61 RepositoryCollection {
62 relayer: Arc::new(RelayerRepositoryStorage::new_redis(
63 connection_manager.clone(),
64 config.redis_key_prefix.clone(),
65 )?),
66 transaction: Arc::new(TransactionRepositoryStorage::new_redis(
67 connection_manager.clone(),
68 config.redis_key_prefix.clone(),
69 )?),
70 signer: Arc::new(SignerRepositoryStorage::new_redis(
71 connection_manager.clone(),
72 config.redis_key_prefix.clone(),
73 )?),
74 notification: Arc::new(NotificationRepositoryStorage::new_redis(
75 connection_manager.clone(),
76 config.redis_key_prefix.clone(),
77 )?),
78 network: Arc::new(NetworkRepositoryStorage::new_redis(
79 connection_manager.clone(),
80 config.redis_key_prefix.clone(),
81 )?),
82 transaction_counter: Arc::new(TransactionCounterRepositoryStorage::new_redis(
83 connection_manager.clone(),
84 config.redis_key_prefix.clone(),
85 )?),
86 plugin: Arc::new(PluginRepositoryStorage::new_redis(
87 connection_manager.clone(),
88 config.redis_key_prefix.clone(),
89 )?),
90 api_key: Arc::new(ApiKeyRepositoryStorage::new_redis(
91 connection_manager,
92 config.redis_key_prefix.clone(),
93 )?),
94 }
95 }
96 };
97
98 Ok(repositories)
99}
100
101pub async fn initialize_app_state(
113 server_config: Arc<ServerConfig>,
114) -> Result<web::ThinData<DefaultAppState>> {
115 let repositories = initialize_repositories(&server_config).await?;
116
117 let queue = Queue::setup().await?;
118 let job_producer = Arc::new(jobs::JobProducer::new(queue.clone()));
119
120 let app_state = web::ThinData(AppState {
121 relayer_repository: repositories.relayer,
122 transaction_repository: repositories.transaction,
123 signer_repository: repositories.signer,
124 network_repository: repositories.network,
125 notification_repository: repositories.notification,
126 transaction_counter_store: repositories.transaction_counter,
127 job_producer,
128 plugin_repository: repositories.plugin,
129 api_key_repository: repositories.api_key,
130 });
131
132 Ok(app_state)
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use crate::{
139 config::RepositoryStorageType,
140 repositories::{ApiKeyRepositoryTrait, Repository},
141 utils::mocks::mockutils::{
142 create_mock_api_key, create_mock_network, create_mock_relayer, create_mock_signer,
143 create_test_server_config,
144 },
145 };
146 use std::sync::Arc;
147
148 #[tokio::test]
149 async fn test_initialize_repositories_in_memory() {
150 let config = create_test_server_config(RepositoryStorageType::InMemory);
151 let result = initialize_repositories(&config).await;
152
153 assert!(result.is_ok());
154 let repositories = result.unwrap();
155
156 assert!(Arc::strong_count(&repositories.relayer) >= 1);
158 assert!(Arc::strong_count(&repositories.transaction) >= 1);
159 assert!(Arc::strong_count(&repositories.signer) >= 1);
160 assert!(Arc::strong_count(&repositories.notification) >= 1);
161 assert!(Arc::strong_count(&repositories.network) >= 1);
162 assert!(Arc::strong_count(&repositories.transaction_counter) >= 1);
163 assert!(Arc::strong_count(&repositories.plugin) >= 1);
164 assert!(Arc::strong_count(&repositories.api_key) >= 1);
165 }
166
167 #[tokio::test]
168 async fn test_repository_collection_functionality() {
169 let config = create_test_server_config(RepositoryStorageType::InMemory);
170 let repositories = initialize_repositories(&config).await.unwrap();
171
172 let relayer = create_mock_relayer("test-relayer".to_string(), false);
174 let signer = create_mock_signer();
175 let network = create_mock_network();
176 let api_key = create_mock_api_key();
177
178 repositories.relayer.create(relayer.clone()).await.unwrap();
180 repositories.signer.create(signer.clone()).await.unwrap();
181 repositories.network.create(network.clone()).await.unwrap();
182 repositories.api_key.create(api_key.clone()).await.unwrap();
183
184 let retrieved_relayer = repositories
185 .relayer
186 .get_by_id("test-relayer".to_string())
187 .await
188 .unwrap();
189 let retrieved_signer = repositories
190 .signer
191 .get_by_id("test".to_string())
192 .await
193 .unwrap();
194 let retrieved_network = repositories
195 .network
196 .get_by_id("test".to_string())
197 .await
198 .unwrap();
199 let retrieved_api_key = repositories
200 .api_key
201 .get_by_id("test-api-key")
202 .await
203 .unwrap();
204
205 assert_eq!(retrieved_relayer.id, "test-relayer");
206 assert_eq!(retrieved_signer.id, "test");
207 assert_eq!(retrieved_network.id, "test");
208 assert_eq!(retrieved_api_key.unwrap().id, "test-api-key");
209 }
210
211 #[tokio::test]
212 async fn test_initialize_app_state_repository_error() {
213 let mut config = create_test_server_config(RepositoryStorageType::Redis);
214 config.redis_url = "redis://invalid_url".to_string();
215
216 let result = initialize_app_state(Arc::new(config)).await;
217
218 assert!(result.is_err());
220 let error = result.unwrap_err();
221 assert!(error.to_string().contains("Redis") || error.to_string().contains("connection"));
222 }
223}