Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ use serde::{Deserialize, Serialize};
use std::any::Any;
use std::rc::Rc;

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TimeDimension {
pub dimension: String,
pub granularity: Option<String>,
#[serde(rename = "dateRange")]
pub date_range: Option<Vec<String>>,
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FilterItem {
pub or: Option<Vec<FilterItem>>,
pub and: Option<Vec<FilterItem>>,
Expand All @@ -31,7 +31,7 @@ pub struct FilterItem {
pub values: Option<Vec<Option<String>>>,
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OrderByItem {
pub id: String,
pub desc: Option<bool>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ pub enum OptionsMember {
MemberExpression(Rc<dyn MemberExpressionDefinition>),
}

impl Clone for OptionsMember {
fn clone(&self) -> Self {
match self {
Self::MemberName(name) => Self::MemberName(name.clone()),
Self::MemberExpression(expr) => Self::MemberExpression(expr.clone()),
}
}
}

impl<IT: InnerTypes> NativeDeserialize<IT> for OptionsMember {
fn from_native(native_object: NativeObjectHandle<IT>) -> Result<Self, CubeError> {
match String::from_native(native_object.clone()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use std::any::Any;
use std::rc::Rc;

use cubenativeutils::CubeError;
use typed_builder::TypedBuilder;

use crate::{
cube_bridge::{
base_query_options::{
BaseQueryOptions, BaseQueryOptionsStatic, FilterItem, OrderByItem, TimeDimension,
},
base_tools::BaseTools,
evaluator::CubeEvaluator,
join_graph::JoinGraph,
join_hints::JoinHintItem,
options_member::OptionsMember,
security_context::SecurityContext,
},
impl_static_data,
};

/// Mock implementation of BaseQueryOptions for testing
#[derive(TypedBuilder, Clone)]
pub struct MockBaseQueryOptions {
// Required fields - dependencies that must be provided
cube_evaluator: Rc<dyn CubeEvaluator>,
base_tools: Rc<dyn BaseTools>,
join_graph: Rc<dyn JoinGraph>,
security_context: Rc<dyn SecurityContext>,

// Optional fields from trait methods
#[builder(default)]
measures: Option<Vec<OptionsMember>>,
#[builder(default)]
dimensions: Option<Vec<OptionsMember>>,
#[builder(default)]
segments: Option<Vec<OptionsMember>>,
#[builder(default)]
join_hints: Option<Vec<JoinHintItem>>,

// Fields from BaseQueryOptionsStatic
#[builder(default)]
time_dimensions: Option<Vec<TimeDimension>>,
#[builder(default)]
timezone: Option<String>,
#[builder(default)]
filters: Option<Vec<FilterItem>>,
#[builder(default)]
order: Option<Vec<OrderByItem>>,
#[builder(default)]
limit: Option<String>,
#[builder(default)]
row_limit: Option<String>,
#[builder(default)]
offset: Option<String>,
#[builder(default)]
ungrouped: Option<bool>,
#[builder(default = false)]
export_annotated_sql: bool,
#[builder(default)]
pre_aggregation_query: Option<bool>,
#[builder(default)]
total_query: Option<bool>,
#[builder(default)]
cubestore_support_multistage: Option<bool>,
#[builder(default = false)]
disable_external_pre_aggregations: bool,
}

impl_static_data!(
MockBaseQueryOptions,
BaseQueryOptionsStatic,
time_dimensions,
timezone,
filters,
order,
limit,
row_limit,
offset,
ungrouped,
export_annotated_sql,
pre_aggregation_query,
total_query,
cubestore_support_multistage,
disable_external_pre_aggregations
);

pub fn members_from_strings<S: ToString>(strings: Vec<S>) -> Vec<OptionsMember> {
strings
.into_iter()
.map(|s| OptionsMember::MemberName(s.to_string()))
.collect()
}

#[allow(dead_code)]
pub fn filter_item<M: ToString, O: ToString, V: ToString>(
member: M,
operator: O,
values: Vec<V>,
) -> FilterItem {
FilterItem {
member: Some(member.to_string()),
dimension: None,
operator: Some(operator.to_string()),
values: Some(values.into_iter().map(|v| Some(v.to_string())).collect()),
or: None,
and: None,
}
}

#[allow(dead_code)]
pub fn filter_or(items: Vec<FilterItem>) -> FilterItem {
FilterItem {
or: Some(items),
member: None,
dimension: None,
operator: None,
values: None,
and: None,
}
}

#[allow(dead_code)]
pub fn filter_and(items: Vec<FilterItem>) -> FilterItem {
FilterItem {
and: Some(items),
member: None,
dimension: None,
operator: None,
values: None,
or: None,
}
}

impl BaseQueryOptions for MockBaseQueryOptions {
crate::impl_static_data_method!(BaseQueryOptionsStatic);

fn has_measures(&self) -> Result<bool, CubeError> {
Ok(self.measures.is_some())
}

fn measures(&self) -> Result<Option<Vec<OptionsMember>>, CubeError> {
Ok(self.measures.clone())
}

fn has_dimensions(&self) -> Result<bool, CubeError> {
Ok(self.dimensions.is_some())
}

fn dimensions(&self) -> Result<Option<Vec<OptionsMember>>, CubeError> {
Ok(self.dimensions.clone())
}

fn has_segments(&self) -> Result<bool, CubeError> {
Ok(self.segments.is_some())
}

fn segments(&self) -> Result<Option<Vec<OptionsMember>>, CubeError> {
Ok(self.segments.clone())
}

fn cube_evaluator(&self) -> Result<Rc<dyn CubeEvaluator>, CubeError> {
Ok(self.cube_evaluator.clone())
}

fn base_tools(&self) -> Result<Rc<dyn BaseTools>, CubeError> {
Ok(self.base_tools.clone())
}

fn join_graph(&self) -> Result<Rc<dyn JoinGraph>, CubeError> {
Ok(self.join_graph.clone())
}

fn security_context(&self) -> Result<Rc<dyn SecurityContext>, CubeError> {
Ok(self.security_context.clone())
}

fn has_join_hints(&self) -> Result<bool, CubeError> {
Ok(self.join_hints.is_some())
}

fn join_hints(&self) -> Result<Option<Vec<JoinHintItem>>, CubeError> {
Ok(self.join_hints.clone())
}

fn as_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#[macro_use]
mod macros;

mod yaml;
pub mod yaml;

pub mod base_query_options;
mod mock_base_tools;
mod mock_case_definition;
mod mock_case_else_item;
Expand Down Expand Up @@ -33,6 +34,7 @@ mod mock_sql_utils;
mod mock_struct_with_sql_member;
mod mock_timeshift_definition;

pub use base_query_options::{members_from_strings, MockBaseQueryOptions};
pub use mock_base_tools::MockBaseTools;
pub use mock_case_definition::MockCaseDefinition;
pub use mock_case_else_item::MockCaseElseItem;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use crate::cube_bridge::base_query_options::{FilterItem, OrderByItem};
use serde::de;
use serde::{Deserialize, Deserializer};

#[derive(Debug, Deserialize)]
pub struct YamlBaseQueryOptions {
#[serde(default)]
pub measures: Option<Vec<String>>,
#[serde(default)]
pub dimensions: Option<Vec<String>>,
#[serde(default)]
pub segments: Option<Vec<String>>,
#[serde(default)]
pub order: Option<Vec<YamlOrderByItem>>,
#[serde(default)]
pub filters: Option<Vec<YamlFilterItem>>,
#[serde(default)]
pub limit: Option<String>,
#[serde(default)]
pub row_limit: Option<String>,
#[serde(default)]
pub offset: Option<String>,
#[serde(default)]
pub ungrouped: Option<bool>,
#[serde(default)]
pub export_annotated_sql: Option<bool>,
#[serde(default)]
pub pre_aggregation_query: Option<bool>,
#[serde(default)]
pub total_query: Option<bool>,
#[serde(default)]
pub cubestore_support_multistage: Option<bool>,
#[serde(default)]
pub disable_external_pre_aggregations: Option<bool>,
}

#[derive(Debug, Deserialize)]
pub struct YamlOrderByItem {
pub id: String,
#[serde(default)]
pub desc: Option<bool>,
}

impl YamlOrderByItem {
pub fn into_order_by_item(self) -> OrderByItem {
OrderByItem {
id: self.id,
desc: self.desc,
}
}
}

#[derive(Debug)]
pub enum YamlFilterItem {
Group(YamlFilterGroup),
Base(YamlBaseFilter),
}

impl<'de> Deserialize<'de> for YamlFilterItem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = serde_yaml::Value::deserialize(deserializer)?;

// Check if it has 'or' or 'and' keys - then it's a Group
if let serde_yaml::Value::Mapping(ref map) = value {
let has_or = map.contains_key(serde_yaml::Value::String("or".to_string()));
let has_and = map.contains_key(serde_yaml::Value::String("and".to_string()));

if has_or || has_and {
return serde_yaml::from_value::<YamlFilterGroup>(value)
.map(YamlFilterItem::Group)
.map_err(de::Error::custom);
}
}

// Otherwise it's a Base filter
serde_yaml::from_value::<YamlBaseFilter>(value)
.map(YamlFilterItem::Base)
.map_err(de::Error::custom)
}
}

#[derive(Debug, Deserialize)]
pub struct YamlFilterGroup {
#[serde(default)]
pub or: Option<Vec<YamlFilterItem>>,
#[serde(default)]
pub and: Option<Vec<YamlFilterItem>>,
}

#[derive(Debug, Deserialize)]
pub struct YamlBaseFilter {
#[serde(default)]
pub member: Option<String>,
#[serde(default)]
pub dimension: Option<String>,
#[serde(default)]
pub operator: Option<String>,
#[serde(default)]
pub values: Option<Vec<Option<String>>>,
}

impl YamlFilterItem {
pub fn into_filter_item(self) -> FilterItem {
match self {
YamlFilterItem::Group(group) => FilterItem {
or: group.or.map(|items| {
items
.into_iter()
.map(|item| item.into_filter_item())
.collect()
}),
and: group.and.map(|items| {
items
.into_iter()
.map(|item| item.into_filter_item())
.collect()
}),
member: None,
dimension: None,
operator: None,
values: None,
},
YamlFilterItem::Base(base) => FilterItem {
or: None,
and: None,
member: base.member,
dimension: base.dimension,
operator: base.operator,
values: base.values,
},
}
}
}
Loading
Loading