openzeppelin_relayer/jobs/handlers/
transaction_request_handler.rs

1//! Transaction request handler for processing incoming transaction jobs.
2//!
3//! Handles the validation and preparation of transactions before they are
4//! submitted to the network
5use actix_web::web::ThinData;
6use apalis::prelude::{Attempt, Context, Data, TaskId, Worker, *};
7use apalis_redis::RedisContext;
8use tracing::instrument;
9
10use crate::{
11    constants::WORKER_TRANSACTION_REQUEST_RETRIES,
12    domain::{get_relayer_transaction, get_transaction_by_id, Transaction},
13    jobs::{handle_result, Job, TransactionRequest},
14    models::DefaultAppState,
15    observability::request_id::set_request_id,
16};
17
18#[instrument(
19    level = "debug",
20    skip(job, state, _worker, _ctx),
21    fields(
22        request_id = ?job.request_id,
23        job_id = %job.message_id,
24        job_type = %job.job_type.to_string(),
25        attempt = %attempt.current(),
26        tx_id = %job.data.transaction_id,
27        relayer_id = %job.data.relayer_id,
28        task_id = %task_id.to_string(),
29    )
30)]
31pub async fn transaction_request_handler(
32    job: Job<TransactionRequest>,
33    state: Data<ThinData<DefaultAppState>>,
34    attempt: Attempt,
35    _worker: Worker<Context>,
36    task_id: TaskId,
37    _ctx: RedisContext,
38) -> Result<(), Error> {
39    if let Some(request_id) = job.request_id.clone() {
40        set_request_id(request_id);
41    }
42
43    let result = handle_request(job.data, state.clone()).await;
44
45    handle_result(
46        result,
47        attempt,
48        "Transaction Request",
49        WORKER_TRANSACTION_REQUEST_RETRIES,
50    )
51}
52
53async fn handle_request(
54    request: TransactionRequest,
55    state: Data<ThinData<DefaultAppState>>,
56) -> eyre::Result<()> {
57    let relayer_transaction = get_relayer_transaction(request.relayer_id, &state).await?;
58
59    let transaction = get_transaction_by_id(request.transaction_id.clone(), &state).await?;
60
61    relayer_transaction.prepare_transaction(transaction).await?;
62
63    Ok(())
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use apalis::prelude::Attempt;
70
71    #[tokio::test]
72    async fn test_handler_result_processing() {
73        // This test focuses only on the interaction with handle_result
74        // which we can test without mocking the entire state
75
76        // Create a minimal job
77        let request = TransactionRequest::new("tx123", "relayer-1");
78        let job = Job::new(crate::jobs::JobType::TransactionRequest, request);
79
80        // Create a test attempt
81        let attempt = Attempt::default();
82
83        // We cannot fully test the transaction_request_handler without extensive mocking
84        // of the domain layer, but we can verify our test setup is correct
85        assert_eq!(job.data.transaction_id, "tx123");
86        assert_eq!(job.data.relayer_id, "relayer-1");
87        assert_eq!(attempt.current(), 0);
88    }
89
90    // Note: Fully testing the functionality would require either:
91    // 1. Dependency injection for all external dependencies
92    // 2. Feature flags to enable mock implementations
93    // 3. Integration tests with a real or test database
94
95    // For now, these tests serve as placeholders to be expanded
96    // when the appropriate testing infrastructure is in place.
97}