pforge_runtime/handlers/
http.rs

1use crate::{Error, Result};
2use reqwest::{Client, Method};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone)]
8pub struct HttpHandler {
9    pub endpoint: String,
10    pub method: HttpMethod,
11    pub headers: HashMap<String, String>,
12    pub auth: Option<AuthConfig>,
13    client: Client,
14}
15
16#[derive(Debug, Clone)]
17pub enum HttpMethod {
18    Get,
19    Post,
20    Put,
21    Delete,
22    Patch,
23}
24
25#[derive(Debug, Clone)]
26pub enum AuthConfig {
27    Bearer { token: String },
28    Basic { username: String, password: String },
29    ApiKey { key: String, header: String },
30}
31
32#[derive(Debug, Deserialize, JsonSchema)]
33pub struct HttpInput {
34    #[serde(default)]
35    pub body: Option<serde_json::Value>,
36    #[serde(default)]
37    pub query: HashMap<String, String>,
38}
39
40#[derive(Debug, Serialize, JsonSchema)]
41pub struct HttpOutput {
42    pub status: u16,
43    pub body: serde_json::Value,
44    pub headers: HashMap<String, String>,
45}
46
47impl HttpHandler {
48    pub fn new(
49        endpoint: String,
50        method: HttpMethod,
51        headers: HashMap<String, String>,
52        auth: Option<AuthConfig>,
53    ) -> Self {
54        Self {
55            endpoint,
56            method,
57            headers,
58            auth,
59            client: Client::new(),
60        }
61    }
62
63    pub async fn execute(&self, input: HttpInput) -> Result<HttpOutput> {
64        let method = match self.method {
65            HttpMethod::Get => Method::GET,
66            HttpMethod::Post => Method::POST,
67            HttpMethod::Put => Method::PUT,
68            HttpMethod::Delete => Method::DELETE,
69            HttpMethod::Patch => Method::PATCH,
70        };
71
72        let mut request = self.client.request(method, &self.endpoint);
73
74        // Add headers
75        for (k, v) in &self.headers {
76            request = request.header(k, v);
77        }
78
79        // Add authentication
80        if let Some(auth) = &self.auth {
81            request = match auth {
82                AuthConfig::Bearer { token } => request.bearer_auth(token),
83                AuthConfig::Basic { username, password } => {
84                    request.basic_auth(username, Some(password))
85                }
86                AuthConfig::ApiKey { key, header } => request.header(header, key),
87            };
88        }
89
90        // Add query parameters
91        if !input.query.is_empty() {
92            request = request.query(&input.query);
93        }
94
95        // Add body for non-GET requests
96        if let Some(body) = input.body {
97            request = request.json(&body);
98        }
99
100        // Execute request
101        let response = request
102            .send()
103            .await
104            .map_err(|e| Error::Http(format!("Request failed: {}", e)))?;
105
106        let status = response.status().as_u16();
107
108        // Extract headers
109        let mut headers = HashMap::new();
110        for (k, v) in response.headers() {
111            if let Ok(v_str) = v.to_str() {
112                headers.insert(k.to_string(), v_str.to_string());
113            }
114        }
115
116        // Parse body as JSON (or empty object if fails)
117        let body = response
118            .json::<serde_json::Value>()
119            .await
120            .unwrap_or(serde_json::json!({}));
121
122        Ok(HttpOutput {
123            status,
124            body,
125            headers,
126        })
127    }
128}