use drasi_lib::reactions::Reaction;
use drasi_plugin_sdk::prelude::*;
use std::collections::HashMap;
use utoipa::OpenApi;
use crate::HttpReactionBuilder;
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[schema(as = reaction::http::CallSpec)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct CallSpecDto {
pub url: String,
pub method: String,
#[serde(default)]
pub body: String,
#[serde(default)]
pub headers: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[schema(as = reaction::http::HttpQueryConfig)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct HttpQueryConfigDto {
#[serde(skip_serializing_if = "Option::is_none")]
pub added: Option<CallSpecDto>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updated: Option<CallSpecDto>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deleted: Option<CallSpecDto>,
}
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[schema(as = reaction::http::HttpReactionConfig)]
#[serde(rename_all = "camelCase")]
pub struct HttpReactionConfigDto {
#[schema(value_type = ConfigValueString)]
pub base_url: ConfigValue<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<ConfigValueString>)]
pub token: Option<ConfigValue<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<ConfigValueU64>)]
pub timeout_ms: Option<ConfigValue<u64>>,
#[serde(default)]
pub routes: HashMap<String, HttpQueryConfigDto>,
}
fn map_call_spec(dto: &CallSpecDto) -> crate::CallSpec {
crate::CallSpec {
url: dto.url.clone(),
method: dto.method.clone(),
body: dto.body.clone(),
headers: dto.headers.clone(),
}
}
fn map_query_config(dto: &HttpQueryConfigDto) -> crate::QueryConfig {
crate::QueryConfig {
added: dto.added.as_ref().map(map_call_spec),
updated: dto.updated.as_ref().map(map_call_spec),
deleted: dto.deleted.as_ref().map(map_call_spec),
}
}
#[derive(OpenApi)]
#[openapi(components(schemas(HttpReactionConfigDto, HttpQueryConfigDto, CallSpecDto,)))]
struct HttpReactionSchemas;
pub struct HttpReactionDescriptor;
#[async_trait]
impl ReactionPluginDescriptor for HttpReactionDescriptor {
fn kind(&self) -> &str {
"http"
}
fn config_version(&self) -> &str {
"1.0.0"
}
fn config_schema_name(&self) -> &str {
"reaction.http.HttpReactionConfig"
}
fn config_schema_json(&self) -> String {
let api = HttpReactionSchemas::openapi();
serde_json::to_string(
&api.components
.as_ref()
.expect("OpenAPI components missing")
.schemas,
)
.expect("Failed to serialize config schema")
}
async fn create_reaction(
&self,
id: &str,
query_ids: Vec<String>,
config_json: &serde_json::Value,
auto_start: bool,
) -> anyhow::Result<Box<dyn Reaction>> {
let dto: HttpReactionConfigDto = serde_json::from_value(config_json.clone())?;
let mapper = DtoMapper::new();
let mut builder = HttpReactionBuilder::new(id)
.with_queries(query_ids)
.with_auto_start(auto_start)
.with_base_url(mapper.resolve_string(&dto.base_url)?);
if let Some(ref token) = dto.token {
builder = builder.with_token(mapper.resolve_string(token)?);
}
if let Some(ref timeout_ms) = dto.timeout_ms {
builder = builder.with_timeout_ms(mapper.resolve_typed(timeout_ms)?);
}
for (query_id, config) in &dto.routes {
builder = builder.with_route(query_id, map_query_config(config));
}
let reaction = builder.build()?;
Ok(Box::new(reaction))
}
}