From c45244aa1385e69c54ce464676fe0c207490fc30 Mon Sep 17 00:00:00 2001 From: Nicolas Zelaya Date: Fri, 3 Nov 2023 12:31:08 -0300 Subject: [PATCH 01/10] New mock for flagsets --- src/__tests__/mocks/redis-commands-sets.txt | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/__tests__/mocks/redis-commands-sets.txt diff --git a/src/__tests__/mocks/redis-commands-sets.txt b/src/__tests__/mocks/redis-commands-sets.txt new file mode 100644 index 000000000..cf425bce6 --- /dev/null +++ b/src/__tests__/mocks/redis-commands-sets.txt @@ -0,0 +1,45 @@ +FLUSHDB +DEL 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT' +SADD 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT' UT_Segment_member +SET 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT.till' 1492721958710 +DEL 'REDIS_NODE_UT.SPLITIO.segment.demo' +SADD 'REDIS_NODE_UT.SPLITIO.segment.demo' nico +SET 'REDIS_NODE_UT.SPLITIO.segment.demo.till' 1489607057740 +DEL 'REDIS_NODE_UT.SPLITIO.segment.qa' +SADD 'REDIS_NODE_UT.SPLITIO.segment.qa' qa-user +SET 'REDIS_NODE_UT.SPLITIO.segment.qa.till' 1488406856133 +DEL 'REDIS_NODE_UT.SPLITIO.segment.qc' +SADD 'REDIS_NODE_UT.SPLITIO.segment.qc' qc-user +SET 'REDIS_NODE_UT.SPLITIO.segment.qc.till' 1484051547045 +DEL 'REDIS_NODE_UT.SPLITIO.segments.registered' +SADD 'REDIS_NODE_UT.SPLITIO.segments.registered' qa demo qc UT_SEGMENT testing_traffic_type +SET 'REDIS_NODE_UT.SPLITIO.split.UT_IN_SEGMENT' '{"changeNumber":1492722104980,"trafficTypeName":"machine","name":"UT_IN_SEGMENT","seed":-202209840,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"UT_SEGMENT"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted segment"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.UT_NOT_IN_SEGMENT' '{"changeNumber":1492722747908,"trafficTypeName":"machine","name":"UT_NOT_IN_SEGMENT","seed":-56653132,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":true,"userDefinedSegmentMatcherData":{"segmentName":"UT_SEGMENT"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"not in segment UT_SEGMENT"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.UT_NOT_SET_MATCHER' '{"changeNumber":1492723024413,"trafficTypeName":"machine","name":"UT_NOT_SET_MATCHER","seed":-93553840,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":"permissions"},"matcherType":"CONTAINS_ANY_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["create","delete","update"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"permissions does not contain any of [create, delete, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.UT_SET_MATCHER' '{"changeNumber":1492722926004,"trafficTypeName":"machine","name":"UT_SET_MATCHER","seed":-1995997836,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":"permissions"},"matcherType":"CONTAINS_ANY_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["admin","premium","idol"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"permissions contains any of [admin, premium, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.always-off' '{"changeNumber":1491519038393,"trafficTypeName":"user","name":"always-off","seed":1952026922,"sets":["set_one","set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.always-on' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-on","seed":1684183541,"sets":["set_one"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.always-o.n-with-config' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-o.n-with-config","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"o.n","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"configurations":{"o.n":"{\"color\":\"brown\"}"}}' +SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_off' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_off","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["off"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on_negated' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.labels' '{"changeNumber":1492023661334,"trafficTypeName":"user","name":"labels","seed":-1240661267,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"demo"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":"n"},"matcherType":"EQUAL_TO","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"NUMBER","value":123},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment demo and n = 123"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.nico_not' '{"changeNumber":1489412422181,"trafficTypeName":"user","name":"nico_not","seed":-788702424,"sets":["set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment qa"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_part_of' '{"changeNumber":1492627582227,"trafficTypeName":"user","name":"not_part_of","seed":-1643575289,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"PART_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx not part of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_all' '{"changeNumber":1492626848560,"trafficTypeName":"user","name":"not_set_contain_all","seed":-1811083551,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ALL_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx does not contain all of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_any' '{"changeNumber":1492627144346,"trafficTypeName":"user","name":"not_set_contain_any","seed":-430318598,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ANY_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx does not contain any of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.not_set_is_equal_to' '{"changeNumber":1492629855568,"trafficTypeName":"user","name":"not_set_is_equal_to","seed":-1042120204,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"EQUAL_TO_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx does not exactly match [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.on-if-in-segment-qa' '{"changeNumber":1484050906786,"trafficTypeName":"user","name":"on-if-in-segment-qa","seed":2023627546,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment qa"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.on-if-in-segment-qc' '{"changeNumber":1484051573721,"trafficTypeName":"user","name":"on-if-in-segment-qc","seed":1996584605,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qc"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment qc"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.part_of' '{"changeNumber":1492627833215,"trafficTypeName":"user","name":"part_of","seed":1985865328,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"PART_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx part of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.set_contain_all' '{"changeNumber":1492627890510,"trafficTypeName":"user","name":"set_contain_all","seed":-1731963136,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ALL_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx contains all of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.set_contain_any' '{"changeNumber":1492627933473,"trafficTypeName":"user","name":"set_contain_any","seed":-1267403715,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ANY_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx contains any of [a, b, ...]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.string_matchers' '{"changeNumber":1492541823531,"trafficTypeName":"user","name":"string_matchers","seed":1037479690,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"st"},"matcherType":"STARTS_WITH","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"st starts with [a, b]"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"en"},"matcherType":"ENDS_WITH","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["ends","another"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"en ends with [ends, another]"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"con"},"matcherType":"CONTAINS_STRING","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"con contains [a, b]"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_type' '{"changeNumber":1489607495199,"trafficTypeName":"machine","name":"testing_traffic_type","seed":1825443152,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment testing_traffic_type"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_types' '{"changeNumber":1490974465415,"trafficTypeName":"machine","name":"testing_traffic_types","seed":475616886,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["sarasa"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["excluded"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"off","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment testing_traffic_type"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.traffic_allocation_testing' '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_one' always-on +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_one' always-off +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_two' always-off +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_two' nico_not +SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413 From d13fdcc3544beb76cb204baae2852792192c6444 Mon Sep 17 00:00:00 2001 From: Nicolas Zelaya Date: Fri, 3 Nov 2023 12:31:38 -0300 Subject: [PATCH 02/10] Adding redis tests part1 for flagsets, this wont work without commons --- src/__tests__/consumer/node_redis.spec.js | 67 ++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 41d1a15af..0af9822dc 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -56,18 +56,24 @@ const expectedImpressionCount = [ `hierarchical_splits_testing_on_negated::${truncateTimeFrame(timeFrame)}`, '1', ]; +const MOCKS = { + '': 'redis-commands', + 'flag_sets': 'redis-commands-sets' +}; + /** * Initialize redis server and run a cli bash command to load redis with data to do the proper tests */ -const initializeRedisServer = () => { +const initializeRedisServer = (mock = '') => { // Simply pass the port that you want a Redis server to listen on. const server = new RedisServer(redisPort); + const mockFileName = MOCKS[mock]; const promise = new Promise((resolve, reject) => { server .open() .then(() => { - exec(`cat ./src/__tests__/mocks/redis-commands.txt | redis-cli -p ${redisPort}`, err => { + exec(`cat ./src/__tests__/mocks/${mockFileName}.txt | redis-cli -p ${redisPort}`, err => { if (err) { reject(server); // node couldn't execute the command @@ -632,4 +638,61 @@ tape('NodeJS Redis', function (t) { server.close().then(assert.end); }); }); + + t.test('Getting treatments with flag sets', assert => { + initializeRedisServer('flag_sets') + .then(async (server) => { + const sdk = SplitFactory(config); + + const client = sdk.client(); + await client.ready(); + + // @TODO: Working to remove this crap, debugging RedisAdapter queueing and why it doesnt like the pipeline exec. + setTimeout(async function () { + + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_one'), + { 'always-on': 'on', 'always-off': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_two'), + { 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_invalid'), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets, discarding invalids.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'invalid_set']), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); + + await client.ready(); // promise already resolved + await client.destroy(); + + // close server connection + server.close().then(assert.end); + },1000); + }); + }); }); From ddeef834f14f4613c41255306946fca47bb100b5 Mon Sep 17 00:00:00 2001 From: Nicolas Zelaya Date: Fri, 3 Nov 2023 16:37:18 -0300 Subject: [PATCH 03/10] add tests with configs and telemetry latency validations --- src/__tests__/consumer/node_redis.spec.js | 28 +++++++++++++++++++-- src/__tests__/mocks/redis-commands-sets.txt | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 0af9822dc..72059e736 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -657,6 +657,12 @@ tape('NodeJS Redis', function (t) { 'Evaluations using Redis storage should be correct for a set.' ); + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSet('nico@test', 'set_one'), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null } }, + 'Evaluations with configs using Redis storage should be correct for a set.' + ); + assert.deepEqual( await client.getTreatmentsByFlagSet('nico@test', 'set_two'), { 'always-off': 'off', 'nico_not': 'off' }, @@ -675,6 +681,12 @@ tape('NodeJS Redis', function (t) { 'Evaluations using Redis storage should be correct for multiple sets.' ); + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null }, 'nico_not': { treatment: 'off', config: '{"text":"Gallardiola"}' } }, + 'Evaluations with configs using Redis storage should be correct for multiple sets.' + ); + assert.deepEqual( await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, @@ -690,8 +702,20 @@ tape('NodeJS Redis', function (t) { await client.ready(); // promise already resolved await client.destroy(); - // close server connection - server.close().then(assert.end); + // Validate stored telemetry + exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HKEYS ${config.storage.prefix}.SPLITIO.telemetry.latencies" | redis-cli -p ${redisPort}`, (error, stdout) => { + if (error) assert.fail('Redis server should be reachable'); + + const [latenciesCount, ...latenciesForFlagSets] = stdout.split('\n').filter(line => line !== ''); + + assert.true(parseInt(latenciesCount) > 0, 'There are stored latencies'); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSet/`), 'The latency entry for treatmentsByFlagSet should be there.')); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSets/`), 'The latency entry for treatmentsByFlagSets should be there.')); + + // close server connection + server.close().then(assert.end); + }); + },1000); }); }); diff --git a/src/__tests__/mocks/redis-commands-sets.txt b/src/__tests__/mocks/redis-commands-sets.txt index cf425bce6..fa0397a52 100644 --- a/src/__tests__/mocks/redis-commands-sets.txt +++ b/src/__tests__/mocks/redis-commands-sets.txt @@ -24,7 +24,7 @@ SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_off' '{"changeN SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.hierarchical_splits_testing_on_negated' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"hierarchical_splits_testing_on","seed":1684183541,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"dependencyMatcherData":{"split":"always-on","treatments":["on"]},"matcherType":"IN_SPLIT_TREATMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":null,"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.labels' '{"changeNumber":1492023661334,"trafficTypeName":"user","name":"labels","seed":-1240661267,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"demo"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"user","attribute":"n"},"matcherType":"EQUAL_TO","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"NUMBER","value":123},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment demo and n = 123"}]}' -SET 'REDIS_NODE_UT.SPLITIO.split.nico_not' '{"changeNumber":1489412422181,"trafficTypeName":"user","name":"nico_not","seed":-788702424,"sets":["set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment qa"}]}' +SET 'REDIS_NODE_UT.SPLITIO.split.nico_not' '{"changeNumber":1489412422181,"trafficTypeName":"user","name":"nico_not","seed":-788702424,"sets":["set_two"],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"qa"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment qa"}],"configurations":{"off":"{\"text\":\"Gallardiola\"}"}}' SET 'REDIS_NODE_UT.SPLITIO.split.not_part_of' '{"changeNumber":1492627582227,"trafficTypeName":"user","name":"not_part_of","seed":-1643575289,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"PART_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx not part of [a, b, ...]"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_all' '{"changeNumber":1492626848560,"trafficTypeName":"user","name":"not_set_contain_all","seed":-1811083551,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ALL_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"setx does not contain all of [a, b, ...]"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.not_set_contain_any' '{"changeNumber":1492627144346,"trafficTypeName":"user","name":"not_set_contain_any","seed":-430318598,"sets":[],"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":"setx"},"matcherType":"CONTAINS_ANY_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["a","b","c"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"setx does not contain any of [a, b, ...]"}]}' From e58c8f970b1edf4db193cc0d5e699ac4d7c0f985 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 27 Nov 2023 15:50:25 -0300 Subject: [PATCH 04/10] Update E2E tests to validate flag sets support in consumer mode --- CHANGES.txt | 6 ++- package-lock.json | 14 +++---- package.json | 2 +- src/__tests__/consumer/node_redis.spec.js | 46 +++++++++++++++-------- src/__tests__/mocks/redis-commands.txt | 5 +++ types/splitio.d.ts | 2 - 6 files changed, 49 insertions(+), 26 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 4bdf958e1..3457d5246 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +10.25.0 (December XX, 2023) + - Added support for Flag Sets in "consumer" mode (Redis storage). + - Updated @splitsoftware/splitio-commons package to version 1.12.0 that includes flag sets support for "consumer" and "partial consumer" modes. + 10.24.0 (November XX, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. @@ -5,7 +9,7 @@ - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. - - Updated the following SDK manager methods to expose flag sets on flag views. + - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - Updated @splitsoftware/splitio-commons package to version 1.11.0 that includes vulnerability fixes, and adds the `defaultTreatment` property to the `SplitView` object. diff --git a/package-lock.json b/package-lock.json index 03895a6dc..15c084ac4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.24.0-beta", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", - "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", + "version": "1.12.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", + "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.10.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.10.1-rc.3.tgz", - "integrity": "sha512-eqJxAMtqFK7fXFKL8gMGfRsMBdxrYI9tIGUHHpY1NcyeKkn4OWqAOZMhX6z2qLdBArzHi34Li0Lb72o+Bh1Tqg==", + "version": "1.12.1-rc.1", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", + "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index a2b693e0e..7f28565df 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.10.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.1", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 41d1a15af..1516eee46 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -20,9 +20,13 @@ import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time const IP_VALUE = ipFunction.address(); const HOSTNAME_VALUE = osFunction.hostname(); const NA = 'NA'; - const redisPort = '6385'; +const TOTAL_RAW_IMPRESSIONS = 16; +const TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS = 10; +const TOTAL_EVENTS = 2; +const DEDUPED_IMPRESSIONS = 3; + const config = { core: { authorizationKey: 'SOME SDK KEY' // in consumer mode, SDK key is only used to track and log warning regarding duplicated SDK instances @@ -92,6 +96,8 @@ tape('NodeJS Redis', function (t) { assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); + assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a']), {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.'); + await client.ready(); assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); @@ -135,8 +141,18 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); + + // Evaluations with flag sets + assert.deepEqual(await client.getTreatmentsByFlagSet('other', 'set_a'), { with_set_a: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSet should be correct.'); + assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['set_a', 'set_b']), { with_set_a: 'on', with_set_b: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSets should be correct.'); + assert.deepEqual(await client.getTreatmentsWithConfigByFlagSet('other', 'set_b'), { with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSet should be correct.'); + assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a', 'set_b']), { with_set_a: { treatment: 'on', config: null }, with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSets should be correct.'); + + assert.deepEqual(await client.getTreatmentsByFlagSet('other'), {}, 'Evaluations without flag set should be empty.'); + assert.deepEqual(await client.getTreatmentsByFlagSets('other', []), {}, 'Evaluations without flag sets should be empty.'); + assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['non_existent_set']), {}, 'Evaluations with non existent flag sets should be empty.'); await client.ready(); // promise already resolved await client.destroy(); @@ -146,7 +162,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [16, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS + TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -253,8 +269,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -269,7 +285,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [13, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS - DEDUPED_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -349,8 +365,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -371,7 +387,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [0, 2], 'No impressions are stored in Redis in NONE impressions mode'); + assert.deepEqual(trackedImpressionsAndEvents, [0, TOTAL_EVENTS], 'No impressions are stored in Redis in NONE impressions mode'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -409,7 +425,7 @@ tape('NodeJS Redis', function (t) { assert.equal(treatment, 'control', 'Evaluations using Redis storage should be control until Redis connection is stablished.'); }); client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then(result => { - assert.true(result, 'If the event was succesfully queued the promise will resolve to true once Redis connection is stablished'); + assert.true(result, 'If the event was successfully queued the promise will resolve to true once Redis connection is stablished'); }); // SDK_READY_TIMED_OUT event must be emitted after 100 millis @@ -448,8 +464,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.destroy(); assert.pass(); @@ -474,8 +490,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client2.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client2.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client2.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client2.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client2.destroy(); diff --git a/src/__tests__/mocks/redis-commands.txt b/src/__tests__/mocks/redis-commands.txt index 2e5daae42..00c31fa61 100644 --- a/src/__tests__/mocks/redis-commands.txt +++ b/src/__tests__/mocks/redis-commands.txt @@ -39,3 +39,8 @@ SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_type' '{"changeNumber":1489 SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_types' '{"changeNumber":1490974465415,"trafficTypeName":"machine","name":"testing_traffic_types","seed":475616886,"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["sarasa"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["excluded"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"off","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment testing_traffic_type"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.traffic_allocation_testing' '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413 +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_a' with_set_a with_sets_a_b +SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_b' with_set_b with_sets_a_b +SET 'REDIS_NODE_UT.SPLITIO.split.with_set_a' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a"]}' +SET 'REDIS_NODE_UT.SPLITIO.split.with_set_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_b","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_b"],"configurations":{"on":"{}"}}' +SET 'REDIS_NODE_UT.SPLITIO.split.with_sets_a_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a","set_b"]}' \ No newline at end of file diff --git a/types/splitio.d.ts b/types/splitio.d.ts index c01f999c7..6006ccb7b 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -223,8 +223,6 @@ interface ISharedSettings { * List of feature flag filters. These filters are used to fetch a subset of the feature flag definitions in your environment, in order to reduce the delay of the SDK to be ready. * This configuration is only meaningful when the SDK is working in "standalone" mode. * - * At the moment, only one type of feature flag filter is supported: by name. - * * Example: * `splitFilter: [ * { type: 'byName', values: ['my_feature_flag_1', 'my_feature_flag_2'] }, // will fetch feature flags named 'my_feature_flag_1' and 'my_feature_flag_2' From 3cd341f17c7e2bfc1ccdc846aced53f7c76e9edb Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 29 Nov 2023 17:25:42 -0300 Subject: [PATCH 05/10] Add manager method asserts for consumer mode with Redis --- CHANGES.txt | 14 +++++------ package-lock.json | 14 +++++------ package.json | 2 +- src/__tests__/consumer/node_redis.spec.js | 29 +++++++++++++++++++---- 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 182f96ac2..c817d63fb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,11 +1,11 @@ 10.24.0 (December XX, 2023) - - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - - getTreatmentsByFlagSet and getTreatmentsByFlagSets - - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets - - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. - - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. - - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. + - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): + - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. + - getTreatmentsByFlagSet and getTreatmentsByFlagSets + - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets + - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. + - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. + - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - Updated @splitsoftware/splitio-commons package to version 1.12.0 that includes vulnerability fixes, flag sets support, and adds the `defaultTreatment` property to the `SplitView` object. - Bugfixing - Fixed SDK key validation in NodeJS to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768). diff --git a/package-lock.json b/package-lock.json index 250edddd1..2d7a1e6df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "10.24.0-beta", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.1", + "@splitsoftware/splitio-commons": "1.12.1-rc.2", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", - "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", + "version": "1.12.1-rc.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", + "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.1.tgz", - "integrity": "sha512-nmMFwCMAJ+WujVEfdf1TwVF/e8cqkjYUJTp0Mb+XWK7S0ZUItf4hzXhC1DOQRntBTH5CDxnL6hdkuACEWhfYEw==", + "version": "1.12.1-rc.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", + "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 7f28565df..710d8f88a 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.1", + "@splitsoftware/splitio-commons": "1.12.1-rc.2", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 1516eee46..6f96a48ea 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -60,6 +60,9 @@ const expectedImpressionCount = [ `hierarchical_splits_testing_on_negated::${truncateTimeFrame(timeFrame)}`, '1', ]; +const expectedSplitName = 'hierarchical_splits_testing_on'; +const expectedSplitView = { name: 'hierarchical_splits_testing_on', trafficType: 'user', killed: false, changeNumber: 1487277320548, treatments: ['on', 'off'], configs: {}, sets: [], defaultTreatment: 'off' }; + /** * Initialize redis server and run a cli bash command to load redis with data to do the proper tests */ @@ -93,13 +96,23 @@ tape('NodeJS Redis', function (t) { .then(async (server) => { const sdk = SplitFactory(config); const client = sdk.client(); + const manager = sdk.manager(); + + /** Evaluation, track and manager methods before SDK_READY */ + client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); + client.getTreatment('other', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); + client.getTreatmentsWithConfigByFlagSets('other', ['set_a']).then(result => assert.deepEqual(result, {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.')); + + manager.names().then((result) => assert.deepEqual(result, [], 'manager `names` method returns an empty list of split names if called before SDK_READY or Redis operation fail')); + manager.split(expectedSplitName).then((result) => assert.deepEqual(result, null, 'manager `split` method returns a null split view if called before SDK_READY or Redis operation fail')); + manager.splits().then((result) => assert.deepEqual(result, [], 'manager `splits` method returns an empty list of split views if called before SDK_READY or Redis operation fail')); - assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); - assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'control', 'Evaluations using Redis storage should be control until connection is stablished.'); - assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a']), {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.'); + client.track('nicolas@split.io', 'user', 'before.ready', 18).then((result) => assert.true(result, 'Redis adapter queue "rpush" operations until it is ready.')); await client.ready(); + /** Evaluation, track and manager methods on SDK_READY */ + assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); @@ -154,6 +167,15 @@ tape('NodeJS Redis', function (t) { assert.deepEqual(await client.getTreatmentsByFlagSets('other', []), {}, 'Evaluations without flag sets should be empty.'); assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['non_existent_set']), {}, 'Evaluations with non existent flag sets should be empty.'); + // Manager methods + const splitNames = await manager.names(); + assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); + assert.equal(splitNames.indexOf(expectedSplitName) > -1, true, 'list of split names should contain expected splits'); + assert.deepEqual(await manager.split(expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); + const splitViews = await manager.splits(); + assert.equal(splitViews.length, 28, 'manager `splits` method returns the list of split views asynchronously'); + assert.deepEqual(splitViews.find(splitView => splitView.name === expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); + await client.ready(); // promise already resolved await client.destroy(); @@ -266,7 +288,6 @@ tape('NodeJS Redis', function (t) { // this should be deduped assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); From 9fee387fdcd9ec929f325dcf858dbcf92040cfaf Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 30 Nov 2023 17:41:15 -0300 Subject: [PATCH 06/10] Updates --- CHANGES.txt | 11 +- src/__tests__/consumer/node_redis.spec.js | 162 +++++++++--------- ...-sets.txt => redis-commands-flag-sets.txt} | 0 types/splitio.d.ts | 2 - 4 files changed, 88 insertions(+), 87 deletions(-) rename src/__tests__/mocks/{redis-commands-sets.txt => redis-commands-flag-sets.txt} (100%) diff --git a/CHANGES.txt b/CHANGES.txt index 0b8e3197b..87e4076e3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,14 +1,15 @@ -10.24.0 (November XX, 2023) - - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): +10.24.0 (December XX, 2023) + - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - getTreatmentsByFlagSet and getTreatmentsByFlagSets - getTreatmentsWithConfigByFlagSets and getTreatmentsWithConfigByFlagSets - Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload. - Note: Only applicable when the SDK is in charge of the rollout data synchronization. When not applicable, the SDK will log a warning on init. - - Updated the following SDK manager methods to expose flag sets on flag views. + - Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views. - Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225). - - Updated @splitsoftware/splitio-commons package to version 1.11.0 that includes vulnerability fixes, and adds the `defaultTreatment` property to the `SplitView` object. - - Bugfixing - Fixed SDK key validation in NodeJS to ensure the SDK_READY_TIMED_OUT event is emitted when a client-side type SDK key is provided instead of a server-side one (Related to issue https://github.com/splitio/javascript-client/issues/768). + - Updated @splitsoftware/splitio-commons package to version 1.12.0 that includes vulnerability fixes, flag sets support, and other improvements. + - Updated Redis adapter to handle timeouts and queueing of some missing commands: 'hincrby', 'popNRaw', and 'pipeline.exec'. + - Bugfixing - Fixed manager methods in consumer modes to return results in a promise when the SDK is not operational (not ready or destroyed). 10.23.1 (September 22, 2023) - Updated @splitsoftware/splitio-commons package to version 1.9.1. This update removes the handler for 'unload' DOM events, that can prevent browsers from being able to put pages in the back/forward cache for faster back and forward loads (Related to issue https://github.com/splitio/javascript-client/issues/759). diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 72059e736..74f233da0 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -23,6 +23,10 @@ const NA = 'NA'; const redisPort = '6385'; +const TOTAL_RAW_IMPRESSIONS = 16; +const TOTAL_EVENTS = 2; +const DEDUPED_IMPRESSIONS = 3; + const config = { core: { authorizationKey: 'SOME SDK KEY' // in consumer mode, SDK key is only used to track and log warning regarding duplicated SDK instances @@ -58,7 +62,7 @@ const expectedImpressionCount = [ const MOCKS = { '': 'redis-commands', - 'flag_sets': 'redis-commands-sets' + 'flag_sets': 'redis-commands-flag-sets' }; /** @@ -141,8 +145,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -152,7 +156,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [16, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -259,8 +263,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -275,7 +279,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [13, 2], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS - DEDUPED_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -355,8 +359,8 @@ tape('NodeJS Redis', function (t) { assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.ready(); // promise already resolved await client.destroy(); @@ -377,7 +381,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [0, 2], 'No impressions are stored in Redis in NONE impressions mode'); + assert.deepEqual(trackedImpressionsAndEvents, [0, TOTAL_EVENTS], 'No impressions are stored in Redis in NONE impressions mode'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { @@ -415,7 +419,7 @@ tape('NodeJS Redis', function (t) { assert.equal(treatment, 'control', 'Evaluations using Redis storage should be control until Redis connection is stablished.'); }); client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then(result => { - assert.true(result, 'If the event was succesfully queued the promise will resolve to true once Redis connection is stablished'); + assert.true(result, 'If the event was successfully queued the promise will resolve to true once Redis connection is stablished'); }); // SDK_READY_TIMED_OUT event must be emitted after 100 millis @@ -454,8 +458,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client.destroy(); assert.pass(); @@ -480,8 +484,8 @@ tape('NodeJS Redis', function (t) { // some asserts to test regular usage assert.equal(await client2.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using Redis storage should be correct.'); assert.equal(await client2.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true'); - assert.false(await client2.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); + assert.false(await client2.track(), 'If the event was NOT successfully queued the promise will resolve to false'); await client2.destroy(); @@ -645,78 +649,76 @@ tape('NodeJS Redis', function (t) { const sdk = SplitFactory(config); const client = sdk.client(); + + client.getTreatmentsWithConfigByFlagSets('other', ['set_one']).then(result => assert.deepEqual(result, {}, 'Flag sets evaluations using Redis storage should be empty until connection is ready.')); + await client.ready(); - // @TODO: Working to remove this crap, debugging RedisAdapter queueing and why it doesnt like the pipeline exec. - setTimeout(async function () { - - - assert.deepEqual( - await client.getTreatmentsByFlagSet('nico@test', 'set_one'), - { 'always-on': 'on', 'always-off': 'off' }, - 'Evaluations using Redis storage should be correct for a set.' - ); - - assert.deepEqual( - await client.getTreatmentsWithConfigByFlagSet('nico@test', 'set_one'), - { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null } }, - 'Evaluations with configs using Redis storage should be correct for a set.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSet('nico@test', 'set_two'), - { 'always-off': 'off', 'nico_not': 'off' }, - 'Evaluations using Redis storage should be correct for a set.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSet('nico@test', 'set_invalid'), - {}, - 'Evaluations using Redis storage should properly handle all invalid sets.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSets('nico@test', ['set_two', 'set_one']), - { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, - 'Evaluations using Redis storage should be correct for multiple sets.' - ); - - assert.deepEqual( - await client.getTreatmentsWithConfigByFlagSets('nico@test', ['set_two', 'set_one']), - { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null }, 'nico_not': { treatment: 'off', config: '{"text":"Gallardiola"}' } }, - 'Evaluations with configs using Redis storage should be correct for multiple sets.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), - { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, - 'Evaluations using Redis storage should be correct for multiple sets, discarding invalids.' - ); - - assert.deepEqual( - await client.getTreatmentsByFlagSets('nico@test', [243, null, 'invalid_set']), - {}, - 'Evaluations using Redis storage should properly handle all invalid sets.' - ); - - await client.ready(); // promise already resolved - await client.destroy(); + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_one'), + { 'always-on': 'on', 'always-off': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSet('nico@test', 'set_one'), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null } }, + 'Evaluations with configs using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_two'), + { 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for a set.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSet('nico@test', 'set_invalid'), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets.' + ); + + assert.deepEqual( + await client.getTreatmentsWithConfigByFlagSets('nico@test', ['set_two', 'set_one']), + { 'always-on': { treatment: 'on', config: null }, 'always-off': { treatment: 'off', config: null }, 'nico_not': { treatment: 'off', config: '{"text":"Gallardiola"}' } }, + 'Evaluations with configs using Redis storage should be correct for multiple sets.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'set_two', 'set_one', 'invalid_set']), + { 'always-on': 'on', 'always-off': 'off', 'nico_not': 'off' }, + 'Evaluations using Redis storage should be correct for multiple sets, discarding invalids.' + ); + + assert.deepEqual( + await client.getTreatmentsByFlagSets('nico@test', [243, null, 'invalid_set']), + {}, + 'Evaluations using Redis storage should properly handle all invalid sets.' + ); - // Validate stored telemetry - exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HKEYS ${config.storage.prefix}.SPLITIO.telemetry.latencies" | redis-cli -p ${redisPort}`, (error, stdout) => { - if (error) assert.fail('Redis server should be reachable'); + await client.ready(); // promise already resolved + await client.destroy(); - const [latenciesCount, ...latenciesForFlagSets] = stdout.split('\n').filter(line => line !== ''); + // Validate stored telemetry + exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HKEYS ${config.storage.prefix}.SPLITIO.telemetry.latencies" | redis-cli -p ${redisPort}`, (error, stdout) => { + if (error) assert.fail('Redis server should be reachable'); - assert.true(parseInt(latenciesCount) > 0, 'There are stored latencies'); - assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSet/`), 'The latency entry for treatmentsByFlagSet should be there.')); - assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSets/`), 'The latency entry for treatmentsByFlagSets should be there.')); + const [latenciesCount, ...latenciesForFlagSets] = stdout.split('\n').filter(line => line !== ''); - // close server connection - server.close().then(assert.end); - }); + assert.true(parseInt(latenciesCount) > 0, 'There are stored latencies'); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSet/`), 'The latency entry for treatmentsByFlagSet should be there.')); + assert.true(latenciesForFlagSets.some(s => s.match(`nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}/treatmentsByFlagSets/`), 'The latency entry for treatmentsByFlagSets should be there.')); + + // close server connection + server.close().then(assert.end); + }); - },1000); }); }); }); diff --git a/src/__tests__/mocks/redis-commands-sets.txt b/src/__tests__/mocks/redis-commands-flag-sets.txt similarity index 100% rename from src/__tests__/mocks/redis-commands-sets.txt rename to src/__tests__/mocks/redis-commands-flag-sets.txt diff --git a/types/splitio.d.ts b/types/splitio.d.ts index c01f999c7..6006ccb7b 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -223,8 +223,6 @@ interface ISharedSettings { * List of feature flag filters. These filters are used to fetch a subset of the feature flag definitions in your environment, in order to reduce the delay of the SDK to be ready. * This configuration is only meaningful when the SDK is working in "standalone" mode. * - * At the moment, only one type of feature flag filter is supported: by name. - * * Example: * `splitFilter: [ * { type: 'byName', values: ['my_feature_flag_1', 'my_feature_flag_2'] }, // will fetch feature flags named 'my_feature_flag_1' and 'my_feature_flag_2' From 7c4eb1bbed99274811e976525b409e821c028e39 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 30 Nov 2023 17:46:14 -0300 Subject: [PATCH 07/10] Revert validation of flag sets evaluation, to merge with branch sdks-7658 --- src/__tests__/consumer/node_redis.spec.js | 15 ++------------- src/__tests__/mocks/redis-commands.txt | 5 ----- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 6f96a48ea..6c44deb67 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -20,10 +20,10 @@ import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time const IP_VALUE = ipFunction.address(); const HOSTNAME_VALUE = osFunction.hostname(); const NA = 'NA'; + const redisPort = '6385'; const TOTAL_RAW_IMPRESSIONS = 16; -const TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS = 10; const TOTAL_EVENTS = 2; const DEDUPED_IMPRESSIONS = 3; @@ -101,7 +101,6 @@ tape('NodeJS Redis', function (t) { /** Evaluation, track and manager methods before SDK_READY */ client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); client.getTreatment('other', 'UT_IN_SEGMENT').then(result => assert.equal(result, 'control', 'Evaluations using Redis storage should be control until connection is stablished.')); - client.getTreatmentsWithConfigByFlagSets('other', ['set_a']).then(result => assert.deepEqual(result, {}, 'Flag sets evaluations using Redis storage should be empty until connection is stablished.')); manager.names().then((result) => assert.deepEqual(result, [], 'manager `names` method returns an empty list of split names if called before SDK_READY or Redis operation fail')); manager.split(expectedSplitName).then((result) => assert.deepEqual(result, null, 'manager `split` method returns a null split view if called before SDK_READY or Redis operation fail')); @@ -157,16 +156,6 @@ tape('NodeJS Redis', function (t) { assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); assert.false(await client.track(), 'If the event was NOT successfully queued the promise will resolve to false'); - // Evaluations with flag sets - assert.deepEqual(await client.getTreatmentsByFlagSet('other', 'set_a'), { with_set_a: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSet should be correct.'); - assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['set_a', 'set_b']), { with_set_a: 'on', with_set_b: 'on', with_sets_a_b: 'on' }, 'Evaluations with getTreatmentsByFlagSets should be correct.'); - assert.deepEqual(await client.getTreatmentsWithConfigByFlagSet('other', 'set_b'), { with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSet should be correct.'); - assert.deepEqual(await client.getTreatmentsWithConfigByFlagSets('other', ['set_a', 'set_b']), { with_set_a: { treatment: 'on', config: null }, with_set_b: { treatment: 'on', config: '{}' }, with_sets_a_b: { treatment: 'on', config: null } }, 'Evaluations with getTreatmentsWithConfigByFlagSets should be correct.'); - - assert.deepEqual(await client.getTreatmentsByFlagSet('other'), {}, 'Evaluations without flag set should be empty.'); - assert.deepEqual(await client.getTreatmentsByFlagSets('other', []), {}, 'Evaluations without flag sets should be empty.'); - assert.deepEqual(await client.getTreatmentsByFlagSets('other', ['non_existent_set']), {}, 'Evaluations with non existent flag sets should be empty.'); - // Manager methods const splitNames = await manager.names(); assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); @@ -184,7 +173,7 @@ tape('NodeJS Redis', function (t) { if (error) assert.fail('Redis server should be reachable'); const trackedImpressionsAndEvents = stdout.split('\n').filter(line => line !== '').map(line => parseInt(line)); - assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS + TOTAL_RAW_IMPRESSIONS_IN_EVALUATIONS_WITH_FLAGSETS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); + assert.deepEqual(trackedImpressionsAndEvents, [TOTAL_RAW_IMPRESSIONS, TOTAL_EVENTS], 'Tracked impressions and events should be stored in Redis'); // Validate stored telemetry exec(`echo "HLEN ${config.storage.prefix}.SPLITIO.telemetry.latencies \n HLEN ${config.storage.prefix}.SPLITIO.telemetry.exceptions \n HGET ${config.storage.prefix}.SPLITIO.telemetry.init nodejs-${version}/${HOSTNAME_VALUE}/${IP_VALUE}" | redis-cli -p ${redisPort}`, (error, stdout) => { diff --git a/src/__tests__/mocks/redis-commands.txt b/src/__tests__/mocks/redis-commands.txt index 00c31fa61..2e5daae42 100644 --- a/src/__tests__/mocks/redis-commands.txt +++ b/src/__tests__/mocks/redis-commands.txt @@ -39,8 +39,3 @@ SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_type' '{"changeNumber":1489 SET 'REDIS_NODE_UT.SPLITIO.split.testing_traffic_types' '{"changeNumber":1490974465415,"trafficTypeName":"machine","name":"testing_traffic_types","seed":475616886,"status":"ACTIVE","killed":false,"defaultTreatment":"on","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["sarasa"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"WHITELIST","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["excluded"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"off","size":100}],"label":"whitelisted"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}},{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"testing_traffic_type"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all and in segment testing_traffic_type"}]}' SET 'REDIS_NODE_UT.SPLITIO.split.traffic_allocation_testing' '{"changeNumber":1490974123779,"trafficTypeName":"user","name":"traffic_allocation_testing","seed":1716284102,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}' SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413 -SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_a' with_set_a with_sets_a_b -SADD 'REDIS_NODE_UT.SPLITIO.flagSet.set_b' with_set_b with_sets_a_b -SET 'REDIS_NODE_UT.SPLITIO.split.with_set_a' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a"]}' -SET 'REDIS_NODE_UT.SPLITIO.split.with_set_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_b","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_b"],"configurations":{"on":"{}"}}' -SET 'REDIS_NODE_UT.SPLITIO.split.with_sets_a_b' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"with_set_a","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"sets":["set_a","set_b"]}' \ No newline at end of file From 550261fed07ca9320a191cdd6d05200c30098bca Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 30 Nov 2023 18:05:51 -0300 Subject: [PATCH 08/10] Fix tests and prepare rc --- .github/workflows/ci-cd.yml | 4 ++-- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/__tests__/consumer/node_redis.spec.js | 6 +++--- src/settings/defaults/version.js | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 0cc8aebaf..d27d27959 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/sdks-7437' || github.ref == 'refs/heads/master') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/sdks-7658' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/sdks-7437' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/sdks-7658' }} strategy: matrix: environment: diff --git a/package-lock.json b/package-lock.json index 2d7a1e6df..38fae3a86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-beta", + "version": "10.24.0-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.24.0-beta", + "version": "10.24.0-rc.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.2", + "@splitsoftware/splitio-commons": "1.12.1-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", - "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", + "version": "1.12.1-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", + "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.2", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.2.tgz", - "integrity": "sha512-b8wVebAbTYDLKoUwWf7hwP+xlyVlbH3hX9+kPTbQWYeHkX2JNBnbEBSM5sLlYUpwbVW6mLEd59qwjwEsa9ZD5g==", + "version": "1.12.1-rc.3", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", + "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 710d8f88a..96db0bc95 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-beta", + "version": "10.24.0-rc.0", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.2", + "@splitsoftware/splitio-commons": "1.12.1-rc.3", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/consumer/node_redis.spec.js b/src/__tests__/consumer/node_redis.spec.js index 090a3c067..cf9239ad6 100644 --- a/src/__tests__/consumer/node_redis.spec.js +++ b/src/__tests__/consumer/node_redis.spec.js @@ -156,7 +156,6 @@ tape('NodeJS Redis', function (t) { assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_off'), 'off', 'Evaluations using Redis storage should be correct.'); assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using Redis storage should be correct.'); - assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); @@ -164,11 +163,11 @@ tape('NodeJS Redis', function (t) { // Manager methods const splitNames = await manager.names(); - assert.equal(splitNames.length, 28, 'manager `names` method returns the list of split names asynchronously'); + assert.equal(splitNames.length, 25, 'manager `names` method returns the list of split names asynchronously'); assert.equal(splitNames.indexOf(expectedSplitName) > -1, true, 'list of split names should contain expected splits'); assert.deepEqual(await manager.split(expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); const splitViews = await manager.splits(); - assert.equal(splitViews.length, 28, 'manager `splits` method returns the list of split views asynchronously'); + assert.equal(splitViews.length, 25, 'manager `splits` method returns the list of split views asynchronously'); assert.deepEqual(splitViews.find(splitView => splitView.name === expectedSplitName), expectedSplitView, 'manager `split` method returns the split view of the given split name asynchronously'); await client.ready(); // promise already resolved @@ -283,6 +282,7 @@ tape('NodeJS Redis', function (t) { // this should be deduped assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using Redis storage should be correct.'); + assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Redis mode.'); assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Redis mode, even when parameters are incorrect.'); assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was successfully queued the promise will resolve to true'); diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 77bd7d677..406da3c36 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.24.0-beta'; +export const packageVersion = '10.24.0-rc.0'; From 6118ecf87403fd6a22e874e21eb4fa108e96e1e3 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 1 Dec 2023 13:32:47 -0300 Subject: [PATCH 09/10] rc with log fixes --- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/settings/defaults/version.js | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 38fae3a86..905567f0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-rc.0", + "version": "10.24.0-rc.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.24.0-rc.0", + "version": "10.24.0-rc.1", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -874,9 +874,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", - "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", + "version": "1.12.1-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.4.tgz", + "integrity": "sha512-tBn3+Vpiw7uKBPEM2H4FKXlA1Cax8/qQT6UgRY9ncDzWDkwuxubSgntopJ2f4Ax+/9BCuyDr/JKCHHc1oMUf4w==", "dependencies": { "tslib": "^2.3.1" }, @@ -8446,9 +8446,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.12.1-rc.3", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.3.tgz", - "integrity": "sha512-fnygMKAWdnsjq7XltJV2RVgQZ0kmBgYQuX9mAkWRejuU4sFbcGnX7pLV8ZzI0OFi4hn0HMbl90Yx2moP4yRu/w==", + "version": "1.12.1-rc.4", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.12.1-rc.4.tgz", + "integrity": "sha512-tBn3+Vpiw7uKBPEM2H4FKXlA1Cax8/qQT6UgRY9ncDzWDkwuxubSgntopJ2f4Ax+/9BCuyDr/JKCHHc1oMUf4w==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 96db0bc95..ef87914da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.24.0-rc.0", + "version": "10.24.0-rc.1", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.12.1-rc.3", + "@splitsoftware/splitio-commons": "1.12.1-rc.4", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 406da3c36..3ceabbd20 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.24.0-rc.0'; +export const packageVersion = '10.24.0-rc.1'; From e65903210bfc7f500c0974b95d5971062f398f61 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 4 Dec 2023 13:06:18 -0300 Subject: [PATCH 10/10] Rollback ci-cd config --- .github/workflows/ci-cd.yml | 4 ++-- CHANGES.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index d27d27959..701c3518d 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -58,7 +58,7 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build - name: Store assets - if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/sdks-7658' || github.ref == 'refs/heads/master') }} + if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || github.ref == 'refs/heads/master') }} uses: actions/upload-artifact@v3 with: name: assets @@ -69,7 +69,7 @@ jobs: name: Upload assets runs-on: ubuntu-20.04 needs: build - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/sdks-7658' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} strategy: matrix: environment: diff --git a/CHANGES.txt b/CHANGES.txt index 9d367d7ca..660656038 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -10.24.0 (December XX, 2023) +10.24.0 (December 4, 2023) - Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation): - Added new variations of the get treatment methods to support evaluating flags in given flag set/s. - getTreatmentsByFlagSet and getTreatmentsByFlagSets