From 0ad97c03dec4793af9d6d0a39909d614d6d204d7 Mon Sep 17 00:00:00 2001 From: kp-cat <52385411+kp-cat@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:05:47 +0200 Subject: [PATCH] evaluation log update: hashed value + max 10 length lists --- configcatclient/configcatclient.py | 4 +- configcatclient/evaluationlogbuilder.py | 50 +++++++++++++++++++ configcatclient/logbuilder.py | 25 ---------- configcatclient/rolloutevaluator.py | 15 ++---- .../2_rules_matching_targeted_attribute.txt | 6 +-- .../2_rules_no_targeted_attribute.txt | 8 +-- .../2_targeting_rules/2_rules_no_user.txt | 4 +- ..._rules_not_matching_targeted_attribute.txt | 6 +-- .../and_rules/and_rules_no_user.txt | 2 +- .../evaluation/and_rules/and_rules_user.txt | 4 +- .../prerequisite_flag/prerequisite_flag.txt | 8 +-- .../evaluation/segment/segment_matching.txt | 2 +- .../segment/segment_no_matching.txt | 2 +- configcatclienttests/test_evaluationlog.py | 13 +++++ 14 files changed, 90 insertions(+), 59 deletions(-) create mode 100644 configcatclient/evaluationlogbuilder.py delete mode 100644 configcatclient/logbuilder.py diff --git a/configcatclient/configcatclient.py b/configcatclient/configcatclient.py index 1527a6f..e130f31 100644 --- a/configcatclient/configcatclient.py +++ b/configcatclient/configcatclient.py @@ -5,7 +5,7 @@ from .configservice import ConfigService from .constants import TARGETING_RULES, VARIATION_ID, PERCENTAGE_OPTIONS, FEATURE_FLAGS, SERVED_VALUE from .evaluationdetails import EvaluationDetails -from .logbuilder import LogBuilder +from .evaluationlogbuilder import EvaluationLogBuilder from .interfaces import ConfigCatClientException from .logger import Logger from .configfetcher import ConfigFetcher @@ -373,7 +373,7 @@ def __evaluate(self, key, user, default_value, default_variation_id, config, fet user = user if user is not None else self._default_user # Skip building the evaluation log if it won't be logged. - log_builder = LogBuilder() if self.log.isEnabledFor(logging.INFO) else None + log_builder = EvaluationLogBuilder() if self.log.isEnabledFor(logging.INFO) else None value, variation_id, rule, percentage_rule, error = self._rollout_evaluator.evaluate( key=key, diff --git a/configcatclient/evaluationlogbuilder.py b/configcatclient/evaluationlogbuilder.py new file mode 100644 index 0000000..9233e2d --- /dev/null +++ b/configcatclient/evaluationlogbuilder.py @@ -0,0 +1,50 @@ +class EvaluationLogBuilder(object): + def __init__(self): + self.indent_level = 0 + self.text = '' + + @staticmethod + def trunc_comparison_value_if_needed(comparator_text, comparison_value): + if '(hashed)' in comparator_text: + if isinstance(comparison_value, list): + length = len(comparison_value) + if length > 1: + return '[<{} hashed values>]'.format(length) + return '[<{} hashed value>]'.format(length) + + return '' + + if isinstance(comparison_value, list): + limit = 10 + length = len(comparison_value) + if length > limit: + remaining = length - limit + if remaining == 1: + more_text = "(1 more value)" + else: + more_text = "({} more values)".format(remaining) + + return str(comparison_value[:limit])[:-1] + ', ... ' + more_text + ']' + + return str(comparison_value) + + def increase_indent(self): + self.indent_level += 1 + return self + + def decrease_indent(self): + self.indent_level = max(0, self.indent_level - 1) + return self + + def append(self, text): + self.text += text + return self + + def new_line(self, text=None): + self.text += '\n' + ' ' * self.indent_level + if text: + self.text += text + return self + + def __str__(self): + return self.text diff --git a/configcatclient/logbuilder.py b/configcatclient/logbuilder.py deleted file mode 100644 index ca1a531..0000000 --- a/configcatclient/logbuilder.py +++ /dev/null @@ -1,25 +0,0 @@ -class LogBuilder(object): - def __init__(self): - self.indent_level = 0 - self.text = '' - - def increase_indent(self): - self.indent_level += 1 - return self - - def decrease_indent(self): - self.indent_level = max(0, self.indent_level - 1) - return self - - def append(self, text): - self.text += text - return self - - def new_line(self, text=None): - self.text += '\n' + ' ' * self.indent_level - if text: - self.text += text - return self - - def __str__(self): - return self.text diff --git a/configcatclient/rolloutevaluator.py b/configcatclient/rolloutevaluator.py index 58e553c..b835ff4 100644 --- a/configcatclient/rolloutevaluator.py +++ b/configcatclient/rolloutevaluator.py @@ -4,6 +4,7 @@ import semver from .evaluationcontext import EvaluationContext +from .evaluationlogbuilder import EvaluationLogBuilder from .logger import Logger from .constants import TARGETING_RULES, VALUE, VARIATION_ID, COMPARISON_ATTRIBUTE, \ @@ -215,19 +216,11 @@ def evaluate(self, key, user, default_value, default_variation_id, config, log_b self.log.error(error, *error_args, event_id=2001) return default_value, default_variation_id, None, None, Logger.format(error, error_args) - def _trunc_if_needed(self, comparator, comparison_value): - if '(hashed)' in self.COMPARATOR_TEXTS[comparator]: - if isinstance(comparison_value, list): - return [item[:10] + '...' for item in comparison_value] - - return comparison_value[:10] + '...' - - return comparison_value - def _format_rule(self, comparison_attribute, comparator, comparison_value): + comparator_text = self.COMPARATOR_TEXTS[comparator] return 'User.%s %s %s' \ - % (comparison_attribute, self.COMPARATOR_TEXTS[comparator], - self._trunc_if_needed(comparator, comparison_value)) + % (comparison_attribute, comparator_text, + EvaluationLogBuilder.trunc_comparison_value_if_needed(comparator_text, comparison_value)) def _handle_invalid_user_attribute(self, comparison_attribute, comparator, comparison_value, key, validation_error): """ diff --git a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_matching_targeted_attribute.txt b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_matching_targeted_attribute.txt index eeeb64e..0f29409 100644 --- a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_matching_targeted_attribute.txt +++ b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_matching_targeted_attribute.txt @@ -1,7 +1,7 @@ -WARNING [3003] Cannot evaluate condition (User.Email IS ONE OF (hashed) ['265522bb68...', '72ff4554fa...']) for setting 'stringIsInDogDefaultCat' (the User.Email attribute is missing). You should set the User.Email attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ +WARNING [3003] Cannot evaluate condition (User.Email IS ONE OF (hashed) [<2 hashed values>]) for setting 'stringIsInDogDefaultCat' (the User.Email attribute is missing). You should set the User.Email attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ INFO [5000] Evaluating 'stringIsInDogDefaultCat' for User '{"Identifier":"12345","Custom1":"admin"}' Evaluating targeting rules and applying the first match if any: - - IF User.Email IS ONE OF (hashed) ['265522bb68...', '72ff4554fa...'] THEN 'Dog' => cannot evaluate, the User.Email attribute is missing + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] THEN 'Dog' => cannot evaluate, the User.Email attribute is missing The current targeting rule is ignored and the evaluation continues with the next rule. - - IF User.Custom1 IS ONE OF (hashed) ['3eec7c82dd...'] THEN 'Dog' => MATCH, applying rule + - IF User.Custom1 IS ONE OF (hashed) [<1 hashed value>] THEN 'Dog' => MATCH, applying rule Returning 'Dog'. diff --git a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_targeted_attribute.txt b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_targeted_attribute.txt index cbb1f0d..c0e9d04 100644 --- a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_targeted_attribute.txt +++ b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_targeted_attribute.txt @@ -1,9 +1,9 @@ -WARNING [3003] Cannot evaluate condition (User.Email IS ONE OF (hashed) ['265522bb68...', '72ff4554fa...']) for setting 'stringIsInDogDefaultCat' (the User.Email attribute is missing). You should set the User.Email attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ -WARNING [3003] Cannot evaluate condition (User.Custom1 IS ONE OF (hashed) ['3eec7c82dd...']) for setting 'stringIsInDogDefaultCat' (the User.Custom1 attribute is missing). You should set the User.Custom1 attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ +WARNING [3003] Cannot evaluate condition (User.Email IS ONE OF (hashed) [<2 hashed values>]) for setting 'stringIsInDogDefaultCat' (the User.Email attribute is missing). You should set the User.Email attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ +WARNING [3003] Cannot evaluate condition (User.Custom1 IS ONE OF (hashed) [<1 hashed value>]) for setting 'stringIsInDogDefaultCat' (the User.Custom1 attribute is missing). You should set the User.Custom1 attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ INFO [5000] Evaluating 'stringIsInDogDefaultCat' for User '{"Identifier":"12345"}' Evaluating targeting rules and applying the first match if any: - - IF User.Email IS ONE OF (hashed) ['265522bb68...', '72ff4554fa...'] THEN 'Dog' => cannot evaluate, the User.Email attribute is missing + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] THEN 'Dog' => cannot evaluate, the User.Email attribute is missing The current targeting rule is ignored and the evaluation continues with the next rule. - - IF User.Custom1 IS ONE OF (hashed) ['3eec7c82dd...'] THEN 'Dog' => cannot evaluate, the User.Custom1 attribute is missing + - IF User.Custom1 IS ONE OF (hashed) [<1 hashed value>] THEN 'Dog' => cannot evaluate, the User.Custom1 attribute is missing The current targeting rule is ignored and the evaluation continues with the next rule. Returning 'Cat'. diff --git a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_user.txt b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_user.txt index 887d393..bbee647 100644 --- a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_user.txt +++ b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_no_user.txt @@ -1,8 +1,8 @@ WARNING [3001] Cannot evaluate targeting rules and % options for setting 'stringIsInDogDefaultCat' (User Object is missing). You should pass a User Object to the evaluation methods like `get_value()` in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ INFO [5000] Evaluating 'stringIsInDogDefaultCat' Evaluating targeting rules and applying the first match if any: - - IF User.Email IS ONE OF (hashed) ['265522bb68...', '72ff4554fa...'] THEN 'Dog' => cannot evaluate, User Object is missing + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] THEN 'Dog' => cannot evaluate, User Object is missing The current targeting rule is ignored and the evaluation continues with the next rule. - - IF User.Custom1 IS ONE OF (hashed) ['3eec7c82dd...'] THEN 'Dog' => cannot evaluate, User Object is missing + - IF User.Custom1 IS ONE OF (hashed) [<1 hashed value>] THEN 'Dog' => cannot evaluate, User Object is missing The current targeting rule is ignored and the evaluation continues with the next rule. Returning 'Cat'. diff --git a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_not_matching_targeted_attribute.txt b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_not_matching_targeted_attribute.txt index 2865e25..6bc6ee0 100644 --- a/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_not_matching_targeted_attribute.txt +++ b/configcatclienttests/data/evaluation/2_targeting_rules/2_rules_not_matching_targeted_attribute.txt @@ -1,7 +1,7 @@ -WARNING [3003] Cannot evaluate condition (User.Email IS ONE OF (hashed) ['265522bb68...', '72ff4554fa...']) for setting 'stringIsInDogDefaultCat' (the User.Email attribute is missing). You should set the User.Email attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ +WARNING [3003] Cannot evaluate condition (User.Email IS ONE OF (hashed) [<2 hashed values>]) for setting 'stringIsInDogDefaultCat' (the User.Email attribute is missing). You should set the User.Email attribute in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ INFO [5000] Evaluating 'stringIsInDogDefaultCat' for User '{"Identifier":"12345","Custom1":"user"}' Evaluating targeting rules and applying the first match if any: - - IF User.Email IS ONE OF (hashed) ['265522bb68...', '72ff4554fa...'] THEN 'Dog' => cannot evaluate, the User.Email attribute is missing + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] THEN 'Dog' => cannot evaluate, the User.Email attribute is missing The current targeting rule is ignored and the evaluation continues with the next rule. - - IF User.Custom1 IS ONE OF (hashed) ['3eec7c82dd...'] THEN 'Dog' => no match + - IF User.Custom1 IS ONE OF (hashed) [<1 hashed value>] THEN 'Dog' => no match Returning 'Cat'. diff --git a/configcatclienttests/data/evaluation/and_rules/and_rules_no_user.txt b/configcatclienttests/data/evaluation/and_rules/and_rules_no_user.txt index e665006..a3ada9b 100644 --- a/configcatclienttests/data/evaluation/and_rules/and_rules_no_user.txt +++ b/configcatclienttests/data/evaluation/and_rules/and_rules_no_user.txt @@ -1,7 +1,7 @@ WARNING [3001] Cannot evaluate targeting rules and % options for setting 'emailAnd' (User Object is missing). You should pass a User Object to the evaluation methods like `get_value()` in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/ INFO [5000] Evaluating 'emailAnd' Evaluating targeting rules and applying the first match if any: - - IF User.Email STARTS WITH ANY OF (hashed) ['4_489600ff...'] => false, skipping the remaining AND conditions + - IF User.Email STARTS WITH ANY OF (hashed) [<1 hashed value>] => false, skipping the remaining AND conditions THEN 'Dog' => cannot evaluate, User Object is missing The current targeting rule is ignored and the evaluation continues with the next rule. Returning 'Cat'. diff --git a/configcatclienttests/data/evaluation/and_rules/and_rules_user.txt b/configcatclienttests/data/evaluation/and_rules/and_rules_user.txt index 5d92a8e..2848601 100644 --- a/configcatclienttests/data/evaluation/and_rules/and_rules_user.txt +++ b/configcatclienttests/data/evaluation/and_rules/and_rules_user.txt @@ -1,7 +1,7 @@ INFO [5000] Evaluating 'emailAnd' for User '{"Identifier":"12345","Email":"jane@configcat.com"}' Evaluating targeting rules and applying the first match if any: - - IF User.Email STARTS WITH ANY OF (hashed) ['4_489600ff...'] => true + - IF User.Email STARTS WITH ANY OF (hashed) [<1 hashed value>] => true AND User.Email CONTAINS ANY OF ['@'] => true - AND User.Email ENDS WITH ANY OF (hashed) ['20_be728e1...'] => false, skipping the remaining AND conditions + AND User.Email ENDS WITH ANY OF (hashed) [<1 hashed value>] => false, skipping the remaining AND conditions THEN 'Dog' => no match Returning 'Cat'. diff --git a/configcatclienttests/data/evaluation/prerequisite_flag/prerequisite_flag.txt b/configcatclienttests/data/evaluation/prerequisite_flag/prerequisite_flag.txt index 948940c..fda6e99 100644 --- a/configcatclienttests/data/evaluation/prerequisite_flag/prerequisite_flag.txt +++ b/configcatclienttests/data/evaluation/prerequisite_flag/prerequisite_flag.txt @@ -4,20 +4,20 @@ INFO [5000] Evaluating 'dependentFeature' for User '{"Identifier":"12345","Email ( Evaluating prerequisite flag 'mainFeature': Evaluating targeting rules and applying the first match if any: - - IF User.Email ENDS WITH ANY OF (hashed) ['21_57e6ffe...'] => false, skipping the remaining AND conditions + - IF User.Email ENDS WITH ANY OF (hashed) [<1 hashed value>] => false, skipping the remaining AND conditions THEN 'private' => no match - - IF User.Country IS ONE OF (hashed) ['172faabf6a...'] => true + - IF User.Country IS ONE OF (hashed) [<1 hashed value>] => true AND User IS NOT IN SEGMENT 'Beta Users' ( Evaluating segment 'Beta Users': - - IF User.Email IS ONE OF (hashed) ['53b705ed36...', '9a043335df...'] => false, skipping the remaining AND conditions + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] => false, skipping the remaining AND conditions Segment evaluation result: User IS NOT IN SEGMENT. Condition (User IS NOT IN SEGMENT 'Beta Users') evaluates to true. ) => true AND User IS NOT IN SEGMENT 'Developers' ( Evaluating segment 'Developers': - - IF User.Email IS ONE OF (hashed) ['242f9fc710...', 'b2f917f062...'] => false, skipping the remaining AND conditions + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] => false, skipping the remaining AND conditions Segment evaluation result: User IS NOT IN SEGMENT. Condition (User IS NOT IN SEGMENT 'Developers') evaluates to true. ) => true diff --git a/configcatclienttests/data/evaluation/segment/segment_matching.txt b/configcatclienttests/data/evaluation/segment/segment_matching.txt index 03137a7..a8e6fe3 100644 --- a/configcatclienttests/data/evaluation/segment/segment_matching.txt +++ b/configcatclienttests/data/evaluation/segment/segment_matching.txt @@ -3,7 +3,7 @@ INFO [5000] Evaluating 'featureWithSegmentTargeting' for User '{"Identifier":"12 - IF User IS IN SEGMENT 'Beta users' ( Evaluating segment 'Beta users': - - IF User.Email IS ONE OF (hashed) ['26fc71b9ce...', 'daaa967a93...'] => true + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] => true Segment evaluation result: User IS IN SEGMENT. Condition (User IS IN SEGMENT 'Beta users') evaluates to true. ) diff --git a/configcatclienttests/data/evaluation/segment/segment_no_matching.txt b/configcatclienttests/data/evaluation/segment/segment_no_matching.txt index f044ba0..830fcac 100644 --- a/configcatclienttests/data/evaluation/segment/segment_no_matching.txt +++ b/configcatclienttests/data/evaluation/segment/segment_no_matching.txt @@ -3,7 +3,7 @@ INFO [5000] Evaluating 'featureWithNegatedSegmentTargeting' for User '{"Identifi - IF User IS NOT IN SEGMENT 'Beta users' ( Evaluating segment 'Beta users': - - IF User.Email IS ONE OF (hashed) ['26fc71b9ce...', 'daaa967a93...'] => true + - IF User.Email IS ONE OF (hashed) [<2 hashed values>] => true Segment evaluation result: User IS IN SEGMENT. Condition (User IS NOT IN SEGMENT 'Beta users') evaluates to false. ) diff --git a/configcatclienttests/test_evaluationlog.py b/configcatclienttests/test_evaluationlog.py index c6c4960..c88e249 100644 --- a/configcatclienttests/test_evaluationlog.py +++ b/configcatclienttests/test_evaluationlog.py @@ -3,6 +3,9 @@ import os import unittest import re + +from configcatclient.evaluationlogbuilder import EvaluationLogBuilder + try: from cStringIO import StringIO # Python 2.7 except ImportError: @@ -65,6 +68,16 @@ def test_epoch_date_validation(self): def test_number_validation(self): self.assertTrue(self._evaluation_log('data/evaluation/number_validation.json')) + def test_list_truncation(self): + self.assertEqual('[<1 hashed value>]', EvaluationLogBuilder.trunc_comparison_value_if_needed('(hashed)', ['1'])) + self.assertEqual('[<4 hashed values>]', EvaluationLogBuilder.trunc_comparison_value_if_needed('(hashed)', ['1', '2', '3', '4'])) + self.assertEqual("['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']", + EvaluationLogBuilder.trunc_comparison_value_if_needed('', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'])) + self.assertEqual("['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', ... (1 more value)]", + EvaluationLogBuilder.trunc_comparison_value_if_needed('', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'])) + self.assertEqual("['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', ... (2 more values)]", + EvaluationLogBuilder.trunc_comparison_value_if_needed('', ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'])) + def _evaluation_log(self, file_path, test_filter=None, generate_expected_log=False): script_dir = os.path.dirname(__file__) file_path = os.path.join(script_dir, file_path)