Skip to content

Commit 98d2d4c

Browse files
authored
Merge pull request #12 from PostHog/investigate
Send distinctID to the decide endpoint to determin if user should have features enabled
2 parents 144a774 + b7c1572 commit 98d2d4c

File tree

7 files changed

+51
-23
lines changed

7 files changed

+51
-23
lines changed

posthog/client.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
import atexit
66
import hashlib
77

8+
import requests
89
from dateutil.tz import tzutc
910
from six import string_types
1011

1112
from posthog.utils import guess_timezone, clean
1213
from posthog.consumer import Consumer
13-
from posthog.request import post, get, APIError
14+
from posthog.request import batch_post, decide, get, APIError
1415
from posthog.version import VERSION
1516
from posthog.poller import Poller
1617

@@ -188,7 +189,7 @@ def _enqueue(self, msg):
188189

189190
if self.sync_mode:
190191
self.log.debug('enqueued with blocking %s.', msg['event'])
191-
post(self.api_key, self.host, gzip=self.gzip,
192+
batch_post(self.api_key, self.host, gzip=self.gzip,
192193
timeout=self.timeout, batch=[msg])
193194

194195
return True, msg
@@ -256,7 +257,6 @@ def load_feature_flags(self):
256257
def feature_enabled(self, key, distinct_id, default=False):
257258
require('key', key, string_types)
258259
require('distinct_id', distinct_id, ID_TYPES)
259-
error = False
260260

261261
if not self.personal_api_key:
262262
self.log.warning('[FEATURE FLAGS] You have to specify a personal_api_key to use feature flags.')
@@ -266,7 +266,6 @@ def feature_enabled(self, key, distinct_id, default=False):
266266
# If loading in previous line failed
267267
if not self.feature_flags:
268268
response = default
269-
error = True
270269
else:
271270
try:
272271
feature_flag = [flag for flag in self.feature_flags if flag['key'] == key][0]
@@ -277,13 +276,16 @@ def feature_enabled(self, key, distinct_id, default=False):
277276
response = _hash(key, distinct_id) <= (feature_flag['rollout_percentage'] / 100)
278277
else:
279278
try:
280-
request = get(self.api_key, '/decide/', self.host, timeout=1)
281-
response = key in request['featureFlags']
279+
request_data = {
280+
"distinct_id": distinct_id,
281+
"personal_api_key": self.personal_api_key,
282+
}
283+
resp_data = decide(self.api_key, self.host, timeout=10, **request_data)
284+
response = key in resp_data['featureFlags']
282285
except Exception as e:
283286
response = default
284287
self.log.warning('[FEATURE FLAGS] Unable to get data for flag %s, because of the following error:' % key)
285288
self.log.warning(e)
286-
error = True
287289

288290
self.capture(distinct_id, '$feature_flag_called', {'$feature_flag': key, '$feature_flag_response': response})
289291
return response

posthog/consumer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import backoff
55
import json
66

7-
from posthog.request import post, APIError, DatetimeSerializer
7+
from posthog.request import batch_post, APIError, DatetimeSerializer
88

99
try:
1010
from queue import Empty
@@ -128,7 +128,7 @@ def fatal_exception(exc):
128128
max_tries=self.retries + 1,
129129
giveup=fatal_exception)
130130
def send_request():
131-
post(self.api_key, self.host, gzip=self.gzip,
131+
batch_post(self.api_key, self.host, gzip=self.gzip,
132132
timeout=self.timeout, batch=batch)
133133

134134
send_request()

posthog/request.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
DEFAULT_HOST = 'https://app.posthog.com'
1616
USER_AGENT = 'posthog-python/' + VERSION
1717

18-
def post(api_key, host=None, gzip=False, timeout=15, **kwargs):
18+
19+
def post(api_key, host=None, path=None, gzip=False, timeout=15, **kwargs):
1920
"""Post the `kwargs` to the API"""
2021
log = logging.getLogger('posthog')
2122
body = kwargs
2223
body["sentAt"] = datetime.utcnow().replace(tzinfo=tzutc()).isoformat()
23-
url = remove_trailing_slash(host or DEFAULT_HOST) + '/batch/'
24+
url = remove_trailing_slash(host or DEFAULT_HOST) + path
2425
body['api_key'] = api_key
2526
data = json.dumps(body, cls=DatetimeSerializer)
2627
log.debug('making request: %s', data)
@@ -44,13 +45,38 @@ def post(api_key, host=None, gzip=False, timeout=15, **kwargs):
4445
log.debug('data uploaded successfully')
4546
return res
4647

48+
49+
def decide(api_key, host=None, gzip=False, timeout=15, **kwargs):
50+
"""Post the `kwargs to the decide API endpoint"""
51+
log = logging.getLogger('posthog')
52+
res = post(api_key, host, '/decide/', gzip, timeout, **kwargs)
53+
if res.status_code == 200:
54+
log.debug('Feature flags decided successfully')
55+
return res.json()
56+
try:
57+
payload = res.json()
58+
log.debug('received response: %s', payload)
59+
raise APIError(res.status_code, payload['detail'])
60+
except ValueError:
61+
raise APIError(res.status_code, res.text)
62+
63+
64+
def batch_post(api_key, host=None, gzip=False, timeout=15, **kwargs):
65+
"""Post the `kwargs` to the batch API endpoint for events"""
66+
log = logging.getLogger('posthog')
67+
res = post(api_key, host, '/batch/', gzip, timeout, **kwargs)
68+
69+
if res.status_code == 200:
70+
log.debug('data uploaded successfully')
71+
return res
4772
try:
4873
payload = res.json()
4974
log.debug('received response: %s', payload)
5075
raise APIError(res.status_code, payload['detail'])
5176
except ValueError:
5277
raise APIError(res.status_code, res.text)
5378

79+
5480
def get(api_key, url, host=None, timeout=None):
5581
log = logging.getLogger('posthog')
5682
url = remove_trailing_slash(host or DEFAULT_HOST) + url

posthog/test/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def mock_post_fn(*args, **kwargs):
208208

209209
# the post function should be called 2 times, with a batch size of 10
210210
# each time.
211-
with mock.patch('posthog.consumer.post', side_effect=mock_post_fn) \
211+
with mock.patch('posthog.consumer.batch_post', side_effect=mock_post_fn) \
212212
as mock_post:
213213
for _ in range(20):
214214
client.identify('distinct_id', {'trait': 'value'})
@@ -259,7 +259,7 @@ def test_feature_enabled_simple(self, patch_get):
259259
}]
260260
self.assertTrue(client.feature_enabled('beta-feature', 'distinct_id'))
261261

262-
@mock.patch('posthog.client.get')
262+
@mock.patch('posthog.client.decide')
263263
def test_feature_enabled_request(self, patch_get):
264264
patch_get.return_value = {
265265
'featureFlags': ['beta-feature']

posthog/test/consumer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def test_flush_interval(self):
6060
flush_interval = 0.3
6161
consumer = Consumer(q, TEST_API_KEY, flush_at=10,
6262
flush_interval=flush_interval)
63-
with mock.patch('posthog.consumer.post') as mock_post:
63+
with mock.patch('posthog.consumer.batch_post') as mock_post:
6464
consumer.start()
6565
for i in range(0, 3):
6666
track = {
@@ -80,7 +80,7 @@ def test_multiple_uploads_per_interval(self):
8080
flush_at = 10
8181
consumer = Consumer(q, TEST_API_KEY, flush_at=flush_at,
8282
flush_interval=flush_interval)
83-
with mock.patch('posthog.consumer.post') as mock_post:
83+
with mock.patch('posthog.consumer.batch_post') as mock_post:
8484
consumer.start()
8585
for i in range(0, flush_at * 2):
8686
track = {
@@ -110,7 +110,7 @@ def mock_post(*args, **kwargs):
110110
raise expected_exception
111111
mock_post.call_count = 0
112112

113-
with mock.patch('posthog.consumer.post',
113+
with mock.patch('posthog.consumer.batch_post',
114114
mock.Mock(side_effect=mock_post)):
115115
track = {
116116
'type': 'track',

posthog/test/request.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,26 @@
33
import json
44
import requests
55

6-
from posthog.request import post, DatetimeSerializer
6+
from posthog.request import batch_post, DatetimeSerializer
77
from posthog.test.utils import TEST_API_KEY
88

99

1010
class TestRequests(unittest.TestCase):
1111

1212
def test_valid_request(self):
13-
res = post(TEST_API_KEY, batch=[{
13+
res = batch_post(TEST_API_KEY, batch=[{
1414
'distinct_id': 'distinct_id',
1515
'event': 'python event',
1616
'type': 'track'
1717
}])
1818
self.assertEqual(res.status_code, 200)
1919

2020
def test_invalid_request_error(self):
21-
self.assertRaises(Exception, post, 'testsecret',
21+
self.assertRaises(Exception, batch_post, 'testsecret',
2222
'https://t.posthog.com', False, '[{]')
2323

2424
def test_invalid_host(self):
25-
self.assertRaises(Exception, post, 'testsecret',
25+
self.assertRaises(Exception, batch_post, 'testsecret',
2626
't.posthog.com/', batch=[])
2727

2828
def test_datetime_serialization(self):
@@ -38,7 +38,7 @@ def test_date_serialization(self):
3838
self.assertEqual(result, expected)
3939

4040
def test_should_not_timeout(self):
41-
res = post(TEST_API_KEY, batch=[{
41+
res = batch_post(TEST_API_KEY, batch=[{
4242
'distinct_id': 'distinct_id',
4343
'event': 'python event',
4444
'type': 'track'
@@ -47,7 +47,7 @@ def test_should_not_timeout(self):
4747

4848
def test_should_timeout(self):
4949
with self.assertRaises(requests.ReadTimeout):
50-
post('key', batch=[{
50+
batch_post('key', batch=[{
5151
'distinct_id': 'distinct_id',
5252
'event': 'python event',
5353
'type': 'track'

posthog/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = '1.0.11'
1+
VERSION = '1.1.2'

0 commit comments

Comments
 (0)