From 51a2edc7ac3ee0a946b5b369ada7e2631ed927cc Mon Sep 17 00:00:00 2001 From: Robbie Date: Tue, 14 Nov 2023 12:19:02 +0000 Subject: [PATCH] feat(web-analytics): add health check warnings (#18572) * Add web analytics health check via the event definition api * Let kea-typegen figure out types * Add a margin to the pageleave warning --- .../web-analytics/WebAnalyticsHealthCheck.tsx | 43 ++++++++++++++ .../src/scenes/web-analytics/WebDashboard.tsx | 2 + .../scenes/web-analytics/webAnalyticsLogic.ts | 56 +++++++++++++++++-- .../web_analytics_query_runner.py | 6 +- 4 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 frontend/src/scenes/web-analytics/WebAnalyticsHealthCheck.tsx diff --git a/frontend/src/scenes/web-analytics/WebAnalyticsHealthCheck.tsx b/frontend/src/scenes/web-analytics/WebAnalyticsHealthCheck.tsx new file mode 100644 index 0000000000000..e87a1fc8b3c9d --- /dev/null +++ b/frontend/src/scenes/web-analytics/WebAnalyticsHealthCheck.tsx @@ -0,0 +1,43 @@ +import { useValues } from 'kea' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { Link } from 'lib/lemon-ui/Link' +import { webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic' + +export const WebAnalyticsHealthCheck = (): JSX.Element | null => { + const { statusCheck } = useValues(webAnalyticsLogic) + + // No need to show loading or error states for this warning + if (!statusCheck) { + return null + } + + if (statusCheck.shouldWarnAboutNoPageviews) { + return ( + +

+ No $pageview{' '} + {statusCheck.shouldWarnAboutNoPageleaves ? ( + <> + or $pageleave{' '} + + ) : null} + events have been received, please read{' '} + the documentation and + fix this before using Web Analytics. +

+
+ ) + } else if (statusCheck.shouldWarnAboutNoPageleaves) { + return ( + +

+ No $pageleave events have been received, this means that Bounce rate and Session + Duration might be inaccurate. Please read{' '} + the documentation and + fix this before using Web Analytics. +

+
+ ) + } + return null +} diff --git a/frontend/src/scenes/web-analytics/WebDashboard.tsx b/frontend/src/scenes/web-analytics/WebDashboard.tsx index 50bda7b46acee..9f4b9f42336a0 100644 --- a/frontend/src/scenes/web-analytics/WebDashboard.tsx +++ b/frontend/src/scenes/web-analytics/WebDashboard.tsx @@ -16,6 +16,7 @@ import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' import clsx from 'clsx' +import { WebAnalyticsHealthCheck } from 'scenes/web-analytics/WebAnalyticsHealthCheck' const Filters = (): JSX.Element => { const { webAnalyticsFilters, dateTo, dateFrom } = useValues(webAnalyticsLogic) @@ -148,6 +149,7 @@ export const WebAnalyticsDashboard = (): JSX.Element => {
+
) diff --git a/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts b/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts index 2e4e2ebf09f93..d3aaac330e695 100644 --- a/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts +++ b/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts @@ -1,6 +1,7 @@ -import { actions, connect, kea, listeners, path, reducers, selectors, sharedListeners } from 'kea' +import { actions, afterMount, connect, kea, path, reducers, selectors } from 'kea' import type { webAnalyticsLogicType } from './webAnalyticsLogicType' + import { NodeKind, QuerySchema, @@ -8,8 +9,10 @@ import { WebAnalyticsPropertyFilters, WebStatsBreakdown, } from '~/queries/schema' -import { BaseMathType, ChartDisplayType, PropertyFilterType, PropertyOperator } from '~/types' +import { BaseMathType, ChartDisplayType, EventDefinitionType, PropertyFilterType, PropertyOperator } from '~/types' import { isNotNil } from 'lib/utils' +import { loaders } from 'kea-loaders' +import api from 'lib/api' export interface WebTileLayout { colSpan?: number @@ -68,6 +71,11 @@ export enum GeographyTab { CITIES = 'CITIES', } +export interface WebAnalyticsStatusCheck { + shouldWarnAboutNoPageviews: boolean + shouldWarnAboutNoPageleaves: boolean +} + export const initialWebAnalyticsFilter = [] as WebAnalyticsPropertyFilters export const webAnalyticsLogic = kea([ @@ -557,6 +565,46 @@ export const webAnalyticsLogic = kea([ }, ], })), - sharedListeners(() => ({})), - listeners(() => ({})), + loaders(() => ({ + // load the status check query here and pass the response into the component, so the response + // is accessible in this logic + statusCheck: { + __default: null as WebAnalyticsStatusCheck | null, + loadStatusCheck: async (): Promise => { + const [pageviewResult, pageleaveResult] = await Promise.allSettled([ + api.eventDefinitions.list({ + event_type: EventDefinitionType.Event, + search: '$pageview', + }), + api.eventDefinitions.list({ + event_type: EventDefinitionType.Event, + search: '$pageleave', + }), + ]) + + // no need to worry about pagination here, event names beginning with $ are reserved, and we're not + // going to add enough reserved event names that match this search term to cause problems + const shouldWarnAboutNoPageviews = + pageviewResult.status === 'fulfilled' && + !pageviewResult.value.next && + (pageviewResult.value.count === 0 || + !pageviewResult.value.results.some((r) => r.name === '$pageview')) + const shouldWarnAboutNoPageleaves = + pageleaveResult.status === 'fulfilled' && + !pageleaveResult.value.next && + (pageleaveResult.value.count === 0 || + !pageleaveResult.value.results.some((r) => r.name === '$pageleave')) + + return { + shouldWarnAboutNoPageviews, + shouldWarnAboutNoPageleaves, + } + }, + }, + })), + + // start the loaders after mounting the logic + afterMount(({ actions }) => { + actions.loadStatusCheck() + }), ]) diff --git a/posthog/hogql_queries/web_analytics/web_analytics_query_runner.py b/posthog/hogql_queries/web_analytics/web_analytics_query_runner.py index 8e8d34d443f62..4c8b2b857eec3 100644 --- a/posthog/hogql_queries/web_analytics/web_analytics_query_runner.py +++ b/posthog/hogql_queries/web_analytics/web_analytics_query_runner.py @@ -20,11 +20,7 @@ PersonPropertyFilter, ) -WebQueryNode = Union[ - WebOverviewQuery, - WebTopClicksQuery, - WebStatsTableQuery, -] +WebQueryNode = Union[WebOverviewQuery, WebTopClicksQuery, WebStatsTableQuery] class WebAnalyticsQueryRunner(QueryRunner, ABC):