1use crate::{
2 models::{ApiResponse, PluginCallRequest, PluginModel, UpdatePluginRequest},
3 repositories::PaginatedResult,
4 services::plugins::PluginHandlerError,
5};
6
7#[utoipa::path(
21 post,
22 path = "/api/v1/plugins/{plugin_id}/call",
23 tag = "Plugins",
24 operation_id = "callPlugin",
25 summary = "Execute a plugin with optional wildcard route routing",
26 security(
27 ("bearer_auth" = [])
28 ),
29 params(
30 ("plugin_id" = String, Path, description = "The unique identifier of the plugin"),
31 ("route" = Option<String>, Query, description = "Optional route suffix for custom routing (e.g., '/verify'). Alternative to appending the route to the URL path.")
32 ),
33 request_body = PluginCallRequest,
34 responses(
35 (
36 status = 200,
37 description = "Plugin call successful",
38 body = ApiResponse<serde_json::Value>,
39 example = json!({
40 "success": true,
41 "data": "done!",
42 "metadata": {
43 "logs": [
44 {
45 "level": "info",
46 "message": "Plugin started..."
47 }
48 ],
49 "traces": [
50 {
51 "method": "sendTransaction",
52 "relayerId": "sepolia-example",
53 "requestId": "6c1f336f-3030-4f90-bd99-ada190a1235b"
54 }
55 ]
56 },
57 "error": null
58 })
59 ),
60 (
61 status = 400,
62 description = "BadRequest (plugin-provided)",
63 body = ApiResponse<PluginHandlerError>,
64 example = json!({
65 "success": false,
66 "error": "Validation failed",
67 "data": { "code": "VALIDATION_FAILED", "details": { "field": "email" } },
68 "metadata": {
69 "logs": [
70 {
71 "level": "error",
72 "message": "Validation failed for field: email"
73 }
74 ]
75 }
76 })
77 ),
78 (
79 status = 401,
80 description = "Unauthorized",
81 body = ApiResponse<String>,
82 example = json!({
83 "success": false,
84 "error": "Unauthorized",
85 "data": null
86 })
87 ),
88 (
89 status = 404,
90 description = "Not Found",
91 body = ApiResponse<String>,
92 example = json!({
93 "success": false,
94 "error": "Plugin with ID plugin_id not found",
95 "data": null
96 })
97 ),
98 (
99 status = 429,
100 description = "Too Many Requests",
101 body = ApiResponse<String>,
102 example = json!({
103 "success": false,
104 "error": "Too Many Requests",
105 "data": null
106 })
107 ),
108 (
109 status = 500,
110 description = "Internal server error",
111 body = ApiResponse<String>,
112 example = json!({
113 "success": false,
114 "error": "Internal Server Error",
115 "data": null
116 })
117 ),
118 )
119)]
120#[allow(dead_code)]
121fn doc_call_plugin() {}
122
123#[utoipa::path(
134 get,
135 path = "/api/v1/plugins/{plugin_id}/call",
136 tag = "Plugins",
137 operation_id = "callPluginGet",
138 summary = "Execute a plugin via GET (must be enabled per plugin)",
139 security(
140 ("bearer_auth" = [])
141 ),
142 params(
143 ("plugin_id" = String, Path, description = "The unique identifier of the plugin"),
144 ("route" = Option<String>, Query, description = "Optional route suffix for custom routing (e.g., '/verify'). Alternative to appending the route to the URL path.")
145 ),
146 responses(
147 (
148 status = 200,
149 description = "Plugin call successful",
150 body = ApiResponse<serde_json::Value>
151 ),
152 (
153 status = 405,
154 description = "Method Not Allowed (GET invocation disabled for this plugin)",
155 body = ApiResponse<String>,
156 example = json!({
157 "success": false,
158 "error": "GET requests are not enabled for this plugin. Set 'allow_get_invocation: true' in plugin configuration to enable.",
159 "data": null
160 })
161 ),
162 (
163 status = 401,
164 description = "Unauthorized",
165 body = ApiResponse<String>,
166 example = json!({
167 "success": false,
168 "error": "Unauthorized",
169 "data": null
170 })
171 ),
172 (
173 status = 404,
174 description = "Not Found",
175 body = ApiResponse<String>,
176 example = json!({
177 "success": false,
178 "error": "Plugin with ID plugin_id not found",
179 "data": null
180 })
181 ),
182 (
183 status = 429,
184 description = "Too Many Requests",
185 body = ApiResponse<String>,
186 example = json!({
187 "success": false,
188 "error": "Too Many Requests",
189 "data": null
190 })
191 ),
192 (
193 status = 500,
194 description = "Internal server error",
195 body = ApiResponse<String>,
196 example = json!({
197 "success": false,
198 "error": "Internal Server Error",
199 "data": null
200 })
201 )
202 )
203)]
204#[allow(dead_code)]
205fn doc_call_plugin_get() {}
206
207#[utoipa::path(
209 get,
210 path = "/api/v1/plugins",
211 tag = "Plugins",
212 operation_id = "listPlugins",
213 security(
214 ("bearer_auth" = [])
215 ),
216 params(
217 ("page" = Option<usize>, Query, description = "Page number for pagination (starts at 1)"),
218 ("per_page" = Option<usize>, Query, description = "Number of items per page (default: 10)")
219 ),
220 responses(
221 (
222 status = 200,
223 description = "Plugins listed successfully",
224 body = ApiResponse<PaginatedResult<PluginModel>>
225 ),
226 (
227 status = 400,
228 description = "BadRequest",
229 body = ApiResponse<String>,
230 example = json!({
231 "success": false,
232 "error": "Bad Request",
233 "data": null
234 })
235 ),
236 (
237 status = 401,
238 description = "Unauthorized",
239 body = ApiResponse<String>,
240 example = json!({
241 "success": false,
242 "error": "Unauthorized",
243 "data": null
244 })
245 ),
246 (
247 status = 404,
248 description = "Not Found",
249 body = ApiResponse<String>,
250 example = json!({
251 "success": false,
252 "error": "Plugin with ID plugin_id not found",
253 "data": null
254 })
255 ),
256 (
257 status = 429,
258 description = "Too Many Requests",
259 body = ApiResponse<String>,
260 example = json!({
261 "success": false,
262 "error": "Too Many Requests",
263 "data": null
264 })
265 ),
266 (
267 status = 500,
268 description = "Internal server error",
269 body = ApiResponse<String>,
270 example = json!({
271 "success": false,
272 "error": "Internal Server Error",
273 "data": null
274 })
275 ),
276 )
277)]
278#[allow(dead_code)]
279fn doc_list_plugins() {}
280
281#[utoipa::path(
283 get,
284 path = "/api/v1/plugins/{plugin_id}",
285 tag = "Plugins",
286 operation_id = "getPlugin",
287 summary = "Get plugin by ID",
288 security(
289 ("bearer_auth" = [])
290 ),
291 params(
292 ("plugin_id" = String, Path, description = "The unique identifier of the plugin")
293 ),
294 responses(
295 (
296 status = 200,
297 description = "Plugin retrieved successfully",
298 body = ApiResponse<PluginModel>,
299 example = json!({
300 "success": true,
301 "data": {
302 "id": "my-plugin",
303 "path": "plugins/my-plugin.ts",
304 "timeout": 30,
305 "emit_logs": false,
306 "emit_traces": false,
307 "raw_response": false,
308 "allow_get_invocation": false,
309 "config": {
310 "featureFlag": true
311 },
312 "forward_logs": false
313 },
314 "error": null
315 })
316 ),
317 (
318 status = 401,
319 description = "Unauthorized",
320 body = ApiResponse<String>,
321 example = json!({
322 "success": false,
323 "error": "Unauthorized",
324 "data": null
325 })
326 ),
327 (
328 status = 404,
329 description = "Plugin not found",
330 body = ApiResponse<String>,
331 example = json!({
332 "success": false,
333 "error": "Plugin with id my-plugin not found",
334 "data": null
335 })
336 ),
337 (
338 status = 429,
339 description = "Too Many Requests",
340 body = ApiResponse<String>,
341 example = json!({
342 "success": false,
343 "error": "Too Many Requests",
344 "data": null
345 })
346 ),
347 (
348 status = 500,
349 description = "Internal server error",
350 body = ApiResponse<String>,
351 example = json!({
352 "success": false,
353 "error": "Internal Server Error",
354 "data": null
355 })
356 )
357 )
358)]
359#[allow(dead_code)]
360fn doc_get_plugin() {}
361
362#[utoipa::path(
371 patch,
372 path = "/api/v1/plugins/{plugin_id}",
373 tag = "Plugins",
374 operation_id = "updatePlugin",
375 summary = "Update plugin configuration",
376 security(
377 ("bearer_auth" = [])
378 ),
379 params(
380 ("plugin_id" = String, Path, description = "The unique identifier of the plugin")
381 ),
382 request_body(
383 content = UpdatePluginRequest,
384 description = "Plugin configuration update. All fields are optional.",
385 example = json!({
386 "timeout": 60,
387 "emit_logs": true,
388 "forward_logs": true,
389 "config": {
390 "featureFlag": true,
391 "apiKey": "xyz123"
392 }
393 })
394 ),
395 responses(
396 (
397 status = 200,
398 description = "Plugin updated successfully",
399 body = ApiResponse<PluginModel>,
400 example = json!({
401 "success": true,
402 "data": {
403 "id": "my-plugin",
404 "path": "plugins/my-plugin.ts",
405 "timeout": 60,
406 "emit_logs": true,
407 "emit_traces": false,
408 "raw_response": false,
409 "allow_get_invocation": false,
410 "config": {
411 "featureFlag": true,
412 "apiKey": "xyz123"
413 },
414 "forward_logs": true
415 },
416 "error": null
417 })
418 ),
419 (
420 status = 400,
421 description = "Bad Request (invalid timeout or other validation error)",
422 body = ApiResponse<String>,
423 example = json!({
424 "success": false,
425 "error": "Timeout must be greater than 0",
426 "data": null
427 })
428 ),
429 (
430 status = 401,
431 description = "Unauthorized",
432 body = ApiResponse<String>,
433 example = json!({
434 "success": false,
435 "error": "Unauthorized",
436 "data": null
437 })
438 ),
439 (
440 status = 404,
441 description = "Plugin not found",
442 body = ApiResponse<String>,
443 example = json!({
444 "success": false,
445 "error": "Plugin with id my-plugin not found",
446 "data": null
447 })
448 ),
449 (
450 status = 429,
451 description = "Too Many Requests",
452 body = ApiResponse<String>,
453 example = json!({
454 "success": false,
455 "error": "Too Many Requests",
456 "data": null
457 })
458 ),
459 (
460 status = 500,
461 description = "Internal server error",
462 body = ApiResponse<String>,
463 example = json!({
464 "success": false,
465 "error": "Internal Server Error",
466 "data": null
467 })
468 )
469 )
470)]
471#[allow(dead_code)]
472fn doc_update_plugin() {}