From d7d03b59856f4a92f2b1630b3dafbd68e11029a0 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Wed, 24 Apr 2024 13:39:43 +0200 Subject: [PATCH 01/10] feat(insights): HogQL calculation of saved legacy insights v4 (#21781) * Revert "revert(insights): HogQL calculation of saved legacy insights v3 (#21778)" This reverts commit c0be1d1412d7cbe7d1f4f1990b6ea3c9fdd93de1. * Move HogQL in insight serialization to its own flag --- ...build-schema.mjs => build-schema-json.mjs} | 0 bin/build-schema-python.sh | 28 +++ frontend/src/lib/constants.tsx | 1 + frontend/src/queries/schema.json | 3 +- frontend/src/queries/schema.ts | 1 - frontend/src/types.ts | 1 + mypy-baseline.txt | 33 ++-- package.json | 4 +- posthog/api/insight.py | 45 +++-- posthog/api/query.py | 9 +- posthog/api/services/query.py | 12 +- posthog/api/test/dashboards/test_dashboard.py | 20 +- posthog/api/test/test_insight.py | 183 ++++++++++++++++-- posthog/api/test/test_insight_query.py | 68 ++----- posthog/caching/calculate_results.py | 81 ++++---- posthog/caching/fetch_from_cache.py | 7 +- posthog/caching/insight_cache.py | 9 +- posthog/caching/test/test_insight_cache.py | 11 +- posthog/clickhouse/client/execute_async.py | 9 +- .../hogql_queries/apply_dashboard_filters.py | 3 + .../insights/test/test_paths_query_runner.py | 26 +++ .../test/test_paths_query_runner_ee.py | 56 ++++++ .../insights/trends/trends_query_runner.py | 8 +- .../legacy_compatibility/feature_flag.py | 26 +-- .../legacy_compatibility/process_insight.py | 51 ----- posthog/hogql_queries/query_runner.py | 98 ++++++++-- .../test/test_events_query_runner.py | 6 +- .../hogql_queries/test/test_query_runner.py | 19 +- posthog/models/insight.py | 24 +-- posthog/models/test/test_insight_model.py | 45 +---- posthog/schema.py | 2 +- 31 files changed, 556 insertions(+), 333 deletions(-) rename bin/{build-schema.mjs => build-schema-json.mjs} (100%) create mode 100755 bin/build-schema-python.sh delete mode 100644 posthog/hogql_queries/legacy_compatibility/process_insight.py diff --git a/bin/build-schema.mjs b/bin/build-schema-json.mjs similarity index 100% rename from bin/build-schema.mjs rename to bin/build-schema-json.mjs diff --git a/bin/build-schema-python.sh b/bin/build-schema-python.sh new file mode 100755 index 0000000000000..4d9f66616fbe4 --- /dev/null +++ b/bin/build-schema-python.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +set -e + +# Generate schema.py from schema.json +datamodel-codegen \ + --class-name='SchemaRoot' --collapse-root-models --target-python-version 3.10 --disable-timestamp \ + --use-one-literal-as-default --use-default --use-default-kwarg --use-subclass-enum \ + --input frontend/src/queries/schema.json --input-file-type jsonschema \ + --output posthog/schema.py --output-model-type pydantic_v2.BaseModel + +# Format schema.py +ruff format posthog/schema.py + +# Check schema.py and autofix +ruff check --fix posthog/schema.py + +# HACK: Datamodel-codegen output for enum-type fields with a default is invalid – the default value is a plain string, +# and not the expected enum member. We fix this using sed, which is pretty hacky, but does the job. +# Specifically, we need to replace `Optional[PropertyOperator] = "exact"` +# with `Optional[PropertyOperator] = PropertyOperator("exact")` to make the default value valid. +# Remove this when https://github.com/koxudaxi/datamodel-code-generator/issues/1929 is resolved. +if [[ "$OSTYPE" == "darwin"* ]]; then + # sed needs `-i` to be followed by `''` on macOS + sed -i '' -e 's/Optional\[PropertyOperator\] = \("[A-Za-z_]*"\)/Optional[PropertyOperator] = PropertyOperator(\1)/g' posthog/schema.py +else + sed -i -e 's/Optional\[PropertyOperator\] = \("[A-Za-z_]*"\)/Optional[PropertyOperator] = PropertyOperator(\1)/g' posthog/schema.py +fi diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index 4525b3c83b13f..0db68836f76d5 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -177,6 +177,7 @@ export const FEATURE_FLAGS = { HOGQL_INSIGHTS_STICKINESS: 'hogql-insights-stickiness', // owner: @Gilbert09 HOGQL_INSIGHTS_FUNNELS: 'hogql-insights-funnels', // owner: @thmsobrmlr HOGQL_INSIGHT_LIVE_COMPARE: 'hogql-insight-live-compare', // owner: @mariusandra + HOGQL_IN_INSIGHT_SERIALIZATION: 'hogql-in-insight-serialization', // owner: @Twixes BI_VIZ: 'bi_viz', // owner: @Gilbert09 WEBHOOKS_DENYLIST: 'webhooks-denylist', // owner: #team-pipeline PERSONS_HOGQL_QUERY: 'persons-hogql-query', // owner: @mariusandra diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index 478f0707d1abf..868156528ece8 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -1200,7 +1200,8 @@ "type": "string" }, "operator": { - "$ref": "#/definitions/PropertyOperator" + "$ref": "#/definitions/PropertyOperator", + "default": "exact" }, "type": { "const": "event", diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index 5c991e3fb1d9c..01708078b175b 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -66,7 +66,6 @@ export enum NodeKind { SavedInsightNode = 'SavedInsightNode', InsightVizNode = 'InsightVizNode', - // New queries, not yet implemented TrendsQuery = 'TrendsQuery', FunnelsQuery = 'FunnelsQuery', RetentionQuery = 'RetentionQuery', diff --git a/frontend/src/types.ts b/frontend/src/types.ts index d94b7c85359c2..0bdf702a82e6a 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -666,6 +666,7 @@ interface BasePropertyFilter { /** Sync with plugin-server/src/types.ts */ export interface EventPropertyFilter extends BasePropertyFilter { type: PropertyFilterType.Event + /** @default 'exact' */ operator: PropertyOperator } diff --git a/mypy-baseline.txt b/mypy-baseline.txt index 4945b638768ce..9b607b6222cd3 100644 --- a/mypy-baseline.txt +++ b/mypy-baseline.txt @@ -108,7 +108,6 @@ posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Argument posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "PathsFilter"; expected "str": "TrendsFilter" [dict-item] posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "LifecycleFilter"; expected "str": "TrendsFilter" [dict-item] posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "StickinessFilter"; expected "str": "TrendsFilter" [dict-item] -posthog/hogql_queries/legacy_compatibility/feature_flag.py:0: error: Item "AnonymousUser" of "User | AnonymousUser" has no attribute "email" [union-attr] posthog/api/utils.py:0: error: Incompatible types in assignment (expression has type "type[EventDefinition]", variable has type "type[EnterpriseEventDefinition]") [assignment] posthog/api/utils.py:0: error: Argument 1 to "UUID" has incompatible type "int | str"; expected "str | None" [arg-type] ee/billing/quota_limiting.py:0: error: List comprehension has incompatible type List[int]; expected List[str] [misc] @@ -301,7 +300,6 @@ posthog/queries/breakdown_props.py:0: error: Argument 1 to "translate_hogql" has posthog/queries/funnels/base.py:0: error: "HogQLContext" has no attribute "person_on_events_mode" [attr-defined] posthog/queries/funnels/base.py:0: error: Argument 1 to "translate_hogql" has incompatible type "str | int"; expected "str" [arg-type] ee/clickhouse/queries/funnels/funnel_correlation.py:0: error: Statement is unreachable [unreachable] -posthog/caching/calculate_results.py:0: error: Argument 3 to "process_query" has incompatible type "bool"; expected "LimitContext | None" [arg-type] posthog/api/person.py:0: error: Argument 1 to has incompatible type "*tuple[str, ...]"; expected "type[BaseRenderer]" [arg-type] posthog/api/person.py:0: error: Argument 1 to "loads" has incompatible type "str | None"; expected "str | bytes | bytearray" [arg-type] posthog/api/person.py:0: error: Argument "user" to "log_activity" has incompatible type "User | AnonymousUser"; expected "User | None" [arg-type] @@ -343,11 +341,6 @@ posthog/hogql_queries/insights/lifecycle_query_runner.py:0: note: Consider using posthog/hogql_queries/insights/lifecycle_query_runner.py:0: error: Argument 1 to "sorted" has incompatible type "list[Any] | None"; expected "Iterable[Any]" [arg-type] posthog/hogql_queries/insights/lifecycle_query_runner.py:0: error: Item "SelectUnionQuery" of "SelectQuery | SelectUnionQuery" has no attribute "select_from" [union-attr] posthog/hogql_queries/insights/lifecycle_query_runner.py:0: error: Item "None" of "JoinExpr | Any | None" has no attribute "sample" [union-attr] -posthog/hogql_queries/legacy_compatibility/process_insight.py:0: error: Incompatible types in assignment (expression has type "PathFilter", variable has type "RetentionFilter") [assignment] -posthog/hogql_queries/legacy_compatibility/process_insight.py:0: error: Incompatible types in assignment (expression has type "StickinessFilter", variable has type "RetentionFilter") [assignment] -posthog/hogql_queries/legacy_compatibility/process_insight.py:0: error: Incompatible types in assignment (expression has type "Filter", variable has type "RetentionFilter") [assignment] -posthog/api/insight.py:0: error: Argument 1 to "is_insight_with_hogql_support" has incompatible type "Insight | DashboardTile"; expected "Insight" [arg-type] -posthog/api/insight.py:0: error: Argument 1 to "process_insight" has incompatible type "Insight | DashboardTile"; expected "Insight" [arg-type] posthog/api/insight.py:0: error: Argument 1 to has incompatible type "*tuple[str, ...]"; expected "type[BaseRenderer]" [arg-type] posthog/api/dashboards/dashboard.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] posthog/api/feature_flag.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] @@ -369,19 +362,6 @@ posthog/tasks/exports/test/test_export_utils.py:0: error: Function is missing a posthog/tasks/exports/test/test_csv_exporter_url_sanitising.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] posthog/tasks/exports/test/test_csv_exporter_url_sanitising.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] posthog/tasks/exports/test/test_csv_exporter_renders.py:0: error: Function is missing a type annotation [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a return type annotation [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] -posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] posthog/session_recordings/queries/session_recording_list_from_replay_summary.py:0: error: Missing return statement [empty-body] posthog/session_recordings/queries/session_recording_list_from_replay_summary.py:0: note: If the method is meant to be abstract, use @abc.abstractmethod posthog/session_recordings/queries/session_recording_list_from_replay_summary.py:0: error: Missing return statement [empty-body] @@ -531,6 +511,19 @@ posthog/temporal/data_imports/pipelines/zendesk/helpers.py:0: error: Argument 1 posthog/temporal/data_imports/pipelines/zendesk/helpers.py:0: error: Argument 1 to "ensure_pendulum_datetime" has incompatible type "DateTime | Date | datetime | date | str | float | int | None"; expected "DateTime | Date | datetime | date | str | float | int" [arg-type] posthog/temporal/data_imports/pipelines/zendesk/helpers.py:0: error: Item "None" of "DateTime | None" has no attribute "int_timestamp" [union-attr] posthog/temporal/data_imports/pipelines/zendesk/helpers.py:0: error: Argument 1 to "ensure_pendulum_datetime" has incompatible type "str | None"; expected "DateTime | Date | datetime | date | str | float | int" [arg-type] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a return type annotation [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] +posthog/tasks/exports/test/test_csv_exporter.py:0: error: Function is missing a type annotation for one or more arguments [no-untyped-def] posthog/queries/trends/test/test_person.py:0: error: "str" has no attribute "get" [attr-defined] posthog/queries/trends/test/test_person.py:0: error: Invalid index type "int" for "HttpResponse"; expected type "str | bytes" [index] posthog/queries/trends/test/test_person.py:0: error: "str" has no attribute "get" [attr-defined] diff --git a/package.json b/package.json index fc5fc30240255..6039d555ad9ff 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "build": "pnpm copy-scripts && pnpm build:esbuild", "build:esbuild": "node frontend/build.mjs", "schema:build": "pnpm run schema:build:json && pnpm run schema:build:python", - "schema:build:json": "ts-node bin/build-schema.mjs && prettier --write frontend/src/queries/schema.json", - "schema:build:python": "datamodel-codegen --class-name='SchemaRoot' --collapse-root-models --target-python-version 3.10 --disable-timestamp --use-one-literal-as-default --use-default --use-default-kwarg --use-subclass-enum --input frontend/src/queries/schema.json --input-file-type jsonschema --output posthog/schema.py --output-model-type pydantic_v2.BaseModel && ruff format posthog/schema.py && ruff check --fix posthog/schema.py", + "schema:build:json": "ts-node bin/build-schema-json.mjs && prettier --write frontend/src/queries/schema.json", + "schema:build:python": "bash bin/build-schema-python.sh", "grammar:build": "npm run grammar:build:python && npm run grammar:build:cpp", "grammar:build:python": "cd posthog/hogql/grammar && antlr -Dlanguage=Python3 HogQLLexer.g4 && antlr -visitor -no-listener -Dlanguage=Python3 HogQLParser.g4", "grammar:build:cpp": "cd posthog/hogql/grammar && antlr -o ../../../hogql_parser -Dlanguage=Cpp HogQLLexer.g4 && antlr -o ../../../hogql_parser -visitor -no-listener -Dlanguage=Cpp HogQLParser.g4", diff --git a/posthog/api/insight.py b/posthog/api/insight.py index 528dc53767934..36495a5469b2e 100644 --- a/posthog/api/insight.py +++ b/posthog/api/insight.py @@ -35,11 +35,7 @@ from posthog.api.tagged_item import TaggedItemSerializerMixin, TaggedItemViewSetMixin from posthog.api.utils import format_paginated_url from posthog.auth import SharingAccessTokenAuthentication -from posthog.caching.fetch_from_cache import ( - InsightResult, - fetch_cached_insight_result, - synchronously_update_cache, -) +from posthog.caching.fetch_from_cache import InsightResult, fetch_cached_insight_result, synchronously_update_cache from posthog.caching.insights_api import should_refresh_insight from posthog.constants import ( INSIGHT, @@ -58,8 +54,8 @@ from posthog.hogql.errors import ExposedHogQLError from posthog.hogql.timings import HogQLTimings from posthog.hogql_queries.apply_dashboard_filters import DATA_TABLE_LIKE_NODE_KINDS -from posthog.hogql_queries.legacy_compatibility.feature_flag import hogql_insights_enabled -from posthog.hogql_queries.legacy_compatibility.process_insight import is_insight_with_hogql_support, process_insight +from posthog.hogql_queries.legacy_compatibility.feature_flag import should_use_hogql_backend_in_insight_serialization +from posthog.hogql_queries.legacy_compatibility.filter_to_query import filter_to_query from posthog.kafka_client.topics import KAFKA_METRICS_TIME_TO_SEE_DATA from posthog.models import DashboardTile, Filter, Insight, User from posthog.models.activity_logging.activity_log import ( @@ -510,7 +506,7 @@ def to_representation(self, instance: Insight): dashboard: Optional[Dashboard] = self.context.get("dashboard") representation["filters"] = instance.dashboard_filters(dashboard=dashboard) - representation["query"] = instance.dashboard_query(dashboard=dashboard) + representation["query"] = instance.get_effective_query(dashboard=dashboard) if "insight" not in representation["filters"] and not representation["query"]: representation["filters"]["insight"] = "TRENDS" @@ -521,14 +517,34 @@ def to_representation(self, instance: Insight): @lru_cache(maxsize=1) def insight_result(self, insight: Insight) -> InsightResult: + from posthog.caching.calculate_results import calculate_for_query_based_insight + dashboard = self.context.get("dashboard", None) dashboard_tile = self.dashboard_tile_from_context(insight, dashboard) - target = insight if dashboard is None else dashboard_tile - if hogql_insights_enabled(self.context.get("request", None).user) and is_insight_with_hogql_support( - target or insight + if insight.query: + try: + return calculate_for_query_based_insight( + insight, dashboard=dashboard, refresh_requested=refresh_requested_by_client(self.context["request"]) + ) + except ExposedHogQLError as e: + raise ValidationError(str(e)) + + if not self.context["request"].user.is_anonymous and should_use_hogql_backend_in_insight_serialization( + self.context["request"].user ): - return process_insight(target or insight, insight.team) + # TRICKY: As running `filters`-based insights on the HogQL-based engine is a transitional mechanism, + # we fake the insight being properly `query`-based. + # To prevent the lie from accidentally being saved to Postgres, we roll it back in the `finally` branch. + insight.query = filter_to_query(insight.filters).model_dump() + try: + return calculate_for_query_based_insight( + insight, dashboard=dashboard, refresh_requested=refresh_requested_by_client(self.context["request"]) + ) + except ExposedHogQLError as e: + raise ValidationError(str(e)) + finally: + insight.query = None is_shared = self.context.get("is_shared", False) refresh_insight_now, refresh_frequency = should_refresh_insight( @@ -539,10 +555,9 @@ def insight_result(self, insight: Insight) -> InsightResult: ) if refresh_insight_now: INSIGHT_REFRESH_INITIATED_COUNTER.labels(is_shared=is_shared).inc() - return synchronously_update_cache(insight, dashboard, refresh_frequency) + return synchronously_update_cache(insight, dashboard, refresh_frequency=refresh_frequency) - # :TODO: Clear up if tile can be null or not - return fetch_cached_insight_result(target or insight, refresh_frequency) + return fetch_cached_insight_result(dashboard_tile or insight, refresh_frequency) @lru_cache(maxsize=1) # each serializer instance should only deal with one insight/tile combo def dashboard_tile_from_context(self, insight: Insight, dashboard: Optional[Dashboard]) -> Optional[DashboardTile]: diff --git a/posthog/api/query.py b/posthog/api/query.py index 5309a96459dd0..197fe79f18e1f 100644 --- a/posthog/api/query.py +++ b/posthog/api/query.py @@ -3,6 +3,7 @@ from django.http import JsonResponse from drf_spectacular.utils import OpenApiResponse +from posthog.hogql_queries.query_runner import ExecutionMode from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.exceptions import ValidationError, NotAuthenticated @@ -75,7 +76,13 @@ def create(self, request, *args, **kwargs) -> Response: tag_queries(query=request.data["query"]) try: - result = process_query_model(self.team, data.query, refresh_requested=data.refresh) + result = process_query_model( + self.team, + data.query, + execution_mode=ExecutionMode.CALCULATION_ALWAYS + if data.refresh + else ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE, + ) return Response(result) except (ExposedHogQLError, ExposedCHQueryError) as e: raise ValidationError(str(e), getattr(e, "code_name", None)) diff --git a/posthog/api/services/query.py b/posthog/api/services/query.py index 75d326afead3a..09d33759d0225 100644 --- a/posthog/api/services/query.py +++ b/posthog/api/services/query.py @@ -11,7 +11,7 @@ from posthog.hogql.autocomplete import get_hogql_autocomplete from posthog.hogql.metadata import get_hogql_metadata from posthog.hogql.modifiers import create_default_modifiers_for_team -from posthog.hogql_queries.query_runner import get_query_runner +from posthog.hogql_queries.query_runner import ExecutionMode, get_query_runner from posthog.models import Team from posthog.queries.time_to_see_data.serializers import SessionEventsQuerySerializer, SessionsQuerySerializer from posthog.queries.time_to_see_data.sessions import get_session_events, get_sessions @@ -59,8 +59,9 @@ def process_query( team: Team, query_json: dict, + *, limit_context: Optional[LimitContext] = None, - refresh_requested: Optional[bool] = False, + execution_mode: ExecutionMode = ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE, ) -> dict: model = QuerySchemaRoot.model_validate(query_json) tag_queries(query=query_json) @@ -68,21 +69,22 @@ def process_query( team, model.root, limit_context=limit_context, - refresh_requested=refresh_requested, + execution_mode=execution_mode, ) def process_query_model( team: Team, query: BaseModel, # mypy has problems with unions and isinstance + *, limit_context: Optional[LimitContext] = None, - refresh_requested: Optional[bool] = False, + execution_mode: ExecutionMode = ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE, ) -> dict: result: dict | BaseModel if isinstance(query, QUERY_WITH_RUNNER): # type: ignore query_runner = get_query_runner(query, team, limit_context=limit_context) - result = query_runner.run(refresh_requested=refresh_requested) + result = query_runner.run(execution_mode=execution_mode) elif isinstance(query, QUERY_WITH_RUNNER_NO_CACHE): # type: ignore query_runner = get_query_runner(query, team, limit_context=limit_context) result = query_runner.calculate() diff --git a/posthog/api/test/dashboards/test_dashboard.py b/posthog/api/test/dashboards/test_dashboard.py index 1f7cd4b533fbd..e4c91f45149a1 100644 --- a/posthog/api/test/dashboards/test_dashboard.py +++ b/posthog/api/test/dashboards/test_dashboard.py @@ -1232,7 +1232,14 @@ def test_create_from_template_json_cam_provide_query_tile(self) -> None: "tiles": [ { "type": "INSIGHT", - "query": {"kind": "a datatable"}, + "query": { + "kind": "DataTableNode", + "columns": ["person", "id", "created_at", "person.$delete"], + "source": { + "kind": "EventsQuery", + "select": ["*"], + }, + }, "filters": {"date_from": None}, "layouts": {}, } @@ -1277,8 +1284,15 @@ def test_create_from_template_json_cam_provide_query_tile(self) -> None: "name": None, "next_allowed_client_refresh": None, "order": None, - "query": {"kind": "a datatable"}, - "result": None, + "query": { + "kind": "DataTableNode", + "columns": ["person", "id", "created_at", "person.$delete"], + "source": { + "kind": "EventsQuery", + "select": ["*"], + }, + }, + "result": [], "saved": False, "short_id": ANY, "tags": [], diff --git a/posthog/api/test/test_insight.py b/posthog/api/test/test_insight.py index b13bf0d6f9189..f707f0330b7fb 100644 --- a/posthog/api/test/test_insight.py +++ b/posthog/api/test/test_insight.py @@ -9,6 +9,7 @@ from django.test import override_settings from django.utils import timezone from freezegun import freeze_time +from posthog.hogql.query import execute_hogql_query from rest_framework import status from posthog.api.test.dashboards import DashboardAPI @@ -27,7 +28,16 @@ OrganizationMembership, Text, ) -from posthog.schema import DataTableNode, DataVisualizationNode, DateRange, HogQLFilters, HogQLQuery +from posthog.schema import ( + DataTableNode, + DataVisualizationNode, + DateRange, + EventPropertyFilter, + EventsNode, + HogQLFilters, + HogQLQuery, + TrendsQuery, +) from posthog.test.base import ( APIBaseTest, ClickhouseTestMixin, @@ -995,11 +1005,8 @@ def test_save_new_funnel(self) -> None: self.assertEqual(objects[0].filters["layout"], "horizontal") self.assertEqual(len(objects[0].short_id), 8) - @patch( - "posthog.api.insight.synchronously_update_cache", - wraps=synchronously_update_cache, - ) - def test_insight_refreshing(self, spy_update_insight_cache) -> None: + @patch("posthog.api.insight.synchronously_update_cache", wraps=synchronously_update_cache) + def test_insight_refreshing_legacy(self, spy_update_insight_cache) -> None: dashboard_id, _ = self.dashboard_api.create_dashboard({"filters": {"date_from": "-14d"}}) with freeze_time("2012-01-14T03:21:34.000Z"): @@ -1124,6 +1131,153 @@ def test_insight_refreshing(self, spy_update_insight_cache) -> None: ], ) + @patch("posthog.hogql_queries.insights.trends.trends_query_runner.execute_hogql_query", wraps=execute_hogql_query) + def test_insight_refreshing_query(self, spy_execute_hogql_query) -> None: + dashboard_id, _ = self.dashboard_api.create_dashboard({"filters": {"date_from": "-14d"}}) + + with freeze_time("2012-01-14T03:21:34.000Z"): + _create_event( + team=self.team, + event="$pageview", + distinct_id="1", + properties={"prop": "val"}, + ) + _create_event( + team=self.team, + event="$pageview", + distinct_id="2", + properties={"prop": "another_val"}, + ) + _create_event( + team=self.team, + event="$pageview", + distinct_id="2", + properties={"prop": "val", "another": "never_return_this"}, + ) + + query_dict = TrendsQuery( + series=[ + EventsNode( + event="$pageview", + properties=[EventPropertyFilter(key="another", value="never_return_this", operator="is_not")], + ) + ] + ).model_dump() + + with freeze_time("2012-01-15T04:01:34.000Z"): + response = self.client.post( + f"/api/projects/{self.team.id}/insights", + data={ + "query": query_dict, + "dashboards": [dashboard_id], + }, + ).json() + self.assertNotIn("code", response) # Watching out for an error code + self.assertEqual(response["last_refresh"], None) + insight_id = response["id"] + + response = self.client.get(f"/api/projects/{self.team.id}/insights/{insight_id}/?refresh=true").json() + self.assertNotIn("code", response) + self.assertEqual(spy_execute_hogql_query.call_count, 1) + self.assertEqual(response["result"][0]["data"], [0, 0, 0, 0, 0, 0, 2, 0]) + self.assertEqual(response["last_refresh"], "2012-01-15T04:01:34Z") + self.assertEqual(response["last_modified_at"], "2012-01-15T04:01:34Z") + self.assertFalse(response["is_cached"]) + + with freeze_time("2012-01-15T05:01:34.000Z"): + _create_event(team=self.team, event="$pageview", distinct_id="1") + response = self.client.get(f"/api/projects/{self.team.id}/insights/{insight_id}/?refresh=true").json() + self.assertNotIn("code", response) + self.assertEqual(spy_execute_hogql_query.call_count, 2) + self.assertEqual(response["result"][0]["data"], [0, 0, 0, 0, 0, 0, 2, 1]) + self.assertEqual(response["last_refresh"], "2012-01-15T05:01:34Z") + self.assertEqual(response["last_modified_at"], "2012-01-15T04:01:34Z") # did not change + self.assertFalse(response["is_cached"]) + + with freeze_time("2012-01-15T05:17:34.000Z"): + response = self.client.get(f"/api/projects/{self.team.id}/insights/{insight_id}/").json() + self.assertNotIn("code", response) + self.assertEqual(spy_execute_hogql_query.call_count, 2) + self.assertEqual(response["result"][0]["data"], [0, 0, 0, 0, 0, 0, 2, 1]) + self.assertEqual(response["last_refresh"], "2012-01-15T05:01:34Z") # Using cached result + self.assertEqual(response["last_modified_at"], "2012-01-15T04:01:34Z") # did not change + self.assertTrue(response["is_cached"]) + + with freeze_time("2012-01-15T05:17:39.000Z"): + # Make sure the /query/ endpoint reuses the same cached result + response = self.client.post(f"/api/projects/{self.team.id}/query/", {"query": query_dict}).json() + self.assertNotIn("code", response) + self.assertEqual(spy_execute_hogql_query.call_count, 2) + self.assertEqual(response["results"][0]["data"], [0, 0, 0, 0, 0, 0, 2, 1]) + self.assertEqual(response["last_refresh"], "2012-01-15T05:01:34Z") # Using cached result + self.assertTrue(response["is_cached"]) + + with freeze_time("2012-01-16T05:01:34.000Z"): + # load it in the context of the dashboard, so has last 14 days as filter + response = self.client.get( + f"/api/projects/{self.team.id}/insights/{insight_id}/?refresh=true&from_dashboard={dashboard_id}" + ).json() + self.assertNotIn("code", response) + self.assertEqual(spy_execute_hogql_query.call_count, 3) + self.assertEqual( + response["result"][0]["data"], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0, + 1.0, + 0.0, + ], + ) + self.assertEqual(response["last_refresh"], "2012-01-16T05:01:34Z") + self.assertEqual(response["last_modified_at"], "2012-01-15T04:01:34Z") # did not change + self.assertFalse(response["is_cached"]) + + #  Test property filter + + dashboard = Dashboard.objects.get(pk=dashboard_id) + dashboard.filters = { + "properties": [{"key": "prop", "value": "val"}], + "date_from": "-14d", + } + dashboard.save() + with freeze_time("2012-01-16T05:01:34.000Z"): + response = self.client.get( + f"/api/projects/{self.team.id}/insights/{insight_id}/?refresh=true&from_dashboard={dashboard_id}" + ).json() + self.assertNotIn("code", response) + self.assertEqual(spy_execute_hogql_query.call_count, 4) + self.assertEqual( + response["result"][0]["data"], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + ], + ) + def test_dashboard_filters_applied_to_data_table_node(self): dashboard_id, _ = self.dashboard_api.create_dashboard( {"name": "the dashboard", "filters": {"date_from": "-180d"}} @@ -1973,7 +2127,7 @@ def test_get_recently_viewed_insights_excludes_query_based_insights_by_default(s "*", "event", "person", - "coalesce(properties.$current_url, properties.$screen_name) # Url / Screen", + "coalesce(properties.$current_url, properties.$screen_name)", "properties.$lib", "timestamp", ], @@ -2014,7 +2168,7 @@ def test_get_recently_viewed_insights_can_include_query_based_insights(self) -> "*", "event", "person", - "coalesce(properties.$current_url, properties.$screen_name) # Url / Screen", + "coalesce(properties.$current_url, properties.$screen_name)", "properties.$lib", "timestamp", ], @@ -2953,22 +3107,19 @@ def test_insight_retention_hogql(self) -> None: def test_insight_with_filters_via_hogql(self) -> None: filter_dict = {"insight": "LIFECYCLE", "events": [{"id": "$pageview"}]} - Insight.objects.create( + insight = Insight.objects.create( filters=Filter(data=filter_dict).to_dict(), team=self.team, short_id="xyz123", ) # fresh response - response = self.client.get(f"/api/projects/{self.team.id}/insights/?short_id=xyz123") + response = self.client.get(f"/api/projects/{self.team.id}/insights/{insight.id}/?refresh=true") self.assertEqual(response.status_code, status.HTTP_200_OK) - - self.assertEqual(len(response.json()["results"]), 1) - self.assertEqual(response.json()["results"][0]["result"][0]["data"], [0, 0, 0, 0, 0, 0, 0, 0]) + self.assertEqual(response.json()["result"][0]["data"], [0, 0, 0, 0, 0, 0, 0, 0]) # cached response - response = self.client.get(f"/api/projects/{self.team.id}/insights/?short_id=xyz123") + response = self.client.get(f"/api/projects/{self.team.id}/insights/{insight.id}/?refresh=true") self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.json()["results"]), 1) - self.assertEqual(response.json()["results"][0]["result"][0]["data"], [0, 0, 0, 0, 0, 0, 0, 0]) + self.assertEqual(response.json()["result"][0]["data"], [0, 0, 0, 0, 0, 0, 0, 0]) diff --git a/posthog/api/test/test_insight_query.py b/posthog/api/test/test_insight_query.py index 79a1785c2846b..6279999bbefcb 100644 --- a/posthog/api/test/test_insight_query.py +++ b/posthog/api/test/test_insight_query.py @@ -25,7 +25,7 @@ def test_can_save_valid_events_query_to_an_insight(self) -> None: "*", "event", "person", - "coalesce(properties.$current_url, properties.$screen_name) # Url / Screen", + "coalesce(properties.$current_url, properties.$screen_name)", "properties.$lib", "timestamp", ], @@ -55,7 +55,7 @@ def test_can_save_valid_events_table_query_to_an_insight(self) -> None: "*", "event", "person", - "coalesce(properties.$current_url, properties.$screen_name) # Url / Screen", + "coalesce(properties.$current_url, properties.$screen_name)", "properties.$lib", "timestamp", ], @@ -82,15 +82,8 @@ def test_can_save_valid_persons_table_query_to_an_insight(self) -> None: "kind": "DataTableNode", "columns": ["person", "id", "created_at", "person.$delete"], "source": { - "kind": "PersonsNode", - "properties": [ - { - "type": "person", - "key": "$browser", - "operator": "exact", - "value": "Chrome", - } - ], + "kind": "EventsQuery", + "select": ["*"], }, }, }, @@ -105,15 +98,8 @@ def test_no_default_filters_on_insight_query(self) -> None: "kind": "DataTableNode", "columns": ["person", "id", "created_at", "person.$delete"], "source": { - "kind": "PersonsNode", - "properties": [ - { - "type": "person", - "key": "$browser", - "operator": "exact", - "value": "Chrome", - } - ], + "kind": "EventsQuery", + "select": ["*"], }, }, }, @@ -178,15 +164,6 @@ def test_can_save_insights_query_to_an_insight(self) -> None: "name": "$pageview", "custom_name": "Views", "event": "$pageview", - "properties": [ - { - "type": "event", - "key": "$browser", - "operator": "exact", - "value": "Chrome", - }, - {"type": "cohort", "key": "id", "value": 2}, - ], "limit": 100, } ], @@ -212,15 +189,8 @@ def test_cannot_save_invalid_persons_table_query_to_an_insight(self) -> None: "query": { "kind": "DataTableNode", "source": { - "kind": "PersonsNode", - "properties": [ - { - "type": "person", - "key": "$browser", - "operator": "exact", - "value": "Chrome", - } - ], + "kind": "EventsQuery", + "select": ["*"], }, }, }, @@ -236,15 +206,8 @@ def test_listing_insights_by_default_does_not_include_those_with_only_queries(se "kind": "DataTableNode", "columns": ["person", "id", "created_at", "person.$delete"], "source": { - "kind": "PersonsNode", - "properties": [ - { - "type": "person", - "key": "$browser", - "operator": "exact", - "value": "Chrome", - } - ], + "kind": "EventsQuery", + "select": ["*"], }, }, }, @@ -266,15 +229,8 @@ def test_can_list_insights_including_those_with_only_queries(self) -> None: "kind": "DataTableNode", "columns": ["person", "id", "created_at", "person.$delete"], "source": { - "kind": "PersonsNode", - "properties": [ - { - "type": "person", - "key": "$browser", - "operator": "exact", - "value": "Chrome", - } - ], + "kind": "EventsQuery", + "select": ["*"], }, }, }, diff --git a/posthog/caching/calculate_results.py b/posthog/caching/calculate_results.py index 1dd9d80538567..ae4d6d8104f68 100644 --- a/posthog/caching/calculate_results.py +++ b/posthog/caching/calculate_results.py @@ -1,5 +1,6 @@ -from typing import Any, Dict, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union +from posthog.api.services.query import ExecutionMode import structlog from sentry_sdk import capture_exception @@ -29,10 +30,7 @@ from posthog.models.filters.stickiness_filter import StickinessFilter from posthog.models.filters.utils import get_filter from posthog.models.insight import generate_insight_cache_key -from posthog.queries.funnels import ( - ClickhouseFunnelTimeToConvert, - ClickhouseFunnelTrends, -) +from posthog.queries.funnels import ClickhouseFunnelTimeToConvert, ClickhouseFunnelTrends from posthog.queries.funnels.utils import get_funnel_order_class from posthog.queries.paths import Paths from posthog.queries.retention import Retention @@ -40,6 +38,9 @@ from posthog.queries.trends.trends import Trends from posthog.types import FilterType +if TYPE_CHECKING: + from posthog.caching.fetch_from_cache import InsightResult + CACHE_TYPE_TO_INSIGHT_CLASS = { CacheType.TRENDS: Trends, CacheType.STICKINESS: Stickiness, @@ -54,7 +55,7 @@ def calculate_cache_key(target: Union[DashboardTile, Insight]) -> Optional[str]: insight = target if isinstance(target, Insight) else target.insight dashboard = target.dashboard if isinstance(target, DashboardTile) else None - if insight is None or (not insight.filters and insight.query is None): + if insight is None or not insight.filters: return None return generate_insight_cache_key(insight, dashboard) @@ -106,57 +107,59 @@ def get_cache_type(cacheable: Optional[FilterType] | Optional[Dict]) -> CacheTyp raise Exception("Could not determine cache type. Must provide a filter or a query") -def calculate_result_by_insight( - team: Team, insight: Insight, dashboard: Optional[Dashboard] -) -> Tuple[str, str, List | Dict]: - """ - Calculates the result for an insight. If the insight is query based, - it will use the query to calculate the result. Even if there is a filter present on the insight +def calculate_for_query_based_insight( + insight: Insight, *, dashboard: Optional[Dashboard] = None, refresh_requested: bool +) -> "InsightResult": + from posthog.api.services.query import process_query + from posthog.caching.fetch_from_cache import InsightResult, NothingInCacheResult - Eventually there will be no filter-based insights left and calculate_for_query_based_insight will be - in-lined into this function - """ - if insight.query is not None: - return calculate_for_query_based_insight(team, insight, dashboard) - else: - return calculate_for_filter_based_insight(team, insight, dashboard) + tag_queries(team_id=insight.team_id, insight_id=insight.pk) + if dashboard: + tag_queries(dashboard_id=dashboard.pk) + effective_query = insight.get_effective_query(dashboard=dashboard) + assert effective_query is not None -def calculate_for_query_based_insight( - team: Team, insight: Insight, dashboard: Optional[Dashboard] -) -> Tuple[str, str, List | Dict]: - cache_key = generate_insight_cache_key(insight, dashboard) - cache_type = get_cache_type(insight.query) - - tag_queries( - team_id=team.pk, - insight_id=insight.pk, - cache_type=cache_type, - cache_key=cache_key, + response = process_query( + insight.team, + effective_query, + execution_mode=ExecutionMode.CALCULATION_ALWAYS + if refresh_requested + else ExecutionMode.CACHE_ONLY_NEVER_CALCULATE, ) - # local import to avoid circular reference - from posthog.api.services.query import process_query - - # TODO need to properly check that hogql is enabled? - return cache_key, cache_type, process_query(team, insight.query, True) + if "results" not in response: + # Translating `CacheMissResponse` to legacy insights shape + return NothingInCacheResult(cache_key=response.get("cache_key")) + + return InsightResult( + # Translating `QueryResponse` to legacy insights shape + # Only `results` is guaranteed even for non-insight queries, such as `EventsQueryResponse` + result=response["results"], + last_refresh=response.get("last_refresh"), + cache_key=response.get("cache_key"), + is_cached=response.get("is_cached", False), + timezone=response.get("timezone"), + next_allowed_client_refresh=response.get("next_allowed_client_refresh"), + timings=response.get("timings"), + ) def calculate_for_filter_based_insight( - team: Team, insight: Insight, dashboard: Optional[Dashboard] + insight: Insight, dashboard: Optional[Dashboard] ) -> Tuple[str, str, List | Dict]: - filter = get_filter(data=insight.dashboard_filters(dashboard), team=team) + filter = get_filter(data=insight.dashboard_filters(dashboard), team=insight.team) cache_key = generate_insight_cache_key(insight, dashboard) cache_type = get_cache_type(filter) tag_queries( - team_id=team.pk, + team_id=insight.team_id, insight_id=insight.pk, cache_type=cache_type, cache_key=cache_key, ) - return cache_key, cache_type, calculate_result_by_cache_type(cache_type, filter, team) + return cache_key, cache_type, calculate_result_by_cache_type(cache_type, filter, insight.team) def calculate_result_by_cache_type(cache_type: CacheType, filter: Filter, team: Team) -> List[Dict[str, Any]]: diff --git a/posthog/caching/fetch_from_cache.py b/posthog/caching/fetch_from_cache.py index 43e847e747a3c..fcbeb0b72e341 100644 --- a/posthog/caching/fetch_from_cache.py +++ b/posthog/caching/fetch_from_cache.py @@ -5,10 +5,7 @@ from django.utils.timezone import now from prometheus_client import Counter -from posthog.caching.calculate_results import ( - calculate_cache_key, - calculate_result_by_insight, -) +from posthog.caching.calculate_results import calculate_cache_key, calculate_for_filter_based_insight from posthog.caching.insight_cache import update_cached_state from posthog.models import DashboardTile, Insight from posthog.models.dashboard import Dashboard @@ -83,7 +80,7 @@ def synchronously_update_cache( dashboard: Optional[Dashboard], refresh_frequency: Optional[timedelta] = None, ) -> InsightResult: - cache_key, cache_type, result = calculate_result_by_insight(team=insight.team, insight=insight, dashboard=dashboard) + cache_key, cache_type, result = calculate_for_filter_based_insight(insight, dashboard) timestamp = now() next_allowed_client_refresh = timestamp + refresh_frequency if refresh_frequency else None diff --git a/posthog/caching/insight_cache.py b/posthog/caching/insight_cache.py index b2f14eab178d4..d73486234dfb1 100644 --- a/posthog/caching/insight_cache.py +++ b/posthog/caching/insight_cache.py @@ -12,8 +12,8 @@ from sentry_sdk.api import capture_exception from statshog.defaults.django import statsd -from posthog.caching.calculate_results import calculate_result_by_insight -from posthog.models import Dashboard, Insight, InsightCachingState, Team +from posthog.caching.calculate_results import calculate_for_filter_based_insight +from posthog.models import Dashboard, Insight, InsightCachingState from posthog.models.instance_setting import get_instance_setting from posthog.tasks.tasks import update_cache_task @@ -90,13 +90,12 @@ def update_cache(caching_state_id: UUID): return insight, dashboard = _extract_insight_dashboard(caching_state) - team: Team = insight.team start_time = perf_counter() exception = cache_key = cache_type = None metadata = { - "team_id": team.pk, + "team_id": insight.team_id, "insight_id": insight.pk, "dashboard_id": dashboard.pk if dashboard else None, "last_refresh": caching_state.last_refresh, @@ -104,7 +103,7 @@ def update_cache(caching_state_id: UUID): } try: - cache_key, cache_type, result = calculate_result_by_insight(team=team, insight=insight, dashboard=dashboard) + cache_key, cache_type, result = calculate_for_filter_based_insight(insight=insight, dashboard=dashboard) except Exception as err: capture_exception(err, metadata) exception = err diff --git a/posthog/caching/test/test_insight_cache.py b/posthog/caching/test/test_insight_cache.py index 269aebf887838..9de2053f6c2f1 100644 --- a/posthog/caching/test/test_insight_cache.py +++ b/posthog/caching/test/test_insight_cache.py @@ -165,16 +165,15 @@ def test_update_cache_updates_identical_cache_keys(team: Team, user: User, cache @pytest.mark.django_db @freeze_time("2020-01-04T13:01:01Z") @patch("posthog.caching.insight_cache.update_cache_task") -@patch("posthog.caching.insight_cache.calculate_result_by_insight") +@patch("posthog.caching.insight_cache.calculate_for_filter_based_insight", side_effect=Exception()) def test_update_cache_when_calculation_fails( - spy_calculate_result_by_insight, + spy_calculate_for_filter_based_insight, spy_update_cache_task, team: Team, user: User, cache, ): caching_state = create_insight_caching_state(team, user, refresh_attempt=1) - spy_calculate_result_by_insight.side_effect = Exception() update_cache(caching_state.pk) @@ -190,8 +189,8 @@ def test_update_cache_when_calculation_fails( @pytest.mark.django_db @freeze_time("2020-01-04T13:01:01Z") -@patch("posthog.caching.insight_cache.calculate_result_by_insight") -def test_update_cache_when_recently_refreshed(spy_calculate_result_by_insight, team: Team, user: User): +@patch("posthog.caching.insight_cache.calculate_for_filter_based_insight") +def test_update_cache_when_recently_refreshed(spy_calculate_for_filter_based_insight, team: Team, user: User): caching_state = create_insight_caching_state( team, user, last_refresh=timedelta(hours=1), target_cache_age=timedelta(days=1) ) @@ -200,7 +199,7 @@ def test_update_cache_when_recently_refreshed(spy_calculate_result_by_insight, t updated_caching_state = InsightCachingState.objects.get(team=team) - assert spy_calculate_result_by_insight.call_count == 0 + assert spy_calculate_for_filter_based_insight.call_count == 0 assert updated_caching_state.last_refresh == caching_state.last_refresh diff --git a/posthog/clickhouse/client/execute_async.py b/posthog/clickhouse/client/execute_async.py index eeed13ce5eed1..2a2e762d5aa56 100644 --- a/posthog/clickhouse/client/execute_async.py +++ b/posthog/clickhouse/client/execute_async.py @@ -83,7 +83,7 @@ def execute_process_query( ): manager = QueryStatusManager(query_id, team_id) - from posthog.api.services.query import process_query + from posthog.api.services.query import process_query, ExecutionMode from posthog.models import Team team = Team.objects.get(pk=team_id) @@ -103,7 +103,12 @@ def execute_process_query( try: tag_queries(client_query_id=query_id, team_id=team_id, user_id=user_id) results = process_query( - team=team, query_json=query_json, limit_context=limit_context, refresh_requested=refresh_requested + team=team, + query_json=query_json, + limit_context=limit_context, + execution_mode=ExecutionMode.CALCULATION_ALWAYS + if refresh_requested + else ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE, ) logger.info("Got results for team %s query %s", team_id, query_id) query_status.complete = True diff --git a/posthog/hogql_queries/apply_dashboard_filters.py b/posthog/hogql_queries/apply_dashboard_filters.py index 9506c3704a4d8..2b1b6dc7b89bb 100644 --- a/posthog/hogql_queries/apply_dashboard_filters.py +++ b/posthog/hogql_queries/apply_dashboard_filters.py @@ -1,3 +1,4 @@ +from sentry_sdk import capture_exception from posthog.hogql_queries.query_runner import get_query_runner from posthog.models import Team from posthog.schema import DashboardFilter, NodeKind @@ -16,9 +17,11 @@ def apply_dashboard_filters(query: dict, filters: dict, team: Team) -> dict: try: query_runner = get_query_runner(query, team) except ValueError: + capture_exception() return query try: return query_runner.apply_dashboard_filters(DashboardFilter(**filters)).dict() except NotImplementedError: # TODO when we implement apply_dashboard_filters on more query runners, we can remove the try/catch + capture_exception() return query diff --git a/posthog/hogql_queries/insights/test/test_paths_query_runner.py b/posthog/hogql_queries/insights/test/test_paths_query_runner.py index 1dd96a3638654..b74102ba70510 100644 --- a/posthog/hogql_queries/insights/test/test_paths_query_runner.py +++ b/posthog/hogql_queries/insights/test/test_paths_query_runner.py @@ -6,6 +6,7 @@ from freezegun import freeze_time from posthog.hogql_queries.insights.paths_query_runner import PathsQueryRunner +from posthog.hogql_queries.query_runner import CachedQueryResponse from posthog.models import Team from posthog.test.base import ( APIBaseTest, @@ -153,6 +154,7 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual(response[0]["source"], "1_/", response) @@ -183,6 +185,8 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) self.assertEqual(len(result.results), 4) date_to = now() @@ -196,6 +200,8 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) self.assertEqual(len(result.results), 4) date_from = now() + relativedelta(days=7) @@ -209,6 +215,8 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) self.assertEqual(len(result.results), 0) date_to = now() - relativedelta(days=7) @@ -222,6 +230,8 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) self.assertEqual(len(result.results), 0) date_from = now() - relativedelta(days=7) @@ -238,6 +248,7 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + assert isinstance(result, CachedQueryResponse) self.assertEqual(len(result.results), 4) # Test account filter @@ -253,6 +264,8 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) self.assertEqual(len(result.results), 3) date_from = now() + relativedelta(days=7) @@ -268,6 +281,8 @@ def test_current_url_paths_and_logic(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) self.assertEqual(len(result.results), 0) def test_custom_event_paths(self): @@ -366,6 +381,7 @@ def test_custom_event_paths(self): }, team=self.team, ).run() + assert isinstance(r, CachedQueryResponse) response = r.results self.assertEqual(response[0]["source"], "1_custom_event_1", response) @@ -481,6 +497,7 @@ def test_custom_hogql_paths(self): }, team=self.team, ).run() + assert isinstance(r, CachedQueryResponse) response = r.results self.assertEqual(response[0]["source"], "1_custom_event_1!", response) @@ -587,6 +604,7 @@ def test_screen_paths(self): }, team=self.team, ).run() + assert isinstance(r, CachedQueryResponse) response = r.results self.assertEqual(response[0]["source"], "1_/", response) @@ -707,6 +725,7 @@ def test_paths_properties_filter(self): }, team=self.team, ).run() + assert isinstance(r, CachedQueryResponse) response = r.results self.assertEqual(response[0]["source"], "1_/") @@ -850,6 +869,7 @@ def test_paths_start(self): }, team=self.team, ).run() + assert isinstance(r, CachedQueryResponse) response = r.results self.assertEqual(len(response), 5) @@ -870,6 +890,8 @@ def test_paths_start(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual(len(response), 5) @@ -889,6 +911,8 @@ def test_paths_start(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual(len(response), 3) @@ -948,6 +972,8 @@ def test_paths_in_window(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual(response[0]["source"], "1_/") diff --git a/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py b/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py index 96ae1ab49eb47..f7d01b9ec9d33 100644 --- a/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py +++ b/posthog/hogql_queries/insights/test/test_paths_query_runner_ee.py @@ -16,6 +16,7 @@ ) from posthog.hogql_queries.actors_query_runner import ActorsQueryRunner from posthog.hogql_queries.insights.paths_query_runner import PathsQueryRunner +from posthog.hogql_queries.query_runner import CachedQueryResponse from posthog.models.filters import Filter, PathFilter from posthog.models.group.util import create_group from posthog.models.group_type_mapping import GroupTypeMapping @@ -152,6 +153,7 @@ def test_step_limit(self): with freeze_time("2012-01-7T03:21:34.000Z"): filter = {"stepLimit": 2} result = PathsQueryRunner(query={"kind": "PathsQuery", "pathsFilter": filter}, team=self.team).run() + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -171,6 +173,7 @@ def test_step_limit(self): with freeze_time("2012-01-7T03:21:34.000Z"): filter = {"stepLimit": 3} result = PathsQueryRunner(query={"kind": "PathsQuery", "pathsFilter": filter}, team=self.team).run() + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -195,6 +198,7 @@ def test_step_limit(self): with freeze_time("2012-01-7T03:21:34.000Z"): filter = {"stepLimit": 4} result = PathsQueryRunner(query={"kind": "PathsQuery", "pathsFilter": filter}, team=self.team).run() + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -307,6 +311,8 @@ def test_step_conversion_times(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -388,6 +394,8 @@ def test_event_ordering(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -1840,6 +1848,8 @@ def test_end(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -1903,6 +1913,8 @@ def test_end(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2045,6 +2057,8 @@ def test_event_inclusion_exclusion_filters(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2077,6 +2091,8 @@ def test_event_inclusion_exclusion_filters(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2109,6 +2125,8 @@ def test_event_inclusion_exclusion_filters(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2142,6 +2160,8 @@ def test_event_inclusion_exclusion_filters(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2265,6 +2285,8 @@ def test_event_exclusion_filters_with_wildcard_groups(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2293,6 +2315,8 @@ def test_event_exclusion_filters_with_wildcard_groups(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual(len(response), 6) @@ -2390,6 +2414,8 @@ def test_event_inclusion_exclusion_filters_across_single_person(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2458,6 +2484,8 @@ def test_event_inclusion_exclusion_filters_across_single_person(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2509,6 +2537,8 @@ def test_event_inclusion_exclusion_filters_across_single_person(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2604,6 +2634,8 @@ def test_respect_session_limits(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2712,6 +2744,8 @@ def test_removes_duplicates(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2876,6 +2910,8 @@ def test_start_and_end(self): query=paths_query.copy(), team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -2898,6 +2934,8 @@ def test_start_and_end(self): query=paths_query, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -3080,6 +3118,8 @@ def test_wildcard_groups_across_people(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -3185,6 +3225,8 @@ def test_wildcard_groups_evil_input(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -3467,6 +3509,8 @@ def test_start_dropping_orphaned_edges(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -3701,6 +3745,8 @@ def test_groups_filtering(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -3751,6 +3797,8 @@ def test_groups_filtering(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -3801,6 +3849,8 @@ def test_groups_filtering(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -3939,6 +3989,8 @@ def test_groups_filtering_person_on_events(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results with override_instance_config("PERSON_ON_EVENTS_ENABLED", True): @@ -3990,6 +4042,7 @@ def test_groups_filtering_person_on_events(self): }, team=self.team, ).run() + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -4040,6 +4093,7 @@ def test_groups_filtering_person_on_events(self): }, team=self.team, ).run() + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( @@ -4141,6 +4195,8 @@ def test_person_on_events_v2(self): }, team=self.team, ).run() + + assert isinstance(result, CachedQueryResponse) response = result.results self.assertEqual( diff --git a/posthog/hogql_queries/insights/trends/trends_query_runner.py b/posthog/hogql_queries/insights/trends/trends_query_runner.py index 3e62d51a6217b..8629d17ec928a 100644 --- a/posthog/hogql_queries/insights/trends/trends_query_runner.py +++ b/posthog/hogql_queries/insights/trends/trends_query_runner.py @@ -818,8 +818,8 @@ def _trends_display(self) -> TrendsDisplay: return TrendsDisplay(display) def apply_dashboard_filters(self, *args, **kwargs) -> RunnableQueryNode: + updated_query = super().apply_dashboard_filters(*args, **kwargs) # Remove any set breakdown limit for display on the dashboard - if self.query.breakdownFilter: - self.query.breakdownFilter.breakdown_limit = None - - return self.query + if updated_query.breakdownFilter: + updated_query.breakdownFilter.breakdown_limit = None + return updated_query diff --git a/posthog/hogql_queries/legacy_compatibility/feature_flag.py b/posthog/hogql_queries/legacy_compatibility/feature_flag.py index 2c1708223d9b9..69e08ea5aa988 100644 --- a/posthog/hogql_queries/legacy_compatibility/feature_flag.py +++ b/posthog/hogql_queries/legacy_compatibility/feature_flag.py @@ -1,26 +1,16 @@ -from typing import cast import posthoganalytics from django.conf import settings -from posthog.cloud_utils import is_cloud from posthog.models.user import User -from django.contrib.auth.models import AnonymousUser -def hogql_insights_enabled(user: User | AnonymousUser) -> bool: +def should_use_hogql_backend_in_insight_serialization(user: User) -> bool: if settings.HOGQL_INSIGHTS_OVERRIDE is not None: return settings.HOGQL_INSIGHTS_OVERRIDE - # on PostHog Cloud, use the feature flag - if is_cloud(): - if not hasattr(user, "distinct_id"): # exclude api endpoints that don't have auth from the flag - return False - - return posthoganalytics.feature_enabled( - "hogql-insights", - cast(str, user.distinct_id), - person_properties={"email": user.email}, - only_evaluate_locally=True, - send_feature_flag_events=False, - ) - else: - return False + return posthoganalytics.feature_enabled( + "hogql-in-insight-serialization", + user.distinct_id, + person_properties={"email": user.email}, + only_evaluate_locally=True, + send_feature_flag_events=False, + ) diff --git a/posthog/hogql_queries/legacy_compatibility/process_insight.py b/posthog/hogql_queries/legacy_compatibility/process_insight.py deleted file mode 100644 index 074128cf86b9b..0000000000000 --- a/posthog/hogql_queries/legacy_compatibility/process_insight.py +++ /dev/null @@ -1,51 +0,0 @@ -from posthog.caching.fetch_from_cache import InsightResult -from posthog.hogql_queries.legacy_compatibility.filter_to_query import filter_to_query -from posthog.hogql_queries.insights.lifecycle_query_runner import LifecycleQueryRunner -from posthog.hogql_queries.query_runner import CachedQueryResponse -from posthog.models.filters.filter import Filter as LegacyFilter -from posthog.models.filters.path_filter import PathFilter as LegacyPathFilter -from posthog.models.filters.retention_filter import RetentionFilter as LegacyRetentionFilter -from posthog.models.filters.stickiness_filter import StickinessFilter as LegacyStickinessFilter -from posthog.models.insight import Insight -from posthog.models.team.team import Team -from posthog.types import InsightQueryNode - - -# sync with frontend/src/queries/utils.ts -def is_insight_with_hogql_support(insight: Insight): - if insight.filters.get("insight") == "LIFECYCLE": - return True - else: - return False - - -def _insight_to_query(insight: Insight, team: Team) -> InsightQueryNode: - if insight.filters.get("insight") == "RETENTION": - filter = LegacyRetentionFilter(data=insight.filters, team=team) - elif insight.filters.get("insight") == "PATHS": - filter = LegacyPathFilter(data=insight.filters, team=team) - elif insight.filters.get("insight") == "STICKINESS": - filter = LegacyStickinessFilter(data=insight.filters, team=team) - else: - filter = LegacyFilter(data=insight.filters, team=team) - return filter_to_query(filter.to_dict()) - - -def _cached_response_to_insight_result(response: CachedQueryResponse) -> InsightResult: - response_dict = response.model_dump() - result_keys = InsightResult.__annotations__.keys() - - # replace 'result' with 'results' for schema compatibility - response_keys = ["results" if key == "result" else key for key in result_keys] - - # use only the keys of the response that are also present in the result - result = InsightResult( - **{result_key: response_dict[response_key] for result_key, response_key in zip(result_keys, response_keys)} - ) - return result - - -def process_insight(insight: Insight, team: Team) -> InsightResult: - query = _insight_to_query(insight, team) - response = LifecycleQueryRunner(query=query, team=team).run(refresh_requested=False) - return _cached_response_to_insight_result(response) diff --git a/posthog/hogql_queries/query_runner.py b/posthog/hogql_queries/query_runner.py index 25a903e839335..bb060e220e3e9 100644 --- a/posthog/hogql_queries/query_runner.py +++ b/posthog/hogql_queries/query_runner.py @@ -1,11 +1,13 @@ from abc import ABC, abstractmethod from datetime import datetime +from enum import IntEnum from typing import Any, Generic, List, Optional, Type, Dict, TypeVar, Union, Tuple, cast, TypeGuard from django.conf import settings from django.core.cache import cache from prometheus_client import Counter from pydantic import BaseModel, ConfigDict +from sentry_sdk import capture_exception, push_scope from posthog.clickhouse.query_tagging import tag_queries from posthog.hogql import ast @@ -17,9 +19,12 @@ from posthog.metrics import LABEL_TEAM_ID from posthog.models import Team from posthog.schema import ( + DateRange, + FilterLogicalOperator, FunnelCorrelationActorsQuery, FunnelCorrelationQuery, FunnelsActorsQuery, + PropertyGroupFilter, TrendsQuery, FunnelsQuery, RetentionQuery, @@ -57,6 +62,15 @@ DataT = TypeVar("DataT") +class ExecutionMode(IntEnum): + CALCULATION_ALWAYS = 2 + """Always recalculate.""" + RECENT_CACHE_CALCULATE_IF_STALE = 1 + """Use cache, unless the results are missing or stale.""" + CACHE_ONLY_NEVER_CALCULATE = 0 + """Do not initiate calculation.""" + + class QueryResponse(BaseModel, Generic[DataT]): model_config = ConfigDict( extra="forbid", @@ -84,6 +98,13 @@ class CachedQueryResponse(QueryResponse): timezone: str +class CacheMissResponse(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + cache_key: str + + RunnableQueryNode = Union[ TrendsQuery, FunnelsQuery, @@ -266,9 +287,12 @@ def get_query_runner( raise ValueError(f"Can't get a runner for an unknown query kind: {kind}") -class QueryRunner(ABC): - query: RunnableQueryNode - query_type: Type[RunnableQueryNode] +Q = TypeVar("Q", bound=RunnableQueryNode) + + +class QueryRunner(ABC, Generic[Q]): + query: Q + query_type: Type[Q] team: Team timings: HogQLTimings modifiers: HogQLQueryModifiers @@ -276,7 +300,7 @@ class QueryRunner(ABC): def __init__( self, - query: RunnableQueryNode | BaseModel | Dict[str, Any], + query: Q | BaseModel | Dict[str, Any], team: Team, timings: Optional[HogQLTimings] = None, modifiers: Optional[HogQLQueryModifiers] = None, @@ -293,7 +317,7 @@ def __init__( assert isinstance(query, self.query_type) self.query = query - def is_query_node(self, data) -> TypeGuard[RunnableQueryNode]: + def is_query_node(self, data) -> TypeGuard[Q]: return isinstance(data, self.query_type) @abstractmethod @@ -302,21 +326,48 @@ def calculate(self) -> BaseModel: # Due to the way schema.py is generated, we don't have a good inheritance story here. raise NotImplementedError() - def run(self, refresh_requested: Optional[bool] = None) -> CachedQueryResponse: + def run( + self, execution_mode: ExecutionMode = ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE + ) -> CachedQueryResponse | CacheMissResponse: cache_key = f"{self._cache_key()}_{self.limit_context or LimitContext.QUERY}" tag_queries(cache_key=cache_key) - if not refresh_requested: - cached_response = get_safe_cache(cache_key) - if cached_response: + if execution_mode != ExecutionMode.CALCULATION_ALWAYS: + # Let's look in the cache first + cached_response: CachedQueryResponse | CacheMissResponse + cached_response_candidate = get_safe_cache(cache_key) + match cached_response_candidate: + case CachedQueryResponse(): + cached_response = cached_response_candidate + cached_response_candidate.is_cached = True + case None: + cached_response = CacheMissResponse(cache_key=cache_key) + case _: + # Whatever's in cache is malformed, so let's treat is as non-existent + cached_response = CacheMissResponse(cache_key=cache_key) + with push_scope() as scope: + scope.set_tag("cache_key", cache_key) + capture_exception( + ValueError(f"Cached response is of unexpected type {type(cached_response)}, ignoring it") + ) + + if isinstance(cached_response, CachedQueryResponse): if not self._is_stale(cached_response): QUERY_CACHE_HIT_COUNTER.labels(team_id=self.team.pk, cache_hit="hit").inc() - cached_response.is_cached = True + # We have a valid result that's fresh enough, let's return it return cached_response else: QUERY_CACHE_HIT_COUNTER.labels(team_id=self.team.pk, cache_hit="stale").inc() + # We have a stale result. If we aren't allowed to calculate, let's still return it + # – otherwise let's proceed to calculation + if execution_mode == ExecutionMode.CACHE_ONLY_NEVER_CALCULATE: + return cached_response else: QUERY_CACHE_HIT_COUNTER.labels(team_id=self.team.pk, cache_hit="miss").inc() + # We have no cached result. If we aren't allowed to calculate, let's return the cache miss + # – otherwise let's proceed to calculation + if execution_mode == ExecutionMode.CACHE_ONLY_NEVER_CALCULATE: + return cached_response fresh_response_dict = cast(QueryResponse, self.calculate()).model_dump() fresh_response_dict["is_cached"] = False @@ -369,5 +420,28 @@ def _is_stale(self, cached_result_package): def _refresh_frequency(self): raise NotImplementedError() - def apply_dashboard_filters(self, dashboard_filter: DashboardFilter) -> RunnableQueryNode: - raise NotImplementedError() + def apply_dashboard_filters(self, dashboard_filter: DashboardFilter) -> Q: + # The default logic below applies to all insights and a lot of other queries + # Notable exception: `HogQLQuery`, which has `properties` and `dateRange` within `HogQLFilters` + if hasattr(self.query, "properties") and hasattr(self.query, "dateRange"): + query_update: Dict[str, Any] = {} + if dashboard_filter.properties: + if self.query.properties: + query_update["properties"] = PropertyGroupFilter( + type=FilterLogicalOperator.AND, values=[self.query.properties, dashboard_filter.properties] + ) + else: + query_update["properties"] = dashboard_filter.properties + if dashboard_filter.date_from or dashboard_filter.date_to: + date_range_update = {} + if dashboard_filter.date_from: + date_range_update["date_from"] = dashboard_filter.date_from + if dashboard_filter.date_to: + date_range_update["date_to"] = dashboard_filter.date_to + if self.query.dateRange: + query_update["dateRange"] = self.query.dateRange.model_copy(update=date_range_update) + else: + query_update["dateRange"] = DateRange(**date_range_update) + return cast(Q, self.query.model_copy(update=query_update)) # Shallow copy! + + raise NotImplementedError(f"{self.query.__class__.__name__} does not support dashboard filters out of the box") diff --git a/posthog/hogql_queries/test/test_events_query_runner.py b/posthog/hogql_queries/test/test_events_query_runner.py index 9aab4ee14a9f6..7c8c62c5fb0fc 100644 --- a/posthog/hogql_queries/test/test_events_query_runner.py +++ b/posthog/hogql_queries/test/test_events_query_runner.py @@ -5,6 +5,7 @@ from posthog.hogql import ast from posthog.hogql.ast import CompareOperationOp from posthog.hogql_queries.events_query_runner import EventsQueryRunner +from posthog.hogql_queries.query_runner import CachedQueryResponse from posthog.models import Person, Team from posthog.models.organization import Organization from posthog.schema import ( @@ -84,7 +85,10 @@ def _run_boolean_field_query(self, filter: EventPropertyFilter): ) runner = EventsQueryRunner(query=query, team=self.team) - return runner.run().results + response = runner.run() + assert isinstance(response, CachedQueryResponse) + results = response.results + return results def test_is_not_set_boolean(self): # see https://github.com/PostHog/posthog/issues/18030 diff --git a/posthog/hogql_queries/test/test_query_runner.py b/posthog/hogql_queries/test/test_query_runner.py index 40c991ec1d038..4905feaa2eae9 100644 --- a/posthog/hogql_queries/test/test_query_runner.py +++ b/posthog/hogql_queries/test/test_query_runner.py @@ -7,6 +7,9 @@ from pydantic import BaseModel from posthog.hogql_queries.query_runner import ( + CacheMissResponse, + CachedQueryResponse, + ExecutionMode, QueryResponse, QueryRunner, ) @@ -125,23 +128,31 @@ def test_cache_response(self): runner = TestQueryRunner(query={"some_attr": "bla"}, team=self.team) with freeze_time(datetime(2023, 2, 4, 13, 37, 42)): + # in cache-only mode, returns cache miss response if uncached + response = runner.run(execution_mode=ExecutionMode.CACHE_ONLY_NEVER_CALCULATE) + self.assertIsInstance(response, CacheMissResponse) + # returns fresh response if uncached - response = runner.run(refresh_requested=False) + response = runner.run(execution_mode=ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE) + self.assertIsInstance(response, CachedQueryResponse) self.assertEqual(response.is_cached, False) self.assertEqual(response.last_refresh, "2023-02-04T13:37:42Z") self.assertEqual(response.next_allowed_client_refresh, "2023-02-04T13:41:42Z") # returns cached response afterwards - response = runner.run(refresh_requested=False) + response = runner.run(execution_mode=ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE) + self.assertIsInstance(response, CachedQueryResponse) self.assertEqual(response.is_cached, True) # return fresh response if refresh requested - response = runner.run(refresh_requested=True) + response = runner.run(execution_mode=ExecutionMode.CALCULATION_ALWAYS) + self.assertIsInstance(response, CachedQueryResponse) self.assertEqual(response.is_cached, False) with freeze_time(datetime(2023, 2, 4, 13, 37 + 11, 42)): # returns fresh response if stale - response = runner.run(refresh_requested=False) + response = runner.run(execution_mode=ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE) + self.assertIsInstance(response, CachedQueryResponse) self.assertEqual(response.is_cached, False) def test_modifier_passthrough(self): diff --git a/posthog/models/insight.py b/posthog/models/insight.py index a3057cdb11c7d..11af57bab3e56 100644 --- a/posthog/models/insight.py +++ b/posthog/models/insight.py @@ -169,12 +169,11 @@ def dashboard_filters(self, dashboard: Optional[Dashboard] = None): else: return self.filters - def dashboard_query(self, dashboard: Optional[Dashboard]) -> Optional[dict]: + def get_effective_query(self, *, dashboard: Optional[Dashboard]) -> Optional[dict]: + from posthog.hogql_queries.apply_dashboard_filters import apply_dashboard_filters + if not dashboard or not self.query: return self.query - from posthog.hogql_queries.apply_dashboard_filters import ( - apply_dashboard_filters, - ) return apply_dashboard_filters(self.query, dashboard.filters, self.team) @@ -197,23 +196,6 @@ class Meta: @timed("generate_insight_cache_key") def generate_insight_cache_key(insight: Insight, dashboard: Optional[Dashboard]) -> str: try: - if insight.query is not None: - dashboard_filters = dashboard.filters if dashboard else None - - if dashboard_filters: - from posthog.hogql_queries.apply_dashboard_filters import ( - apply_dashboard_filters, - ) - - q = apply_dashboard_filters(insight.query, dashboard_filters, insight.team) - else: - q = insight.query - - if q.get("source"): - q = q["source"] - - return generate_cache_key("{}_{}_{}".format(q, dashboard_filters, insight.team_id)) - dashboard_insight_filter = get_filter(data=insight.dashboard_filters(dashboard=dashboard), team=insight.team) candidate_filters_hash = generate_cache_key("{}_{}".format(dashboard_insight_filter.toJSON(), insight.team_id)) return candidate_filters_hash diff --git a/posthog/models/test/test_insight_model.py b/posthog/models/test/test_insight_model.py index cc0e52943ffaf..9933531b100a3 100644 --- a/posthog/models/test/test_insight_model.py +++ b/posthog/models/test/test_insight_model.py @@ -99,27 +99,6 @@ def test_dashboard_with_date_from_changes_filters_hash(self) -> None: assert filters_hash_one != filters_hash_two - def test_query_hash_matches_same_query_source(self) -> None: - insight_with_query_at_top_level = Insight.objects.create(team=self.team, query={"kind": "EventsQuery"}) - insight_with_query_in_source = Insight.objects.create( - team=self.team, - query={"kind": "DataTable", "source": {"kind": "EventsQuery"}}, - ) - - filters_hash_one = generate_insight_cache_key(insight_with_query_at_top_level, None) - filters_hash_two = generate_insight_cache_key(insight_with_query_in_source, None) - - assert filters_hash_one == filters_hash_two - - def test_query_hash_varies_with_query_content(self) -> None: - insight_one = Insight.objects.create(team=self.team, query={"kind": "EventsQuery"}) - insight_two = Insight.objects.create(team=self.team, query={"kind": "EventsQuery", "anything": "else"}) - - filters_hash_one = generate_insight_cache_key(insight_one, None) - filters_hash_two = generate_insight_cache_key(insight_two, None) - - assert filters_hash_one != filters_hash_two - def test_dashboard_with_query_insight_and_filters(self) -> None: browser_equals_firefox = { "key": "$browser", @@ -245,29 +224,7 @@ def test_dashboard_with_query_insight_and_filters(self) -> None: ) dashboard = Dashboard.objects.create(team=self.team, filters=dashboard_filters) - data = query_insight.dashboard_query(dashboard) + data = query_insight.get_effective_query(dashboard=dashboard) assert data actual = data["source"]["filters"] assert expected_filters == actual - - def test_query_hash_varies_with_dashboard_filters(self) -> None: - query = { - "kind": "DataTableNode", - "source": { - "filters": {"dateRange": {"date_from": "-14d", "date_to": "-7d"}}, - "kind": "HogQLQuery", - "modifiers": None, - "query": "select * from events where {filters}", - "response": None, - "values": None, - }, - } - dashboard_filters = {"date_from": "-4d", "date_to": "-3d"} - - query_insight = Insight.objects.create(team=self.team, query=query) - dashboard = Dashboard.objects.create(team=self.team, filters=dashboard_filters) - - hash_sans_dashboard = generate_insight_cache_key(query_insight, None) - hash_with_dashboard = generate_insight_cache_key(query_insight, dashboard) - - assert hash_sans_dashboard != hash_with_dashboard diff --git a/posthog/schema.py b/posthog/schema.py index 68471d4510b06..5673db2a3bf54 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -1184,7 +1184,7 @@ class EventPropertyFilter(BaseModel): ) key: str label: Optional[str] = None - operator: PropertyOperator + operator: Optional[PropertyOperator] = PropertyOperator("exact") type: Literal["event"] = Field(default="event", description="Event properties") value: Optional[Union[str, float, List[Union[str, float]]]] = None From 0955a0bc52c79ed5b85a3c32e0cc3596102494d7 Mon Sep 17 00:00:00 2001 From: Nikita Vorobev Date: Wed, 24 Apr 2024 15:07:22 +0300 Subject: [PATCH 02/10] fix: allow choose another dashboard template after pressing back (#21248) --- .github/workflows/ci-backend.yml | 1 + .github/workflows/ci-e2e.yml | 1 + .github/workflows/report-pr-age.yml | 1 + cypress/e2e/dashboard.cy.ts | 34 +++++++++++++++++++ .../scenes/dashboard/NewDashboardModal.tsx | 1 + .../src/scenes/dashboard/newDashboardLogic.ts | 1 + 6 files changed, 39 insertions(+) diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml index e0a5e2694fa14..8da19852c721b 100644 --- a/.github/workflows/ci-backend.yml +++ b/.github/workflows/ci-backend.yml @@ -417,6 +417,7 @@ jobs: echo running_time_run_id=${run_id} >> $GITHUB_ENV echo running_time_run_started_at=${run_started_at} >> $GITHUB_ENV - name: Capture running time to PostHog + if: github.repository == 'PostHog/posthog' uses: PostHog/posthog-github-action@v0.1 with: posthog-token: ${{secrets.POSTHOG_API_TOKEN}} diff --git a/.github/workflows/ci-e2e.yml b/.github/workflows/ci-e2e.yml index 4f4489437ec62..5a50db9f5c981 100644 --- a/.github/workflows/ci-e2e.yml +++ b/.github/workflows/ci-e2e.yml @@ -305,6 +305,7 @@ jobs: echo running_time_run_id=${run_id} >> $GITHUB_ENV echo running_time_run_started_at=${run_started_at} >> $GITHUB_ENV - name: Capture running time to PostHog + if: github.repository == 'PostHog/posthog' uses: PostHog/posthog-github-action@v0.1 with: posthog-token: ${{secrets.POSTHOG_API_TOKEN}} diff --git a/.github/workflows/report-pr-age.yml b/.github/workflows/report-pr-age.yml index 1e20ccfc8b687..4bee112c25ba2 100644 --- a/.github/workflows/report-pr-age.yml +++ b/.github/workflows/report-pr-age.yml @@ -23,6 +23,7 @@ jobs: echo is_revert=false >> $GITHUB_ENV fi - name: Capture PR age to PostHog + if: github.repository == 'PostHog/posthog' uses: PostHog/posthog-github-action@v0.1 with: posthog-token: ${{secrets.POSTHOG_API_TOKEN}} diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts index ed3a92a465e04..959aa5118fb80 100644 --- a/cypress/e2e/dashboard.cy.ts +++ b/cypress/e2e/dashboard.cy.ts @@ -100,6 +100,40 @@ describe('Dashboard', () => { cy.get('[data-attr^="breadcrumb-Dashboard:"]').should('have.text', TEST_DASHBOARD_NAME + 'UnnamedCancelSave') }) + const assertVariablesConfigurationScreenIsShown = (): void => { + cy.get('[data-attr="new-dashboard-chooser"]').contains('Unique variable name').should('exist') + } + + it('Allow reselecting a dashboard after pressing back', () => { + cy.intercept('GET', /\/api\/projects\/\d+\/dashboard_templates/, (req) => { + req.reply((response) => { + response.body.results[0].variables = [ + { + id: 'id', + name: 'Unique variable name', + type: 'event', + default: {}, + required: true, + description: 'description', + }, + ] + return response + }) + }) + + // Request templates again. + cy.clickNavMenu('dashboards') + + cy.get('[data-attr="new-dashboard"]').click() + cy.get('[data-attr="create-dashboard-from-template"]').click() + assertVariablesConfigurationScreenIsShown() + + cy.contains('.LemonButton', 'Back').click() + + cy.get('[data-attr="create-dashboard-from-template"]').click() + assertVariablesConfigurationScreenIsShown() + }) + it('Click on a dashboard item dropdown and view graph', () => { cy.get('[data-attr=dashboard-name]').contains('Web Analytics').click() cy.get('.InsightCard [data-attr=more-button]').first().click() diff --git a/frontend/src/scenes/dashboard/NewDashboardModal.tsx b/frontend/src/scenes/dashboard/NewDashboardModal.tsx index 6c20019690db9..2d58b0cbe653d 100644 --- a/frontend/src/scenes/dashboard/NewDashboardModal.tsx +++ b/frontend/src/scenes/dashboard/NewDashboardModal.tsx @@ -34,6 +34,7 @@ export function NewDashboardModal(): JSX.Element { onClose={hideNewDashboardModal} isOpen={newDashboardModalVisible} title={activeDashboardTemplate ? 'Choose your events' : 'Create a dashboard'} + data-attr="new-dashboard-chooser" description={ activeDashboardTemplate ? (

diff --git a/frontend/src/scenes/dashboard/newDashboardLogic.ts b/frontend/src/scenes/dashboard/newDashboardLogic.ts index d63a829f5b0f6..264c4b5af135d 100644 --- a/frontend/src/scenes/dashboard/newDashboardLogic.ts +++ b/frontend/src/scenes/dashboard/newDashboardLogic.ts @@ -89,6 +89,7 @@ export const newDashboardLogic = kea([ hideNewDashboardModal: () => false, submitNewDashboardSuccess: () => false, submitNewDashboardFailure: () => false, + clearActiveDashboardTemplate: () => false, }, ], newDashboardModalVisible: [ From 944a600a05f508be8ed94d77d0ecf124d6e9e040 Mon Sep 17 00:00:00 2001 From: timgl Date: Wed, 24 Apr 2024 14:45:05 +0200 Subject: [PATCH 03/10] fix: Use default event instead of all events for new graph series (#21784) --- .../scenes/insights/filters/ActionFilter/entityFilterLogic.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts b/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts index 9614c49c7542a..3bf18724e605b 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts +++ b/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts @@ -2,6 +2,7 @@ import { actions, connect, events, kea, key, listeners, path, props, reducers, s import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' import { uuid } from 'lib/utils' import { eventUsageLogic, GraphSeriesAddedSource } from 'lib/utils/eventUsageLogic' +import { getDefaultEventName } from 'lib/utils/getAppContext' import { insightDataLogic } from 'scenes/insights/insightDataLogic' import { @@ -262,7 +263,7 @@ export const entityFilterLogic = kea([ const precedingEntity = values.localFilters[previousLength - 1] as LocalFilter | undefined const order = precedingEntity ? precedingEntity.order + 1 : 0 const newFilter = { - id: null, + id: getDefaultEventName(), uuid: uuid(), type: EntityTypes.EVENTS, order: order, From 671b0536650f65ae5711b699150aff3bce4def09 Mon Sep 17 00:00:00 2001 From: David Newell Date: Wed, 24 Apr 2024 14:00:11 +0100 Subject: [PATCH 04/10] chore: upgrade sentry dependency (#21801) --- package.json | 4 +- pnpm-lock.yaml | 121 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 96 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 6039d555ad9ff..2b219265f03a1 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@posthog/plugin-scaffold": "^1.4.4", "@react-hook/size": "^2.1.2", "@rrweb/types": "2.0.0-alpha.13", - "@sentry/react": "7.22.0", + "@sentry/react": "7.112.1", "@tailwindcss/container-queries": "^0.1.1", "@testing-library/dom": ">=7.21.4", "@tiptap/core": "^2.1.16", @@ -190,7 +190,7 @@ "@babel/preset-typescript": "^7.22.5", "@cypress/webpack-preprocessor": "^5.17.1", "@playwright/test": "1.41.2", - "@sentry/types": "7.22.0", + "@sentry/types": "7.112.1", "@storybook/addon-a11y": "^7.6.4", "@storybook/addon-actions": "^7.6.4", "@storybook/addon-essentials": "^7.6.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f62e8012a8ccb..60df6879d6248 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,8 +62,8 @@ dependencies: specifier: 2.0.0-alpha.13 version: 2.0.0-alpha.13 '@sentry/react': - specifier: 7.22.0 - version: 7.22.0(react@18.2.0) + specifier: 7.112.1 + version: 7.112.1(react@18.2.0) '@tailwindcss/container-queries': specifier: ^0.1.1 version: 0.1.1(tailwindcss@3.4.0) @@ -393,8 +393,8 @@ devDependencies: specifier: 1.41.2 version: 1.41.2 '@sentry/types': - specifier: 7.22.0 - version: 7.22.0 + specifier: 7.112.1 + version: 7.112.1 '@storybook/addon-a11y': specifier: ^7.6.4 version: 7.6.4 @@ -5898,49 +5898,99 @@ packages: rrweb-snapshot: 2.0.0-alpha.13 dev: false - /@sentry/browser@7.22.0: - resolution: {integrity: sha512-8MA+f46+T3G7fb4BYYX9Wl3bMDloG5a3Ng0GWdBeq6DE2tXVHeCvba8Yrrcnn1qFHpmnOn5Nz4xWBUDs3uBFxA==} + /@sentry-internal/feedback@7.112.1: + resolution: {integrity: sha512-ejE4eRXLqv5emxVWudBkRQCv5Q7s21thei7gqSxGLBXe8AUrCjTiD0qA1ToJAKcleIyRRf/TQvGb/T7U6vwAAw==} + engines: {node: '>=12'} + dependencies: + '@sentry/core': 7.112.1 + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 + dev: false + + /@sentry-internal/replay-canvas@7.112.1: + resolution: {integrity: sha512-+xDd/LEiJZGk4PQKs4xcAWKJFzFKpuNF64DFW/JWuJ5FDnKB+t7w198nQyAZKGjupN7LixLb49Z8O2Gda7fHQQ==} + engines: {node: '>=12'} + dependencies: + '@sentry/core': 7.112.1 + '@sentry/replay': 7.112.1 + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 + dev: false + + /@sentry-internal/tracing@7.112.1: + resolution: {integrity: sha512-pZVIOB6+t4HlgU3mCRtIbvo//t8uQY9tnBjbJJ2nEv8nTu8A7/dZ5ebrLOWStV3bNp/+uCqLuLuuimJeNNn6vQ==} engines: {node: '>=8'} dependencies: - '@sentry/core': 7.22.0 - '@sentry/types': 7.22.0 - '@sentry/utils': 7.22.0 - tslib: 1.14.1 + '@sentry/core': 7.112.1 + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 dev: false - /@sentry/core@7.22.0: - resolution: {integrity: sha512-qYJiJrL1mfQQln84mNunBRUkXq7xDGQQoNh4Sz9VYP5698va51zmS5BLYRCZ5CkPwRYNuhUqlUXN7bpYGYOOIA==} + /@sentry/browser@7.112.1: + resolution: {integrity: sha512-NRTo3mJbhiCd9GEFEWL8SplFJhTCPjiAlOhjUw8MnJb7pkxWm2xhC7PVi6SUE8hF/g1rrEwgUr9SX5v8+xwK6g==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.22.0 - '@sentry/utils': 7.22.0 - tslib: 1.14.1 + '@sentry-internal/feedback': 7.112.1 + '@sentry-internal/replay-canvas': 7.112.1 + '@sentry-internal/tracing': 7.112.1 + '@sentry/core': 7.112.1 + '@sentry/integrations': 7.112.1 + '@sentry/replay': 7.112.1 + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 + dev: false + + /@sentry/core@7.112.1: + resolution: {integrity: sha512-ZhOxt4sZVLqHurWqIY1ExWYZ20ViFTbqgW2GdJGHz4XwJhBln0ZVpHD+tKXy3GBEY+2Ee4qoqHi6tDrFgPvJqw==} + engines: {node: '>=8'} + dependencies: + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 dev: false - /@sentry/react@7.22.0(react@18.2.0): - resolution: {integrity: sha512-nbZD+bobhV65r/4mpfRgGct1nrYWEmnNzTYZM4PQyPyImuk/VmNNdnzP3BLWqAnV4LvbVWEkgZIcquN8yA098g==} + /@sentry/integrations@7.112.1: + resolution: {integrity: sha512-jIgXT+ahUS7zmhDMAzsgQHCNA6ZwZAp0Bwjoz0tcuGzNcv7mOCnjHz5YooJVQgXuREV653RmEuGGTklrpn6S2w==} + engines: {node: '>=8'} + dependencies: + '@sentry/core': 7.112.1 + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 + localforage: 1.10.0 + dev: false + + /@sentry/react@7.112.1(react@18.2.0): + resolution: {integrity: sha512-q0fDW3omq/NPaL7yRqWA1USxGtEAcdFZOngIMsr9Bc4fJBGXDO+xLwPWjo1MIVvdDBJJYL/9Z56ppqTb3kiGXw==} engines: {node: '>=8'} peerDependencies: react: 15.x || 16.x || 17.x || 18.x dependencies: - '@sentry/browser': 7.22.0 - '@sentry/types': 7.22.0 - '@sentry/utils': 7.22.0 + '@sentry/browser': 7.112.1 + '@sentry/core': 7.112.1 + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 hoist-non-react-statics: 3.3.2 react: 18.2.0 - tslib: 1.14.1 dev: false - /@sentry/types@7.22.0: - resolution: {integrity: sha512-LhCL+wb1Jch+OesB2CIt6xpfO1Ab6CRvoNYRRzVumWPLns1T3ZJkarYfhbLaOEIb38EIbPgREdxn2AJT560U4Q==} + /@sentry/replay@7.112.1: + resolution: {integrity: sha512-4lobxfgmbB2C7ZHk1inWt9IRIvlQa2Sczau5ngE4Qd4mZSKIgIYGtIJC52uOuGvBcP8gHiIbA7ACihkd7834Ew==} + engines: {node: '>=12'} + dependencies: + '@sentry-internal/tracing': 7.112.1 + '@sentry/core': 7.112.1 + '@sentry/types': 7.112.1 + '@sentry/utils': 7.112.1 + dev: false + + /@sentry/types@7.112.1: + resolution: {integrity: sha512-5dLIxWZfCXH5kExrsWc+R6loMr3RR6OQuonVNL3Fa8Dw37Q7aExCrjRmocOHeQKhHwNBd3QhYm7phjnbxS6Oaw==} engines: {node: '>=8'} - /@sentry/utils@7.22.0: - resolution: {integrity: sha512-1GiNw1opIngxg0nktCTc9wibh4/LV12kclrnB9dAOHrqazZXHXZRAkjqrhQphKcMpT+3By91W6EofjaDt5a/hg==} + /@sentry/utils@7.112.1: + resolution: {integrity: sha512-/AMGDD6OMvT2cpfL5KuDC10oTS8yOt7BAPomXJNS/xn1TRcEEEZ1TWbYZiGT5ijggQEL1OXSojpeQU8XEW8dcQ==} engines: {node: '>=8'} dependencies: - '@sentry/types': 7.22.0 - tslib: 1.14.1 + '@sentry/types': 7.112.1 dev: false /@sideway/address@4.1.4: @@ -13583,6 +13633,10 @@ packages: requiresBuild: true optional: true + /immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + dev: false + /immutable@4.1.0: resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==} @@ -15273,6 +15327,12 @@ packages: type-check: 0.4.0 dev: true + /lie@3.1.1: + resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} + dependencies: + immediate: 3.0.6 + dev: false + /lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -15375,6 +15435,12 @@ packages: json5: 2.2.3 dev: true + /localforage@1.10.0: + resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} + dependencies: + lie: 3.1.1 + dev: false + /locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -20808,6 +20874,7 @@ packages: /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true /tslib@2.4.1: resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} From 390e633ecae2324e1ae6bb6ac21aa79655dcbb68 Mon Sep 17 00:00:00 2001 From: Juraj Majerik Date: Wed, 24 Apr 2024 16:22:31 +0200 Subject: [PATCH 05/10] fix(experiments): update time/sample/mde on goal change (#21808) --- .../experiments/ExperimentView/Goal.tsx | 7 +++++- .../scenes/experiments/experimentLogic.tsx | 24 ++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/frontend/src/scenes/experiments/ExperimentView/Goal.tsx b/frontend/src/scenes/experiments/ExperimentView/Goal.tsx index 651528abaf908..5ceadd7c28349 100644 --- a/frontend/src/scenes/experiments/ExperimentView/Goal.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/Goal.tsx @@ -91,7 +91,9 @@ export function ExposureMetric({ experimentId }: { experimentId: Experiment['id' } export function ExperimentGoalModal({ experimentId }: { experimentId: Experiment['id'] }): JSX.Element { - const { experiment, isExperimentGoalModalOpen, experimentLoading } = useValues(experimentLogic({ experimentId })) + const { experiment, isExperimentGoalModalOpen, experimentLoading, goalInsightDataLoading } = useValues( + experimentLogic({ experimentId }) + ) const { closeExperimentGoalModal, updateExperimentGoal, setNewExperimentInsight } = useActions( experimentLogic({ experimentId }) ) @@ -108,6 +110,9 @@ export function ExperimentGoalModal({ experimentId }: { experimentId: Experiment Cancel { updateExperimentGoal(experiment.filters) diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx index 7f77ed039e26b..f0bff3999795f 100644 --- a/frontend/src/scenes/experiments/experimentLogic.tsx +++ b/frontend/src/scenes/experiments/experimentLogic.tsx @@ -106,6 +106,8 @@ export const experimentLogic = kea([ ['conversionMetrics'], trendsDataLogic({ dashboardItemId: EXPERIMENT_INSIGHT_ID }), ['results as trendResults'], + insightDataLogic({ dashboardItemId: EXPERIMENT_INSIGHT_ID }), + ['insightDataLoading as goalInsightDataLoading'], ], actions: [ experimentsLogic, @@ -309,7 +311,7 @@ export const experimentLogic = kea([ // Terminate if the insight did not manage to load in time if (!minimumDetectableChange) { eventUsageLogic.actions.reportExperimentInsightLoadFailed() - lemonToast.error( + return lemonToast.error( 'Failed to load insight. Experiment cannot be saved without this value. Try changing the experiment goal.' ) } @@ -481,10 +483,26 @@ export const experimentLogic = kea([ values.experiment && actions.reportExperimentArchived(values.experiment) }, updateExperimentGoal: async ({ filters }) => { - // We never want to update global properties in the experiment + const { recommendedRunningTime, recommendedSampleSize, minimumDetectableChange } = values + if (!minimumDetectableChange) { + eventUsageLogic.actions.reportExperimentInsightLoadFailed() + return lemonToast.error( + 'Failed to load insight. Experiment cannot be saved without this value. Try changing the experiment goal.' + ) + } + const filtersToUpdate = { ...filters } delete filtersToUpdate.properties - actions.updateExperiment({ filters: filtersToUpdate }) + + actions.updateExperiment({ + filters: filtersToUpdate, + parameters: { + ...values.experiment?.parameters, + recommended_running_time: recommendedRunningTime, + recommended_sample_size: recommendedSampleSize, + minimum_detectable_effect: minimumDetectableChange, + }, + }) actions.closeExperimentGoalModal() }, updateExperimentExposure: async ({ filters }) => { From ab3a64d6c84c9b40c6b285cc8081f96356aa6f49 Mon Sep 17 00:00:00 2001 From: David Newell Date: Wed, 24 Apr 2024 15:30:17 +0100 Subject: [PATCH 06/10] fix: skip custom split (#21812) --- patches/rrweb@2.0.0-alpha.13.patch | 12 ++++++++++-- pnpm-lock.yaml | 6 +++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/patches/rrweb@2.0.0-alpha.13.patch b/patches/rrweb@2.0.0-alpha.13.patch index 46feaf865a45f..26b8a6cd262fb 100644 --- a/patches/rrweb@2.0.0-alpha.13.patch +++ b/patches/rrweb@2.0.0-alpha.13.patch @@ -489,10 +489,18 @@ index 0d49411b1f6d31103bed898c0e81d1d74ab51234..0b2160ef08aa3ae5310f63c295abc0a5 } applyMutation(d, isSync) { diff --git a/es/rrweb/packages/rrweb-snapshot/es/rrweb-snapshot.js b/es/rrweb/packages/rrweb-snapshot/es/rrweb-snapshot.js -index 38a23aaae8d683fa584329eced277dd8de55d1ff..a20f6c785ca3fde692295b7c80fa53d6211315b4 100644 +index 38a23aaae8d683fa584329eced277dd8de55d1ff..44efee95395f6612204e051724a108833741a492 100644 --- a/es/rrweb/packages/rrweb-snapshot/es/rrweb-snapshot.js +++ b/es/rrweb/packages/rrweb-snapshot/es/rrweb-snapshot.js -@@ -1278,8 +1278,15 @@ function parse(css, options = {}) { +@@ -1271,15 +1271,22 @@ function parse(css, options = {}) { + .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, (m) => { + return m.replace(/,/g, '\u200C'); + }); +- return customSplit(cleanedInput).map((s) => s.replace(/\u200C/g, ',').trim()); ++ return cleanedInput.split(/\s*(?![^(]*\)),\s*/).map((s) => s.replace(/\u200C/g, ',').trim()); + } + function customSplit(input) { + const result = []; let currentSegment = ''; let depthParentheses = 0; let depthBrackets = 0; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60df6879d6248..0df3d13eaf730 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ patchedDependencies: hash: gydrxrztd4ruyhouu6tu7zh43e path: patches/heatmap.js@2.0.5.patch rrweb@2.0.0-alpha.13: - hash: kfckshv6vjunfa5tljyhkshxxe + hash: yb2e6fk2apwhhkdrx63qgi45uq path: patches/rrweb@2.0.0-alpha.13.patch dependencies: @@ -333,7 +333,7 @@ dependencies: version: 1.5.1 rrweb: specifier: 2.0.0-alpha.13 - version: 2.0.0-alpha.13(patch_hash=kfckshv6vjunfa5tljyhkshxxe) + version: 2.0.0-alpha.13(patch_hash=yb2e6fk2apwhhkdrx63qgi45uq) sass: specifier: ^1.26.2 version: 1.56.0 @@ -19277,7 +19277,7 @@ packages: resolution: {integrity: sha512-slbhNBCYjxLGCeH95a67ECCy5a22nloXp1F5wF7DCzUNw80FN7tF9Lef1sRGLNo32g3mNqTc2sWLATlKejMxYw==} dev: false - /rrweb@2.0.0-alpha.13(patch_hash=kfckshv6vjunfa5tljyhkshxxe): + /rrweb@2.0.0-alpha.13(patch_hash=yb2e6fk2apwhhkdrx63qgi45uq): resolution: {integrity: sha512-a8GXOCnzWHNaVZPa7hsrLZtNZ3CGjiL+YrkpLo0TfmxGLhjNZbWY2r7pE06p+FcjFNlgUVTmFrSJbK3kO7yxvw==} dependencies: '@rrweb/types': 2.0.0-alpha.13 From 212431ef4f9c9cba23d3c1158457462291ce3729 Mon Sep 17 00:00:00 2001 From: David Newell Date: Wed, 24 Apr 2024 15:32:11 +0100 Subject: [PATCH 07/10] fix: respect time when only allowing upcoming dates in LemonCalendar (#21757) --- ...on-calendar--only-allow-upcoming--dark.png | Bin 0 -> 14042 bytes ...n-calendar--only-allow-upcoming--light.png | Bin 0 -> 14063 bytes .../LemonCalendar/LemonCalendar.stories.tsx | 6 +- .../lemon-ui/LemonCalendar/LemonCalendar.tsx | 4 +- .../LemonCalendarSelect.test.tsx | 62 +++++++++++++++++ .../LemonCalendar/LemonCalendarSelect.tsx | 64 ++++++++++++------ .../feature-flags/FeatureFlagSchedule.tsx | 2 +- 7 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--only-allow-upcoming--dark.png create mode 100644 frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--only-allow-upcoming--light.png diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--only-allow-upcoming--dark.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--only-allow-upcoming--dark.png new file mode 100644 index 0000000000000000000000000000000000000000..56fb681c1295ede79c3d1a63c00120f420fb38ae GIT binary patch literal 14042 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

bgL!gC>fTokpWt6lDb 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 { // only changes the hour expect(onChange).toHaveBeenCalledWith(dayjs('2023-01-15T08:42:00.000Z')) }) + + test('onlyAllowUpcoming', async () => { + const onClose = jest.fn() + const onChange = jest.fn() + window.HTMLElement.prototype.scrollIntoView = jest.fn() + + jest.useFakeTimers().setSystemTime(new Date('2023-01-10 17:22:08')) + + function TestSelect(): JSX.Element { + const [value, setValue] = useState(null) + return ( + { + setValue(value) + onChange(value) + }} + showTime + onlyAllowUpcoming + /> + ) + } + const { container } = render() + + async function clickOnDate(day: string): Promise { + const element = container.querySelector('.LemonCalendar__month') as HTMLElement + if (element) { + userEvent.click(await within(element).findByText(day)) + userEvent.click(getByDataAttr(container, 'lemon-calendar-select-apply')) + } + } + + async function clickOnTime(props: GetLemonButtonTimePropsOpts): Promise { + const element = getTimeElement(container.querySelector('.LemonCalendar__time'), props) + if (element) { + userEvent.click(element) + userEvent.click(getByDataAttr(container, 'lemon-calendar-select-apply')) + } + } + + // click on minute + await clickOnTime({ unit: 'm', value: 42 }) + // time is disabled until a date is clicked + expect(onChange).not.toHaveBeenCalled() + + // click on current date + await clickOnDate('9') + // cannot select a date in the past + expect(onChange).not.toHaveBeenCalled() + + // click on current date + await clickOnDate('10') + // chooses the current date and sets the time to the current hour and minute + expect(onChange).toHaveBeenCalledWith(dayjs('2023-01-10T17:22:00.000Z')) + + // click on an earlier hour + await clickOnTime({ unit: 'a', value: 'am' }) + // does not update the date because it is in the past + expect(onChange).lastCalledWith(dayjs('2023-01-10T17:22:00.000Z')) + }) }) diff --git a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx index 85b8a3bcc2086..44662382d9edc 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx @@ -1,6 +1,6 @@ import { IconX } from '@posthog/icons' import { dayjs } from 'lib/dayjs' -import { LemonButton, LemonButtonWithSideActionProps, SideAction } from 'lib/lemon-ui/LemonButton' +import { LemonButton, LemonButtonProps, LemonButtonWithSideActionProps, SideAction } from 'lib/lemon-ui/LemonButton' import { GetLemonButtonTimePropsOpts, LemonCalendar } from 'lib/lemon-ui/LemonCalendar/LemonCalendar' import { useEffect, useMemo, useRef, useState } from 'react' @@ -28,13 +28,27 @@ function scrollToTimeElement( }) } +function proposedDate(target: dayjs.Dayjs | null, { value, unit }: GetLemonButtonTimePropsOpts): dayjs.Dayjs { + let date = target || dayjs().startOf('day') + if (value != date.format(unit)) { + if (unit === 'h') { + date = date.hour(date.format('a') === 'am' || value === 12 ? Number(value) : Number(value) + 12) + } else if (unit === 'm') { + date = date.minute(Number(value)) + } else if (unit === 'a') { + date = value === 'am' ? date.subtract(12, 'hour') : date.add(12, 'hour') + } + } + return date +} + export interface LemonCalendarSelectProps { value?: dayjs.Dayjs | null onChange: (date: dayjs.Dayjs) => void months?: number onClose?: () => void showTime?: boolean - fromToday?: boolean + onlyAllowUpcoming?: boolean } export function LemonCalendarSelect({ @@ -43,13 +57,14 @@ export function LemonCalendarSelect({ months, onClose, showTime, - fromToday, + onlyAllowUpcoming, }: LemonCalendarSelectProps): JSX.Element { const calendarRef = useRef(null) const [selectValue, setSelectValue] = useState( value ? (showTime ? value : value.startOf('day')) : null ) + const now = dayjs() const isAM = useMemo(() => selectValue?.format('a') === 'am', [selectValue]) const scrollToTime = (date: dayjs.Dayjs, skipAnimation: boolean): void => { @@ -62,8 +77,6 @@ export function LemonCalendarSelect({ } const onDateClick = (date: dayjs.Dayjs | null): void => { - const now = dayjs() - if (date) { date = showTime ? date.hour(selectValue === null ? now.hour() : selectValue.hour()) : date.startOf('hour') date = showTime @@ -82,17 +95,7 @@ export function LemonCalendarSelect({ }, []) const onTimeClick = (props: GetLemonButtonTimePropsOpts): void => { - const { value, unit } = props - - let date = selectValue || dayjs().startOf('day') - if (unit === 'h') { - date = date.hour(date.format('a') === 'am' ? Number(value) : Number(value) + 12) - } else if (unit === 'm') { - date = date.minute(Number(value)) - } else if (unit === 'a') { - date = value === 'am' ? date.subtract(12, 'hour') : date.add(12, 'hour') - } - + const date = proposedDate(selectValue, props) scrollToTime(date, false) setSelectValue(date) } @@ -111,17 +114,40 @@ export function LemonCalendarSelect({ leftmostMonth={selectValue?.startOf('month')} months={months} getLemonButtonProps={({ date, props }) => { + const modifiedProps: LemonButtonProps = { ...props } + const isDisabled = + onlyAllowUpcoming && + selectValue && + date.isSame(now.tz('utc'), 'date') && + (selectValue.hour() < now.hour() || + (selectValue.hour() === now.hour() && selectValue.minute() <= now.minute())) + + if (isDisabled) { + modifiedProps.disabledReason = 'Pick a time in the future first' + } + if (date.isSame(selectValue, 'd')) { - return { ...props, status: 'default', type: 'primary' } + return { ...modifiedProps, status: 'default', type: 'primary' } } - return props + return modifiedProps }} getLemonButtonTimeProps={(props) => { const selected = selectValue ? selectValue.format(props.unit) : null + const newDate = proposedDate(selectValue, props) + + const disabledReason = onlyAllowUpcoming + ? selectValue + ? newDate.isBefore(now) + ? 'Cannot choose a time in the past' + : undefined + : 'Choose a date first' + : undefined + return { active: selected === String(props.value), className: 'rounded-none', 'data-attr': timeDataAttr(props), + disabledReason: disabledReason, onClick: () => { if (selected != props.value) { onTimeClick(props) @@ -130,7 +156,7 @@ export function LemonCalendarSelect({ } }} showTime={showTime} - fromToday={fromToday} + onlyAllowUpcoming={onlyAllowUpcoming} />

diff --git a/frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx b/frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx index e729b3e753e3e..b9d5d4260ff83 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx @@ -164,8 +164,8 @@ export default function FeatureFlagSchedule(): JSX.Element { value={scheduleDateMarker} onChange={(value) => setScheduleDateMarker(value)} placeholder="Select date" + onlyAllowUpcoming showTime - fromToday />
From 4d1e6021269196d144cd39e465e21f31bd93c988 Mon Sep 17 00:00:00 2001 From: David Newell Date: Wed, 24 Apr 2024 15:37:28 +0100 Subject: [PATCH 08/10] feat: improved inspector positioning (#21736) --- ...r-failure--recent-recordings-404--dark.png | Bin 138079 -> 138296 bytes ...-failure--recent-recordings-404--light.png | Bin 140624 -> 140863 bytes ...p-notebooks--recordings-playlist--dark.png | Bin 45512 -> 45835 bytes ...-notebooks--recordings-playlist--light.png | Bin 45421 -> 45444 bytes .../session-recordings/player/PlayerMeta.tsx | 11 +-- .../player/PlayerMetaLinks.tsx | 41 ++++---- .../player/SessionRecordingPlayer.scss | 43 ++------- .../player/SessionRecordingPlayer.tsx | 88 +++++++++++------- .../player/controller/PlayerController.tsx | 46 +++++---- .../player/inspector/PlayerInspector.tsx | 49 +++++----- .../inspector/PlayerInspectorControls.tsx | 22 ++++- .../inspector/PlayerInspectorPreview.tsx | 49 ---------- .../playlist-popover/PlaylistPopover.tsx | 4 +- .../playlist/SessionRecordingsPlaylist.scss | 10 +- .../playlist/SessionRecordingsPlaylist.tsx | 49 ++++++++-- .../sessionRecordingsPlaylistLogic.ts | 8 ++ 16 files changed, 224 insertions(+), 196 deletions(-) delete mode 100644 frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx diff --git a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png index 4338f4712a54ab0b35a3aefad2f132fb2945b171..2fe69211c78a0dab8ff90b46eee55ee5984fdb37 100644 GIT binary patch literal 138296 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 A=l}o! literal 138079 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