Skip to content

Commit 329eea3

Browse files
authored
feat: make it possible to disable using offset on API endpoint per team (#42534)
1 parent 0de916c commit 329eea3

File tree

4 files changed

+51
-9
lines changed

4 files changed

+51
-9
lines changed

posthog/api/event.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,18 @@ def list(self, request: request.Request, *args: Any, **kwargs: Any) -> response.
203203
except ValueError:
204204
offset = 0
205205

206-
if settings.PATCH_EVENT_LIST_MAX_OFFSET > 0:
206+
team = self.team
207+
208+
deprecate_offset = (
209+
settings.PATCH_EVENT_LIST_MAX_OFFSET > 1 or team.id in settings.PATCH_EVENT_LIST_MAX_OFFSET_PER_TEAM
210+
)
211+
if settings.PATCH_EVENT_LIST_MAX_OFFSET > 0 or deprecate_offset:
207212
if offset > 0:
208213
time.sleep(1)
209-
if offset > 50000 and (
210-
settings.PATCH_EVENT_LIST_MAX_OFFSET > 1 or random.random() < 0.01
211-
): # 1% of queries fail
212-
raise serializers.ValidationError("Max supported offset value is 50000")
214+
if offset > 50000 and (deprecate_offset or random.random() < 0.01): # 1% of queries fail
215+
raise serializers.ValidationError("Offset is deprecated. Max supported offset value is 50000")
213216

214-
team = self.team
215-
filter = Filter(request=request, team=self.team)
217+
filter = Filter(request=request, team=team)
216218
order_by: list[str] = (
217219
list(json.loads(request.GET["orderBy"])) if request.GET.get("orderBy") else ["-timestamp"]
218220
)
@@ -251,7 +253,14 @@ def list(self, request: request.Request, *args: Any, **kwargs: Any) -> response.
251253
next_url = self._build_next_url(request, query_result[limit - 1]["timestamp"], order_by)
252254
headers = None
253255
if settings.PATCH_EVENT_LIST_MAX_OFFSET > 0:
254-
headers = {"X-PostHog-Notif": "https://posthog.com/docs/events_list-upcoming-changes"}
256+
headers = {"X-PostHog-Warn": "https://posthog.com/docs/events_list-upcoming-changes"}
257+
elif deprecate_offset and offset:
258+
headers = {
259+
"X-PostHog-Warn": (
260+
"offset is deprecated. "
261+
"Use: https://posthog.com/docs/api/queries#5-use-timestamp-based-pagination-instead-of-offset"
262+
)
263+
}
255264
return response.Response({"next": next_url, "results": result}, headers=headers)
256265

257266
except Exception as ex:

posthog/settings/data_stores.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from posthog.settings.base_variables import DEBUG, IN_EVAL_TESTING, IS_COLLECT_STATIC, TEST
1212
from posthog.settings.utils import get_from_env, get_list, str_to_bool
13+
from posthog.utils import str_to_int_set
1314

1415
# See https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-DATABASE-DISABLE_SERVER_SIDE_CURSORS
1516
DISABLE_SERVER_SIDE_CURSORS: bool = get_from_env("USING_PGBOUNCER", False, type_cast=str_to_bool)
@@ -482,3 +483,6 @@ def _parse_kafka_hosts(hosts_string: str) -> list[str]:
482483

483484
# Limiting event_list API, saving ClickHouse, 0 - disabled, 1 - migration period, 2 - enabled.
484485
PATCH_EVENT_LIST_MAX_OFFSET: int = get_from_env("PATCH_EVENT_LIST_MAX_OFFSET", 0, type_cast=int)
486+
PATCH_EVENT_LIST_MAX_OFFSET_PER_TEAM: set[int] = get_from_env(
487+
"PATCH_EVENT_LIST_MAX_OFFSET_PER_TEAM", default=set[int]([]), type_cast=str_to_int_set
488+
)

posthog/test/test_utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from django.test import TestCase
1414
from django.test.client import RequestFactory
1515

16+
from parameterized import parameterized
1617
from rest_framework.request import Request
1718

1819
from posthog.exceptions import RequestParsingError, UnspecifiedCompressionFallbackParsingError
@@ -31,6 +32,7 @@
3132
load_data_from_request,
3233
refresh_requested_by_client,
3334
relative_date_parse,
35+
str_to_int_set,
3436
)
3537

3638

@@ -588,3 +590,20 @@ def create_group_type_mapping_without_created_at(**kwargs) -> "GroupTypeMapping"
588590
GroupTypeMapping.objects.filter(id=instance.id).update(created_at=None)
589591
instance.refresh_from_db()
590592
return instance
593+
594+
595+
class TestStrToIntSet(TestCase):
596+
@parameterized.expand(
597+
[
598+
(None, set()),
599+
("", set()),
600+
("[]", set()),
601+
("[1, 2, 3]", {1, 2, 3}),
602+
("[1, 1, 2]", {1, 2}),
603+
('["1", "2"]', {1, 2}),
604+
("invalid", set()),
605+
("123", set()),
606+
]
607+
)
608+
def test_str_to_int_set(self, value, expected):
609+
assert str_to_int_set(value) == expected

posthog/utils.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import datetime as dt
1515
import dataclasses
1616
from collections.abc import Callable, Generator, Mapping, Sequence
17-
from contextlib import contextmanager
17+
from contextlib import contextmanager, suppress
1818
from enum import Enum
1919
from functools import lru_cache, wraps
2020
from operator import itemgetter
@@ -1287,6 +1287,16 @@ def _request_has_key_set(key: str, request: Request, allowed_values: Optional[li
12871287
return False
12881288

12891289

1290+
def str_to_int_set(value: Any) -> set[int]:
1291+
"""Return a set of integers"""
1292+
if not value:
1293+
return set[int]([])
1294+
with suppress(Exception):
1295+
as_json = json.loads(str(value))
1296+
return {int(v) for v in as_json}
1297+
return set[int]([])
1298+
1299+
12901300
def str_to_bool(value: Any) -> bool:
12911301
"""Return whether the provided string (or any value really) represents true. Otherwise, false.
12921302
Just like plugin server stringToBoolean.

0 commit comments

Comments
 (0)