From 52ce278267fb4c6f337549901b4159e2919e82d1 Mon Sep 17 00:00:00 2001 From: Konstantin Burkalev Date: Tue, 17 Dec 2024 17:38:11 +0200 Subject: [PATCH] =?UTF-8?q?always=20use=20transform=20data=20native=20for?= =?UTF-8?q?=C2=A0all=20results?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/cubejs-api-gateway/src/gateway.ts | 135 +++++------------- packages/cubejs-backend-native/js/index.ts | 12 +- .../src/node_obj_deserializer.rs | 2 +- .../cubejs-backend-native/src/orchestrator.rs | 87 ++++++----- rust/cubeorchestrator/src/lib.rs | 4 +- ...sage_parser.rs => query_message_parser.rs} | 51 +++++-- ...transform.rs => query_result_transform.rs} | 32 +++-- rust/cubeorchestrator/src/transport.rs | 8 +- 8 files changed, 171 insertions(+), 160 deletions(-) rename rust/cubeorchestrator/src/{cubestore_message_parser.rs => query_message_parser.rs} (70%) rename rust/cubeorchestrator/src/{cubestore_result_transform.rs => query_result_transform.rs} (94%) diff --git a/packages/cubejs-api-gateway/src/gateway.ts b/packages/cubejs-api-gateway/src/gateway.ts index c24d95b773d01..fbed0677418e0 100644 --- a/packages/cubejs-api-gateway/src/gateway.ts +++ b/packages/cubejs-api-gateway/src/gateway.ts @@ -11,9 +11,9 @@ import { QueryAlias, } from '@cubejs-backend/shared'; import { - getFinalCubestoreResult, - getFinalCubestoreResultArray, - getFinalCubestoreResultMulti, + getFinalQueryResult, + getFinalQueryResultArray, + getFinalQueryResultMulti, transformData as transformDataNative, TransformDataResponse } from '@cubejs-backend/native'; @@ -123,10 +123,8 @@ function systemAsyncHandler(handler: (req: Request & { context: ExtendedRequestC function cleanupResult(result) { return { ...result, - dataCb: undefined, rawData: undefined, transformDataParams: undefined, - isNative: undefined, }; } @@ -1625,23 +1623,8 @@ class ApiGateway { }; // We postpone data transformation until the last minute - // in case when all responses are native - we process them in native part - const dataCb: TransformDataResponseCb = response.data.isNative ? - async () => { - const jsonData = await transformDataNative( - transformDataParams, response.data.getNativeRef() - ); - return JSON.parse(jsonData.result) as TransformDataResponse; - } - : - async () => transformData({ - ...transformDataParams, - data: response.data, - }); - return { query: normalizedQuery, - dataCb, rawData: response.data, transformDataParams, lastRefreshTime: response.lastRefreshTime?.toISOString(), @@ -1663,7 +1646,6 @@ class ApiGateway { external: response.external, slowQuery: Boolean(response.slowQuery), total: normalizedQuery.total ? response.total : null, - isNative: response.data.isNative }; } @@ -1822,57 +1804,30 @@ class ApiGateway { context, ); - const allNative = results.every(r => r.isNative); - if (props.queryType === 'multi') { - // If all query results are from Cubestore (are native) - // we prepare the final json result on native side - if (allNative) { - const [transformDataJson, rawDataRef, cleanResultList] = results.reduce<[Object[], any[], Object[]]>( - ([transformList, rawList, resultList], r) => { - transformList.push(r.transformDataParams); - rawList.push(r.rawData.getNativeRef()); - resultList.push(cleanupResult(r)); - return [transformList, rawList, resultList]; - }, - [[], [], []] - ); + // We prepare the final json result on native side + const [transformDataJson, rawDataRef, cleanResultList] = results.reduce<[Object[], any[], Object[]]>( + ([transformList, rawList, resultList], r) => { + transformList.push(r.transformDataParams); + rawList.push(r.rawData.isNative ? r.rawData.getNativeRef() : r.rawData); + resultList.push(cleanupResult(r)); + return [transformList, rawList, resultList]; + }, + [[], [], []] + ); - const responseDataObj = { - queryType, - results: cleanResultList, - slowQuery - }; + const responseDataObj = { + queryType, + results: cleanResultList, + slowQuery + }; - res(await getFinalCubestoreResultMulti(transformDataJson, rawDataRef, responseDataObj)); - } else { - // if we have mixed query results (there are js and native) - // we prepare results separately: on js and native sides - // and serve final response from JS side - res({ - queryType, - results: await Promise.all(results.map(async (r) => { - const data = await r.dataCb(); - return { - ...cleanupResult(r), - data, - }; - })), - pivotQuery: getPivotQuery(queryType, normalizedQueries), - slowQuery - }); - } - } else if (allNative) { + res(await getFinalQueryResultMulti(transformDataJson, rawDataRef, responseDataObj)); + } else { // We prepare the full final json result on native side const r = results[0]; - const rawDataRef = r.rawData.getNativeRef(); - res(await getFinalCubestoreResult(r.transformDataParams, rawDataRef, cleanupResult(r))); - } else { - const data = await results[0].dataCb(); - res({ - ...cleanupResult(results[0]), - data, - }); + const rawData = r.rawData.isNative ? r.rawData.getNativeRef() : r.rawData; + res(await getFinalQueryResult(r.transformDataParams, rawData, cleanupResult(r))); } } catch (e: any) { this.handleError({ @@ -2002,40 +1957,22 @@ class ApiGateway { }) ); - const allNative = results.every(r => r.isNative); - if (!request.streaming) { - // If all query results are from Cubestore (are native) - // we prepare the final json result on native side - if (allNative) { - const [transformDataJson, rawDataRef, resultDataJson] = (results as { - transformDataParams: any; - rawData: { getNativeRef: () => any }; - }[]).reduce<[Object[], any[], Object[]]>( - ([transformList, rawList, resultList], r) => { - transformList.push(r.transformDataParams); - rawList.push(r.rawData.getNativeRef()); - resultList.push(cleanupResult(r)); - return [transformList, rawList, resultList]; - }, - [[], [], []] - ); + // We prepare the final json result on native side + const [transformDataJson, rawData, resultDataJson] = (results as { + transformDataParams: any; + rawData: { isNative: boolean, getNativeRef: () => any }; + }[]).reduce<[Object[], any[], Object[]]>( + ([transformList, rawList, resultList], r) => { + transformList.push(r.transformDataParams); + rawList.push(r.rawData.isNative ? r.rawData.getNativeRef() : r.rawData); + resultList.push(cleanupResult(r)); + return [transformList, rawList, resultList]; + }, + [[], [], []] + ); - res(await getFinalCubestoreResultArray(transformDataJson, rawDataRef, resultDataJson)); - } else { - // if we have mixed query results (there are js and native) - // we prepare results separately: on js and native sides - // and serve final response from JS side - res({ - results: await Promise.all(results.map(async (r) => { - const data = await r.dataCb(); - return { - ...cleanupResult(r), - data, - }; - })), - }); - } + res(await getFinalQueryResultArray(transformDataJson, rawData, resultDataJson)); } else { res(results[0]); } diff --git a/packages/cubejs-backend-native/js/index.ts b/packages/cubejs-backend-native/js/index.ts index f2c30844bd5b6..1a429217474a4 100644 --- a/packages/cubejs-backend-native/js/index.ts +++ b/packages/cubejs-backend-native/js/index.ts @@ -389,22 +389,22 @@ export const transformData = (transformDataObj: Object, rows: any): Promise => { +export const getFinalQueryResult = (transformDataObj: Object, rows: any, resultData: Object): Promise => { const native = loadNative(); - return native.getFinalCubestoreResult(transformDataObj, rows, resultData); + return native.getFinalQueryResult(transformDataObj, rows, resultData); }; -export const getFinalCubestoreResultArray = (transformDataArr: Object[], rows: any[], resultDataArr: Object[]): Promise => { +export const getFinalQueryResultArray = (transformDataArr: Object[], rows: any[], resultDataArr: Object[]): Promise => { const native = loadNative(); - return native.getFinalCubestoreResultArray(transformDataArr, rows, resultDataArr); + return native.getFinalQueryResultArray(transformDataArr, rows, resultDataArr); }; -export const getFinalCubestoreResultMulti = (transformDataArr: Object[], rows: any[], responseData: Object): Promise => { +export const getFinalQueryResultMulti = (transformDataArr: Object[], rows: any[], responseData: Object): Promise => { const native = loadNative(); - return native.getFinalCubestoreResultMulti(transformDataArr, rows, responseData); + return native.getFinalQueryResultMulti(transformDataArr, rows, responseData); }; export interface PyConfiguration { diff --git a/packages/cubejs-backend-native/src/node_obj_deserializer.rs b/packages/cubejs-backend-native/src/node_obj_deserializer.rs index 3b666599e568f..95787b4275665 100644 --- a/packages/cubejs-backend-native/src/node_obj_deserializer.rs +++ b/packages/cubejs-backend-native/src/node_obj_deserializer.rs @@ -103,7 +103,7 @@ impl<'de, 'a, 'b> Deserializer<'de> for JsValueDeserializer<'a, 'b> { } Err(JsDeserializationError( - "Unsupported type for deserialization".to_string(), + "Unsupported number type for deserialization".to_string(), )) } else if self.value.is_a::(self.cx) { let value = self diff --git a/packages/cubejs-backend-native/src/orchestrator.rs b/packages/cubejs-backend-native/src/orchestrator.rs index 861a36e4d4ce7..d4b0183759c8f 100644 --- a/packages/cubejs-backend-native/src/orchestrator.rs +++ b/packages/cubejs-backend-native/src/orchestrator.rs @@ -1,10 +1,10 @@ use crate::node_obj_deserializer::JsValueDeserializer; -use cubeorchestrator::cubestore_message_parser::CubeStoreResult; -use cubeorchestrator::cubestore_result_transform::{ +use cubeorchestrator::query_message_parser::QueryResult; +use cubeorchestrator::query_result_transform::{ get_final_cubestore_result_array, RequestResultArray, RequestResultData, RequestResultDataMulti, TransformedData, }; -use cubeorchestrator::transport::TransformDataRequest; +use cubeorchestrator::transport::{JsRawData, TransformDataRequest}; use neon::context::{Context, FunctionContext, ModuleContext}; use neon::handle::Handle; use neon::object::Object; @@ -22,9 +22,9 @@ pub fn register_module(cx: &mut ModuleContext) -> NeonResult<()> { )?; cx.export_function("getCubestoreResult", get_cubestore_result)?; cx.export_function("transformQueryData", transform_query_data)?; - cx.export_function("getFinalCubestoreResult", final_cubestore_result)?; - cx.export_function("getFinalCubestoreResultMulti", final_cubestore_result_multi)?; - cx.export_function("getFinalCubestoreResultArray", final_cubestore_result_array)?; + cx.export_function("getFinalQueryResult", final_query_result)?; + cx.export_function("getFinalQueryResultMulti", final_query_result_multi)?; + cx.export_function("getFinalQueryResultArray", final_query_result_array)?; Ok(()) } @@ -50,12 +50,31 @@ where } } +fn extract_query_result( + cx: &mut FunctionContext<'_>, + data_arg: Handle, +) -> Result, anyhow::Error> { + + if let Ok(js_box) = data_arg.downcast::>, _>(cx) { + Ok(Arc::clone(&js_box)) + } else if let Ok(js_array) = data_arg.downcast::(cx) { + let deserializer = JsValueDeserializer::new(cx, js_array.upcast()); + let js_raw_data: JsRawData = Deserialize::deserialize(deserializer)?; + + QueryResult::from_js_raw_data(js_raw_data) + .map(Arc::new) + .map_err(anyhow::Error::from) + } else { + Err(anyhow::anyhow!("Second argument must be an Array of JsBox> or JsArray")) + } +} + pub fn parse_cubestore_result_message(mut cx: FunctionContext) -> JsResult { let msg = cx.argument::(0)?; let msg_data = msg.as_slice(&cx).to_vec(); let promise = cx - .task(move || CubeStoreResult::from_fb(&msg_data)) + .task(move || QueryResult::from_cubestore_fb(&msg_data)) .promise(move |mut cx, res| match res { Ok(result) => Ok(cx.boxed(Arc::new(result))), Err(err) => cx.throw_error(err.to_string()), @@ -65,7 +84,7 @@ pub fn parse_cubestore_result_message(mut cx: FunctionContext) -> JsResult JsResult { - let result = cx.argument::>>(0)?; + let result = cx.argument::>>(0)?; let js_array = cx.execute_scoped(|mut cx| { let js_array = JsArray::new(&mut cx, result.rows.len()); @@ -99,7 +118,7 @@ pub fn transform_query_data(mut cx: FunctionContext) -> JsResult { Err(err) => return cx.throw_error(err.to_string()), }; - let cube_store_result = cx.argument::>>(1)?; + let cube_store_result = cx.argument::>>(1)?; let cube_store_result = Arc::clone(&cube_store_result); let promise = cx @@ -126,7 +145,7 @@ pub fn transform_query_data(mut cx: FunctionContext) -> JsResult { Ok(promise) } -pub fn final_cubestore_result(mut cx: FunctionContext) -> JsResult { +pub fn final_query_result(mut cx: FunctionContext) -> JsResult { let transform_data_js_object = cx.argument::(0)?; let deserializer = JsValueDeserializer::new(&mut cx, transform_data_js_object); let transform_request_data: TransformDataRequest = match Deserialize::deserialize(deserializer) @@ -135,8 +154,12 @@ pub fn final_cubestore_result(mut cx: FunctionContext) -> JsResult { Err(err) => return cx.throw_error(err.to_string()), }; - let cube_store_result = cx.argument::>>(1)?; - let cube_store_result = Arc::clone(&cube_store_result); + let data_arg = cx.argument::(1)?; + let cube_store_result: Arc = match extract_query_result(&mut cx, data_arg) { + Ok(query_result) => query_result, + Err(err) => return cx.throw_error(err.to_string()), + }; + let result_data_js_object = cx.argument::(2)?; let deserializer = JsValueDeserializer::new(&mut cx, result_data_js_object); let mut result_data: RequestResultData = match Deserialize::deserialize(deserializer) { @@ -158,7 +181,7 @@ pub fn final_cubestore_result(mut cx: FunctionContext) -> JsResult { Ok(promise) } -pub fn final_cubestore_result_array(mut cx: FunctionContext) -> JsResult { +pub fn final_query_result_array(mut cx: FunctionContext) -> JsResult { let transform_data_array = cx.argument::(0)?; let deserializer = JsValueDeserializer::new(&mut cx, transform_data_array); let transform_requests: Vec = match Deserialize::deserialize(deserializer) @@ -167,16 +190,14 @@ pub fn final_cubestore_result_array(mut cx: FunctionContext) -> JsResult return cx.throw_error(err.to_string()), }; - let cube_store_array = cx.argument::(1)?; - let cube_store_results_boxed: Vec>>> = cube_store_array - .to_vec(&mut cx)? - .into_iter() - .map(|js_value| js_value.downcast_or_throw::>, _>(&mut cx)) - .collect::>()?; - let cube_store_results: Vec> = cube_store_results_boxed - .iter() - .map(|handle| (**handle).clone()) - .collect(); + let data_array = cx.argument::(1)?; + let mut cube_store_results: Vec> = vec![]; + for data_arg in data_array.to_vec(&mut cx)? { + match extract_query_result(&mut cx, data_arg) { + Ok(query_result) => cube_store_results.push(query_result), + Err(err) => return cx.throw_error(err.to_string()), + }; + } let results_data_array = cx.argument::(2)?; let deserializer = JsValueDeserializer::new(&mut cx, results_data_array); @@ -207,7 +228,7 @@ pub fn final_cubestore_result_array(mut cx: FunctionContext) -> JsResult JsResult { +pub fn final_query_result_multi(mut cx: FunctionContext) -> JsResult { let transform_data_array = cx.argument::(0)?; let deserializer = JsValueDeserializer::new(&mut cx, transform_data_array); let transform_requests: Vec = match Deserialize::deserialize(deserializer) @@ -216,16 +237,14 @@ pub fn final_cubestore_result_multi(mut cx: FunctionContext) -> JsResult return cx.throw_error(err.to_string()), }; - let cube_store_array = cx.argument::(1)?; - let cube_store_results_boxed: Vec>>> = cube_store_array - .to_vec(&mut cx)? - .into_iter() - .map(|js_value| js_value.downcast_or_throw::>, _>(&mut cx)) - .collect::>()?; - let cube_store_results: Vec> = cube_store_results_boxed - .iter() - .map(|handle| (**handle).clone()) - .collect(); + let data_array = cx.argument::(1)?; + let mut cube_store_results: Vec> = vec![]; + for data_arg in data_array.to_vec(&mut cx)? { + match extract_query_result(&mut cx, data_arg) { + Ok(query_result) => cube_store_results.push(query_result), + Err(err) => return cx.throw_error(err.to_string()), + }; + } let result_data_js_object = cx.argument::(2)?; let deserializer = JsValueDeserializer::new(&mut cx, result_data_js_object); diff --git a/rust/cubeorchestrator/src/lib.rs b/rust/cubeorchestrator/src/lib.rs index 6532689df15d9..03f0453c16db7 100644 --- a/rust/cubeorchestrator/src/lib.rs +++ b/rust/cubeorchestrator/src/lib.rs @@ -1,3 +1,3 @@ -pub mod cubestore_message_parser; -pub mod cubestore_result_transform; +pub mod query_message_parser; +pub mod query_result_transform; pub mod transport; diff --git a/rust/cubeorchestrator/src/cubestore_message_parser.rs b/rust/cubeorchestrator/src/query_message_parser.rs similarity index 70% rename from rust/cubeorchestrator/src/cubestore_message_parser.rs rename to rust/cubeorchestrator/src/query_message_parser.rs index 2be4df110a251..5e7a84edbfaca 100644 --- a/rust/cubeorchestrator/src/cubestore_message_parser.rs +++ b/rust/cubeorchestrator/src/query_message_parser.rs @@ -1,8 +1,8 @@ -use crate::cubestore_result_transform::{DBResponsePrimitive, DBResponseValue}; +use crate::query_result_transform::{DBResponsePrimitive, DBResponseValue}; use cubeshared::codegen::{root_as_http_message, HttpCommand}; use neon::prelude::Finalize; -use serde::Deserialize; use std::collections::HashMap; +use crate::transport::JsRawData; #[derive(Debug)] pub enum ParseError { @@ -29,18 +29,18 @@ impl std::fmt::Display for ParseError { impl std::error::Error for ParseError {} -#[derive(Debug, Clone, Deserialize)] -pub struct CubeStoreResult { +#[derive(Debug, Clone)] +pub struct QueryResult { pub columns: Vec, pub rows: Vec>, pub columns_pos: HashMap, } -impl Finalize for CubeStoreResult {} +impl Finalize for QueryResult {} -impl CubeStoreResult { - pub fn from_fb(msg_data: &[u8]) -> Result { - let mut result = CubeStoreResult { +impl QueryResult { + pub fn from_cubestore_fb(msg_data: &[u8]) -> Result { + let mut result = QueryResult { columns: vec![], rows: vec![], columns_pos: HashMap::new(), @@ -101,4 +101,39 @@ impl CubeStoreResult { _ => Err(ParseError::UnsupportedCommand), } } + + pub fn from_js_raw_data(js_raw_data: JsRawData) -> Result { + if js_raw_data.is_empty() { + return Err(ParseError::EmptyResultSet); + } + + let first_row = &js_raw_data[0]; + let columns: Vec = first_row.keys().cloned().collect(); + let columns_pos: HashMap = columns + .iter() + .enumerate() + .map(|(index, column)| (column.clone(), index)) + .collect(); + + let rows: Vec> = js_raw_data + .into_iter() + .map(|row_map| { + columns + .iter() + .map(|col| { + row_map + .get(col) + .map(|val| DBResponseValue::Primitive(val.clone())) + .unwrap_or(DBResponseValue::Primitive(DBResponsePrimitive::Null)) + }) + .collect() + }) + .collect(); + + Ok(QueryResult { + columns, + rows, + columns_pos, + }) + } } diff --git a/rust/cubeorchestrator/src/cubestore_result_transform.rs b/rust/cubeorchestrator/src/query_result_transform.rs similarity index 94% rename from rust/cubeorchestrator/src/cubestore_result_transform.rs rename to rust/cubeorchestrator/src/query_result_transform.rs index 3dc1c5ef01d16..328cb68b4b8ef 100644 --- a/rust/cubeorchestrator/src/cubestore_result_transform.rs +++ b/rust/cubeorchestrator/src/query_result_transform.rs @@ -1,5 +1,5 @@ use crate::{ - cubestore_message_parser::CubeStoreResult, + query_message_parser::QueryResult, transport::{ ConfigItem, MembersMap, NormalizedQuery, QueryTimeDimension, QueryType, ResultType, TransformDataRequest, @@ -115,7 +115,7 @@ pub fn get_blending_response_key( pub fn get_members( query_type: &QueryType, query: &NormalizedQuery, - db_data: &CubeStoreResult, + db_data: &QueryResult, alias_to_member_name_map: &HashMap, annotation: &HashMap, ) -> Result { @@ -337,13 +337,19 @@ pub fn get_pivot_query( let mut merged_dimensions = HashSet::new(); for query in queries { - merged_measures.extend(query.measures.iter().cloned()); + if let Some(measures) = &query.measures { + merged_measures.extend(measures.iter().cloned()); + } if let Some(dimensions) = &query.dimensions { merged_dimensions.extend(dimensions.iter().cloned()); } } - pivot_query.measures = merged_measures.into_iter().collect(); + pivot_query.measures = if !merged_measures.is_empty() { + Some(merged_measures.into_iter().collect()) + } else { + None + }; pivot_query.dimensions = if !merged_dimensions.is_empty() { Some(merged_dimensions.into_iter().collect()) } else { @@ -378,7 +384,7 @@ pub fn get_pivot_query( pub fn get_final_cubestore_result_array( transform_requests: &[TransformDataRequest], - cube_store_results: &[Arc], + cube_store_results: &[Arc], result_data: &mut [RequestResultData], ) -> Result<()> { for (transform_data, cube_store_result, result) in multizip(( @@ -406,7 +412,7 @@ impl TransformedData { /// Transforms queried data array to the output format. pub fn transform( request_data: &TransformDataRequest, - cube_store_result: &CubeStoreResult, + cube_store_result: &QueryResult, ) -> Result { let alias_to_member_name_map = &request_data.alias_to_member_name_map; let annotation = &request_data.annotation; @@ -469,6 +475,7 @@ pub struct RequestResultDataMulti { pub query_type: QueryType, pub results: Vec, #[serde(rename = "pivotQuery")] + #[serde(skip_serializing_if = "Option::is_none")] pub pivot_query: Option, #[serde(rename = "slowQuery")] pub slow_query: bool, @@ -480,7 +487,7 @@ impl RequestResultDataMulti { pub fn prepare_results( &mut self, request_data: &[TransformDataRequest], - cube_store_result: &[Arc], + cube_store_result: &[Arc], ) -> Result<()> { for (transform_data, cube_store_result, result) in multizip(( request_data.iter(), @@ -506,14 +513,19 @@ impl RequestResultDataMulti { pub struct RequestResultData { pub query: NormalizedQuery, #[serde(rename = "lastRefreshTime")] + #[serde(skip_serializing_if = "Option::is_none")] pub last_refresh_time: Option, #[serde(rename = "refreshKeyValues")] + #[serde(skip_serializing_if = "Option::is_none")] pub refresh_key_values: Option, #[serde(rename = "usedPreAggregations")] + #[serde(skip_serializing_if = "Option::is_none")] pub used_pre_aggregations: Option, #[serde(rename = "transformedQuery")] + #[serde(skip_serializing_if = "Option::is_none")] pub transformed_query: Option, #[serde(rename = "requestId")] + #[serde(skip_serializing_if = "Option::is_none")] pub request_id: Option, pub annotation: HashMap>, #[serde(rename = "dataSource")] @@ -521,11 +533,14 @@ pub struct RequestResultData { #[serde(rename = "dbType")] pub db_type: String, #[serde(rename = "extDbType")] + #[serde(skip_serializing_if = "Option::is_none")] pub ext_db_type: Option, pub external: bool, #[serde(rename = "slowQuery")] pub slow_query: bool, + #[serde(skip_serializing_if = "Option::is_none")] pub total: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub data: Option, } @@ -534,7 +549,7 @@ impl RequestResultData { pub fn prepare_results( &mut self, request_data: &TransformDataRequest, - cube_store_result: &CubeStoreResult, + cube_store_result: &QueryResult, ) -> Result<()> { let transformed = TransformedData::transform(request_data, cube_store_result)?; self.data = Some(transformed); @@ -549,6 +564,7 @@ pub struct RequestResultArray { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] pub enum DBResponsePrimitive { Null, Boolean(bool), diff --git a/rust/cubeorchestrator/src/transport.rs b/rust/cubeorchestrator/src/transport.rs index 77d4c409e7151..68aad19ec9ad2 100644 --- a/rust/cubeorchestrator/src/transport.rs +++ b/rust/cubeorchestrator/src/transport.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::{collections::HashMap, fmt::Display}; +use crate::query_result_transform::DBResponsePrimitive; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ResultType { @@ -242,15 +243,16 @@ pub struct Query { pub renew_query: Option, #[serde(skip_serializing_if = "Option::is_none")] pub ungrouped: Option, - #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "responseFormat")] + #[serde(skip_serializing_if = "Option::is_none")] pub response_format: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NormalizedQuery { + #[serde(skip_serializing_if = "Option::is_none")] // pub measures: Vec, - pub measures: Vec, + pub measures: Option>, #[serde(skip_serializing_if = "Option::is_none")] // pub dimensions: Option>, pub dimensions: Option>, @@ -301,3 +303,5 @@ pub struct TransformDataRequest { #[serde(rename = "resType")] pub res_type: Option, } + +pub type JsRawData = Vec>;