openzeppelin_relayer/repositories/
mod.rs

1//! # Repository Module
2//!
3//! Implements data persistence layer for the relayer service using Repository pattern.
4
5use crate::models::{PaginationQuery, RepositoryError};
6use async_trait::async_trait;
7use eyre::Result;
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11mod relayer;
12pub use relayer::*;
13
14pub mod transaction;
15pub use transaction::*;
16
17mod signer;
18pub use signer::*;
19
20pub mod notification;
21pub use notification::*;
22
23mod transaction_counter;
24pub use transaction_counter::*;
25
26pub mod network;
27pub use network::*;
28
29mod plugin;
30pub use plugin::*;
31
32pub mod api_key;
33pub use api_key::*;
34
35// Redis base utilities for shared functionality
36pub mod redis_base;
37
38#[derive(Debug, Serialize, Deserialize, ToSchema)]
39pub struct PaginatedResult<T> {
40    pub items: Vec<T>,
41    pub total: u64,
42    pub page: u32,
43    pub per_page: u32,
44}
45
46pub struct BatchRetrievalResult<T> {
47    pub results: Vec<T>,
48    pub failed_ids: Vec<String>,
49}
50
51/// Result of a batch delete operation
52#[derive(Debug, Default)]
53pub struct BatchDeleteResult {
54    /// Number of entities successfully deleted
55    pub deleted_count: usize,
56    /// IDs that failed to delete with their error messages
57    pub failed: Vec<(String, String)>,
58}
59
60/// Request to delete a transaction with pre-extracted data needed for index cleanup.
61/// This avoids re-fetching transaction data when the caller already has it.
62#[derive(Debug, Clone)]
63pub struct TransactionDeleteRequest {
64    /// Transaction ID
65    pub id: String,
66    /// Relayer ID (needed for building index keys)
67    pub relayer_id: String,
68    /// Nonce if available (needed for nonce index cleanup, EVM-specific)
69    pub nonce: Option<u64>,
70}
71
72impl TransactionDeleteRequest {
73    pub fn new(id: String, relayer_id: String, nonce: Option<u64>) -> Self {
74        Self {
75            id,
76            relayer_id,
77            nonce,
78        }
79    }
80}
81
82#[cfg(test)]
83use mockall::automock;
84use utoipa::ToSchema;
85
86#[async_trait]
87#[allow(dead_code)]
88#[cfg_attr(test, automock)]
89pub trait Repository<T, ID> {
90    async fn create(&self, entity: T) -> Result<T, RepositoryError>;
91    async fn get_by_id(&self, id: ID) -> Result<T, RepositoryError>;
92    async fn list_all(&self) -> Result<Vec<T>, RepositoryError>;
93    async fn list_paginated(
94        &self,
95        query: PaginationQuery,
96    ) -> Result<PaginatedResult<T>, RepositoryError>;
97    async fn update(&self, id: ID, entity: T) -> Result<T, RepositoryError>;
98    async fn delete_by_id(&self, id: ID) -> Result<(), RepositoryError>;
99    async fn count(&self) -> Result<usize, RepositoryError>;
100
101    /// Check if the repository contains any entries.
102    async fn has_entries(&self) -> Result<bool, RepositoryError>;
103
104    /// Drop all entries from storage.
105    /// This completely clears all data, indexes, and metadata.
106    /// Use with caution as this permanently deletes all data.
107    async fn drop_all_entries(&self) -> Result<(), RepositoryError>;
108}
109
110#[derive(Error, Debug)]
111pub enum ConversionError {
112    #[error("Invalid type: {0}")]
113    InvalidType(String),
114    #[error("Invalid config: {0}")]
115    InvalidConfig(String),
116}