servers/http/result/
json_result.rs1use axum::http::{HeaderValue, header};
16use axum::response::{IntoResponse, Response};
17use common_error::status_code::StatusCode;
18use common_query::Output;
19use mime_guess::mime;
20use serde::{Deserialize, Serialize};
21use serde_json::{Map, Value, json};
22
23use crate::http::header::{GREPTIME_DB_HEADER_EXECUTION_TIME, GREPTIME_DB_HEADER_FORMAT};
24use crate::http::result::error_result::ErrorResponse;
25use crate::http::{GreptimeQueryOutput, HttpResponse, ResponseFormat, handler, process_with_limit};
26
27#[derive(Serialize, Deserialize, Debug)]
30pub struct JsonResponse {
31 output: Vec<GreptimeQueryOutput>,
32 execution_time_ms: u64,
33}
34
35impl JsonResponse {
36 pub async fn from_output(outputs: Vec<crate::error::Result<Output>>) -> HttpResponse {
37 match handler::from_output(outputs).await {
38 Err(err) => HttpResponse::Error(err),
39 Ok((output, _)) => {
40 if output.len() > 1 {
41 HttpResponse::Error(ErrorResponse::from_error_message(
42 StatusCode::InvalidArguments,
43 "cannot output multi-statements result in json format".to_string(),
44 ))
45 } else {
46 HttpResponse::Json(JsonResponse {
47 output,
48 execution_time_ms: 0,
49 })
50 }
51 }
52 }
53 }
54
55 pub fn output(&self) -> &[GreptimeQueryOutput] {
56 &self.output
57 }
58
59 pub fn with_execution_time(mut self, execution_time: u64) -> Self {
60 self.execution_time_ms = execution_time;
61 self
62 }
63
64 pub fn execution_time_ms(&self) -> u64 {
65 self.execution_time_ms
66 }
67
68 pub fn with_limit(mut self, limit: usize) -> Self {
69 self.output = process_with_limit(self.output, limit);
70 self
71 }
72}
73
74impl IntoResponse for JsonResponse {
75 fn into_response(mut self) -> Response {
76 debug_assert!(
77 self.output.len() <= 1,
78 "self.output has extra elements: {}",
79 self.output.len()
80 );
81
82 let execution_time = self.execution_time_ms;
83 let payload = match self.output.pop() {
84 None => String::default(),
85 Some(GreptimeQueryOutput::AffectedRows(n)) => json!({
86 "data": [],
87 "affected_rows": n,
88 "execution_time_ms": execution_time,
89 })
90 .to_string(),
91
92 Some(GreptimeQueryOutput::Records(records)) => {
93 let schema = &records.schema;
95
96 let data: Vec<Map<String, Value>> = records
97 .rows
98 .into_iter()
99 .map(|mut row| {
100 schema
103 .column_schemas
104 .iter()
105 .zip(row[..schema.column_schemas.len()].iter_mut())
106 .map(|(col, value)| (col.name.clone(), value.take()))
107 .collect::<Map<String, Value>>()
108 })
109 .collect();
110
111 let data = Value::Array(data.into_iter().map(Value::Object).collect());
112 let mut payload = Map::new();
113 payload.insert("data".to_string(), data);
114 payload.insert("execution_time_ms".to_string(), Value::from(execution_time));
115 Value::Object(payload).to_string()
116 }
117 };
118
119 (
120 [
121 (
122 header::CONTENT_TYPE,
123 HeaderValue::from_static(mime::APPLICATION_JSON.as_ref()),
124 ),
125 (
126 GREPTIME_DB_HEADER_FORMAT.clone(),
127 HeaderValue::from_static(ResponseFormat::Json.as_str()),
128 ),
129 (
130 GREPTIME_DB_HEADER_EXECUTION_TIME.clone(),
131 HeaderValue::from(execution_time),
132 ),
133 ],
134 payload,
135 )
136 .into_response()
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use axum::body::to_bytes;
143
144 use super::*;
145
146 #[tokio::test]
147 async fn test_records_response_preserves_values_and_duplicate_columns() {
148 let response: JsonResponse = serde_json::from_value(json!({
149 "output": [{"records": {
150 "schema": {"column_schemas": [
151 {"name": "duplicate", "data_type": "String"},
152 {"name": "nested", "data_type": "Json"},
153 {"name": "duplicate", "data_type": "String"},
154 {"name": "escaped\"column", "data_type": "String"}
155 ]},
156 "rows": [
157 ["discarded", {"array": [null, true, "中文"]}, "last", "line\n\\\""],
158 ["discarded", [1, {"key": "value"}], null, ""]
159 ]
160 }}],
161 "execution_time_ms": 7
162 }))
163 .unwrap();
164
165 let response = response.into_response();
166 assert_eq!(response.status(), axum::http::StatusCode::OK);
167 assert_eq!(response.headers()[header::CONTENT_TYPE], "application/json");
168 assert_eq!(response.headers()[&GREPTIME_DB_HEADER_FORMAT], "json");
169 assert_eq!(response.headers()[&GREPTIME_DB_HEADER_EXECUTION_TIME], "7");
170 let body = to_bytes(response.into_body(), usize::MAX).await.unwrap();
171 assert_eq!(
172 serde_json::from_slice::<Value>(&body).unwrap(),
173 json!({
174 "data": [
175 {"duplicate": "last", "nested": {"array": [null, true, "中文"]}, "escaped\"column": "line\n\\\""},
176 {"duplicate": null, "nested": [1, {"key": "value"}], "escaped\"column": ""}
177 ],
178 "execution_time_ms": 7
179 })
180 );
181 }
182}