Skip to content

Commit f3ee806

Browse files
authored
prepare 6.5.0 release (#100)
1 parent a495b8c commit f3ee806

File tree

8 files changed

+91
-179
lines changed

8 files changed

+91
-179
lines changed

NOTICE.txt

Lines changed: 0 additions & 2 deletions
This file was deleted.

ldclient/client.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,10 @@ def all_flags_state(self, user, **kwargs):
296296
:param kwargs: optional parameters affecting how the state is computed: set
297297
`client_side_only=True` to limit it to only flags that are marked for use with the
298298
client-side SDK (by default, all flags are included); set `with_reasons=True` to
299-
include evaluation reasons in the state (see `variation_detail`)
299+
include evaluation reasons in the state (see `variation_detail`); set
300+
`details_only_for_tracked_flags=True` to omit any metadata that is normally only
301+
used for event generation, such as flag versions and evaluation reasons, unless
302+
the flag has event tracking or debugging turned on
300303
:return: a FeatureFlagsState object (will never be None; its 'valid' property will be False
301304
if the client is offline, has not been initialized, or the user is None or has no key)
302305
:rtype: FeatureFlagsState
@@ -319,6 +322,7 @@ def all_flags_state(self, user, **kwargs):
319322
state = FeatureFlagsState(True)
320323
client_only = kwargs.get('client_side_only', False)
321324
with_reasons = kwargs.get('with_reasons', False)
325+
details_only_if_tracked = kwargs.get('details_only_for_tracked_flags', False)
322326
try:
323327
flags_map = self._store.all(FEATURES, lambda x: x)
324328
if flags_map is None:
@@ -333,12 +337,12 @@ def all_flags_state(self, user, **kwargs):
333337
try:
334338
detail = evaluate(flag, user, self._store, False).detail
335339
state.add_flag(flag, detail.value, detail.variation_index,
336-
detail.reason if with_reasons else None)
340+
detail.reason if with_reasons else None, details_only_if_tracked)
337341
except Exception as e:
338342
log.error("Error evaluating flag \"%s\" in all_flags_state: %s" % (key, e))
339343
log.debug(traceback.format_exc())
340344
reason = {'kind': 'ERROR', 'errorKind': 'EXCEPTION'}
341-
state.add_flag(flag, None, None, reason if with_reasons else None)
345+
state.add_flag(flag, None, None, reason if with_reasons else None, details_only_if_tracked)
342346

343347
return state
344348

ldclient/expiringdict.py

Lines changed: 0 additions & 155 deletions
This file was deleted.

ldclient/flags_state.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import time
23

34
class FeatureFlagsState(object):
45
"""
@@ -12,15 +13,24 @@ def __init__(self, valid):
1213
self.__flag_metadata = {}
1314
self.__valid = valid
1415

15-
def add_flag(self, flag, value, variation, reason):
16+
def add_flag(self, flag, value, variation, reason, details_only_if_tracked):
1617
"""Used internally to build the state map."""
1718
key = flag['key']
1819
self.__flag_values[key] = value
19-
meta = { 'version': flag.get('version'), 'trackEvents': flag.get('trackEvents') }
20+
meta = {}
21+
with_details = (not details_only_if_tracked) or flag.get('trackEvents')
22+
if not with_details:
23+
if flag.get('debugEventsUntilDate'):
24+
now = int(time.time() * 1000)
25+
with_details = (flag.get('debugEventsUntilDate') > now)
26+
if with_details:
27+
meta['version'] = flag.get('version')
28+
if reason is not None:
29+
meta['reason'] = reason
2030
if variation is not None:
2131
meta['variation'] = variation
22-
if reason is not None:
23-
meta['reason'] = reason
32+
if flag.get('trackEvents'):
33+
meta['trackEvents'] = True
2434
if flag.get('debugEventsUntilDate') is not None:
2535
meta['debugEventsUntilDate'] = flag.get('debugEventsUntilDate')
2636
self.__flag_metadata[key] = meta

ldclient/redis_feature_store.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import json
22
from pprint import pprint
33

4+
from expiringdict import ExpiringDict
45
import redis
56

67
from ldclient import log
7-
from ldclient.expiringdict import ExpiringDict
88
from ldclient.interfaces import FeatureStore
99
from ldclient.memoized_value import MemoizedValue
1010
from ldclient.versioned_data_kind import FEATURES

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
backoff>=1.4.3
22
certifi>=2018.4.16
3+
expiringdict>=1.1.4
34
future>=0.16.0
45
six>=1.10.0
56
pyRFC3339>=1.0

testing/test_flags_state.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
def test_can_get_flag_value():
77
state = FeatureFlagsState(True)
88
flag = { 'key': 'key' }
9-
state.add_flag(flag, 'value', 1, None)
9+
state.add_flag(flag, 'value', 1, None, False)
1010
assert state.get_flag_value('key') == 'value'
1111

1212
def test_returns_none_for_unknown_flag():
@@ -17,16 +17,16 @@ def test_can_convert_to_values_map():
1717
state = FeatureFlagsState(True)
1818
flag1 = { 'key': 'key1' }
1919
flag2 = { 'key': 'key2' }
20-
state.add_flag(flag1, 'value1', 0, None)
21-
state.add_flag(flag2, 'value2', 1, None)
20+
state.add_flag(flag1, 'value1', 0, None, False)
21+
state.add_flag(flag2, 'value2', 1, None, False)
2222
assert state.to_values_map() == { 'key1': 'value1', 'key2': 'value2' }
2323

2424
def test_can_convert_to_json_dict():
2525
state = FeatureFlagsState(True)
2626
flag1 = { 'key': 'key1', 'version': 100, 'offVariation': 0, 'variations': [ 'value1' ], 'trackEvents': False }
2727
flag2 = { 'key': 'key2', 'version': 200, 'offVariation': 1, 'variations': [ 'x', 'value2' ], 'trackEvents': True, 'debugEventsUntilDate': 1000 }
28-
state.add_flag(flag1, 'value1', 0, None)
29-
state.add_flag(flag2, 'value2', 1, None)
28+
state.add_flag(flag1, 'value1', 0, None, False)
29+
state.add_flag(flag2, 'value2', 1, None, False)
3030

3131
result = state.to_json_dict()
3232
assert result == {
@@ -35,8 +35,7 @@ def test_can_convert_to_json_dict():
3535
'$flagsState': {
3636
'key1': {
3737
'variation': 0,
38-
'version': 100,
39-
'trackEvents': False
38+
'version': 100
4039
},
4140
'key2': {
4241
'variation': 1,
@@ -52,8 +51,8 @@ def test_can_convert_to_json_string():
5251
state = FeatureFlagsState(True)
5352
flag1 = { 'key': 'key1', 'version': 100, 'offVariation': 0, 'variations': [ 'value1' ], 'trackEvents': False }
5453
flag2 = { 'key': 'key2', 'version': 200, 'offVariation': 1, 'variations': [ 'x', 'value2' ], 'trackEvents': True, 'debugEventsUntilDate': 1000 }
55-
state.add_flag(flag1, 'value1', 0, None)
56-
state.add_flag(flag2, 'value2', 1, None)
54+
state.add_flag(flag1, 'value1', 0, None, False)
55+
state.add_flag(flag2, 'value2', 1, None, False)
5756

5857
obj = state.to_json_dict()
5958
str = state.to_json_string()
@@ -63,8 +62,8 @@ def test_can_serialize_with_jsonpickle():
6362
state = FeatureFlagsState(True)
6463
flag1 = { 'key': 'key1', 'version': 100, 'offVariation': 0, 'variations': [ 'value1' ], 'trackEvents': False }
6564
flag2 = { 'key': 'key2', 'version': 200, 'offVariation': 1, 'variations': [ 'x', 'value2' ], 'trackEvents': True, 'debugEventsUntilDate': 1000 }
66-
state.add_flag(flag1, 'value1', 0, None)
67-
state.add_flag(flag2, 'value2', 1, None)
65+
state.add_flag(flag1, 'value1', 0, None, False)
66+
state.add_flag(flag2, 'value2', 1, None, False)
6867

6968
obj = state.to_json_dict()
7069
str = jsonpickle.encode(state, unpicklable=False)

testing/test_ldclient_evaluation.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22
import json
3+
import time
34
from ldclient.client import LDClient, Config
45
from ldclient.feature_store import InMemoryFeatureStore
56
from ldclient.flag import EvaluationDetail
@@ -149,8 +150,7 @@ def test_all_flags_state_returns_state():
149150
'$flagsState': {
150151
'key1': {
151152
'variation': 0,
152-
'version': 100,
153-
'trackEvents': False
153+
'version': 100
154154
},
155155
'key2': {
156156
'variation': 1,
@@ -176,7 +176,6 @@ def test_all_flags_state_returns_state_with_reasons():
176176
'key1': {
177177
'variation': 0,
178178
'version': 100,
179-
'trackEvents': False,
180179
'reason': {'kind': 'OFF'}
181180
},
182181
'key2': {
@@ -229,6 +228,62 @@ def test_all_flags_state_can_be_filtered_for_client_side_flags():
229228
values = state.to_values_map()
230229
assert values == { 'client-side-1': 'value1', 'client-side-2': 'value2' }
231230

231+
def test_all_flags_state_can_omit_details_for_untracked_flags():
232+
future_time = (time.time() * 1000) + 100000
233+
flag1 = {
234+
'key': 'key1',
235+
'version': 100,
236+
'on': False,
237+
'offVariation': 0,
238+
'variations': [ 'value1' ],
239+
'trackEvents': False
240+
}
241+
flag2 = {
242+
'key': 'key2',
243+
'version': 200,
244+
'on': False,
245+
'offVariation': 1,
246+
'variations': [ 'x', 'value2' ],
247+
'trackEvents': True
248+
}
249+
flag3 = {
250+
'key': 'key3',
251+
'version': 300,
252+
'on': False,
253+
'offVariation': 1,
254+
'variations': [ 'x', 'value3' ],
255+
'debugEventsUntilDate': future_time
256+
}
257+
store = InMemoryFeatureStore()
258+
store.init({ FEATURES: { 'key1': flag1, 'key2': flag2, 'key3': flag3 } })
259+
client = make_client(store)
260+
state = client.all_flags_state(user, with_reasons=True, details_only_for_tracked_flags=True)
261+
assert state.valid == True
262+
result = state.to_json_dict()
263+
assert result == {
264+
'key1': 'value1',
265+
'key2': 'value2',
266+
'key3': 'value3',
267+
'$flagsState': {
268+
'key1': {
269+
'variation': 0
270+
},
271+
'key2': {
272+
'variation': 1,
273+
'version': 200,
274+
'trackEvents': True,
275+
'reason': {'kind': 'OFF'}
276+
},
277+
'key3': {
278+
'variation': 1,
279+
'version': 300,
280+
'debugEventsUntilDate': future_time,
281+
'reason': {'kind': 'OFF'}
282+
}
283+
},
284+
'$valid': True
285+
}
286+
232287
def test_all_flags_state_returns_empty_state_if_user_is_none():
233288
store = InMemoryFeatureStore()
234289
store.init({ FEATURES: { 'key1': flag1, 'key2': flag2 } })

0 commit comments

Comments
 (0)