diff --git a/packages/cubejs-api-gateway/openspec.yml b/packages/cubejs-api-gateway/openspec.yml index 51dfd77ffa45a..9110604d3500f 100644 --- a/packages/cubejs-api-gateway/openspec.yml +++ b/packages/cubejs-api-gateway/openspec.yml @@ -129,6 +129,9 @@ components: type: "string" type: type: "string" + aliasMember: + description: "When dimension is defined in View, it keeps the original path: Cube.dimension" + type: "string" granularities: type: array items: diff --git a/packages/cubejs-schema-compiler/package.json b/packages/cubejs-schema-compiler/package.json index 3212257042ada..25760383030c6 100644 --- a/packages/cubejs-schema-compiler/package.json +++ b/packages/cubejs-schema-compiler/package.json @@ -99,6 +99,7 @@ "coveragePathIgnorePatterns": [ ".*\\.d\\.ts" ], - "globalSetup": "/dist/test/global-setup.js" + "globalSetup": "/dist/test/global-setup.js", + "snapshotResolver": "/test/snapshotResolver.js" } } diff --git a/packages/cubejs-schema-compiler/src/compiler/CubeToMetaTransformer.js b/packages/cubejs-schema-compiler/src/compiler/CubeToMetaTransformer.js index 5235557c3c0a8..10edba0b87430 100644 --- a/packages/cubejs-schema-compiler/src/compiler/CubeToMetaTransformer.js +++ b/packages/cubejs-schema-compiler/src/compiler/CubeToMetaTransformer.js @@ -80,6 +80,7 @@ export class CubeToMetaTransformer { ? this.isVisible(nameToDimension[1], !nameToDimension[1].primaryKey) : false, primaryKey: !!nameToDimension[1].primaryKey, + aliasMember: nameToDimension[1].aliasMember, granularities: nameToDimension[1].granularities ? R.compose(R.map((g) => ({ diff --git a/packages/cubejs-schema-compiler/test/snapshotResolver.js b/packages/cubejs-schema-compiler/test/snapshotResolver.js new file mode 100644 index 0000000000000..3d92d5d12fcaf --- /dev/null +++ b/packages/cubejs-schema-compiler/test/snapshotResolver.js @@ -0,0 +1,26 @@ +const path = require('path'); + +function resolveSnapshotPath(testPath, snapshotExtension) { + const testSourcePath = testPath.replace('dist/', ''); + const testDirectory = path.dirname(testSourcePath); + const testFilename = path.basename(testSourcePath).replace('.js', '.ts'); + + return `${testDirectory}/__snapshots__/${testFilename}${snapshotExtension}`; +} + +function resolveTestPath(snapshotFilePath, snapshotExtension) { + const testSourceFile = snapshotFilePath + .replace('test/unit/__snapshots__', 'dist/test/unit') + .replace('test/integration/__snapshots__', 'dist/test/integration') + .replace('.ts', '.js') + .replace(snapshotExtension, ''); + + return testSourceFile; +} + +module.exports = { + resolveSnapshotPath, + resolveTestPath, + + testPathForConsistencyCheck: 'dist/test/unit/Test.spec.js' +}; diff --git a/packages/cubejs-schema-compiler/test/unit/__snapshots__/views.test.ts.snap b/packages/cubejs-schema-compiler/test/unit/__snapshots__/views.test.ts.snap new file mode 100644 index 0000000000000..5f9f19f47aee3 --- /dev/null +++ b/packages/cubejs-schema-compiler/test/unit/__snapshots__/views.test.ts.snap @@ -0,0 +1,364 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Views YAML includes * (a,b) + exclude id from b 1`] = ` +Object { + "connectedComponent": undefined, + "description": undefined, + "dimensions": Array [ + Object { + "aliasMember": "CubeA.id", + "description": "Description for CubeA.id", + "format": "imageUrl", + "granularities": undefined, + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeA.id", + }, + "name": "simple_view.id", + "primaryKey": false, + "public": true, + "shortTitle": "Title for CubeA.id", + "suggestFilterValues": true, + "title": "Simple View Title for CubeA.id", + "type": "number", + }, + Object { + "aliasMember": "CubeB.other_id", + "description": "Description for CubeB.other_id", + "format": "imageUrl", + "granularities": undefined, + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.other_id", + }, + "name": "simple_view.other_id", + "primaryKey": false, + "public": true, + "shortTitle": "Title for CubeB.other_id", + "suggestFilterValues": true, + "title": "Simple View Title for CubeB.other_id", + "type": "number", + }, + ], + "hierarchies": Array [], + "isVisible": true, + "measures": Array [ + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeA.count_a", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeA.count_a", + }, + "name": "simple_view.count_a", + "public": true, + "shortTitle": "Title for CubeA.count_a", + "title": "Simple View Title for CubeA.count_a", + "type": "number", + }, + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeB.count_b", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.count_b", + }, + "name": "simple_view.count_b", + "public": true, + "shortTitle": "Title for CubeB.count_b", + "title": "Simple View Title for CubeB.count_b", + "type": "number", + }, + ], + "meta": undefined, + "name": "simple_view", + "public": true, + "segments": Array [], + "title": "Simple View", + "type": "view", +} +`; + +exports[`Views YAML includes * + prefix a,b + exclude ids 1`] = ` +Object { + "connectedComponent": undefined, + "description": undefined, + "dimensions": Array [ + Object { + "aliasMember": "CubeB.other_id", + "description": "Description for CubeB.other_id", + "format": "imageUrl", + "granularities": undefined, + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.other_id", + }, + "name": "simple_view.CubeB_other_id", + "primaryKey": false, + "public": true, + "shortTitle": "Title for CubeB.other_id", + "suggestFilterValues": true, + "title": "Simple View Title for CubeB.other_id", + "type": "number", + }, + ], + "hierarchies": Array [], + "isVisible": true, + "measures": Array [ + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeA.count_a", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeA.count_a", + }, + "name": "simple_view.CubeA_count_a", + "public": true, + "shortTitle": "Title for CubeA.count_a", + "title": "Simple View Title for CubeA.count_a", + "type": "number", + }, + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeB.count_b", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.count_b", + }, + "name": "simple_view.CubeB_count_b", + "public": true, + "shortTitle": "Title for CubeB.count_b", + "title": "Simple View Title for CubeB.count_b", + "type": "number", + }, + ], + "meta": undefined, + "name": "simple_view", + "public": true, + "segments": Array [], + "title": "Simple View", + "type": "view", +} +`; + +exports[`Views YAML includes * + prefix a,b,c 1`] = ` +Object { + "connectedComponent": undefined, + "description": undefined, + "dimensions": Array [ + Object { + "aliasMember": "CubeA.id", + "description": "Description for CubeA.id", + "format": "imageUrl", + "granularities": undefined, + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeA.id", + }, + "name": "simple_view.CubeA_id", + "primaryKey": false, + "public": true, + "shortTitle": "Title for CubeA.id", + "suggestFilterValues": true, + "title": "Simple View Title for CubeA.id", + "type": "number", + }, + Object { + "aliasMember": "CubeB.id", + "description": "Description for CubeB.id", + "format": "imageUrl", + "granularities": undefined, + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.id", + }, + "name": "simple_view.CubeB_id", + "primaryKey": false, + "public": true, + "shortTitle": "Title for CubeB.id", + "suggestFilterValues": true, + "title": "Simple View Title for CubeB.id", + "type": "number", + }, + Object { + "aliasMember": "CubeB.other_id", + "description": "Description for CubeB.other_id", + "format": "imageUrl", + "granularities": undefined, + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.other_id", + }, + "name": "simple_view.CubeB_other_id", + "primaryKey": false, + "public": true, + "shortTitle": "Title for CubeB.other_id", + "suggestFilterValues": true, + "title": "Simple View Title for CubeB.other_id", + "type": "number", + }, + ], + "hierarchies": Array [], + "isVisible": true, + "measures": Array [ + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeA.count_a", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeA.count_a", + }, + "name": "simple_view.CubeA_count_a", + "public": true, + "shortTitle": "Title for CubeA.count_a", + "title": "Simple View Title for CubeA.count_a", + "type": "number", + }, + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeB.count_b", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.count_b", + }, + "name": "simple_view.CubeB_count_b", + "public": true, + "shortTitle": "Title for CubeB.count_b", + "title": "Simple View Title for CubeB.count_b", + "type": "number", + }, + ], + "meta": undefined, + "name": "simple_view", + "public": true, + "segments": Array [], + "title": "Simple View", + "type": "view", +} +`; + +exports[`Views YAML includes * + prefix b + exclude ids 1`] = ` +Object { + "connectedComponent": undefined, + "description": undefined, + "dimensions": Array [ + Object { + "aliasMember": "CubeB.other_id", + "description": "Description for CubeB.other_id", + "format": "imageUrl", + "granularities": undefined, + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.other_id", + }, + "name": "simple_view.CubeB_other_id", + "primaryKey": false, + "public": true, + "shortTitle": "Title for CubeB.other_id", + "suggestFilterValues": true, + "title": "Simple View Title for CubeB.other_id", + "type": "number", + }, + ], + "hierarchies": Array [], + "isVisible": true, + "measures": Array [ + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeA.count_a", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeA.count_a", + }, + "name": "simple_view.count_a", + "public": true, + "shortTitle": "Title for CubeA.count_a", + "title": "Simple View Title for CubeA.count_a", + "type": "number", + }, + Object { + "aggType": "number", + "cumulative": false, + "cumulativeTotal": false, + "description": "Description for CubeB.count_b", + "drillMembers": Array [], + "drillMembersGrouped": Object { + "dimensions": Array [], + "measures": Array [], + }, + "format": "number", + "isVisible": true, + "meta": Object { + "key": "Meta.key for CubeB.count_b", + }, + "name": "simple_view.CubeB_count_b", + "public": true, + "shortTitle": "Title for CubeB.count_b", + "title": "Simple View Title for CubeB.count_b", + "type": "number", + }, + ], + "meta": undefined, + "name": "simple_view", + "public": true, + "segments": Array [], + "title": "Simple View", + "type": "view", +} +`; diff --git a/packages/cubejs-schema-compiler/test/unit/views.test.ts b/packages/cubejs-schema-compiler/test/unit/views.test.ts index 273cbe10c8eb0..eaea59650f8b8 100644 --- a/packages/cubejs-schema-compiler/test/unit/views.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/views.test.ts @@ -3,7 +3,7 @@ import { createSchemaYaml } from './utils'; describe('Views YAML', () => { const schemaCompile = async (views: unknown[]) => { - const { compiler, cubeEvaluator } = prepareYamlCompiler( + const { compiler, cubeEvaluator, metaTransformer } = prepareYamlCompiler( createSchemaYaml({ cubes: [ { @@ -129,7 +129,7 @@ describe('Views YAML', () => { ); await compiler.compile(); - return { compiler, cubeEvaluator }; + return { compiler, cubeEvaluator, metaTransformer }; }; function dimensionFixtureForCube(aliasName: string, name: string = aliasName) { @@ -146,7 +146,7 @@ describe('Views YAML', () => { type: 'number', }; } - + function measuresFixtureForCube(aliasName: string, name: string = aliasName) { return { description: `Description for ${name}`, @@ -164,7 +164,7 @@ describe('Views YAML', () => { } it('includes * + prefix a,b,c', async () => { - const { cubeEvaluator } = await schemaCompile([{ + const { cubeEvaluator, metaTransformer } = await schemaCompile([{ name: 'simple_view', cubes: [ { @@ -179,21 +179,28 @@ describe('Views YAML', () => { }, ] }]); - - expect(cubeEvaluator.getCubeDefinition('simple_view').dimensions).toEqual({ + + const simpleViewDef = cubeEvaluator.getCubeDefinition('simple_view'); + + expect(simpleViewDef.dimensions).toEqual({ CubeA_id: dimensionFixtureForCube('CubeA.id'), CubeB_id: dimensionFixtureForCube('CubeB.id'), CubeB_other_id: dimensionFixtureForCube('CubeB.other_id'), }); - expect(cubeEvaluator.getCubeDefinition('simple_view').measures).toEqual({ + expect(simpleViewDef.measures).toEqual({ CubeA_count_a: measuresFixtureForCube('CubeA.count_a'), CubeB_count_b: measuresFixtureForCube('CubeB.count_b'), }); + + const simpleViewMeta = metaTransformer.cubes.map((def) => def.config).find((def) => def.name === 'simple_view'); + expect(simpleViewMeta).toBeDefined(); + + expect(simpleViewMeta).toMatchSnapshot(); }); it('includes * + prefix a,b + exclude ids', async () => { - const { cubeEvaluator } = await schemaCompile([{ + const { cubeEvaluator, metaTransformer } = await schemaCompile([{ name: 'simple_view', cubes: [ { @@ -218,10 +225,15 @@ describe('Views YAML', () => { expect(cubeEvaluator.getCubeDefinition('simple_view').dimensions).toEqual({ CubeB_other_id: dimensionFixtureForCube('CubeB.other_id'), }); + + const simpleViewMeta = metaTransformer.cubes.map((def) => def.config).find((def) => def.name === 'simple_view'); + expect(simpleViewMeta).toBeDefined(); + + expect(simpleViewMeta).toMatchSnapshot(); }); it('includes * + prefix b + exclude ids', async () => { - const { cubeEvaluator } = await schemaCompile([{ + const { cubeEvaluator, metaTransformer } = await schemaCompile([{ name: 'simple_view', cubes: [ { @@ -245,10 +257,15 @@ describe('Views YAML', () => { expect(cubeEvaluator.getCubeDefinition('simple_view').dimensions).toEqual({ CubeB_other_id: dimensionFixtureForCube('CubeB.other_id'), }); + + const simpleViewMeta = metaTransformer.cubes.map((def) => def.config).find((def) => def.name === 'simple_view'); + expect(simpleViewMeta).toBeDefined(); + + expect(simpleViewMeta).toMatchSnapshot(); }); it('includes * (a,b) + exclude id from b', async () => { - const { cubeEvaluator } = await schemaCompile([{ + const { cubeEvaluator, metaTransformer } = await schemaCompile([{ name: 'simple_view', cubes: [ { @@ -269,6 +286,11 @@ describe('Views YAML', () => { id: dimensionFixtureForCube('CubeA.id'), other_id: dimensionFixtureForCube('CubeB.other_id'), }); + + const simpleViewMeta = metaTransformer.cubes.map((def) => def.config).find((def) => def.name === 'simple_view'); + expect(simpleViewMeta).toBeDefined(); + + expect(simpleViewMeta).toMatchSnapshot(); }); it('includes * (a,b, a.c) with prefix + exclude id from b,c', async () => { diff --git a/rust/cubesql/cubeclient/src/models/v1_cube_meta_dimension.rs b/rust/cubesql/cubeclient/src/models/v1_cube_meta_dimension.rs index e48e3fd30bec3..5b84d59776393 100644 --- a/rust/cubesql/cubeclient/src/models/v1_cube_meta_dimension.rs +++ b/rust/cubesql/cubeclient/src/models/v1_cube_meta_dimension.rs @@ -16,6 +16,9 @@ pub struct V1CubeMetaDimension { pub description: Option, #[serde(rename = "type")] pub r#type: String, + /// When dimension is defined in View, it keeps the original path: Cube.dimension + #[serde(rename = "aliasMember", skip_serializing_if = "Option::is_none")] + pub alias_member: Option, #[serde(rename = "granularities", skip_serializing_if = "Option::is_none")] pub granularities: Option>, #[serde(rename = "meta", skip_serializing_if = "Option::is_none")] @@ -28,6 +31,7 @@ impl V1CubeMetaDimension { name, description: None, r#type, + alias_member: None, granularities: None, meta: None, } diff --git a/rust/cubesql/cubesql/benches/large_model.rs b/rust/cubesql/cubesql/benches/large_model.rs index f8fb0f293044b..d1dee60d5a9ed 100644 --- a/rust/cubesql/cubesql/benches/large_model.rs +++ b/rust/cubesql/cubesql/benches/large_model.rs @@ -11,7 +11,7 @@ use cubesql::{ }, sql_generator, }, - transport::MetaContext, + transport::{CubeMetaDimension, MetaContext}, }; use egg::StopReason; use itertools::Itertools; @@ -94,10 +94,8 @@ pub fn get_large_model_test_meta(dims: usize) -> Vec { dimensions: (1..=dims) .map(|n| V1CubeMetaDimension { name: format!("{}.n{}", cube_name, n), - description: None, r#type: "number".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }) .collect(), segments: vec![], diff --git a/rust/cubesql/cubesql/src/compile/test/mod.rs b/rust/cubesql/cubesql/src/compile/test/mod.rs index 7fb8672fe856f..bb044f5efaa88 100644 --- a/rust/cubesql/cubesql/src/compile/test/mod.rs +++ b/rust/cubesql/cubesql/src/compile/test/mod.rs @@ -52,45 +52,34 @@ pub fn get_test_meta() -> Vec { dimensions: vec![ CubeMetaDimension { name: "KibanaSampleDataEcommerce.order_date".to_string(), - description: None, r#type: "time".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: "KibanaSampleDataEcommerce.last_mod".to_string(), - description: None, r#type: "time".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: "KibanaSampleDataEcommerce.customer_gender".to_string(), description: Some("Customer gender".to_string()), r#type: "string".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: "KibanaSampleDataEcommerce.notes".to_string(), - description: None, r#type: "string".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: "KibanaSampleDataEcommerce.taxful_total_price".to_string(), - description: None, r#type: "number".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: "KibanaSampleDataEcommerce.has_subscription".to_string(), - description: None, r#type: "boolean".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, ], measures: vec![ @@ -173,24 +162,18 @@ pub fn get_test_meta() -> Vec { dimensions: vec![ CubeMetaDimension { name: "Logs.id".to_string(), - description: None, r#type: "number".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: "Logs.read".to_string(), - description: None, r#type: "boolean".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: "Logs.content".to_string(), - description: None, r#type: "string".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, ], measures: vec![ @@ -244,10 +227,8 @@ pub fn get_test_meta() -> Vec { dimensions: (0..100) .map(|i| CubeMetaDimension { name: format!("WideCube.dim{}", i), - description: None, r#type: "number".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }) .collect(), measures: (0..100) @@ -321,22 +302,19 @@ pub fn get_test_meta() -> Vec { name: format!("MultiTypeCube.dim_num{}", i), description: Some(format!("Test numeric dimention {i}")), r#type: "number".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: format!("MultiTypeCube.dim_str{}", i), description: Some(format!("Test string dimention {i}")), r#type: "string".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, CubeMetaDimension { name: format!("MultiTypeCube.dim_date{}", i), description: Some(format!("Test time dimention {i}")), r#type: "time".to_string(), - granularities: None, - meta: None, + ..CubeMetaDimension::default() }, ] })