diff --git a/packages/cubejs-testing/test/driver-postgres.test.ts b/packages/cubejs-testing/test/driver-postgres.test.ts index 01704791f1dbf..38f85585d494b 100644 --- a/packages/cubejs-testing/test/driver-postgres.test.ts +++ b/packages/cubejs-testing/test/driver-postgres.test.ts @@ -1,4 +1,4 @@ -import { mainTestSet, preAggsTestSet, productionTestSet } from './driverTests/testSets'; +import { mainTestSet, multiQueryTestSet, preAggsTestSet, productionTestSet } from './driverTests/testSets'; import { executeTestSuite } from './driver-test-suite'; executeTestSuite({ @@ -6,6 +6,11 @@ executeTestSuite({ tests: mainTestSet, }); +executeTestSuite({ + type: 'postgres', + tests: multiQueryTestSet, +}); + executeTestSuite({ type: 'postgres', tests: mainTestSet, diff --git a/packages/cubejs-testing/test/driver-test-suite.ts b/packages/cubejs-testing/test/driver-test-suite.ts index 765414670fd5d..3a923b893b04e 100644 --- a/packages/cubejs-testing/test/driver-test-suite.ts +++ b/packages/cubejs-testing/test/driver-test-suite.ts @@ -68,7 +68,7 @@ export function executeTestSuite({ type, tests, config = {} }: TestSuite) { for (const t of tests) { const jsonConfig = JSON.stringify(overridedConfig); const testNameWithHash = `${t.name}_${jsonConfig}`; - + if (t.type === 'basic') { // eslint-disable-next-line no-loop-func const cbFn = async () => { @@ -83,6 +83,26 @@ export function executeTestSuite({ type, tests, config = {} }: TestSuite) { } }; + if (t.skip) { + test.skip(testNameWithHash, cbFn); + } else { + test(testNameWithHash, cbFn); + } + } else if (t.type === 'multi') { + // eslint-disable-next-line no-loop-func + const cbFn = async () => { + const response = await client.load(t.query); + + const resultSets = response.decompose(); + expect(resultSets).toMatchSnapshot('query'); + + if (t.expectArray) { + for (const expectFn of t.expectArray) { + expectFn(response); + } + } + }; + if (t.skip) { test.skip(testNameWithHash, cbFn); } else { diff --git a/packages/cubejs-testing/test/driverTests/driverTest.ts b/packages/cubejs-testing/test/driverTests/driverTest.ts index 9272164c90d0f..b4cecec6728b9 100644 --- a/packages/cubejs-testing/test/driverTests/driverTest.ts +++ b/packages/cubejs-testing/test/driverTests/driverTest.ts @@ -7,7 +7,7 @@ export type TestType = 'basic' | 'withError' | 'testFn'; type DriverTestArg = { name: string; - query: Query; + query: Query | Query[]; expectArray?: ((response: ResultSet>) => any)[]; schemas: Schemas; skip?: boolean; @@ -23,7 +23,7 @@ type DriverTestWithErrorArg = { export type DriverTestBasic = { name: string, - query: Query, + query: Query | Query[], expectArray?: ((response: ResultSet>) => any)[] schemas: Schemas, skip?: boolean; @@ -32,13 +32,22 @@ export type DriverTestBasic = { export type DriverTestWithError = { name: string; - query: Query; + query: Query | Query[]; expectArray?: ((e: Error) => any)[]; schemas: Schemas; skip?: boolean; type: 'withError'; }; +export type DriverTestMulti = { + name: string, + query: Query | Query[], + expectArray?: ((response: ResultSet>) => any)[] + schemas: Schemas, + skip?: boolean; + type: 'multi'; +}; + type DriverTestFnArg = { name: string; schemas: Schemas, @@ -50,7 +59,7 @@ export type DriverTestFn = DriverTestFnArg & { type: 'testFn'; }; -export type DriverTest = DriverTestBasic | DriverTestWithError | DriverTestFn; +export type DriverTest = DriverTestBasic | DriverTestWithError | DriverTestFn | DriverTestMulti; export function driverTest( { name, query, expectArray = [], skip, schemas }: DriverTestArg @@ -70,6 +79,12 @@ export function driverTestFn( return { name, testFn, schemas, skip, type: 'testFn' }; } +export function driverTestMulti( + { name, query, expectArray = [], skip, schemas }: DriverTestArg +): DriverTestMulti { + return { name, query, expectArray, schemas, skip, type: 'multi' }; +} + export function testSet(tests: DriverTest[]) { const uniqTests = uniqBy((t) => t.name, tests); return uniqTests; diff --git a/packages/cubejs-testing/test/driverTests/testSets.ts b/packages/cubejs-testing/test/driverTests/testSets.ts index 9b73e0a12b555..2e533ac031a6e 100644 --- a/packages/cubejs-testing/test/driverTests/testSets.ts +++ b/packages/cubejs-testing/test/driverTests/testSets.ts @@ -62,6 +62,7 @@ import { hiddenCube, viewMetaExposed, preAggsCustomersRunningTotal, + queryingECommerceCompareDateRangesByCustomerOverProductNameByMonth, } from './tests'; import { testSet } from './driverTest'; @@ -156,3 +157,7 @@ export const productionTestSet = testSet([ hiddenMember, hiddenCube, ]); + +export const multiQueryTestSet = testSet([ + queryingECommerceCompareDateRangesByCustomerOverProductNameByMonth, +]); diff --git a/packages/cubejs-testing/test/driverTests/tests.ts b/packages/cubejs-testing/test/driverTests/tests.ts index 7cb3d0e52bb9e..6e8adfddbd0ee 100644 --- a/packages/cubejs-testing/test/driverTests/tests.ts +++ b/packages/cubejs-testing/test/driverTests/tests.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line import/no-extraneous-dependencies import { expect } from '@jest/globals'; -import { driverTest, driverTestFn, driverTestWithError } from './driverTest'; +import { driverTest, driverTestFn, driverTestMulti, driverTestWithError } from './driverTest'; const commonSchemas = [ 'CAST.js', @@ -189,7 +189,7 @@ export const filteringCustomersCubeThird = driverTest( }, schemas: commonSchemas, } - + ); export const filteringCustomersEndsWithFilterFirst = driverTest({ @@ -1296,6 +1296,31 @@ export const preAggsCustomersRunningTotal = driverTest({ schemas: commonSchemas }); +export const queryingECommerceCompareDateRangesByCustomerOverProductNameByMonth = driverTestMulti({ + name: 'querying ECommerce: compare DateRanges by customer over productName by month', + query: [{ + timeDimensions: [{ + dimension: 'ECommerce.orderDate', + granularity: 'month', + compareDateRange: [ + ['2023-01-01', '2024-01-01'], + ['2024-01-01', '2025-01-01'] + ] + }], + dimensions: [ + 'ECommerce.productName' + ], + measures: [ + 'ECommerce.count' + ], + order: { + 'ECommerce.orderDate': 'desc', + 'ECommerce.productName': 'asc', + }, + }], + schemas: commonSchemas +}); + export const queryingEcommerceTotalQuantifyAvgDiscountTotal = driverTestWithError({ name: 'querying ECommerce: total quantity, avg discount, total ' + 'sales, total profit by product + order + total -- noisy ' + diff --git a/rust/cubeorchestrator/src/transport.rs b/rust/cubeorchestrator/src/transport.rs index 5bcd358a28a40..b45ea96c4dec5 100644 --- a/rust/cubeorchestrator/src/transport.rs +++ b/rust/cubeorchestrator/src/transport.rs @@ -82,6 +82,13 @@ pub struct GroupingSet { pub sub_id: Option, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CompareDateRangeType { + Single(Vec), + Multi(Vec>), +} + // We can do nothing with JS functions here, // but to keep DTOs in sync with reality, let's keep it. pub type JsFunction = String; @@ -120,7 +127,7 @@ pub struct ParsedMemberExpression { pub struct QueryTimeDimension { pub dimension: String, pub date_range: Option>, - pub compare_date_range: Option>, + pub compare_date_range: Option, pub granularity: Option, } @@ -161,6 +168,8 @@ pub struct ConfigItem { pub drill_members_grouped: Option, #[serde(skip_serializing_if = "Option::is_none")] pub granularities: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub granularity: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/rust/cubestore/package.json b/rust/cubestore/package.json index 1096bf92660ad..311443507d022 100644 --- a/rust/cubestore/package.json +++ b/rust/cubestore/package.json @@ -27,7 +27,7 @@ "author": "Cube Dev, Inc.", "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.2.4", + "@cubejs-backend/linter": "1.2.5", "@types/jest": "^27", "@types/node": "^12", "jest": "^27", @@ -37,7 +37,7 @@ "access": "public" }, "dependencies": { - "@cubejs-backend/shared": "1.2.4", + "@cubejs-backend/shared": "1.2.5", "@octokit/core": "^3.2.5", "source-map-support": "^0.5.19" },