splitclient-rb 6.3.0 → 8.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/pull_request_template.md +9 -0
- data/.github/workflows/ci.yml +90 -0
- data/.github/workflows/update-license-year.yml +45 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +46 -3
- data/CHANGES.txt +158 -11
- data/CONTRIBUTORS-GUIDE.md +49 -0
- data/LICENSE +169 -13
- data/NOTICE.txt +5 -0
- data/README.md +67 -27
- data/Rakefile +1 -8
- data/ext/murmurhash/3_x64_128.c +117 -0
- data/ext/murmurhash/murmurhash.c +5 -1
- data/lib/murmurhash/murmurhash.jar +0 -0
- data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +3 -3
- data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +4 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +7 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +12 -4
- data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +83 -0
- data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +70 -0
- data/lib/splitclient-rb/cache/filter/bloom_filter.rb +67 -0
- data/lib/splitclient-rb/cache/filter/filter_adapter.rb +32 -0
- data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
- data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
- data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
- data/lib/splitclient-rb/cache/observers/noop_impression_observer.rb +10 -0
- data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +26 -14
- data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +9 -14
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +31 -10
- data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
- data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
- data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +22 -23
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +15 -22
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +6 -31
- data/lib/splitclient-rb/cache/repositories/repository.rb +6 -5
- data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +46 -6
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +232 -43
- data/lib/splitclient-rb/cache/routers/impression_router.rb +24 -22
- data/lib/splitclient-rb/cache/senders/events_sender.rb +12 -29
- data/lib/splitclient-rb/cache/senders/impressions_adapter/memory_sender.rb +71 -0
- data/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +69 -0
- data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +43 -0
- data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +27 -13
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +11 -25
- data/lib/splitclient-rb/cache/senders/impressions_sender_adapter.rb +21 -0
- data/lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb +47 -0
- data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +95 -0
- data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +110 -0
- data/lib/splitclient-rb/cache/stores/store_utils.rb +13 -0
- data/lib/splitclient-rb/clients/split_client.rb +385 -138
- data/lib/splitclient-rb/constants.rb +16 -0
- data/lib/splitclient-rb/engine/api/client.rb +38 -43
- data/lib/splitclient-rb/engine/api/events.rb +19 -11
- data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +1 -0
- data/lib/splitclient-rb/engine/api/impressions.rb +49 -14
- data/lib/splitclient-rb/engine/api/segments.rb +31 -24
- data/lib/splitclient-rb/engine/api/splits.rb +108 -33
- data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
- data/lib/splitclient-rb/engine/auth_api_client.rb +96 -0
- data/lib/splitclient-rb/engine/back_off.rb +26 -0
- data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
- data/lib/splitclient-rb/engine/common/impressions_manager.rb +165 -0
- data/lib/splitclient-rb/engine/common/noop_impressions_counter.rb +27 -0
- data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
- data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
- data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
- data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
- data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
- data/lib/splitclient-rb/engine/fallback_treatment_calculator.rb +48 -0
- data/lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb +17 -0
- data/lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb +144 -0
- data/lib/splitclient-rb/engine/matchers/all_keys_matcher.rb +1 -1
- data/lib/splitclient-rb/engine/matchers/between_matcher.rb +7 -5
- data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
- data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +10 -8
- data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +2 -6
- data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +1 -5
- data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +7 -5
- data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +6 -5
- data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +5 -4
- data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +6 -4
- data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +1 -5
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +6 -4
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +6 -4
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/matcher.rb +22 -0
- data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +2 -6
- data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
- data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +78 -0
- data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
- data/lib/splitclient-rb/engine/matchers/set_matcher.rb +2 -1
- data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +4 -3
- data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +3 -2
- data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +7 -5
- data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +3 -65
- data/lib/splitclient-rb/engine/models/evaluation_options.rb +9 -0
- data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
- data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
- data/lib/splitclient-rb/engine/models/fallback_treatment.rb +11 -0
- data/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +36 -0
- data/lib/splitclient-rb/engine/models/label.rb +3 -0
- data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
- data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
- data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +14 -0
- data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
- data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +81 -20
- data/lib/splitclient-rb/engine/parser/evaluator.rb +40 -51
- data/lib/splitclient-rb/engine/push_manager.rb +66 -0
- data/lib/splitclient-rb/engine/status_manager.rb +39 -0
- data/lib/splitclient-rb/engine/sync_manager.rb +180 -0
- data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
- data/lib/splitclient-rb/exceptions.rb +20 -1
- data/lib/splitclient-rb/helpers/decryption_helper.rb +25 -0
- data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
- data/lib/splitclient-rb/helpers/repository_helper.rb +61 -0
- data/lib/splitclient-rb/helpers/thread_helper.rb +24 -0
- data/lib/splitclient-rb/helpers/util.rb +26 -0
- data/lib/splitclient-rb/managers/split_manager.rb +58 -20
- data/lib/splitclient-rb/spec.rb +9 -0
- data/lib/splitclient-rb/split_config.rb +336 -54
- data/lib/splitclient-rb/split_factory.rb +219 -33
- data/lib/splitclient-rb/split_factory_builder.rb +1 -22
- data/lib/splitclient-rb/split_factory_registry.rb +63 -0
- data/lib/splitclient-rb/split_logger.rb +9 -10
- data/lib/splitclient-rb/sse/event_source/client.rb +263 -0
- data/lib/splitclient-rb/sse/event_source/event_parser.rb +65 -0
- data/lib/splitclient-rb/sse/event_source/event_types.rb +15 -0
- data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
- data/lib/splitclient-rb/sse/notification_manager_keeper.rb +84 -0
- data/lib/splitclient-rb/sse/notification_processor.rb +48 -0
- data/lib/splitclient-rb/sse/sse_handler.rb +44 -0
- data/lib/splitclient-rb/sse/workers/segments_worker.rb +62 -0
- data/lib/splitclient-rb/sse/workers/splits_worker.rb +149 -0
- data/lib/splitclient-rb/telemetry/domain/constants.rb +48 -0
- data/lib/splitclient-rb/telemetry/domain/structs.rb +35 -0
- data/lib/splitclient-rb/telemetry/evaluation_consumer.rb +14 -0
- data/lib/splitclient-rb/telemetry/evaluation_producer.rb +21 -0
- data/lib/splitclient-rb/telemetry/init_consumer.rb +14 -0
- data/lib/splitclient-rb/telemetry/init_producer.rb +19 -0
- data/lib/splitclient-rb/telemetry/memory/memory_evaluation_consumer.rb +32 -0
- data/lib/splitclient-rb/telemetry/memory/memory_evaluation_producer.rb +24 -0
- data/lib/splitclient-rb/telemetry/memory/memory_init_consumer.rb +28 -0
- data/lib/splitclient-rb/telemetry/memory/memory_init_producer.rb +34 -0
- data/lib/splitclient-rb/telemetry/memory/memory_runtime_consumer.rb +119 -0
- data/lib/splitclient-rb/telemetry/memory/memory_runtime_producer.rb +87 -0
- data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +213 -0
- data/lib/splitclient-rb/telemetry/redis/redis_evaluation_producer.rb +38 -0
- data/lib/splitclient-rb/telemetry/redis/redis_init_producer.rb +37 -0
- data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +27 -0
- data/lib/splitclient-rb/telemetry/runtime_consumer.rb +25 -0
- data/lib/splitclient-rb/telemetry/runtime_producer.rb +25 -0
- data/lib/splitclient-rb/telemetry/storages/memory.rb +159 -0
- data/lib/splitclient-rb/telemetry/sync_task.rb +36 -0
- data/lib/splitclient-rb/telemetry/synchronizer.rb +33 -0
- data/lib/splitclient-rb/utilitites.rb +8 -0
- data/lib/splitclient-rb/validators.rb +142 -38
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +101 -16
- data/sonar-project.properties +6 -0
- data/splitclient-rb.gemspec +28 -23
- metadata +262 -82
- data/.travis.yml +0 -11
- data/Appraisals +0 -10
- data/Detailed-README.md +0 -588
- data/NEWS +0 -141
- data/lib/splitclient-rb/cache/repositories/metrics/memory_repository.rb +0 -127
- data/lib/splitclient-rb/cache/repositories/metrics/redis_repository.rb +0 -96
- data/lib/splitclient-rb/cache/repositories/metrics_repository.rb +0 -21
- data/lib/splitclient-rb/cache/senders/metrics_sender.rb +0 -56
- data/lib/splitclient-rb/cache/stores/sdk_blocker.rb +0 -46
- data/lib/splitclient-rb/cache/stores/segment_store.rb +0 -81
- data/lib/splitclient-rb/cache/stores/split_store.rb +0 -102
- data/lib/splitclient-rb/clients/localhost_split_client.rb +0 -183
- data/lib/splitclient-rb/engine/api/faraday_adapter/patched_net_http_persistent.rb +0 -46
- data/lib/splitclient-rb/engine/api/metrics.rb +0 -60
- data/lib/splitclient-rb/engine/metrics/metrics.rb +0 -80
- data/lib/splitclient-rb/engine/parser/split_adapter.rb +0 -81
- data/lib/splitclient-rb/localhost_split_factory.rb +0 -13
- data/lib/splitclient-rb/localhost_utils.rb +0 -59
- data/lib/splitclient-rb/managers/localhost_split_manager.rb +0 -60
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class GreaterThanOrEqualToSemverMatcher < Matcher
|
|
5
|
+
MATCHER_TYPE = 'GREATER_THAN_OR_EQUAL_TO_SEMVER'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute, string_value, logger, validator)
|
|
10
|
+
super(logger)
|
|
11
|
+
@validator = validator
|
|
12
|
+
@attribute = attribute
|
|
13
|
+
@semver = SplitIoClient::Semver.build(string_value, logger)
|
|
14
|
+
@logger = logger
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def match?(args)
|
|
18
|
+
return false unless verify_semver_arg?(args, 'GreaterThanOrEqualsToSemverMatcher')
|
|
19
|
+
|
|
20
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
|
21
|
+
return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE)
|
|
22
|
+
|
|
23
|
+
matches = [0, 1].include?(value_to_match.compare(@semver))
|
|
24
|
+
@logger.debug("[GreaterThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
|
|
25
|
+
matches
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class InListSemverMatcher < Matcher
|
|
5
|
+
MATCHER_TYPE = 'IN_LIST_SEMVER'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute, list_value, logger, validator)
|
|
10
|
+
super(logger)
|
|
11
|
+
@validator = validator
|
|
12
|
+
@attribute = attribute
|
|
13
|
+
@semver_list = []
|
|
14
|
+
|
|
15
|
+
list_value.map do |item|
|
|
16
|
+
version = SplitIoClient::Semver.build(item, logger)
|
|
17
|
+
@semver_list << version unless version.nil?
|
|
18
|
+
end
|
|
19
|
+
@logger = logger
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def match?(args)
|
|
23
|
+
return false if @semver_list.empty? || !verify_semver_arg?(args, 'InListSemverMatcher')
|
|
24
|
+
|
|
25
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
|
26
|
+
if value_to_match.nil?
|
|
27
|
+
@logger.error('whitelistMatcherData is required for IN_LIST_SEMVER matcher type')
|
|
28
|
+
return false
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
matches = (@semver_list.map { |item| item.version == value_to_match.version }).any? { |item| item == true }
|
|
32
|
+
@logger.debug("[InListSemverMatcher] #{value_to_match} matches -> #{matches}")
|
|
33
|
+
matches
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -6,21 +6,23 @@ module SplitIoClient
|
|
|
6
6
|
|
|
7
7
|
attr_reader :attribute
|
|
8
8
|
|
|
9
|
-
def initialize(attribute_hash)
|
|
9
|
+
def initialize(attribute_hash, logger, validator)
|
|
10
|
+
super(logger)
|
|
11
|
+
@validator = validator
|
|
10
12
|
@attribute = attribute_hash[:attribute]
|
|
11
13
|
@data_type = attribute_hash[:data_type]
|
|
12
14
|
@value = formatted_value(attribute_hash[:value], true)
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def match?(args)
|
|
16
|
-
|
|
18
|
+
@logger.log_if_debug('[LessThanOrEqualToMatcher] evaluating value and attributes.')
|
|
17
19
|
|
|
18
|
-
return false unless
|
|
20
|
+
return false unless @validator.valid_matcher_arguments(args)
|
|
19
21
|
|
|
20
22
|
value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
|
|
21
23
|
|
|
22
24
|
matches = value.is_a?(Integer) ? (value <= @value) : false
|
|
23
|
-
|
|
25
|
+
@logger.log_if_debug("[LessThanOrEqualToMatcher] #{value} less than or equal to #{@value} -> #{matches}")
|
|
24
26
|
matches
|
|
25
27
|
end
|
|
26
28
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class LessThanOrEqualToSemverMatcher < Matcher
|
|
5
|
+
MATCHER_TYPE = 'LESS_THAN_OR_EQUAL_TO_SEMVER'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute, string_value, logger, validator)
|
|
10
|
+
super(logger)
|
|
11
|
+
@validator = validator
|
|
12
|
+
@attribute = attribute
|
|
13
|
+
@semver = SplitIoClient::Semver.build(string_value, logger)
|
|
14
|
+
@logger = logger
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def match?(args)
|
|
18
|
+
return false unless verify_semver_arg?(args, 'LessThanOrEqualsToSemverMatcher')
|
|
19
|
+
|
|
20
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
|
21
|
+
return false unless check_semver_value_to_match(value_to_match, MATCHER_TYPE)
|
|
22
|
+
|
|
23
|
+
matches = [0, -1].include?(value_to_match.compare(@semver))
|
|
24
|
+
@logger.debug("[LessThanOrEqualsToSemverMatcher] #{value_to_match} matches -> #{matches}")
|
|
25
|
+
matches
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -5,6 +5,10 @@ module SplitIoClient
|
|
|
5
5
|
# class to implement the all keys matcher
|
|
6
6
|
#
|
|
7
7
|
class Matcher
|
|
8
|
+
def initialize(logger)
|
|
9
|
+
@logger = logger
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
#
|
|
9
13
|
# evaluates if the given object equals the matcher
|
|
10
14
|
#
|
|
@@ -26,5 +30,23 @@ module SplitIoClient
|
|
|
26
30
|
def string_type?
|
|
27
31
|
false
|
|
28
32
|
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def verify_semver_arg?(args, matcher_name)
|
|
37
|
+
@logger.debug("[#{matcher_name}] evaluating value and attributes.")
|
|
38
|
+
return false unless @validator.valid_matcher_arguments(args)
|
|
39
|
+
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def check_semver_value_to_match(value_to_match, matcher_spec_name)
|
|
44
|
+
if value_to_match.nil? || @semver.nil?
|
|
45
|
+
@logger.error("stringMatcherData is required for #{matcher_spec_name} matcher type")
|
|
46
|
+
return false
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
true
|
|
50
|
+
end
|
|
29
51
|
end
|
|
30
52
|
end
|
|
@@ -6,9 +6,10 @@ module SplitIoClient
|
|
|
6
6
|
|
|
7
7
|
attr_reader :attribute
|
|
8
8
|
|
|
9
|
-
def initialize(attribute, regexp_string)
|
|
9
|
+
def initialize(attribute, regexp_string, logger)
|
|
10
10
|
@attribute = attribute
|
|
11
11
|
@regexp_string = @regexp_string.is_a?(Regexp) ? regexp_string : Regexp.new(regexp_string)
|
|
12
|
+
@logger = logger
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def match?(args)
|
|
@@ -17,7 +18,7 @@ module SplitIoClient
|
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
matches = !(value =~ @regexp_string).nil?
|
|
20
|
-
|
|
21
|
+
@logger.log_if_debug("[MatchesStringMatcher] #{value} matches #{@regexp_string} -> #{matches}")
|
|
21
22
|
matches
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -7,7 +7,8 @@ module SplitIoClient
|
|
|
7
7
|
class NegationMatcher < Matcher
|
|
8
8
|
MATCHER_TYPE = 'NEGATION_MATCHER'
|
|
9
9
|
|
|
10
|
-
def initialize(matcher = nil)
|
|
10
|
+
def initialize(logger, matcher = nil)
|
|
11
|
+
super(logger)
|
|
11
12
|
@matcher = matcher
|
|
12
13
|
end
|
|
13
14
|
|
|
@@ -19,7 +20,7 @@ module SplitIoClient
|
|
|
19
20
|
# @return [boolean] evaluation of the negation matcher
|
|
20
21
|
def match?(args)
|
|
21
22
|
matches = !@matcher.match?(args)
|
|
22
|
-
|
|
23
|
+
@logger.log_if_debug("[NegationMatcherMatcher] Matcher #{@matcher} Arguments #{args} -> #{matches}")
|
|
23
24
|
matches
|
|
24
25
|
end
|
|
25
26
|
|
|
@@ -6,20 +6,16 @@ module SplitIoClient
|
|
|
6
6
|
|
|
7
7
|
attr_reader :attribute
|
|
8
8
|
|
|
9
|
-
def initialize(attribute, remote_array)
|
|
10
|
-
super(attribute, remote_array)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
9
|
def match?(args)
|
|
14
10
|
@local_set = local_set(args[:attributes], @attribute)
|
|
15
11
|
|
|
16
12
|
if @local_set.empty?
|
|
17
|
-
|
|
13
|
+
@logger.log_if_debug('[PartOfSetMatcher] Local Set is empty.')
|
|
18
14
|
return false
|
|
19
15
|
end
|
|
20
16
|
|
|
21
17
|
matches = @local_set.subset? @remote_set
|
|
22
|
-
|
|
18
|
+
@logger.log_if_debug("[PartOfSetMatcher] Local Set #{@local_set} is a subset of #{@remote_set} -> #{matches}")
|
|
23
19
|
matches
|
|
24
20
|
end
|
|
25
21
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class PrerequisitesMatcher
|
|
5
|
+
def initialize(prerequisites, logger)
|
|
6
|
+
@prerequisites = prerequisites
|
|
7
|
+
@logger = logger
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def match?(args)
|
|
11
|
+
keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] }
|
|
12
|
+
|
|
13
|
+
match = true
|
|
14
|
+
@prerequisites.each do |prerequisite|
|
|
15
|
+
evaluate = args[:evaluator].evaluate_feature_flag(keys, prerequisite[:n], args[:attributes])
|
|
16
|
+
next if prerequisite[:ts].include?(evaluate[:treatment])
|
|
17
|
+
|
|
18
|
+
@logger.log_if_debug("[PrerequisitesMatcher] feature flag #{prerequisite[:n]} evaluated to #{evaluate[:treatment]} \
|
|
19
|
+
that does not exist in prerequisited treatments.")
|
|
20
|
+
match = false
|
|
21
|
+
break
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
match
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def string_type?
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
#
|
|
5
|
+
# class to implement the user defined matcher
|
|
6
|
+
#
|
|
7
|
+
class RuleBasedSegmentMatcher < Matcher
|
|
8
|
+
MATCHER_TYPE = 'IN_RULE_BASED_SEGMENT'
|
|
9
|
+
|
|
10
|
+
def initialize(segments_repository, rule_based_segments_repository, segment_name, config)
|
|
11
|
+
super(config.logger)
|
|
12
|
+
@rule_based_segments_repository = rule_based_segments_repository
|
|
13
|
+
@segments_repository = segments_repository
|
|
14
|
+
@segment_name = segment_name
|
|
15
|
+
@config = config
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# evaluates if the key matches the matcher
|
|
20
|
+
#
|
|
21
|
+
# @param key [string] key value to be matched
|
|
22
|
+
#
|
|
23
|
+
# @return [boolean] evaluation of the key against the segment
|
|
24
|
+
def match?(args)
|
|
25
|
+
rule_based_segment = @rule_based_segments_repository.get_rule_based_segment(@segment_name)
|
|
26
|
+
return false if rule_based_segment.nil?
|
|
27
|
+
|
|
28
|
+
key = update_key(args)
|
|
29
|
+
return false if rule_based_segment[:excluded][:keys].include?(key)
|
|
30
|
+
|
|
31
|
+
return false unless check_excluded_segments(rule_based_segment, key, args)
|
|
32
|
+
|
|
33
|
+
matched = false
|
|
34
|
+
rule_based_segment[:conditions].each do |c|
|
|
35
|
+
condition = SplitIoClient::Condition.new(c, @config)
|
|
36
|
+
next if condition.empty?
|
|
37
|
+
|
|
38
|
+
matched = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository, @rule_based_segments_repository).match?(args)
|
|
39
|
+
|
|
40
|
+
break if matched
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
@logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matched}")
|
|
44
|
+
matched
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def check_excluded_segments(rule_based_segment, key, args)
|
|
50
|
+
rule_based_segment[:excluded][:segments].each do |segment|
|
|
51
|
+
if segment[:type] == SplitIoClient::Engine::Models::SegmentType::STANDARD &&
|
|
52
|
+
@segments_repository.in_segment?(segment[:name], key)
|
|
53
|
+
return false
|
|
54
|
+
end
|
|
55
|
+
return false if segment[:type] == SplitIoClient::Engine::Models::SegmentType::RULE_BASED_SEGMENT && match_rbs(
|
|
56
|
+
@rule_based_segments_repository.get_rule_based_segment(segment[:name]), args
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def update_key(args)
|
|
63
|
+
if args[:value].nil? || args[:value].empty?
|
|
64
|
+
args[:matching_key]
|
|
65
|
+
else
|
|
66
|
+
args[:value]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def match_rbs(rule_based_segment, args)
|
|
71
|
+
rbs_matcher = RuleBasedSegmentMatcher.new(@segments_repository, @rule_based_segments_repository,
|
|
72
|
+
rule_based_segment[:name], @config)
|
|
73
|
+
rbs_matcher.match?(matching_key: args[:matching_key],
|
|
74
|
+
bucketing_key: args[:value],
|
|
75
|
+
attributes: args[:attributes])
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class Semver
|
|
5
|
+
METADATA_DELIMITER = '+'
|
|
6
|
+
PRE_RELEASE_DELIMITER = '-'
|
|
7
|
+
VALUE_DELIMITER = '.'
|
|
8
|
+
|
|
9
|
+
attr_reader :major, :minor, :patch, :pre_release, :is_stable, :version
|
|
10
|
+
|
|
11
|
+
def initialize(version)
|
|
12
|
+
@major = 0
|
|
13
|
+
@minor = 0
|
|
14
|
+
@patch = 0
|
|
15
|
+
@pre_release = []
|
|
16
|
+
@is_stable = false
|
|
17
|
+
@version = ''
|
|
18
|
+
@metadata = ''
|
|
19
|
+
parse(version)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# Class builder
|
|
24
|
+
#
|
|
25
|
+
# @param version [String] raw version as read from splitChanges response.
|
|
26
|
+
#
|
|
27
|
+
# @return [type] Semver instance
|
|
28
|
+
def self.build(version, logger)
|
|
29
|
+
new(version)
|
|
30
|
+
rescue NoMethodError => e
|
|
31
|
+
logger.error("Failed to parse Semver data, incorrect data type: #{e}")
|
|
32
|
+
nil
|
|
33
|
+
rescue StandardError => e
|
|
34
|
+
logger.error("Failed to parse Semver data: #{e}")
|
|
35
|
+
nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Check if there is any metadata characters in version.
|
|
40
|
+
#
|
|
41
|
+
# @return [type] String semver without the metadata
|
|
42
|
+
#
|
|
43
|
+
def remove_metadata_if_exists(old_version)
|
|
44
|
+
index = old_version.index(METADATA_DELIMITER)
|
|
45
|
+
return old_version if index.nil?
|
|
46
|
+
|
|
47
|
+
@metadata = old_version[index + 1, old_version.length]
|
|
48
|
+
old_version[0, index]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Compare the current Semver object to a given Semver object, return:
|
|
52
|
+
# 0: if self == passed
|
|
53
|
+
# 1: if self > passed
|
|
54
|
+
# -1: if self < passed
|
|
55
|
+
#
|
|
56
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
|
57
|
+
#
|
|
58
|
+
# @returns [Integer] based on comparison
|
|
59
|
+
def compare(to_compare)
|
|
60
|
+
return 0 if @version == to_compare.version
|
|
61
|
+
|
|
62
|
+
# Compare major, minor, and patch versions numerically
|
|
63
|
+
result = compare_attributes(to_compare)
|
|
64
|
+
return result if result != 0
|
|
65
|
+
|
|
66
|
+
# Compare pre-release versions lexically
|
|
67
|
+
compare_pre_release(to_compare)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def integer?(value)
|
|
73
|
+
!!value.match(/^(\d)+$/)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#
|
|
77
|
+
# Parse the string in version to update the other internal variables
|
|
78
|
+
#
|
|
79
|
+
def parse(old_version)
|
|
80
|
+
without_metadata = remove_metadata_if_exists(old_version)
|
|
81
|
+
|
|
82
|
+
index = without_metadata.index(PRE_RELEASE_DELIMITER)
|
|
83
|
+
if index.nil?
|
|
84
|
+
@is_stable = true
|
|
85
|
+
else
|
|
86
|
+
pre_release_data = without_metadata[index + 1..-1]
|
|
87
|
+
without_metadata = without_metadata[0, index]
|
|
88
|
+
@pre_release = pre_release_data.split(VALUE_DELIMITER)
|
|
89
|
+
end
|
|
90
|
+
assign_major_minor_and_patch(without_metadata)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
#
|
|
94
|
+
# Set the major, minor and patch internal variables based on string passed.
|
|
95
|
+
#
|
|
96
|
+
# @param version [String] raw version containing major.minor.patch numbers.
|
|
97
|
+
def assign_major_minor_and_patch(version)
|
|
98
|
+
parts = version.split(VALUE_DELIMITER)
|
|
99
|
+
if parts.length != 3 ||
|
|
100
|
+
!(integer?(parts[0]) &&
|
|
101
|
+
integer?(parts[1]) &&
|
|
102
|
+
integer?(parts[2]))
|
|
103
|
+
raise "Unable to convert to Semver, incorrect format: #{version}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
@major = parts[0].to_i
|
|
107
|
+
@minor = parts[1].to_i
|
|
108
|
+
@patch = parts[2].to_i
|
|
109
|
+
@version = "#{@major}#{VALUE_DELIMITER}#{@minor}#{VALUE_DELIMITER}#{@patch}"
|
|
110
|
+
@version += parse_pre_release
|
|
111
|
+
@version += "#{METADATA_DELIMITER}#{@metadata}" unless @metadata.empty?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def parse_pre_release
|
|
115
|
+
return '' if @pre_release.empty?
|
|
116
|
+
|
|
117
|
+
pre_parsed = []
|
|
118
|
+
@pre_release.each do |pre_digit|
|
|
119
|
+
pre_digit = pre_digit.to_i if integer?(pre_digit)
|
|
120
|
+
pre_parsed << pre_digit
|
|
121
|
+
end
|
|
122
|
+
"#{PRE_RELEASE_DELIMITER}#{pre_parsed.join('.')}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
#
|
|
126
|
+
# Compare 2 variables and return int as follows:
|
|
127
|
+
# 0: if var1 == var2
|
|
128
|
+
# 1: if var1 > var2
|
|
129
|
+
# -1: if var1 < var2
|
|
130
|
+
#
|
|
131
|
+
# @param var1 [type] String/Integer object that accept ==, < or > operators
|
|
132
|
+
# @param var2 [type] String/Integer object that accept ==, < or > operators
|
|
133
|
+
#
|
|
134
|
+
# @returns [Integer] based on comparison
|
|
135
|
+
def compare_vars(var1, var2)
|
|
136
|
+
return 0 if var1 == var2
|
|
137
|
+
|
|
138
|
+
return 1 if var1 > var2
|
|
139
|
+
|
|
140
|
+
-1
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Compare the current Semver object's major, minor, patch and is_stable attributes to a given Semver object, return:
|
|
144
|
+
# 0: if self == passed
|
|
145
|
+
# 1: if self > passed
|
|
146
|
+
# -1: if self < passed
|
|
147
|
+
#
|
|
148
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
|
149
|
+
#
|
|
150
|
+
# @returns [Integer] based on comparison
|
|
151
|
+
def compare_attributes(to_compare)
|
|
152
|
+
result = compare_vars(@major, to_compare.major)
|
|
153
|
+
return result if result != 0
|
|
154
|
+
|
|
155
|
+
result = compare_vars(@minor, to_compare.minor)
|
|
156
|
+
return result if result != 0
|
|
157
|
+
|
|
158
|
+
result = compare_vars(@patch, to_compare.patch)
|
|
159
|
+
return result if result != 0
|
|
160
|
+
|
|
161
|
+
return -1 if !@is_stable && to_compare.is_stable
|
|
162
|
+
|
|
163
|
+
return 1 if @is_stable && !to_compare.is_stable
|
|
164
|
+
|
|
165
|
+
0
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Compare the current Semver object's pre_release attribute to a given Semver object, return:
|
|
169
|
+
# 0: if self == passed
|
|
170
|
+
# 1: if self > passed
|
|
171
|
+
# -1: if self < passed
|
|
172
|
+
#
|
|
173
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
|
174
|
+
#
|
|
175
|
+
# @returns [Integer] based on comparison
|
|
176
|
+
def compare_pre_release(to_compare)
|
|
177
|
+
min_length = get_pre_min_length(to_compare)
|
|
178
|
+
0.upto(min_length - 1) do |i|
|
|
179
|
+
next if @pre_release[i] == to_compare.pre_release[i]
|
|
180
|
+
|
|
181
|
+
if integer?(@pre_release[i]) && integer?(to_compare.pre_release[i])
|
|
182
|
+
return compare_vars(@pre_release[i].to_i, to_compare.pre_release[i].to_i)
|
|
183
|
+
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
return compare_vars(@pre_release[i], to_compare.pre_release[i])
|
|
187
|
+
end
|
|
188
|
+
# Compare lengths of pre-release versions
|
|
189
|
+
compare_vars(@pre_release.length, to_compare.pre_release.length)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Get minimum of current Semver object's pre_release attributes length to a given Semver object
|
|
193
|
+
#
|
|
194
|
+
# @param to_compare [trype] splitio.models.grammar.matchers.semver.Semver object
|
|
195
|
+
#
|
|
196
|
+
# @returns [Integer]
|
|
197
|
+
def get_pre_min_length(to_compare)
|
|
198
|
+
[@pre_release.length, to_compare.pre_release.length].min
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -10,9 +10,10 @@ module SplitIoClient
|
|
|
10
10
|
|
|
11
11
|
protected
|
|
12
12
|
|
|
13
|
-
def initialize(attribute, remote_array)
|
|
13
|
+
def initialize(attribute, remote_array, logger)
|
|
14
14
|
@attribute = attribute
|
|
15
15
|
@remote_set = remote_array.to_set
|
|
16
|
+
@logger = logger
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def local_set(data, attribute)
|
|
@@ -6,21 +6,22 @@ module SplitIoClient
|
|
|
6
6
|
|
|
7
7
|
attr_reader :attribute
|
|
8
8
|
|
|
9
|
-
def initialize(attribute, prefix_list)
|
|
9
|
+
def initialize(attribute, prefix_list, logger)
|
|
10
10
|
@attribute = attribute
|
|
11
11
|
@prefix_list = prefix_list
|
|
12
|
+
@logger = logger
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def match?(args)
|
|
15
16
|
if @prefix_list.empty?
|
|
16
|
-
|
|
17
|
+
@logger.log_if_debug('[StartsWithMatcher] Prefix List is empty.')
|
|
17
18
|
return false
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
value = get_value(args)
|
|
21
22
|
|
|
22
23
|
matches = @prefix_list.any? { |prefix| value.to_s.start_with? prefix }
|
|
23
|
-
|
|
24
|
+
@logger.log_if_debug("[StartsWithMatcher] #{value} matches any of #{@prefix_list} -> #{matches}")
|
|
24
25
|
matches
|
|
25
26
|
end
|
|
26
27
|
|
|
@@ -7,7 +7,8 @@ module SplitIoClient
|
|
|
7
7
|
class UserDefinedSegmentMatcher < Matcher
|
|
8
8
|
MATCHER_TYPE = 'IN_SEGMENT'
|
|
9
9
|
|
|
10
|
-
def initialize(segments_repository, segment_name)
|
|
10
|
+
def initialize(segments_repository, segment_name, logger)
|
|
11
|
+
super(logger)
|
|
11
12
|
@segments_repository = segments_repository
|
|
12
13
|
@segment_name = segment_name
|
|
13
14
|
end
|
|
@@ -20,7 +21,7 @@ module SplitIoClient
|
|
|
20
21
|
# @return [boolean] evaluation of the key against the segment
|
|
21
22
|
def match?(args)
|
|
22
23
|
matches = @segments_repository.in_segment?(@segment_name, args[:value] || args[:matching_key])
|
|
23
|
-
|
|
24
|
+
@logger.log_if_debug("[InSegmentMatcher] #{@segment_name} is in segment -> #{matches}")
|
|
24
25
|
matches
|
|
25
26
|
end
|
|
26
27
|
end
|
|
@@ -9,7 +9,9 @@ module SplitIoClient
|
|
|
9
9
|
|
|
10
10
|
attr_reader :attribute
|
|
11
11
|
|
|
12
|
-
def initialize(whitelist_data)
|
|
12
|
+
def initialize(whitelist_data, logger, validator)
|
|
13
|
+
super(logger)
|
|
14
|
+
@validator = validator
|
|
13
15
|
@whitelist = case whitelist_data
|
|
14
16
|
when Array
|
|
15
17
|
whitelist_data
|
|
@@ -26,9 +28,9 @@ module SplitIoClient
|
|
|
26
28
|
def match?(args)
|
|
27
29
|
return matches_user_whitelist(args) unless @matcher_type == 'ATTR_WHITELIST'
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
@logger.log_if_debug('[WhitelistMatcher] evaluating value and attributes.')
|
|
30
32
|
|
|
31
|
-
return false unless
|
|
33
|
+
return false unless @validator.valid_matcher_arguments(args)
|
|
32
34
|
|
|
33
35
|
matches_attr_whitelist(args)
|
|
34
36
|
end
|
|
@@ -49,14 +51,14 @@ module SplitIoClient
|
|
|
49
51
|
|
|
50
52
|
def matches_user_whitelist(args)
|
|
51
53
|
matches = @whitelist.include?(args[:value] || args[:matching_key])
|
|
52
|
-
|
|
54
|
+
@logger.log_if_debug("[WhitelistMatcher] #{@whitelist} include \
|
|
53
55
|
#{args[:value] || args[:matching_key]} -> #{matches}")
|
|
54
56
|
matches
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
def matches_attr_whitelist(args)
|
|
58
60
|
matches = @whitelist.include?(args[:value] || args[:attributes][@attribute.to_sym])
|
|
59
|
-
|
|
61
|
+
@logger.log_if_debug("[WhitelistMatcher] #{@whitelist} include \
|
|
60
62
|
#{args[:value] || args[:attributes][@attribute.to_sym]} -> #{matches}")
|
|
61
63
|
matches
|
|
62
64
|
end
|