diff --git a/ee/app/assets/javascripts/product_analytics/dashboards/data_sources/cube_analytics.js b/ee/app/assets/javascripts/product_analytics/dashboards/data_sources/cube_analytics.js index b7d3e79c4d309856b3d9efdd7dff5e4d537244d3..209567f4d8da318835f384a69c2a4c4534d6311c 100644 --- a/ee/app/assets/javascripts/product_analytics/dashboards/data_sources/cube_analytics.js +++ b/ee/app/assets/javascripts/product_analytics/dashboards/data_sources/cube_analytics.js @@ -5,6 +5,7 @@ import csrf from '~/lib/utils/csrf'; // This can be any value because the cube proxy adds the real API token. const CUBE_API_TOKEN = '1'; const PRODUCT_ANALYTICS_CUBE_PROXY = '/api/v4/projects/:id/product_analytics/request'; +const DEFAULT_JITSU_COUNT_KEY = 'Jitsu.count'; const createCubeJsApi = (projectId) => new CubejsApi(CUBE_API_TOKEN, { @@ -44,14 +45,15 @@ const convertToTableFormat = (resultSet) => { }); }; -const convertToSingleValue = (resultSet) => { +const convertToSingleValue = (resultSet, query) => { + const [measure] = query?.measures ?? []; const [row] = resultSet.rawData(); if (!row) { return null; } - return Object.values(row)[0]; + return row[measure ?? DEFAULT_JITSU_COUNT_KEY] ?? Object.values(row)[0]; }; const VISUALIZATION_PARSERS = { @@ -61,18 +63,19 @@ const VISUALIZATION_PARSERS = { }; export const fetch = async ({ projectId, visualizationType, query, queryOverrides = {} }) => { - const resultSet = await createCubeJsApi(projectId).load({ ...query, ...queryOverrides }); + const userQuery = { ...query, ...queryOverrides }; + const resultSet = await createCubeJsApi(projectId).load(userQuery); - return VISUALIZATION_PARSERS[visualizationType](resultSet); + return VISUALIZATION_PARSERS[visualizationType](resultSet, userQuery); }; export const NO_DATABASE_ERROR_MESSAGE = '404 Clickhouse Database Not Found'; export const hasAnalyticsData = async (projectId) => { try { - const data = await createCubeJsApi(projectId).load({ measures: ['Jitsu.count'] }); + const data = await createCubeJsApi(projectId).load({ measures: [DEFAULT_JITSU_COUNT_KEY] }); - return data.rawData()[0]['Jitsu.count'] > 0; + return data.rawData()[0][DEFAULT_JITSU_COUNT_KEY] > 0; } catch (error) { const errorMessage = error?.response?.message; diff --git a/ee/spec/frontend/product_analytics/dashboards/components/data_sources/cube_analytics_spec.js b/ee/spec/frontend/product_analytics/dashboards/components/data_sources/cube_analytics_spec.js index 8202948702f3f7e2d46c48b3f5523b975c1f77c5..a78af768a25820a77a91f69234bd9624649c9513 100644 --- a/ee/spec/frontend/product_analytics/dashboards/components/data_sources/cube_analytics_spec.js +++ b/ee/spec/frontend/product_analytics/dashboards/components/data_sources/cube_analytics_spec.js @@ -40,8 +40,8 @@ const itSetsUpCube = () => { describe('Cube Analytics Data Source', () => { const projectId = 'TEST_ID'; const visualizationType = 'LineChart'; - const query = { alpha: 'one' }; - const queryOverrides = { alpha: 'two' }; + const query = { measures: ['Jitsu.count'] }; + const queryOverrides = { measures: ['Jitsu.userLanguage'] }; describe('fetch', () => { beforeEach(() => { @@ -51,7 +51,7 @@ describe('Cube Analytics Data Source', () => { itSetsUpCube(); it('loads the query with the query override', () => { - expect(mockLoad).toHaveBeenCalledWith({ alpha: 'two' }); + expect(mockLoad).toHaveBeenCalledWith(queryOverrides); }); describe('formarts the data', () => { @@ -82,6 +82,30 @@ describe('Cube Analytics Data Source', () => { expect(result).toBe('36'); }); + + it('returns the expected data format for single stats with custom measure', async () => { + const override = { measures: ['Jitsu.url'] }; + const result = await fetch({ + projectId, + visualizationType: 'SingleStat', + query, + queryOverrides: override, + }); + + expect(result).toBe('https://example.com/us'); + }); + + it('returns the expected data format for single stats when the measure is unknown', async () => { + const override = { measures: ['unknown'] }; + const result = await fetch({ + projectId, + visualizationType: 'SingleStat', + query, + queryOverrides: override, + }); + + expect(result).toBe('en-US'); + }); }); }); diff --git a/ee/spec/frontend/product_analytics/dashboards/components/mock_data.js b/ee/spec/frontend/product_analytics/dashboards/components/mock_data.js index b8f074e3c6683d6f23eeb5916c34c85fa2b7f45f..e03b26d2f399640d2e7a8bd8113290374f47511f 100644 --- a/ee/spec/frontend/product_analytics/dashboards/components/mock_data.js +++ b/ee/spec/frontend/product_analytics/dashboards/components/mock_data.js @@ -55,7 +55,14 @@ export const mockResultSet = { ], rawData: () => [ { + 'Jitsu.userLanguage': 'en-US', 'Jitsu.count': '36', + 'Jitsu.url': 'https://example.com/us', + }, + { + 'Jitsu.userLanguage': 'es-ES', + 'Jitsu.count': '60', + 'Jitsu.url': 'https://example.com/es', }, ], };