1use std::collections::BTreeMap;
8
9use serde::{Deserialize, Serialize};
10
11use super::error::ErrorDetail;
12use super::log::LogEntry;
13use super::metric::{MetricInfoDetail, MetricSeries};
14use super::trace::{Span, TraceDetail};
15use crate::provider::results::MetricResultType;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
22#[serde(bound(
23 serialize = "T: Serialize",
24 deserialize = "T: serde::de::DeserializeOwned"
25))]
26pub struct Response<T> {
27 pub status: ResponseStatus,
29
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub metadata: Option<QueryMetadata>,
33
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub data: Option<T>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
40 pub error: Option<ErrorDetail>,
41
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub warnings: Option<Vec<String>>,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49#[serde(rename_all = "snake_case")]
50pub enum ResponseStatus {
51 Success,
53 Error,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct QueryMetadata {
64 pub provider: String,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub provider_type: Option<String>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub query_language: Option<String>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub query: Option<String>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub time_range: Option<TimeRange>,
82
83 pub total_count: usize,
85
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub returned_series: Option<usize>,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
94 pub is_complete: Option<bool>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub cursor: Option<String>,
99}
100
101#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
103pub struct TimeRange {
104 pub start: i64,
106 pub end: i64,
108}
109
110impl<T: Serialize> Response<T> {
111 pub fn success(metadata: QueryMetadata, data: T) -> Self {
113 Self {
114 status: ResponseStatus::Success,
115 metadata: Some(metadata),
116 data: Some(data),
117 error: None,
118 warnings: None,
119 }
120 }
121}
122
123impl<T> Response<T> {
124 pub fn error(error: ErrorDetail) -> Self {
128 Self {
129 status: ResponseStatus::Error,
130 metadata: None,
131 data: None,
132 error: Some(error),
133 warnings: None,
134 }
135 }
136}
137
138pub const RESULT_TYPE_METRIC_LIST: &str = "metric_list";
143pub const RESULT_TYPE_METRIC_INFO: &str = "metric_info";
145pub const RESULT_TYPE_LABEL_LIST: &str = "label_list";
147pub const RESULT_TYPE_LABEL_VALUES: &str = "label_values";
149pub const RESULT_TYPE_SERIES: &str = "series";
151pub const RESULT_TYPE_LOG_ENTRIES: &str = "log_entries";
153pub const RESULT_TYPE_SPANS: &str = "spans";
155pub const RESULT_TYPE_TRACE_DETAIL: &str = "trace_detail";
157
158#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct MetricQueryData {
165 pub result_type: MetricResultType,
167 pub series: Vec<MetricSeries>,
169}
170
171#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ScalarData {
174 pub result_type: MetricResultType,
176 pub scalar: (i64, f64),
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct StringListData {
183 pub result_type: String,
185 pub items: Vec<String>,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct LabelValuesData {
192 pub result_type: String,
194 pub label: String,
196 pub items: Vec<String>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct MetricInfoData {
203 pub result_type: String,
205 pub info: Option<MetricInfoDetail>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct SeriesListData {
212 pub result_type: String,
214 pub series: Vec<BTreeMap<String, String>>,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct LogSearchData {
221 pub result_type: String,
223 pub entries: Vec<LogEntry>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct TraceSearchData {
230 pub result_type: String,
232 pub spans: Vec<Span>,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct TraceDetailData {
239 pub result_type: String,
241 #[serde(flatten)]
243 pub detail: TraceDetail,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct ExtensionData {
252 pub result_type: String,
254 pub data: serde_json::Value,
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_response_status_serialization() {
264 assert_eq!(
265 serde_json::to_string(&ResponseStatus::Success).unwrap(),
266 r#""success""#
267 );
268 assert_eq!(
269 serde_json::to_string(&ResponseStatus::Error).unwrap(),
270 r#""error""#
271 );
272 }
273
274 #[test]
275 fn test_success_response_structure() {
276 let metadata = QueryMetadata {
277 provider: "dev-vm".to_string(),
278 provider_type: None,
279 query_language: None,
280 query: None,
281 time_range: None,
282 total_count: 2,
283 returned_series: None,
284 is_complete: None,
285 cursor: None,
286 };
287
288 let resp = Response::success(
289 metadata,
290 serde_json::json!({"result_type": "matrix", "series": []}),
291 );
292
293 let json = serde_json::to_value(&resp).unwrap();
294 assert_eq!(json["status"], "success");
295 assert!(json.get("error").is_none());
296 assert_eq!(json["metadata"]["provider"], "dev-vm");
297 assert_eq!(json["metadata"]["total_count"], 2);
298 }
299
300 #[test]
301 fn test_error_response_structure() {
302 use super::super::error::{ErrorCategory, ErrorCode};
303
304 let detail = ErrorDetail {
305 category: ErrorCategory::Provider,
306 code: ErrorCode::QuerySyntax,
307 provider: Some("dev-vm".to_string()),
308 message: "invalid expression".to_string(),
309 raw_error: Some("bad_data".to_string()),
310 recoverable: false,
311 suggestion: Some("Check PromQL syntax".to_string()),
312 doc_url: None,
313 source_chain: None,
314 };
315
316 let resp: Response<serde_json::Value> = Response::error(detail);
317
318 let json = serde_json::to_value(&resp).unwrap();
319 assert_eq!(json["status"], "error");
320 assert!(json.get("data").is_none());
321 assert_eq!(json["error"]["category"], "provider");
322 assert_eq!(json["error"]["code"], "query_syntax");
323 }
324
325 #[test]
326 fn test_extension_data_serialization_string_list() {
327 let metadata = QueryMetadata {
328 provider: "dev-vt".to_string(),
329 provider_type: None,
330 query_language: None,
331 query: None,
332 time_range: None,
333 total_count: 3,
334 returned_series: None,
335 is_complete: None,
336 cursor: None,
337 };
338
339 let resp = Response::success(
340 metadata,
341 ExtensionData {
342 result_type: "services".to_string(),
343 data: serde_json::json!(["cart", "payment", "frontend"]),
344 },
345 );
346
347 let json = serde_json::to_value(&resp).unwrap();
348 assert_eq!(json["status"], "success");
349 assert_eq!(json["metadata"]["total_count"], 3);
350 assert_eq!(json["data"]["result_type"], "services");
351 let items = json["data"]["data"].as_array().unwrap();
352 assert_eq!(items.len(), 3);
353 assert_eq!(items[0], "cart");
354 }
355
356 #[test]
357 fn test_extension_data_serialization_structured() {
358 let metadata = QueryMetadata {
359 provider: "dev-vt".to_string(),
360 provider_type: None,
361 query_language: None,
362 query: None,
363 time_range: None,
364 total_count: 2,
365 returned_series: None,
366 is_complete: None,
367 cursor: None,
368 };
369
370 let resp = Response::success(
371 metadata,
372 ExtensionData {
373 result_type: "services".to_string(),
374 data: serde_json::json!([
375 {"name": "cart", "spans": 100},
376 {"name": "payment", "spans": 50}
377 ]),
378 },
379 );
380
381 let json = serde_json::to_value(&resp).unwrap();
382 assert_eq!(json["status"], "success");
383 assert_eq!(json["data"]["result_type"], "services");
384 let data = json["data"]["data"].as_array().unwrap();
385 assert_eq!(data.len(), 2);
386 assert_eq!(data[0]["name"], "cart");
387 assert_eq!(data[0]["spans"], 100);
388 }
389}