Skip to content

Commit 95dab44

Browse files
committed
feat(flags): Always pass project API key in remote config requests
Updates `remote_config` to always make POST requests with the project API key in the request body for deterministic project selection and validation. - Modified remote_config() to use POST instead of GET - Always include `project_api_key` parameter in request body - Ensures deterministic routing for personal API keys - Validates secret API keys match expected project - Updated tests to reflect new behavior This aligns with backend changes in PostHog/posthog#36154
1 parent 09dad81 commit 95dab44

File tree

3 files changed

+41
-22
lines changed

3 files changed

+41
-22
lines changed

posthog/client.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,26 @@
44
import sys
55
from datetime import datetime, timedelta
66
from typing import Any, Dict, Optional, Union
7-
from typing_extensions import Unpack
87
from uuid import uuid4
98

109
from dateutil.tz import tzutc
1110
from six import string_types
11+
from typing_extensions import Unpack
1212

13-
from posthog.args import OptionalCaptureArgs, OptionalSetArgs, ID_TYPES, ExceptionArg
13+
from posthog.args import ID_TYPES, ExceptionArg, OptionalCaptureArgs, OptionalSetArgs
1414
from posthog.consumer import Consumer
15+
from posthog.contexts import (
16+
_get_current_context,
17+
get_context_distinct_id,
18+
get_context_session_id,
19+
new_context,
20+
)
1521
from posthog.exception_capture import ExceptionCapture
1622
from posthog.exception_utils import (
1723
exc_info_from_error,
24+
exception_is_already_captured,
1825
exceptions_from_error_tuple,
1926
handle_in_app,
20-
exception_is_already_captured,
2127
mark_exception_as_captured,
2228
)
2329
from posthog.feature_flags import InconclusiveMatchError, match_feature_flag_properties
@@ -31,12 +37,6 @@
3137
get,
3238
remote_config,
3339
)
34-
from posthog.contexts import (
35-
_get_current_context,
36-
get_context_distinct_id,
37-
get_context_session_id,
38-
new_context,
39-
)
4040
from posthog.types import (
4141
FeatureFlag,
4242
FeatureFlagResult,
@@ -1629,6 +1629,7 @@ def get_remote_config_payload(self, key: str):
16291629
self.personal_api_key,
16301630
self.host,
16311631
key,
1632+
project_api_key=self.api_key,
16321633
timeout=self.feature_flags_request_timeout_seconds,
16331634
)
16341635
except Exception as e:
@@ -1845,9 +1846,9 @@ def _initialize_flag_cache(self, cache_url):
18451846
return None
18461847

18471848
try:
1848-
from urllib.parse import urlparse, parse_qs
1849+
from urllib.parse import parse_qs, urlparse
18491850
except ImportError:
1850-
from urlparse import urlparse, parse_qs
1851+
from urlparse import parse_qs, urlparse
18511852

18521853
try:
18531854
parsed = urlparse(cache_url)

posthog/request.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,33 @@ def flags(
132132

133133

134134
def remote_config(
135-
personal_api_key: str, host: Optional[str] = None, key: str = "", timeout: int = 15
135+
personal_api_key: str,
136+
host: Optional[str] = None,
137+
key: str = "",
138+
project_api_key: str = "",
139+
timeout: int = 15,
136140
) -> Any:
137141
"""Get remote config flag value from remote_config API endpoint"""
138-
return get(
139-
personal_api_key,
140-
f"/api/projects/@current/feature_flags/{key}/remote_config/",
141-
host,
142-
timeout,
142+
url = (
143+
remove_trailing_slash(host or DEFAULT_HOST)
144+
+ f"/api/projects/@current/feature_flags/{key}/remote_config/"
145+
)
146+
147+
body = {"project_api_key": project_api_key, "sentAt": datetime.now(tz=tzutc()).isoformat()}
148+
149+
headers = {
150+
"Authorization": f"Bearer {personal_api_key}",
151+
"Content-Type": "application/json",
152+
"User-Agent": USER_AGENT,
153+
}
154+
155+
data = json.dumps(body, cls=DatetimeSerializer)
156+
157+
res = _session.post(url, data=data, headers=headers, timeout=timeout)
158+
159+
return _process_response(
160+
res,
161+
success_message=f"POST /api/projects/@current/feature_flags/{key}/remote_config/ completed successfully",
143162
)
144163

145164

posthog/test/test_client.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
import unittest
33
from datetime import datetime
44
from uuid import uuid4
5-
from posthog.contexts import get_context_session_id, set_context_session, new_context
65

76
import mock
87
import six
98
from parameterized import parameterized
109

1110
from posthog.client import Client
11+
from posthog.contexts import get_context_session_id, new_context, set_context_session
1212
from posthog.request import APIError
1313
from posthog.test.test_utils import FAKE_TEST_API_KEY
1414
from posthog.types import FeatureFlag, LegacyFlagMetadata
@@ -2057,7 +2057,7 @@ def test_set_context_session_with_page_explicit_properties(self):
20572057

20582058
def test_set_context_session_override_in_capture(self):
20592059
"""Test that explicit session ID overrides context session ID in capture"""
2060-
from posthog.contexts import set_context_session, new_context
2060+
from posthog.contexts import new_context, set_context_session
20612061

20622062
with mock.patch("posthog.client.batch_post") as mock_post:
20632063
client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, sync_mode=True)
@@ -2160,6 +2160,7 @@ def test_get_remote_config_payload_works_without_poller(self, patch_remote_confi
21602160
"test-personal-key",
21612161
client.host,
21622162
"test-flag",
2163+
project_api_key=FAKE_TEST_API_KEY,
21632164
timeout=client.feature_flags_request_timeout_seconds,
21642165
)
21652166

@@ -2282,9 +2283,7 @@ def test_get_feature_flag_result_with_empty_string_payload(self, patch_batch_pos
22822283
}
22832284
]
22842285
},
2285-
"payloads": {
2286-
"empty-variant": "" # Empty string payload
2287-
},
2286+
"payloads": {"empty-variant": ""}, # Empty string payload
22882287
},
22892288
}
22902289
]

0 commit comments

Comments
 (0)