splitclient-rb 1.0.2.wip → 8.11.1.pre.rc1-java
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 +5 -5
- 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 +20 -0
- data/.rubocop.yml +76 -0
- data/.simplecov +1 -0
- data/CHANGES.txt +361 -2
- data/CONTRIBUTORS-GUIDE.md +49 -0
- data/Gemfile +2 -0
- data/LICENSE +3 -36
- data/NOTICE.txt +5 -0
- data/README.md +59 -127
- data/Rakefile +27 -3
- data/ext/murmurhash/MurmurHash3.java +302 -0
- data/gemfiles/faraday_after_0.13.gemfile +7 -0
- data/gemfiles/faraday_before_0.13.gemfile +8 -0
- data/lib/murmurhash/base.rb +58 -0
- data/lib/murmurhash/murmurhash.jar +0 -0
- data/lib/murmurhash/murmurhash_mri.rb +3 -0
- data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +130 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +137 -0
- data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +53 -0
- data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +178 -0
- 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 +45 -0
- data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +35 -0
- data/lib/splitclient-rb/cache/repositories/events_repository.rb +61 -0
- 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 +56 -0
- data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +57 -0
- data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +23 -0
- data/lib/splitclient-rb/cache/repositories/repository.rb +24 -0
- data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
- data/lib/splitclient-rb/cache/repositories/segments_repository.rb +124 -0
- data/lib/splitclient-rb/cache/repositories/splits_repository.rb +296 -0
- data/lib/splitclient-rb/cache/routers/impression_router.rb +60 -0
- data/lib/splitclient-rb/cache/senders/events_sender.rb +42 -0
- 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 +95 -0
- data/lib/splitclient-rb/cache/senders/impressions_sender.rb +52 -0
- 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 +530 -0
- data/lib/splitclient-rb/constants.rb +16 -0
- data/lib/splitclient-rb/engine/api/client.rb +87 -0
- data/lib/splitclient-rb/engine/api/events.rb +57 -0
- data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +58 -0
- data/lib/splitclient-rb/engine/api/impressions.rb +79 -0
- data/lib/splitclient-rb/engine/api/segments.rb +78 -0
- data/lib/splitclient-rb/engine/api/splits.rb +139 -0
- data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
- data/lib/splitclient-rb/engine/auth_api_client.rb +100 -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-engine → splitclient-rb/engine}/evaluator/splitter.rb +35 -22
- 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-engine → splitclient-rb/engine}/matchers/all_keys_matcher.rb +10 -21
- data/lib/splitclient-rb/engine/matchers/between_matcher.rb +47 -0
- data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/combiners.rb +4 -6
- data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +80 -0
- data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +20 -0
- data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +16 -0
- data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +43 -0
- data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +26 -0
- data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +42 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +37 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +44 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +16 -0
- data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +43 -0
- 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 +43 -0
- data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/matcher.rb +52 -0
- data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +29 -0
- data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +47 -0
- data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +22 -0
- 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 +27 -0
- data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +40 -0
- data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +28 -0
- data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
- data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +66 -0
- 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 +10 -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 +16 -0
- data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
- data/lib/splitclient-rb/engine/models/split.rb +17 -0
- data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
- data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
- data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
- data/lib/splitclient-rb/engine/parser/condition.rb +271 -0
- data/lib/splitclient-rb/engine/parser/evaluator.rb +112 -0
- data/lib/{splitclient-engine → splitclient-rb/engine}/parser/partition.rb +2 -4
- 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 +183 -0
- data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
- data/lib/splitclient-rb/exceptions.rb +26 -0
- 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 +121 -0
- data/lib/splitclient-rb/spec.rb +9 -0
- data/lib/splitclient-rb/split_config.rb +591 -47
- data/lib/splitclient-rb/split_factory.rb +284 -0
- data/lib/splitclient-rb/split_factory_builder.rb +7 -0
- data/lib/splitclient-rb/split_factory_registry.rb +63 -0
- data/lib/splitclient-rb/split_logger.rb +23 -0
- data/lib/splitclient-rb/sse/event_source/client.rb +264 -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 +153 -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 +49 -0
- data/lib/splitclient-rb/validators.rb +361 -0
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb.rb +177 -25
- data/sonar-project.properties +6 -0
- data/splitclient-rb.gemspec +53 -26
- data/tasks/benchmark_get_treatment.rake +31 -17
- data/tasks/irb.rake +6 -0
- metadata +411 -86
- data/NEWS +0 -9
- data/lib/splitclient-cache/local_store.rb +0 -45
- data/lib/splitclient-engine/impressions/impressions.rb +0 -79
- data/lib/splitclient-engine/matchers/between_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/combining_matcher.rb +0 -94
- data/lib/splitclient-engine/matchers/equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/greater_than_or_equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/less_than_or_equal_to_matcher.rb +0 -48
- data/lib/splitclient-engine/matchers/negation_matcher.rb +0 -54
- data/lib/splitclient-engine/matchers/user_defined_segment_matcher.rb +0 -61
- data/lib/splitclient-engine/matchers/whitelist_matcher.rb +0 -64
- data/lib/splitclient-engine/metrics/binary_search_latency_tracker.rb +0 -122
- data/lib/splitclient-engine/metrics/metrics.rb +0 -158
- data/lib/splitclient-engine/parser/condition.rb +0 -143
- data/lib/splitclient-engine/parser/segment.rb +0 -84
- data/lib/splitclient-engine/parser/segment_parser.rb +0 -46
- data/lib/splitclient-engine/parser/split.rb +0 -68
- data/lib/splitclient-engine/parser/split_adapter.rb +0 -433
- data/lib/splitclient-engine/parser/split_parser.rb +0 -139
- data/lib/splitclient-engine/partitions/treatments.rb +0 -40
- data/lib/splitclient-rb/split_client.rb +0 -156
- data/lib/splitclient-rb_utilitites.rb +0 -26
- data/tasks/console.rake +0 -4
- data/tasks/rspec.rake +0 -3
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
class FallbackTreatmentCalculator
|
|
6
|
+
attr_accessor :fallback_treatments_configuration, :label_prefix
|
|
7
|
+
|
|
8
|
+
def initialize(fallback_treatment_configuration)
|
|
9
|
+
@label_prefix = 'fallback - '
|
|
10
|
+
@fallback_treatments_configuration = fallback_treatment_configuration
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def resolve(flag_name, label)
|
|
14
|
+
default_fallback_treatment = Engine::Models::FallbackTreatment.new(
|
|
15
|
+
Engine::Models::Treatment::CONTROL,
|
|
16
|
+
nil,
|
|
17
|
+
label
|
|
18
|
+
)
|
|
19
|
+
return default_fallback_treatment if @fallback_treatments_configuration.nil?
|
|
20
|
+
|
|
21
|
+
if !@fallback_treatments_configuration.by_flag_fallback_treatment.nil? \
|
|
22
|
+
&& !@fallback_treatments_configuration.by_flag_fallback_treatment.fetch(flag_name, nil).nil?
|
|
23
|
+
return copy_with_label(
|
|
24
|
+
@fallback_treatments_configuration.by_flag_fallback_treatment[flag_name],
|
|
25
|
+
resolve_label(label)
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
return copy_with_label(@fallback_treatments_configuration.global_fallback_treatment, resolve_label(label)) \
|
|
30
|
+
unless @fallback_treatments_configuration.global_fallback_treatment.nil?
|
|
31
|
+
|
|
32
|
+
default_fallback_treatment
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def resolve_label(label)
|
|
38
|
+
return nil if label.nil?
|
|
39
|
+
|
|
40
|
+
@label_prefix + label
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def copy_with_label(fallback_treatment, label)
|
|
44
|
+
Engine::Models::FallbackTreatment.new(fallback_treatment.treatment, fallback_treatment.config, label)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
module Impressions
|
|
6
|
+
class UniqueKeysTracker
|
|
7
|
+
INTERVAL_TO_CLEAR_LONG_TERM_CACHE = 86_400 # 24 hours
|
|
8
|
+
|
|
9
|
+
def initialize(config,
|
|
10
|
+
filter_adapter,
|
|
11
|
+
sender_adapter,
|
|
12
|
+
cache)
|
|
13
|
+
@config = config
|
|
14
|
+
@filter_adapter = filter_adapter
|
|
15
|
+
@sender_adapter = sender_adapter
|
|
16
|
+
@cache = cache
|
|
17
|
+
@max_bulk_size = config.unique_keys_bulk_size
|
|
18
|
+
@semaphore = Mutex.new
|
|
19
|
+
@keys_size = 0
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
@config.threads[:unique_keys_sender] = Thread.new { send_bulk_data_thread }
|
|
24
|
+
@config.threads[:clear_filter] = Thread.new { clear_filter_thread }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def track(feature_name, key)
|
|
28
|
+
return false if @filter_adapter.contains?(feature_name, key)
|
|
29
|
+
|
|
30
|
+
@filter_adapter.add(feature_name, key)
|
|
31
|
+
|
|
32
|
+
add_or_update(feature_name, key)
|
|
33
|
+
@keys_size += 1
|
|
34
|
+
|
|
35
|
+
send_bulk_data if @keys_size >= @max_bulk_size
|
|
36
|
+
|
|
37
|
+
true
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def send_bulk_data_thread
|
|
46
|
+
@config.logger.info('Starting Unique Keys Tracker.') if @config.debug_enabled
|
|
47
|
+
loop do
|
|
48
|
+
sleep(@config.unique_keys_refresh_rate)
|
|
49
|
+
send_bulk_data
|
|
50
|
+
end
|
|
51
|
+
rescue SplitIoClient::SDKShutdownException
|
|
52
|
+
send_bulk_data
|
|
53
|
+
@config.logger.info('Posting unique keys due to shutdown')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def clear_filter_thread
|
|
57
|
+
loop do
|
|
58
|
+
sleep(INTERVAL_TO_CLEAR_LONG_TERM_CACHE)
|
|
59
|
+
@config.logger.debug('Starting task to clean the filter cache.') if @config.debug_enabled
|
|
60
|
+
@filter_adapter.clear
|
|
61
|
+
end
|
|
62
|
+
rescue SplitIoClient::SDKShutdownException
|
|
63
|
+
@filter_adapter.clear
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def add_or_update(feature_name, key)
|
|
67
|
+
if @cache[feature_name].nil?
|
|
68
|
+
@cache[feature_name] = Set.new([key])
|
|
69
|
+
else
|
|
70
|
+
@cache[feature_name].add(key)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def clear_cache
|
|
75
|
+
uniques = @cache.clone
|
|
76
|
+
keys_size = @keys_size
|
|
77
|
+
@cache.clear
|
|
78
|
+
@keys_size = 0
|
|
79
|
+
|
|
80
|
+
[uniques, keys_size]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def send_bulk_data
|
|
84
|
+
@semaphore.synchronize do
|
|
85
|
+
return if @cache.empty?
|
|
86
|
+
|
|
87
|
+
uniques, keys_size = clear_cache
|
|
88
|
+
if keys_size <= @max_bulk_size
|
|
89
|
+
@sender_adapter.record_uniques_key(uniques)
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
bulks = flatten_bulks(uniques)
|
|
94
|
+
bulks_to_post = group_bulks_by_max_size(bulks)
|
|
95
|
+
@sender_adapter.record_uniques_key(bulks_to_post)
|
|
96
|
+
end
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def group_bulks_by_max_size(bulks)
|
|
102
|
+
current_size = 0
|
|
103
|
+
bulks_to_post = Concurrent::Hash.new
|
|
104
|
+
bulks.each do |bulk|
|
|
105
|
+
key, value = bulk.first
|
|
106
|
+
if (value.size + current_size) > @max_bulk_size
|
|
107
|
+
@sender_adapter.record_uniques_key(bulks_to_post)
|
|
108
|
+
bulks_to_post = Concurrent::Hash.new
|
|
109
|
+
current_size = 0
|
|
110
|
+
end
|
|
111
|
+
bulks_to_post[key] = value
|
|
112
|
+
current_size += value.size
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
bulks_to_post
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def flatten_bulks(uniques)
|
|
119
|
+
bulks = []
|
|
120
|
+
uniques.each_key do |unique_key|
|
|
121
|
+
bulks += check_keys_and_split_to_bulks(uniques[unique_key], unique_key)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
bulks
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def check_keys_and_split_to_bulks(value, key)
|
|
128
|
+
unique_updated = []
|
|
129
|
+
if value.size > @max_bulk_size
|
|
130
|
+
sub_bulks = SplitIoClient::Utilities.split_bulk_to_send(value, @max_bulk_size)
|
|
131
|
+
sub_bulks.each do |sub_bulk|
|
|
132
|
+
unique_updated << { key => sub_bulk.to_set }
|
|
133
|
+
end
|
|
134
|
+
return unique_updated
|
|
135
|
+
|
|
136
|
+
end
|
|
137
|
+
unique_updated << { key => value }
|
|
138
|
+
|
|
139
|
+
unique_updated
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module SplitIoClient
|
|
3
4
|
#
|
|
4
5
|
# class to implement the all keys matcher
|
|
5
6
|
#
|
|
6
|
-
class AllKeysMatcher <
|
|
7
|
-
|
|
8
|
-
attr_reader :matcher_type
|
|
9
|
-
|
|
10
|
-
def initialize
|
|
11
|
-
@matcher_type = "ALL_KEYS"
|
|
12
|
-
end
|
|
7
|
+
class AllKeysMatcher < Matcher
|
|
8
|
+
MATCHER_TYPE = 'ALL_KEYS'
|
|
13
9
|
|
|
14
10
|
#
|
|
15
11
|
# evaluates if the key matches the matcher
|
|
16
12
|
#
|
|
17
|
-
# @param key [string] key value to be matched
|
|
18
|
-
#
|
|
19
13
|
# @return [boolean] true for all instances
|
|
20
|
-
def match?(
|
|
14
|
+
def match?(_args)
|
|
15
|
+
@logger.log_if_debug('[AllKeysMatcher] is always -> true')
|
|
21
16
|
true
|
|
22
17
|
end
|
|
23
18
|
|
|
@@ -26,27 +21,21 @@ module SplitIoClient
|
|
|
26
21
|
#
|
|
27
22
|
# @param obj [object] object to be evaluated
|
|
28
23
|
#
|
|
29
|
-
# @
|
|
24
|
+
# @return [boolean] true if obj equals the matcher
|
|
30
25
|
def equals?(obj)
|
|
31
|
-
if obj.
|
|
32
|
-
false
|
|
33
|
-
elsif self.equal?(obj)
|
|
26
|
+
if obj.instance_of?(AllKeysMatcher)
|
|
34
27
|
true
|
|
35
|
-
elsif !obj.instance_of?(AllKeysMatcher)
|
|
36
|
-
false
|
|
37
28
|
else
|
|
38
|
-
|
|
29
|
+
super(obj)
|
|
39
30
|
end
|
|
40
31
|
end
|
|
41
32
|
|
|
42
33
|
#
|
|
43
34
|
# function to print string value for this matcher
|
|
44
35
|
#
|
|
45
|
-
# @
|
|
36
|
+
# @return [string] string value of this matcher
|
|
46
37
|
def to_s
|
|
47
38
|
'in segment all'
|
|
48
39
|
end
|
|
49
|
-
|
|
50
40
|
end
|
|
51
|
-
|
|
52
41
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class BetweenMatcher < Matcher
|
|
5
|
+
MATCHER_TYPE = 'BETWEEN'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute_hash, logger, validator)
|
|
10
|
+
super(logger)
|
|
11
|
+
@validator = validator
|
|
12
|
+
@attribute = attribute_hash[:attribute]
|
|
13
|
+
@data_type = attribute_hash[:data_type]
|
|
14
|
+
@start_value = formatted_value(attribute_hash[:start_value], true)
|
|
15
|
+
@end_value = formatted_value(attribute_hash[:end_value], true)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def match?(args)
|
|
19
|
+
@logger.log_if_debug('[BetweenMatcher] evaluating value and attributes.')
|
|
20
|
+
|
|
21
|
+
return false unless @validator.valid_matcher_arguments(args)
|
|
22
|
+
|
|
23
|
+
value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
|
|
24
|
+
@logger.log_if_debug("[BetweenMatcher] Value from parameters: #{value}.")
|
|
25
|
+
return false unless value.is_a?(Integer)
|
|
26
|
+
|
|
27
|
+
matches = (@start_value..@end_value).cover? value
|
|
28
|
+
@logger.log_if_debug("[BetweenMatcher] is #{value} between #{@start_value} and #{@end_value} -> #{matches} .")
|
|
29
|
+
matches
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def formatted_value(value, sdk_data = false)
|
|
35
|
+
case @data_type
|
|
36
|
+
when 'NUMBER'
|
|
37
|
+
value
|
|
38
|
+
when 'DATETIME'
|
|
39
|
+
value /= 1000 if sdk_data
|
|
40
|
+
|
|
41
|
+
SplitIoClient::Utilities.to_milis_zero_out_from_seconds(value)
|
|
42
|
+
else
|
|
43
|
+
@logger.error('Invalid data type')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class BetweenSemverMatcher < Matcher
|
|
5
|
+
MATCHER_TYPE = 'BETWEEN_SEMVER'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute, start_value, end_value, logger, validator)
|
|
10
|
+
super(logger)
|
|
11
|
+
@validator = validator
|
|
12
|
+
@attribute = attribute
|
|
13
|
+
@semver_start = SplitIoClient::Semver.build(start_value, logger)
|
|
14
|
+
@semver_end = SplitIoClient::Semver.build(end_value, logger)
|
|
15
|
+
@logger = logger
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def match?(args)
|
|
19
|
+
return false unless verify_semver_arg?(args, 'BetweenSemverMatcher')
|
|
20
|
+
|
|
21
|
+
value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger)
|
|
22
|
+
if value_to_match.nil? || @semver_start.nil? || @semver_end.nil?
|
|
23
|
+
@logger.error('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type')
|
|
24
|
+
return false
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
matches = ([0, -1].include?(@semver_start.compare(value_to_match)) &&
|
|
28
|
+
[0, 1].include?(@semver_end.compare(value_to_match)))
|
|
29
|
+
@logger.debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}")
|
|
30
|
+
matches
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module SplitIoClient
|
|
3
4
|
#
|
|
4
5
|
# class to represent combiner values
|
|
5
6
|
#
|
|
6
|
-
class Combiners
|
|
7
|
-
|
|
7
|
+
class Combiners
|
|
8
8
|
# available combiners of the sdk
|
|
9
9
|
AND = 'AND'
|
|
10
|
-
|
|
11
10
|
end
|
|
12
|
-
|
|
13
|
-
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
#
|
|
5
|
+
# class to implement the combining matcher
|
|
6
|
+
#
|
|
7
|
+
class CombiningMatcher < Matcher
|
|
8
|
+
MATCHER_TYPE = 'COMBINING_MATCHER'
|
|
9
|
+
|
|
10
|
+
def initialize(logger, combiner = '', matchers = [])
|
|
11
|
+
super(logger)
|
|
12
|
+
@combiner = combiner
|
|
13
|
+
@matchers = matchers
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# evaluates if the key matches the matchers within the combiner
|
|
18
|
+
#
|
|
19
|
+
# @param matching_key [string] key value to be matched
|
|
20
|
+
# @param bucketing_key [string] bucketing key to be matched
|
|
21
|
+
# @param evaluator [instance of Evaluator class]
|
|
22
|
+
# @param attributes [hash]
|
|
23
|
+
#
|
|
24
|
+
# @return [boolean]
|
|
25
|
+
def match?(args)
|
|
26
|
+
if @matchers.empty?
|
|
27
|
+
@logger.log_if_debug('[CombiningMatcher] Matchers Empty')
|
|
28
|
+
return false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
case @combiner
|
|
32
|
+
when Combiners::AND
|
|
33
|
+
matches = eval_and(args)
|
|
34
|
+
@logger.log_if_debug("[CombiningMatcher] Combiner AND result -> #{matches}")
|
|
35
|
+
return matches
|
|
36
|
+
else
|
|
37
|
+
@logger.log_if_debug("[CombiningMatcher] Invalid Combiner Type - Combiner -> #{@combiner}")
|
|
38
|
+
@logger.error('Invalid combiner type')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# auxiliary method to evaluate each of the matchers within the combiner
|
|
46
|
+
#
|
|
47
|
+
# @param matching_key [string] key value to be matched
|
|
48
|
+
# @param bucketing_key [string] bucketing key to be matched
|
|
49
|
+
# @param evaluator [Evaluator] used in dependency_matcher
|
|
50
|
+
# @param attributes [hash] attributes to pass to the treatment class
|
|
51
|
+
#
|
|
52
|
+
# @return [boolean] match value for combiner delegates
|
|
53
|
+
def eval_and(args)
|
|
54
|
+
# Convert all keys to symbols
|
|
55
|
+
args[:attributes] = args[:attributes].each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v } if args && args[:attributes]
|
|
56
|
+
|
|
57
|
+
@matchers.all? do |matcher|
|
|
58
|
+
if match_with_key?(matcher)
|
|
59
|
+
key = args[:value]
|
|
60
|
+
key = args[:matching_key] unless args[:matching_key].nil?
|
|
61
|
+
matcher.match?(value: key)
|
|
62
|
+
else
|
|
63
|
+
matcher.match?(args)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def match_with_key?(matcher)
|
|
69
|
+
matcher.respond_to?(:attribute) && matcher.attribute.nil? && matcher.string_type?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#
|
|
73
|
+
# function to print string value for this matcher
|
|
74
|
+
#
|
|
75
|
+
# @return [string] string value of this matcher
|
|
76
|
+
def to_s
|
|
77
|
+
@matcher_list.map(&:to_s).join("#{@combiner} ")
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class ContainsAllMatcher < SetMatcher
|
|
5
|
+
MATCHER_TYPE = 'CONTAINS_ALL'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def match?(args)
|
|
10
|
+
if @remote_set.empty?
|
|
11
|
+
@logger.log_if_debug('[ContainsAllMatcher] Remote Set Empty')
|
|
12
|
+
return false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
matches = @remote_set.subset? local_set(args[:attributes], @attribute)
|
|
16
|
+
@logger.log_if_debug("[ContainsAllMatcher] Remote Set #{@remote_set} contains #{@attribute} -> #{matches}")
|
|
17
|
+
matches
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class ContainsAnyMatcher < SetMatcher
|
|
5
|
+
MATCHER_TYPE = 'CONTAINS_ANY'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def match?(args)
|
|
10
|
+
matches = local_set(args[:attributes], @attribute).intersect? @remote_set
|
|
11
|
+
@logger.log_if_debug("[ContainsAnyMatcher] Remote Set #{@remote_set} contains any \
|
|
12
|
+
#{@attribute} or #{args[:attributes]}-> #{matches}")
|
|
13
|
+
matches
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class ContainsMatcher
|
|
5
|
+
MATCHER_TYPE = 'CONTAINS_WITH'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute, substr_list, logger, validator)
|
|
10
|
+
@attribute = attribute
|
|
11
|
+
@substr_list = substr_list
|
|
12
|
+
@logger = logger
|
|
13
|
+
@validator = validator
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def match?(args)
|
|
17
|
+
@logger.log_if_debug('[ContainsMatcher] evaluating value and attributes.')
|
|
18
|
+
|
|
19
|
+
return false unless @validator.valid_matcher_arguments(args)
|
|
20
|
+
|
|
21
|
+
value = get_value(args)
|
|
22
|
+
|
|
23
|
+
@logger.log_if_debug("[ContainsMatcher] Value from parameters: #{value}.")
|
|
24
|
+
return false if @substr_list.empty?
|
|
25
|
+
|
|
26
|
+
matches = @substr_list.any? { |substr| value.to_s.include? substr }
|
|
27
|
+
@logger.log_if_debug("[ContainsMatcher] #{@value} contains any of #{@substr_list} -> #{matches} .")
|
|
28
|
+
matches
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def string_type?
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def get_value(args)
|
|
38
|
+
args[:value] || args[:attributes].fetch(@attribute) do |a|
|
|
39
|
+
args[:attributes][a.to_s] || args[:attributes][a.to_sym]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class DependencyMatcher
|
|
5
|
+
MATCHER_TYPE = 'IN_SPLIT_TREATMENT'
|
|
6
|
+
|
|
7
|
+
def initialize(feature_flag, treatments, logger)
|
|
8
|
+
@feature_flag = feature_flag
|
|
9
|
+
@treatments = treatments
|
|
10
|
+
@logger = logger
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def match?(args)
|
|
14
|
+
keys = { matching_key: args[:matching_key], bucketing_key: args[:bucketing_key] }
|
|
15
|
+
evaluate = args[:evaluator].evaluate_feature_flag(keys, @feature_flag, args[:attributes])
|
|
16
|
+
matches = @treatments.include?(evaluate[:treatment])
|
|
17
|
+
@logger.log_if_debug("[dependencyMatcher] Parent feature flag #{@feature_flag} evaluated to #{evaluate[:treatment]} \
|
|
18
|
+
with label #{evaluate[:label]}. #{@feature_flag} evaluated treatment is part of [#{@treatments}] ? #{matches}.")
|
|
19
|
+
matches
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def string_type?
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class EndsWithMatcher
|
|
5
|
+
MATCHER_TYPE = 'ENDS_WITH'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute, suffix_list, logger)
|
|
10
|
+
@attribute = attribute
|
|
11
|
+
@suffix_list = suffix_list
|
|
12
|
+
@logger = logger
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def match?(args)
|
|
16
|
+
value = get_value(args)
|
|
17
|
+
|
|
18
|
+
@logger.log_if_debug("[EndsWithMatcher] Value from parameters: #{value}.")
|
|
19
|
+
|
|
20
|
+
if @suffix_list.empty?
|
|
21
|
+
@logger.log_if_debug('[EndsWithMatcher] Sufix List empty.')
|
|
22
|
+
return false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
matches = @suffix_list.any? { |suffix| value.to_s.end_with? suffix }
|
|
26
|
+
@logger.log_if_debug("[EndsWithMatcher] #{value} ends with any #{@suffix_list}")
|
|
27
|
+
matches
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def string_type?
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def get_value(args)
|
|
37
|
+
args[:value] || args[:attributes].fetch(@attribute) do |a|
|
|
38
|
+
args[:attributes][a.to_s] || args[:attributes][a.to_sym]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class EqualToBooleanMatcher
|
|
5
|
+
MATCHER_TYPE = 'EQUAL_TO_BOOLEAN'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute, boolean, logger)
|
|
10
|
+
@attribute = attribute
|
|
11
|
+
@boolean = boolean
|
|
12
|
+
@logger = logger
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def match?(args)
|
|
16
|
+
value = get_value(args)
|
|
17
|
+
value = false if value.to_s.casecmp('false').zero?
|
|
18
|
+
value = true if value.to_s.casecmp('true').zero?
|
|
19
|
+
|
|
20
|
+
matches = value == @boolean
|
|
21
|
+
@logger.log_if_debug("[EqualToBooleanMatcher] #{value} equals to #{@boolean} -> #{matches}")
|
|
22
|
+
matches
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def string_type?
|
|
26
|
+
false
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def get_value(args)
|
|
32
|
+
args[:attributes].fetch(@attribute) do |a|
|
|
33
|
+
args[:attributes][a.to_s] || args[:attributes][a.to_sym]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
class EqualToMatcher < Matcher
|
|
5
|
+
MATCHER_TYPE = 'EQUAL_TO'
|
|
6
|
+
|
|
7
|
+
attr_reader :attribute
|
|
8
|
+
|
|
9
|
+
def initialize(attribute_hash, logger, validator)
|
|
10
|
+
super(logger)
|
|
11
|
+
@validator = validator
|
|
12
|
+
@attribute = attribute_hash[:attribute]
|
|
13
|
+
@data_type = attribute_hash[:data_type]
|
|
14
|
+
@value = formatted_value(attribute_hash[:value], true)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def match?(args)
|
|
18
|
+
@logger.log_if_debug('[EqualsToMatcher] evaluating value and attributes.')
|
|
19
|
+
|
|
20
|
+
return false unless @validator.valid_matcher_arguments(args)
|
|
21
|
+
|
|
22
|
+
value = formatted_value(args[:value] || args[:attributes][@attribute.to_sym])
|
|
23
|
+
|
|
24
|
+
matches = value.is_a?(Integer) ? (value == @value) : false
|
|
25
|
+
@logger.log_if_debug("[EqualsToMatcher] #{value} equals to #{@value} -> #{matches}")
|
|
26
|
+
matches
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def formatted_value(value, sdk_data = false)
|
|
32
|
+
case @data_type
|
|
33
|
+
when 'NUMBER'
|
|
34
|
+
value
|
|
35
|
+
when 'DATETIME'
|
|
36
|
+
value /= 1000 if sdk_data
|
|
37
|
+
|
|
38
|
+
SplitIoClient::Utilities.to_milis_zero_out_from_hour value
|
|
39
|
+
else
|
|
40
|
+
@logger.error('Invalid data type')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|