From 714fb09863ad60cf95b5355face4cb572e9e7e7b Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Wed, 7 Jul 2021 15:23:37 +0200 Subject: [PATCH 1/4] Handle `Action.DoesNotExist` more --- ee/clickhouse/models/action.py | 2 +- ee/clickhouse/queries/clickhouse_stickiness.py | 10 ++++++---- ee/clickhouse/queries/trends/util.py | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ee/clickhouse/models/action.py b/ee/clickhouse/models/action.py index 7cce620f991ab..4ec6b273b8d2f 100644 --- a/ee/clickhouse/models/action.py +++ b/ee/clickhouse/models/action.py @@ -94,7 +94,7 @@ def format_entity_filter(entity: Entity, prepend: str = "action", filter_by_team action = Action.objects.get(pk=entity.id) entity_filter, params = format_action_filter(action, prepend=prepend, filter_by_team=filter_by_team) except Action.DoesNotExist: - raise ValidationError("This action does not exist") + raise ValidationError(f"Action ID {entity.id} does not exist.") else: key = f"{prepend}_event" entity_filter = f"event = %({key})s" diff --git a/ee/clickhouse/queries/clickhouse_stickiness.py b/ee/clickhouse/queries/clickhouse_stickiness.py index a12f766a10c50..ccbd1053b1fb8 100644 --- a/ee/clickhouse/queries/clickhouse_stickiness.py +++ b/ee/clickhouse/queries/clickhouse_stickiness.py @@ -45,7 +45,10 @@ def stickiness(self, entity: Entity, filter: StickinessFilter, team_id: int) -> params: Dict = {"team_id": team_id} params = {**params, **prop_filter_params, "num_intervals": filter.total_intervals} if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get(pk=entity.id) + try: + action = Action.objects.get(pk=entity.id) + except Action.DoesNotExist: + raise ValidationError(f"Action ID {entity.id} does not exist.") action_query, action_params = format_action_filter(action) if action_query == "": return {} @@ -85,10 +88,9 @@ def _format_entity_filter(entity: Entity) -> Tuple[str, Dict]: try: action = Action.objects.get(pk=entity.id) action_query, params = format_action_filter(action) - entity_filter = "AND {}".format(action_query) - except Action.DoesNotExist: - raise ValidationError("This action does not exist") + raise ValidationError(f"Action ID {entity.id} does not exist.") + entity_filter = "AND {}".format(action_query) else: entity_filter = "AND event = %(event)s" params = {"event": entity.id} diff --git a/ee/clickhouse/queries/trends/util.py b/ee/clickhouse/queries/trends/util.py index ee1809d8b1e4c..7d681ca909487 100644 --- a/ee/clickhouse/queries/trends/util.py +++ b/ee/clickhouse/queries/trends/util.py @@ -101,7 +101,7 @@ def populate_entity_params(entity: Entity) -> Tuple[Dict, Dict]: params = {**action_params} content_sql_params = {"entity_query": "AND {action_query}".format(action_query=action_query)} except: - raise ValidationError("Action does not exist") + raise ValidationError(f"Action ID {entity.id} does not exist.") else: content_sql_params = {"entity_query": "AND event = %(event)s"} params = {"event": entity.id} From 21da2f88d8a0cb675c1b987754d40da7b4af8476 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Wed, 7 Jul 2021 15:36:50 +0200 Subject: [PATCH 2/4] Reuse try-except logic for `Action.DoesNotExist` --- ee/clickhouse/models/action.py | 7 ++-- .../queries/clickhouse_stickiness.py | 12 ++----- ee/clickhouse/queries/funnels/base.py | 2 +- .../queries/funnels/funnel_event_query.py | 2 +- ee/clickhouse/queries/sessions/util.py | 2 +- ee/clickhouse/queries/trends/breakdown.py | 2 +- ee/clickhouse/queries/trends/lifecycle.py | 4 +-- ee/clickhouse/queries/trends/util.py | 11 +++--- posthog/models/action.py | 36 ++++++++++++------- 9 files changed, 39 insertions(+), 39 deletions(-) diff --git a/ee/clickhouse/models/action.py b/ee/clickhouse/models/action.py index 4ec6b273b8d2f..406cd981c775a 100644 --- a/ee/clickhouse/models/action.py +++ b/ee/clickhouse/models/action.py @@ -90,11 +90,8 @@ def filter_event( def format_entity_filter(entity: Entity, prepend: str = "action", filter_by_team=True) -> Tuple[str, Dict]: if entity.type == TREND_FILTER_TYPE_ACTIONS: - try: - action = Action.objects.get(pk=entity.id) - entity_filter, params = format_action_filter(action, prepend=prepend, filter_by_team=filter_by_team) - except Action.DoesNotExist: - raise ValidationError(f"Action ID {entity.id} does not exist.") + action = Action.objects.get_from_entity(entity) + entity_filter, params = format_action_filter(action, prepend=prepend, filter_by_team=filter_by_team) else: key = f"{prepend}_event" entity_filter = f"event = %({key})s" diff --git a/ee/clickhouse/queries/clickhouse_stickiness.py b/ee/clickhouse/queries/clickhouse_stickiness.py index ccbd1053b1fb8..cafcb8cc1e516 100644 --- a/ee/clickhouse/queries/clickhouse_stickiness.py +++ b/ee/clickhouse/queries/clickhouse_stickiness.py @@ -45,10 +45,7 @@ def stickiness(self, entity: Entity, filter: StickinessFilter, team_id: int) -> params: Dict = {"team_id": team_id} params = {**params, **prop_filter_params, "num_intervals": filter.total_intervals} if entity.type == TREND_FILTER_TYPE_ACTIONS: - try: - action = Action.objects.get(pk=entity.id) - except Action.DoesNotExist: - raise ValidationError(f"Action ID {entity.id} does not exist.") + action = Action.objects.get_from_entity(entity) action_query, action_params = format_action_filter(action) if action_query == "": return {} @@ -85,11 +82,8 @@ def _retrieve_people( def _format_entity_filter(entity: Entity) -> Tuple[str, Dict]: if entity.type == TREND_FILTER_TYPE_ACTIONS: - try: - action = Action.objects.get(pk=entity.id) - action_query, params = format_action_filter(action) - except Action.DoesNotExist: - raise ValidationError(f"Action ID {entity.id} does not exist.") + action = Action.objects.get_from_entity(entity) + action_query, params = format_action_filter(action) entity_filter = "AND {}".format(action_query) else: entity_filter = "AND event = %(event)s" diff --git a/ee/clickhouse/queries/funnels/base.py b/ee/clickhouse/queries/funnels/base.py index 5e5f1c683b35f..9876a5a877613 100644 --- a/ee/clickhouse/queries/funnels/base.py +++ b/ee/clickhouse/queries/funnels/base.py @@ -252,7 +252,7 @@ def _get_step_col(self, entity: Entity, index: int, entity_name: str) -> List[st def _build_step_query(self, entity: Entity, index: int, entity_name: str) -> str: filters = self._build_filters(entity, index) if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get(pk=entity.id) + action = Action.objects.get_from_entity(entity) for action_step in action.steps.all(): self.params[entity_name].append(action_step.event) action_query, action_params = format_action_filter(action, "{}_step_{}".format(entity_name, index)) diff --git a/ee/clickhouse/queries/funnels/funnel_event_query.py b/ee/clickhouse/queries/funnels/funnel_event_query.py index fbb82d45c55f9..e4b4441708cda 100644 --- a/ee/clickhouse/queries/funnels/funnel_event_query.py +++ b/ee/clickhouse/queries/funnels/funnel_event_query.py @@ -49,7 +49,7 @@ def _get_entity_query(self, entities=None, entity_name="events") -> Tuple[str, D for entity in entities_to_use: if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get(pk=entity.id) + action = Action.objects.get_from_entity(entity) for action_step in action.steps.all(): events.append(action_step.event) else: diff --git a/ee/clickhouse/queries/sessions/util.py b/ee/clickhouse/queries/sessions/util.py index 7c8b00b3e9889..4ea48f4ed9546 100644 --- a/ee/clickhouse/queries/sessions/util.py +++ b/ee/clickhouse/queries/sessions/util.py @@ -26,7 +26,7 @@ def entity_query_conditions(filter: Filter, team: Team) -> Tuple[List[str], Dict params: Dict[str, Any] = {} for index, entity in enumerate(filter.entities): if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get(pk=entity.id) + action = Action.objects.get_from_entity(entity) action_query, action_params = format_action_filter(action, prepend=f"action_{index}") entity_conditions.append(action_query) params = {**params, **action_params} diff --git a/ee/clickhouse/queries/trends/breakdown.py b/ee/clickhouse/queries/trends/breakdown.py index 2dddfc64efade..dffd4fe20ed8a 100644 --- a/ee/clickhouse/queries/trends/breakdown.py +++ b/ee/clickhouse/queries/trends/breakdown.py @@ -62,7 +62,7 @@ def _format_breakdown_query(self, entity: Entity, filter: Filter, team_id: int) action_query = "" action_params: Dict = {} if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get(pk=entity.id) + action = Action.objects.get_from_entity(entity) action_query, action_params = format_action_filter(action, table_name="e") params = { diff --git a/ee/clickhouse/queries/trends/lifecycle.py b/ee/clickhouse/queries/trends/lifecycle.py index 97189c5484edf..9718f59bafd8e 100644 --- a/ee/clickhouse/queries/trends/lifecycle.py +++ b/ee/clickhouse/queries/trends/lifecycle.py @@ -57,7 +57,7 @@ def _format_lifecycle_query(self, entity: Entity, filter: Filter, team_id: int) if entity.type == TREND_FILTER_TYPE_ACTIONS: try: - action = Action.objects.get(pk=entity.id) + action = Action.objects.get_from_entity(entity) event_query, event_params = format_action_filter(action) except: return "", {}, self._parse_result(filter, entity) @@ -130,7 +130,7 @@ def get_people( if entity.type == TREND_FILTER_TYPE_ACTIONS: try: - action = Action.objects.get(pk=entity.id) + action = Action.objects.get_from_entity(entity) event_query, event_params = format_action_filter(action) except: return [] diff --git a/ee/clickhouse/queries/trends/util.py b/ee/clickhouse/queries/trends/util.py index 7d681ca909487..e59e5898090cf 100644 --- a/ee/clickhouse/queries/trends/util.py +++ b/ee/clickhouse/queries/trends/util.py @@ -95,13 +95,10 @@ def get_active_user_params(filter: Filter, entity: Entity, team_id: int) -> Dict def populate_entity_params(entity: Entity) -> Tuple[Dict, Dict]: params, content_sql_params = {}, {} if entity.type == TREND_FILTER_TYPE_ACTIONS: - try: - action = Action.objects.get(pk=entity.id) - action_query, action_params = format_action_filter(action) - params = {**action_params} - content_sql_params = {"entity_query": "AND {action_query}".format(action_query=action_query)} - except: - raise ValidationError(f"Action ID {entity.id} does not exist.") + action = Action.objects.get_from_entity(entity) + action_query, action_params = format_action_filter(action) + params = {**action_params} + content_sql_params = {"entity_query": "AND {action_query}".format(action_query=action_query)} else: content_sql_params = {"entity_query": "AND event = %(event)s"} params = {"event": entity.id} diff --git a/posthog/models/action.py b/posthog/models/action.py index b8b64e24aa38b..ebbcde72fa13d 100644 --- a/posthog/models/action.py +++ b/posthog/models/action.py @@ -9,18 +9,42 @@ from django.db.models.signals import post_delete, post_save from django.dispatch.dispatcher import receiver from django.utils import timezone +from rest_framework.exceptions import ValidationError from rest_hooks.signals import raw_hook_event from sentry_sdk import capture_exception +from posthog.models.entity import Entity from posthog.redis import get_client +class ActionManager(models.Manager): + def get_from_entity(self, entity: Entity) -> "Action": + try: + return Action.objects.get(id=entity.id) + except: + raise ValidationError(f"Action ID {entity.id} does not exist.") + + class Action(models.Model): class Meta: indexes = [ models.Index(fields=["team_id", "-updated_at"]), ] + name: models.CharField = models.CharField(max_length=400, null=True, blank=True) + team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) + created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True, blank=True) + created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.CASCADE, null=True, blank=True) + deleted: models.BooleanField = models.BooleanField(default=False) + events: models.ManyToManyField = models.ManyToManyField("Event", blank=True) + post_to_slack: models.BooleanField = models.BooleanField(default=False) + slack_message_format: models.CharField = models.CharField(default="", max_length=200, blank=True) + is_calculating: models.BooleanField = models.BooleanField(default=False) + updated_at: models.DateTimeField = models.DateTimeField(auto_now=True) + last_calculated_at: models.DateTimeField = models.DateTimeField(default=timezone.now, blank=True) + + objects: ActionManager = ActionManager() + def calculate_events(self, start=None, end=None): recalculate_all = False if start is None and end is None: @@ -90,18 +114,6 @@ def on_perform(self, event): sender=None, event_name="action_performed", instance=self, payload=payload, user=event.team, ) - name: models.CharField = models.CharField(max_length=400, null=True, blank=True) - team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) - created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True, blank=True) - created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.CASCADE, null=True, blank=True) - deleted: models.BooleanField = models.BooleanField(default=False) - events: models.ManyToManyField = models.ManyToManyField("Event", blank=True) - post_to_slack: models.BooleanField = models.BooleanField(default=False) - slack_message_format: models.CharField = models.CharField(default="", max_length=200, blank=True) - is_calculating: models.BooleanField = models.BooleanField(default=False) - updated_at: models.DateTimeField = models.DateTimeField(auto_now=True) - last_calculated_at: models.DateTimeField = models.DateTimeField(default=timezone.now, blank=True) - def __str__(self): return self.name From 7e5f42b4589cfa87fc02c277b22ebbed35d5f84c Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Wed, 7 Jul 2021 15:59:33 +0200 Subject: [PATCH 3/4] Fix circular import --- ee/clickhouse/models/action.py | 2 +- ee/clickhouse/queries/clickhouse_stickiness.py | 4 ++-- ee/clickhouse/queries/funnels/base.py | 2 +- .../queries/funnels/funnel_event_query.py | 2 +- ee/clickhouse/queries/sessions/util.py | 2 +- ee/clickhouse/queries/trends/breakdown.py | 2 +- ee/clickhouse/queries/trends/lifecycle.py | 4 ++-- ee/clickhouse/queries/trends/util.py | 2 +- posthog/models/action.py | 12 ------------ posthog/models/entity.py | 15 ++++++++++++++- posthog/models/property.py | 1 - 11 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ee/clickhouse/models/action.py b/ee/clickhouse/models/action.py index 406cd981c775a..f31c15ee8a329 100644 --- a/ee/clickhouse/models/action.py +++ b/ee/clickhouse/models/action.py @@ -90,7 +90,7 @@ def filter_event( def format_entity_filter(entity: Entity, prepend: str = "action", filter_by_team=True) -> Tuple[str, Dict]: if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() entity_filter, params = format_action_filter(action, prepend=prepend, filter_by_team=filter_by_team) else: key = f"{prepend}_event" diff --git a/ee/clickhouse/queries/clickhouse_stickiness.py b/ee/clickhouse/queries/clickhouse_stickiness.py index cafcb8cc1e516..be8803d0775ad 100644 --- a/ee/clickhouse/queries/clickhouse_stickiness.py +++ b/ee/clickhouse/queries/clickhouse_stickiness.py @@ -45,7 +45,7 @@ def stickiness(self, entity: Entity, filter: StickinessFilter, team_id: int) -> params: Dict = {"team_id": team_id} params = {**params, **prop_filter_params, "num_intervals": filter.total_intervals} if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() action_query, action_params = format_action_filter(action) if action_query == "": return {} @@ -82,7 +82,7 @@ def _retrieve_people( def _format_entity_filter(entity: Entity) -> Tuple[str, Dict]: if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() action_query, params = format_action_filter(action) entity_filter = "AND {}".format(action_query) else: diff --git a/ee/clickhouse/queries/funnels/base.py b/ee/clickhouse/queries/funnels/base.py index 9876a5a877613..e6f493a3f7b45 100644 --- a/ee/clickhouse/queries/funnels/base.py +++ b/ee/clickhouse/queries/funnels/base.py @@ -252,7 +252,7 @@ def _get_step_col(self, entity: Entity, index: int, entity_name: str) -> List[st def _build_step_query(self, entity: Entity, index: int, entity_name: str) -> str: filters = self._build_filters(entity, index) if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() for action_step in action.steps.all(): self.params[entity_name].append(action_step.event) action_query, action_params = format_action_filter(action, "{}_step_{}".format(entity_name, index)) diff --git a/ee/clickhouse/queries/funnels/funnel_event_query.py b/ee/clickhouse/queries/funnels/funnel_event_query.py index e4b4441708cda..484393534d1b9 100644 --- a/ee/clickhouse/queries/funnels/funnel_event_query.py +++ b/ee/clickhouse/queries/funnels/funnel_event_query.py @@ -49,7 +49,7 @@ def _get_entity_query(self, entities=None, entity_name="events") -> Tuple[str, D for entity in entities_to_use: if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() for action_step in action.steps.all(): events.append(action_step.event) else: diff --git a/ee/clickhouse/queries/sessions/util.py b/ee/clickhouse/queries/sessions/util.py index 4ea48f4ed9546..47ebcb0613092 100644 --- a/ee/clickhouse/queries/sessions/util.py +++ b/ee/clickhouse/queries/sessions/util.py @@ -26,7 +26,7 @@ def entity_query_conditions(filter: Filter, team: Team) -> Tuple[List[str], Dict params: Dict[str, Any] = {} for index, entity in enumerate(filter.entities): if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() action_query, action_params = format_action_filter(action, prepend=f"action_{index}") entity_conditions.append(action_query) params = {**params, **action_params} diff --git a/ee/clickhouse/queries/trends/breakdown.py b/ee/clickhouse/queries/trends/breakdown.py index dffd4fe20ed8a..809392ce6fd5e 100644 --- a/ee/clickhouse/queries/trends/breakdown.py +++ b/ee/clickhouse/queries/trends/breakdown.py @@ -62,7 +62,7 @@ def _format_breakdown_query(self, entity: Entity, filter: Filter, team_id: int) action_query = "" action_params: Dict = {} if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() action_query, action_params = format_action_filter(action, table_name="e") params = { diff --git a/ee/clickhouse/queries/trends/lifecycle.py b/ee/clickhouse/queries/trends/lifecycle.py index 9718f59bafd8e..dd408d634bc25 100644 --- a/ee/clickhouse/queries/trends/lifecycle.py +++ b/ee/clickhouse/queries/trends/lifecycle.py @@ -57,7 +57,7 @@ def _format_lifecycle_query(self, entity: Entity, filter: Filter, team_id: int) if entity.type == TREND_FILTER_TYPE_ACTIONS: try: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() event_query, event_params = format_action_filter(action) except: return "", {}, self._parse_result(filter, entity) @@ -130,7 +130,7 @@ def get_people( if entity.type == TREND_FILTER_TYPE_ACTIONS: try: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() event_query, event_params = format_action_filter(action) except: return [] diff --git a/ee/clickhouse/queries/trends/util.py b/ee/clickhouse/queries/trends/util.py index e59e5898090cf..d5f88280ec10c 100644 --- a/ee/clickhouse/queries/trends/util.py +++ b/ee/clickhouse/queries/trends/util.py @@ -95,7 +95,7 @@ def get_active_user_params(filter: Filter, entity: Entity, team_id: int) -> Dict def populate_entity_params(entity: Entity) -> Tuple[Dict, Dict]: params, content_sql_params = {}, {} if entity.type == TREND_FILTER_TYPE_ACTIONS: - action = Action.objects.get_from_entity(entity) + action = entity.get_action() action_query, action_params = format_action_filter(action) params = {**action_params} content_sql_params = {"entity_query": "AND {action_query}".format(action_query=action_query)} diff --git a/posthog/models/action.py b/posthog/models/action.py index ebbcde72fa13d..12c1500b12c1c 100644 --- a/posthog/models/action.py +++ b/posthog/models/action.py @@ -9,22 +9,12 @@ from django.db.models.signals import post_delete, post_save from django.dispatch.dispatcher import receiver from django.utils import timezone -from rest_framework.exceptions import ValidationError from rest_hooks.signals import raw_hook_event from sentry_sdk import capture_exception -from posthog.models.entity import Entity from posthog.redis import get_client -class ActionManager(models.Manager): - def get_from_entity(self, entity: Entity) -> "Action": - try: - return Action.objects.get(id=entity.id) - except: - raise ValidationError(f"Action ID {entity.id} does not exist.") - - class Action(models.Model): class Meta: indexes = [ @@ -43,8 +33,6 @@ class Meta: updated_at: models.DateTimeField = models.DateTimeField(auto_now=True) last_calculated_at: models.DateTimeField = models.DateTimeField(default=timezone.now, blank=True) - objects: ActionManager = ActionManager() - def calculate_events(self, start=None, end=None): recalculate_all = False if start is None and end is None: diff --git a/posthog/models/entity.py b/posthog/models/entity.py index b2e2ff64dd9a8..8ac1992d90c8f 100644 --- a/posthog/models/entity.py +++ b/posthog/models/entity.py @@ -1,6 +1,9 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, Optional, Union + +from rest_framework.exceptions import ValidationError from posthog.constants import TREND_FILTER_TYPE_ACTIONS, TREND_FILTER_TYPE_EVENTS +from posthog.models.action import Action from posthog.models.filters.mixins.property import PropertyMixin @@ -63,3 +66,13 @@ def equals(self, other) -> bool: return False return True + + def get_action(self) -> Action: + if self.type != TREND_FILTER_TYPE_ACTIONS: + raise ValueError( + f"Action can only be fetched for entities of type {TREND_FILTER_TYPE_ACTIONS}, not {self.type}!" + ) + try: + return Action.objects.get(id=self.id) + except: + raise ValidationError(f"Action ID {self.id} does not exist!") diff --git a/posthog/models/property.py b/posthog/models/property.py index c57b43eb091ce..52cfa15bfd940 100644 --- a/posthog/models/property.py +++ b/posthog/models/property.py @@ -3,7 +3,6 @@ from django.db.models import Exists, OuterRef, Q -from posthog.models import cohort from posthog.utils import is_valid_regex ValueT = Union[str, int, List[str]] From 93aeeeaac2fc4478f8db1e35d2902c1434e17582 Mon Sep 17 00:00:00 2001 From: Michael Matloka Date: Thu, 8 Jul 2021 23:29:37 +0200 Subject: [PATCH 4/4] Add `test_funnel_invalid_action_handled` --- ee/clickhouse/views/test/test_clickhouse_insights.py | 6 ++++++ posthog/test/base.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ee/clickhouse/views/test/test_clickhouse_insights.py b/ee/clickhouse/views/test/test_clickhouse_insights.py index c37637e96fec6..18ea74d9bd974 100644 --- a/ee/clickhouse/views/test/test_clickhouse_insights.py +++ b/ee/clickhouse/views/test/test_clickhouse_insights.py @@ -123,3 +123,9 @@ def test_funnel_time_to_convert_auto_bins(self): self.assertEqual( response.json(), {"result": [[2220.0, 2], [29080.0, 0], [55940.0, 0], [82800.0, 1],]}, ) + + def test_funnel_invalid_action_handled(self): + response = self.client.post("/api/insight/funnel/", {"actions": [{"id": 666, "type": "actions", "order": 0},]},) + + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), self.validation_error_response("Action ID 666 does not exist!")) diff --git a/posthog/test/base.py b/posthog/test/base.py index 5be6326848412..45b5e3523359c 100644 --- a/posthog/test/base.py +++ b/posthog/test/base.py @@ -67,7 +67,7 @@ def unauthenticated_response( } def validation_error_response( - self, message: str = "Malformed request", code: str = "invalid", attr: Optional[str] = None, + self, message: str = "Malformed request", code: str = "invalid_input", attr: Optional[str] = None, ) -> Dict[str, Optional[str]]: return { "type": "validation_error",