openzeppelin_relayer/api/controllers/
plugin.rs1use crate::{
6 jobs::JobProducerTrait,
7 models::{
8 ApiError, ApiResponse, NetworkRepoModel, NotificationRepoModel, PaginationMeta,
9 PaginationQuery, PluginCallRequest, PluginModel, RelayerRepoModel, SignerRepoModel,
10 ThinDataAppState, TransactionRepoModel,
11 },
12 repositories::{
13 NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository,
14 TransactionCounterTrait, TransactionRepository,
15 },
16 services::plugins::{PluginCallResponse, PluginRunner, PluginService, PluginServiceTrait},
17};
18use actix_web::HttpResponse;
19use eyre::Result;
20use std::sync::Arc;
21
22pub async fn call_plugin<J, RR, TR, NR, NFR, SR, TCR, PR>(
34 plugin_id: String,
35 plugin_call_request: PluginCallRequest,
36 state: ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
37) -> Result<HttpResponse, ApiError>
38where
39 J: JobProducerTrait + Send + Sync + 'static,
40 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
41 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
42 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
43 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
44 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
45 TCR: TransactionCounterTrait + Send + Sync + 'static,
46 PR: PluginRepositoryTrait + Send + Sync + 'static,
47{
48 let plugin = state
49 .plugin_repository
50 .get_by_id(&plugin_id)
51 .await?
52 .ok_or_else(|| ApiError::NotFound(format!("Plugin with id {} not found", plugin_id)))?;
53
54 let plugin_runner = PluginRunner;
55 let plugin_service = PluginService::new(plugin_runner);
56 let result = plugin_service
57 .call_plugin(plugin, plugin_call_request, Arc::new(state))
58 .await;
59
60 match result {
61 Ok(plugin_result) => Ok(HttpResponse::Ok().json(ApiResponse::success(plugin_result))),
62 Err(e) => Ok(HttpResponse::Ok().json(ApiResponse::<PluginCallResponse>::error(e))),
63 }
64}
65
66pub async fn list_plugins<J, RR, TR, NR, NFR, SR, TCR, PR>(
79 query: PaginationQuery,
80 state: ThinDataAppState<J, RR, TR, NR, NFR, SR, TCR, PR>,
81) -> Result<HttpResponse, ApiError>
82where
83 J: JobProducerTrait + Send + Sync + 'static,
84 RR: RelayerRepository + Repository<RelayerRepoModel, String> + Send + Sync + 'static,
85 TR: TransactionRepository + Repository<TransactionRepoModel, String> + Send + Sync + 'static,
86 NR: NetworkRepository + Repository<NetworkRepoModel, String> + Send + Sync + 'static,
87 NFR: Repository<NotificationRepoModel, String> + Send + Sync + 'static,
88 SR: Repository<SignerRepoModel, String> + Send + Sync + 'static,
89 TCR: TransactionCounterTrait + Send + Sync + 'static,
90 PR: PluginRepositoryTrait + Send + Sync + 'static,
91{
92 let plugins = state.plugin_repository.list_paginated(query).await?;
93
94 let plugin_items: Vec<PluginModel> = plugins.items.into_iter().collect();
95
96 Ok(HttpResponse::Ok().json(ApiResponse::paginated(
97 plugin_items,
98 PaginationMeta {
99 total_items: plugins.total,
100 current_page: plugins.page,
101 per_page: plugins.per_page,
102 },
103 )))
104}
105
106#[cfg(test)]
107mod tests {
108 use std::time::Duration;
109
110 use super::*;
111 use actix_web::web;
112
113 use crate::{
114 constants::DEFAULT_PLUGIN_TIMEOUT_SECONDS, models::PluginModel,
115 utils::mocks::mockutils::create_mock_app_state,
116 };
117
118 #[actix_web::test]
119 async fn test_call_plugin() {
120 let plugin = PluginModel {
121 id: "test-plugin".to_string(),
122 path: "test-path".to_string(),
123 timeout: Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS),
124 };
125 let app_state = create_mock_app_state(None, None, None, Some(vec![plugin]), None).await;
126 let plugin_call_request = PluginCallRequest {
127 params: serde_json::json!({"key":"value"}),
128 };
129 let response = call_plugin(
130 "test-plugin".to_string(),
131 plugin_call_request,
132 web::ThinData(app_state),
133 )
134 .await;
135 assert!(response.is_ok());
136 }
137}