openzeppelin_relayer/repositories/
signer.rs1use crate::{
5 models::{RepositoryError, SignerRepoModel},
6 repositories::*,
7};
8use async_trait::async_trait;
9use eyre::Result;
10use std::collections::HashMap;
11use tokio::sync::{Mutex, MutexGuard};
12
13#[derive(Debug)]
14pub struct InMemorySignerRepository {
15 store: Mutex<HashMap<String, SignerRepoModel>>,
16}
17
18#[allow(dead_code)]
19impl InMemorySignerRepository {
20 pub fn new() -> Self {
21 Self {
22 store: Mutex::new(HashMap::new()),
23 }
24 }
25
26 async fn acquire_lock<T>(lock: &Mutex<T>) -> Result<MutexGuard<T>, RepositoryError> {
27 Ok(lock.lock().await)
28 }
29}
30
31impl Default for InMemorySignerRepository {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37#[async_trait]
38impl Repository<SignerRepoModel, String> for InMemorySignerRepository {
39 async fn create(&self, signer: SignerRepoModel) -> Result<SignerRepoModel, RepositoryError> {
40 let mut store: MutexGuard<'_, HashMap<String, SignerRepoModel>> =
41 Self::acquire_lock(&self.store).await?;
42 if store.contains_key(&signer.id) {
43 return Err(RepositoryError::ConstraintViolation(format!(
44 "Signer with ID {} already exists",
45 signer.id
46 )));
47 }
48 store.insert(signer.id.clone(), signer.clone());
49 Ok(signer)
50 }
51
52 async fn get_by_id(&self, id: String) -> Result<SignerRepoModel, RepositoryError> {
53 let store: MutexGuard<'_, HashMap<String, SignerRepoModel>> =
54 Self::acquire_lock(&self.store).await?;
55 match store.get(&id) {
56 Some(signer) => Ok(signer.clone()),
57 None => Err(RepositoryError::NotFound(format!(
58 "Signer with ID {} not found",
59 id
60 ))),
61 }
62 }
63
64 #[allow(clippy::map_entry)]
65 async fn update(
66 &self,
67 _id: String,
68 _relayer: SignerRepoModel,
69 ) -> Result<SignerRepoModel, RepositoryError> {
70 Err(RepositoryError::NotSupported("Not supported".to_string()))
71 }
72
73 async fn delete_by_id(&self, _id: String) -> Result<(), RepositoryError> {
74 Err(RepositoryError::NotSupported("Not supported".to_string()))
75 }
76
77 async fn list_all(&self) -> Result<Vec<SignerRepoModel>, RepositoryError> {
78 let store: MutexGuard<'_, HashMap<String, SignerRepoModel>> =
79 Self::acquire_lock(&self.store).await?;
80 let signers: Vec<SignerRepoModel> = store.values().cloned().collect();
81 Ok(signers)
82 }
83
84 async fn list_paginated(
85 &self,
86 query: PaginationQuery,
87 ) -> Result<PaginatedResult<SignerRepoModel>, RepositoryError> {
88 let total = self.count().await?;
89 let start = ((query.page - 1) * query.per_page) as usize;
90 let items: Vec<SignerRepoModel> = self
91 .store
92 .lock()
93 .await
94 .values()
95 .skip(start)
96 .take(query.per_page as usize)
97 .cloned()
98 .collect();
99
100 Ok(PaginatedResult {
101 items,
102 total: total as u64,
103 page: query.page,
104 per_page: query.per_page,
105 })
106 }
107
108 async fn count(&self) -> Result<usize, RepositoryError> {
109 let store: MutexGuard<'_, HashMap<String, SignerRepoModel>> =
110 Self::acquire_lock(&self.store).await?;
111 let length = store.len();
112 Ok(length)
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use secrets::SecretVec;
119
120 use crate::models::{LocalSignerConfig, SignerConfig};
121
122 use super::*;
123
124 fn create_test_signer(id: String) -> SignerRepoModel {
125 SignerRepoModel {
126 id: id.clone(),
127 config: SignerConfig::Local(LocalSignerConfig {
128 raw_key: SecretVec::zero(0),
129 }),
130 }
131 }
132
133 #[actix_web::test]
134 async fn test_new_repository_is_empty() {
135 let repo = InMemorySignerRepository::new();
136 assert_eq!(repo.count().await.unwrap(), 0);
137 }
138
139 #[actix_web::test]
140 async fn test_add_signer() {
141 let repo = InMemorySignerRepository::new();
142 let signer = create_test_signer("test".to_string());
143
144 repo.create(signer.clone()).await.unwrap();
145 assert_eq!(repo.count().await.unwrap(), 1);
146
147 let stored = repo.get_by_id("test".to_string()).await.unwrap();
148 assert_eq!(stored.id, signer.id);
149 }
150
151 #[actix_web::test]
152 async fn test_update_signer() {
153 let repo = InMemorySignerRepository::new();
154 let signer = create_test_signer("test".to_string());
155
156 let result = repo.update("test".to_string(), signer).await;
157 assert!(matches!(result, Err(RepositoryError::NotSupported(_))));
158 }
159
160 #[actix_web::test]
161 async fn test_list_signers() {
162 let repo = InMemorySignerRepository::new();
163 let signer1 = create_test_signer("test".to_string());
164 let signer2 = create_test_signer("test2".to_string());
165
166 repo.create(signer1.clone()).await.unwrap();
167 repo.create(signer2).await.unwrap();
168
169 let signers = repo.list_all().await.unwrap();
170 assert_eq!(signers.len(), 2);
171 }
172
173 #[actix_web::test]
174 async fn test_update_nonexistent_signer() {
175 let repo = InMemorySignerRepository::new();
176 let signer = create_test_signer("test".to_string());
177
178 let result = repo.update("test2".to_string(), signer).await;
179 assert!(matches!(result, Err(RepositoryError::NotSupported(_))));
180 }
181
182 #[actix_web::test]
183 async fn test_get_nonexistent_relayer() {
184 let repo = InMemorySignerRepository::new();
185
186 let result = repo.get_by_id("test".to_string()).await;
187 assert!(matches!(result, Err(RepositoryError::NotFound(_))));
188 }
189}