misp_client/
client_factory.rs1use std::time::Duration;
4use tracing::debug;
5
6use crate::attributes::AttributesClient;
7use crate::client::MispClient;
8use crate::error::MispError;
9use crate::events::EventsClient;
10use crate::galaxies::GalaxiesClient;
11use crate::sightings::SightingsClient;
12use crate::warninglists::WarninglistsClient;
13
14#[derive(Clone)]
15pub struct MispClientFactory {
16 base_url: String,
17 api_key: String,
18 verify_ssl: bool,
19 timeout: Duration,
20}
21
22impl MispClientFactory {
23 pub fn builder() -> MispClientFactoryBuilder {
24 MispClientFactoryBuilder::default()
25 }
26
27 fn create_client(&self) -> MispClient {
28 MispClient::with_timeout(&self.base_url, &self.api_key, self.verify_ssl, self.timeout)
29 }
30
31 pub fn events(&self) -> EventsClient {
32 debug!("Creating EventsClient");
33 EventsClient::new(self.create_client())
34 }
35
36 pub fn attributes(&self) -> AttributesClient {
37 debug!("Creating AttributesClient");
38 AttributesClient::new(self.create_client())
39 }
40
41 pub fn galaxies(&self) -> GalaxiesClient {
42 debug!("Creating GalaxiesClient");
43 GalaxiesClient::new(self.create_client())
44 }
45
46 pub fn sightings(&self) -> SightingsClient {
47 debug!("Creating SightingsClient");
48 SightingsClient::new(self.create_client())
49 }
50
51 pub fn warninglists(&self) -> WarninglistsClient {
52 debug!("Creating WarninglistsClient");
53 WarninglistsClient::new(self.create_client())
54 }
55
56 pub fn all(&self) -> MispClients {
57 debug!("Creating all MISP clients");
58 MispClients {
59 events: self.events(),
60 attributes: self.attributes(),
61 galaxies: self.galaxies(),
62 sightings: self.sightings(),
63 warninglists: self.warninglists(),
64 }
65 }
66
67 pub async fn test_connection(&self) -> Result<ServerInfo, MispError> {
68 debug!("Testing MISP connection");
69 let client = self.create_client();
70 let resp = client.get("/servers/getVersion").await?;
71
72 let version = resp
73 .get("version")
74 .and_then(|v| v.as_str())
75 .unwrap_or("unknown")
76 .to_string();
77
78 let perm_sync = resp
79 .get("perm_sync")
80 .and_then(|v| v.as_bool())
81 .unwrap_or(false);
82
83 let perm_sighting = resp
84 .get("perm_sighting")
85 .and_then(|v| v.as_bool())
86 .unwrap_or(false);
87
88 Ok(ServerInfo {
89 version,
90 perm_sync,
91 perm_sighting,
92 })
93 }
94}
95
96impl std::fmt::Debug for MispClientFactory {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 f.debug_struct("MispClientFactory")
99 .field("base_url", &self.base_url)
100 .field("verify_ssl", &self.verify_ssl)
101 .field("timeout", &self.timeout)
102 .finish()
103 }
104}
105
106#[derive(Default)]
107pub struct MispClientFactoryBuilder {
108 base_url: Option<String>,
109 api_key: Option<String>,
110 verify_ssl: Option<bool>,
111 timeout: Option<Duration>,
112}
113
114impl MispClientFactoryBuilder {
115 pub fn base_url(mut self, url: impl Into<String>) -> Self {
116 self.base_url = Some(url.into());
117 self
118 }
119
120 pub fn api_key(mut self, key: impl Into<String>) -> Self {
121 self.api_key = Some(key.into());
122 self
123 }
124
125 pub fn verify_ssl(mut self, verify: bool) -> Self {
126 self.verify_ssl = Some(verify);
127 self
128 }
129
130 pub fn timeout(mut self, timeout: Duration) -> Self {
131 self.timeout = Some(timeout);
132 self
133 }
134
135 pub fn build(self) -> MispClientFactory {
136 MispClientFactory {
137 base_url: self.base_url.expect("base_url is required"),
138 api_key: self.api_key.expect("api_key is required"),
139 verify_ssl: self.verify_ssl.unwrap_or(true),
140 timeout: self.timeout.unwrap_or(Duration::from_secs(30)),
141 }
142 }
143}
144
145#[derive(Debug)]
146pub struct MispClients {
147 pub events: EventsClient,
148 pub attributes: AttributesClient,
149 pub galaxies: GalaxiesClient,
150 pub sightings: SightingsClient,
151 pub warninglists: WarninglistsClient,
152}
153
154#[derive(Debug, Clone)]
155pub struct ServerInfo {
156 pub version: String,
157 pub perm_sync: bool,
158 pub perm_sighting: bool,
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn builder_creates_factory() {
167 let factory = MispClientFactory::builder()
168 .base_url("https://misp.local")
169 .api_key("test-key")
170 .verify_ssl(false)
171 .build();
172
173 let _events = factory.events();
174 let _attributes = factory.attributes();
175 let _galaxies = factory.galaxies();
176 }
177
178 #[test]
179 #[should_panic(expected = "base_url is required")]
180 fn builder_requires_base_url() {
181 MispClientFactory::builder().api_key("test-key").build();
182 }
183
184 #[test]
185 #[should_panic(expected = "api_key is required")]
186 fn builder_requires_api_key() {
187 MispClientFactory::builder()
188 .base_url("https://misp.local")
189 .build();
190 }
191}