bgpkit_broker/query.rs
1//! Query-related structs and implementation.
2use crate::BrokerItem;
3use serde::{Deserialize, Serialize};
4use std::fmt::{Display, Formatter};
5use std::net::IpAddr;
6
7/// QueryParams represents the query parameters to the backend API.
8///
9/// Example for constructing a QueryParams:
10/// ```
11/// use bgpkit_broker::QueryParams;
12/// let mut params = QueryParams::new();
13/// params = params.ts_start("1633046400");
14/// params = params.ts_end("1633132800");
15/// params = params.collector_id("rrc00");
16/// params = params.project("riperis");
17/// params = params.data_type("rib");
18/// params = params.page(2);
19/// params = params.page_size(20);
20/// ```
21/// The above example constructs a query that searches for BGP archive files that are:
22/// - after 2021-10-01T00:00:00 UTC
23/// - before 2021-10-02T00:00:00 UTC
24/// - from collector `rrc00`
25/// - from `riperis` collectors (already implied by collector=`rrc00` though)
26/// - rib table dump files
27/// - second page
28/// - each page contains 20 items
29#[derive(Debug, Serialize, Deserialize, Clone)]
30pub struct QueryParams {
31 /// start unix timestamp: files with time after or equals to `ts_start` will match
32 pub ts_start: Option<String>,
33 /// end unix timestamp: files with time before or equals to `ts_end` will match
34 pub ts_end: Option<String>,
35 /// collector identifier, e.g. `rrc00` or `route-views2`
36 pub collector_id: Option<String>,
37 /// archive project name: `riperis` or `routeviews`
38 pub project: Option<String>,
39 /// archive data type: `rib` or `updates`
40 pub data_type: Option<String>,
41 /// page number to seek to, starting from 1, default to 1
42 pub page: i64,
43 /// number of items each page contains, default to 10, max to 100000
44 pub page_size: i64,
45 /// collector peer IP address (for listing peers info)
46 pub peers_ip: Option<IpAddr>,
47 /// collector peer ASN (for listing peers info)
48 pub peers_asn: Option<u32>,
49 /// collector peer full feed status (for listing peers info)
50 pub peers_only_full_feed: bool,
51}
52
53/// Sorting order enum
54#[derive(Debug, Serialize, Deserialize, Clone)]
55pub enum SortOrder {
56 /// `ASC` -> sort by increasing on timestamp
57 ASC,
58 /// `DESC` -> sort by decreasing on timestamp
59 DESC,
60}
61
62/// Default [QueryParams] values
63impl Default for QueryParams {
64 fn default() -> Self {
65 QueryParams {
66 ts_start: None,
67 ts_end: None,
68 collector_id: None,
69 project: None,
70 data_type: None,
71 page: 1,
72 page_size: 100,
73 peers_ip: None,
74 peers_asn: None,
75 peers_only_full_feed: false,
76 }
77 }
78}
79
80impl Display for SortOrder {
81 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
82 match self {
83 SortOrder::ASC => {
84 write!(f, "asc")
85 }
86 SortOrder::DESC => {
87 write!(f, "desc")
88 }
89 }
90 }
91}
92
93impl Display for QueryParams {
94 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
95 let mut params_vec = vec![];
96 if let Some(v) = &self.ts_start {
97 params_vec.push(format!("ts_start={}", v));
98 }
99 if let Some(v) = &self.ts_end {
100 params_vec.push(format!("ts_end={}", v));
101 }
102 if let Some(v) = &self.collector_id {
103 params_vec.push(format!("collector_id={}", v));
104 }
105 if let Some(v) = &self.project {
106 params_vec.push(format!("project={}", v));
107 }
108 if let Some(v) = &self.data_type {
109 params_vec.push(format!("data_type={}", v));
110 }
111 params_vec.push(format!("page={}", self.page));
112 params_vec.push(format!("page_size={}", self.page_size));
113
114 if !params_vec.is_empty() {
115 write!(f, "?{}", params_vec.join("&"))
116 } else {
117 write!(f, "")
118 }
119 }
120}
121
122impl QueryParams {
123 pub fn new() -> QueryParams {
124 QueryParams {
125 ts_start: None,
126 ts_end: None,
127 collector_id: None,
128 project: None,
129 data_type: None,
130 page: 1,
131 page_size: 10,
132 ..Default::default()
133 }
134 }
135
136 /// set starting timestamp for the search and returns a new [QueryParams] object.
137 ///
138 /// ```
139 /// use bgpkit_broker::QueryParams;
140 /// let mut params = QueryParams::new();
141 /// params = params.ts_start("1633046400");
142 /// ```
143 pub fn ts_start(self, ts_start: &str) -> Self {
144 QueryParams {
145 ts_start: Some(ts_start.to_string()),
146 ..self
147 }
148 }
149
150 /// set ending timestamp for the search and returns a new [QueryParams] object.
151 ///
152 /// ```
153 /// use bgpkit_broker::QueryParams;
154 /// let mut params = QueryParams::new();
155 /// params = params.ts_end("1633046400");
156 /// ```
157 pub fn ts_end(self, ts_end: &str) -> Self {
158 QueryParams {
159 ts_end: Some(ts_end.to_string()),
160 ..self
161 }
162 }
163
164 /// set page number for the each for pagination. **the page number starts from 1**.
165 ///
166 /// ```
167 /// use bgpkit_broker::QueryParams;
168 /// let mut params = QueryParams::new();
169 /// params = params.page(3);
170 /// ```
171 pub fn page(self, page: i64) -> Self {
172 QueryParams { page, ..self }
173 }
174
175 /// set each page's size (number of items per page).
176 ///
177 /// ```
178 /// use bgpkit_broker::QueryParams;
179 /// let mut params = QueryParams::new();
180 /// params = params.page_size(20);
181 /// ```
182 pub fn page_size(self, page_size: i64) -> Self {
183 QueryParams { page_size, ..self }
184 }
185
186 /// set the type of data to search for:
187 /// - `rib`: table dump files
188 /// - `updates`: BGP updates files
189 ///
190 /// Without specifying a data type, it defaults to search for all types.
191 ///
192 /// ```
193 /// use bgpkit_broker::QueryParams;
194 /// let mut params = QueryParams::new();
195 /// params = params.data_type("rib");
196 /// ```
197 pub fn data_type(self, data_type: &str) -> Self {
198 QueryParams {
199 data_type: Some(data_type.to_string()),
200 ..self
201 }
202 }
203
204 /// set searching for only data from specific project:
205 /// - `routeviews`: RouteViews
206 /// - `riperis`: RIPE RIS
207 ///
208 /// ```
209 /// use bgpkit_broker::QueryParams;
210 /// let mut params = QueryParams::new();
211 /// params = params.project("routeviews");
212 /// ```
213 pub fn project(self, project: &str) -> Self {
214 QueryParams {
215 project: Some(project.to_string()),
216 ..self
217 }
218 }
219
220 /// set searching for only data from specific collector,
221 /// examples: `rrc00`, `route-views2`
222 ///
223 /// ```
224 /// use bgpkit_broker::QueryParams;
225 /// let mut params = QueryParams::new();
226 /// params = params.collector_id("rrc00");
227 /// ```
228 pub fn collector_id(self, collector_id: &str) -> Self {
229 QueryParams {
230 collector_id: Some(collector_id.to_string()),
231 ..self
232 }
233 }
234}
235
236#[allow(dead_code)]
237#[derive(Debug, Clone, Serialize, Deserialize)]
238#[cfg_attr(feature = "cli", derive(tabled::Tabled))]
239pub struct BrokerCollector {
240 pub id: i64,
241 pub name: String,
242 pub url: String,
243 pub project: String,
244 pub updates_interval: i64,
245}
246
247#[allow(dead_code)]
248#[derive(Debug, Clone, Serialize, Deserialize)]
249#[cfg_attr(feature = "cli", derive(tabled::Tabled))]
250pub struct BrokerItemType {
251 pub id: i64,
252 pub name: String,
253}
254
255#[derive(Debug, Serialize, Deserialize)]
256pub(crate) struct CollectorLatestResult {
257 /// total number of items
258 pub count: u32,
259
260 /// array of [BrokerItem]
261 pub data: Vec<BrokerItem>,
262}
263
264/// Query result struct that contains data or error message
265#[derive(Debug, Serialize, Deserialize)]
266pub(crate) struct BrokerQueryResult {
267 /// total number of items
268 pub total: Option<i64>,
269 /// number of items returned in **current** call
270 pub count: Option<i64>,
271 /// the page number of the current call
272 pub page: Option<i64>,
273 /// the number of items per page
274 pub page_size: Option<i64>,
275 /// Error message
276 pub error: Option<String>,
277 /// the returning data [Item]s
278 pub data: Vec<BrokerItem>,
279}
280
281#[allow(clippy::unwrap_used)]
282impl Display for BrokerQueryResult {
283 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
284 write!(f, "{}", serde_json::to_string(self).unwrap())
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn test_param_to_string() {
294 let param = QueryParams {
295 ts_start: Some("1".to_string()),
296 ts_end: Some("2".to_string()),
297 collector_id: None,
298 project: Some("test_project".to_string()),
299 data_type: None,
300 page: 1,
301 page_size: 20,
302 ..Default::default()
303 };
304
305 assert_eq!(
306 "?ts_start=1&ts_end=2&project=test_project&page=1&page_size=20".to_string(),
307 param.to_string()
308 );
309
310 let param = QueryParams {
311 ts_start: None,
312 ts_end: None,
313 collector_id: None,
314 project: None,
315 data_type: None,
316 page: 1,
317 page_size: 20,
318 ..Default::default()
319 };
320
321 assert_eq!("?page=1&page_size=20".to_string(), param.to_string());
322 }
323}