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,530 @@
|
|
|
1
|
+
module SplitIoClient
|
|
2
|
+
EVENTS_SIZE_THRESHOLD = 32768
|
|
3
|
+
EVENT_AVERAGE_SIZE = 1024
|
|
4
|
+
GET_TREATMENT = 'get_treatment'
|
|
5
|
+
GET_TREATMENTS = 'get_treatments'
|
|
6
|
+
GET_TREATMENT_WITH_CONFIG = 'get_treatment_with_config'
|
|
7
|
+
GET_TREATMENTS_WITH_CONFIG = 'get_treatments_with_config'
|
|
8
|
+
GET_TREATMENTS_BY_FLAG_SET = 'get_treatments_by_flag_set'
|
|
9
|
+
GET_TREATMENTS_BY_FLAG_SETS = 'get_treatments_by_flag_sets'
|
|
10
|
+
GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET = 'get_treatments_with_config_by_flag_set'
|
|
11
|
+
GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS = 'get_treatments_with_config_by_flag_sets'
|
|
12
|
+
TRACK = 'track'
|
|
13
|
+
|
|
14
|
+
class SplitClient
|
|
15
|
+
#
|
|
16
|
+
# Creates a new split client instance that connects to split.io API.
|
|
17
|
+
#
|
|
18
|
+
# @param sdk_key [String] the SDK key for your split account
|
|
19
|
+
#
|
|
20
|
+
# @return [SplitIoClient] split.io client instance
|
|
21
|
+
def initialize(sdk_key, repositories, status_manager, config, impressions_manager, telemetry_evaluation_producer, evaluator, split_validator, fallback_treatment_calculator, events_manager)
|
|
22
|
+
@api_key = sdk_key
|
|
23
|
+
@splits_repository = repositories[:splits]
|
|
24
|
+
@segments_repository = repositories[:segments]
|
|
25
|
+
@rule_based_segments_repository = repositories[:rule_based_segments]
|
|
26
|
+
@impressions_repository = repositories[:impressions]
|
|
27
|
+
@events_repository = repositories[:events]
|
|
28
|
+
@status_manager = status_manager
|
|
29
|
+
@destroyed = false
|
|
30
|
+
@config = config
|
|
31
|
+
@impressions_manager = impressions_manager
|
|
32
|
+
@telemetry_evaluation_producer = telemetry_evaluation_producer
|
|
33
|
+
@split_validator = split_validator
|
|
34
|
+
@evaluator = evaluator
|
|
35
|
+
@fallback_treatment_calculator = fallback_treatment_calculator
|
|
36
|
+
@events_manager = events_manager
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def get_treatment(
|
|
40
|
+
key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = nil,
|
|
41
|
+
multiple = false, evaluator = nil
|
|
42
|
+
)
|
|
43
|
+
log_deprecated_warning(GET_TREATMENT, evaluator, 'evaluator')
|
|
44
|
+
|
|
45
|
+
result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT, multiple, evaluation_options)
|
|
46
|
+
return result.tap { |t| t.delete(:config) } if multiple
|
|
47
|
+
result[:treatment]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_treatment_with_config(
|
|
51
|
+
key, split_name, attributes = {}, evaluation_options = nil, split_data = nil, store_impressions = nil,
|
|
52
|
+
multiple = false, evaluator = nil
|
|
53
|
+
)
|
|
54
|
+
log_deprecated_warning(GET_TREATMENT, evaluator, 'evaluator')
|
|
55
|
+
result = treatment(key, split_name, attributes, split_data, store_impressions, GET_TREATMENT_WITH_CONFIG, multiple, evaluation_options)
|
|
56
|
+
|
|
57
|
+
{ :config => result[:config], :treatment => result[:treatment] }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def get_treatments(key, split_names, attributes = {}, evaluation_options = nil)
|
|
61
|
+
treatments = treatments(key, split_names, attributes, evaluation_options)
|
|
62
|
+
|
|
63
|
+
return treatments if treatments.nil?
|
|
64
|
+
keys = treatments.keys
|
|
65
|
+
treats = treatments.map { |_,t| t[:treatment] }
|
|
66
|
+
Hash[keys.zip(treats)]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def get_treatments_with_config(key, split_names, attributes = {}, evaluation_options = nil)
|
|
70
|
+
results = treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_WITH_CONFIG)
|
|
71
|
+
|
|
72
|
+
results.map{|key, value|
|
|
73
|
+
[key, { treatment: value[:treatment], config: value[:config] }]
|
|
74
|
+
}.to_h
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def get_treatments_by_flag_set(key, flag_set, attributes = {}, evaluation_options = nil)
|
|
78
|
+
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SET, [flag_set])
|
|
79
|
+
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
|
|
80
|
+
treatments = treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_BY_FLAG_SET)
|
|
81
|
+
return treatments if treatments.nil?
|
|
82
|
+
keys = treatments.keys
|
|
83
|
+
treats = treatments.map { |_,t| t[:treatment] }
|
|
84
|
+
Hash[keys.zip(treats)]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def get_treatments_by_flag_sets(key, flag_sets, attributes = {}, evaluation_options = nil)
|
|
88
|
+
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_BY_FLAG_SETS, flag_sets)
|
|
89
|
+
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
|
|
90
|
+
treatments = treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_BY_FLAG_SETS)
|
|
91
|
+
return treatments if treatments.nil?
|
|
92
|
+
keys = treatments.keys
|
|
93
|
+
treats = treatments.map { |_,t| t[:treatment] }
|
|
94
|
+
Hash[keys.zip(treats)]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def get_treatments_with_config_by_flag_set(key, flag_set, attributes = {}, evaluation_options = nil)
|
|
98
|
+
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, [flag_set])
|
|
99
|
+
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
|
|
100
|
+
results = treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET)
|
|
101
|
+
|
|
102
|
+
results.map{|key, value|
|
|
103
|
+
[key, { treatment: value[:treatment], config: value[:config] }]
|
|
104
|
+
}.to_h
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def get_treatments_with_config_by_flag_sets(key, flag_sets, attributes = {}, evaluation_options = nil)
|
|
108
|
+
valid_flag_set = @split_validator.valid_flag_sets(GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, flag_sets)
|
|
109
|
+
split_names = @splits_repository.get_feature_flags_by_sets(valid_flag_set)
|
|
110
|
+
results = treatments(key, split_names, attributes, evaluation_options, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS)
|
|
111
|
+
|
|
112
|
+
results.map{|key, value|
|
|
113
|
+
[key, { treatment: value[:treatment], config: value[:config] }]
|
|
114
|
+
}.to_h
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def destroy
|
|
118
|
+
@config.logger.info('Split client shutdown started...') if @config.debug_enabled
|
|
119
|
+
if !@config.cache_adapter.is_a?(SplitIoClient::Cache::Adapters::RedisAdapter) && @config.impressions_mode != :none &&
|
|
120
|
+
(!@impressions_repository.empty? || !@events_repository.empty?)
|
|
121
|
+
@config.logger.debug("Impressions and/or Events cache is not empty") if @config.debug_enabled
|
|
122
|
+
# Adding small delay to ensure sender threads are fully running
|
|
123
|
+
sleep(0.1)
|
|
124
|
+
if !@config.threads.key?(:impressions_sender) || !@config.threads.key?(:events_sender)
|
|
125
|
+
@config.logger.debug("Periodic data recording thread has not started yet, waiting for service startup.") if @config.debug_enabled
|
|
126
|
+
@config.threads[:start_sdk].join(5) if @config.threads.key?(:start_sdk)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
@config.threads.select { |name, thread| name.to_s.end_with? 'sender' }.values.each do |thread|
|
|
130
|
+
thread.raise(SplitIoClient::SDKShutdownException)
|
|
131
|
+
thread.join
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
@config.threads.values.each { |thread| Thread.kill(thread) }
|
|
135
|
+
|
|
136
|
+
@splits_repository.clear
|
|
137
|
+
@segments_repository.clear
|
|
138
|
+
@rule_based_segments_repository.clear
|
|
139
|
+
|
|
140
|
+
SplitIoClient.load_factory_registry
|
|
141
|
+
SplitIoClient.split_factory_registry.remove(@api_key)
|
|
142
|
+
|
|
143
|
+
@config.logger.info('Split client shutdown complete') if @config.debug_enabled
|
|
144
|
+
@config.valid_mode = false
|
|
145
|
+
@destroyed = true
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def track(key, traffic_type_name, event_type, value = nil, properties = nil)
|
|
149
|
+
return false unless valid_client && @config.split_validator.valid_track_parameters(key, traffic_type_name, event_type, value, properties)
|
|
150
|
+
|
|
151
|
+
start = Time.now
|
|
152
|
+
properties_size = EVENT_AVERAGE_SIZE
|
|
153
|
+
|
|
154
|
+
if !properties.nil?
|
|
155
|
+
properties, size = validate_properties(properties)
|
|
156
|
+
properties_size += size
|
|
157
|
+
return false unless check_properties_size(properties_size)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
if ready? && !@config.localhost_mode && !@splits_repository.traffic_type_exists(traffic_type_name)
|
|
161
|
+
@config.logger.warn("track: Traffic Type #{traffic_type_name} " \
|
|
162
|
+
"does not have any corresponding feature flags in this environment, make sure you're tracking " \
|
|
163
|
+
'your events to a valid traffic type defined in the Split user interface')
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
@events_repository.add(key.to_s, traffic_type_name.downcase, event_type.to_s, (Time.now.to_f * 1000).to_i, value, properties, properties_size)
|
|
167
|
+
record_latency(TRACK, start)
|
|
168
|
+
true
|
|
169
|
+
rescue StandardError => e
|
|
170
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
171
|
+
record_exception(TRACK)
|
|
172
|
+
|
|
173
|
+
false
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def block_until_ready(time = nil)
|
|
177
|
+
@status_manager.wait_until_ready(time) if @status_manager
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def register(sdk_event, handler)
|
|
181
|
+
@events_manager.register(sdk_event, handler)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def unregister(sdk_event, handler)
|
|
185
|
+
@events_manager.unregister(sdk_event)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
def check_properties_size(properties_size, msg = "Event not queued")
|
|
191
|
+
if (properties_size > EVENTS_SIZE_THRESHOLD)
|
|
192
|
+
@config.logger.error("The maximum size allowed for the properties is #{EVENTS_SIZE_THRESHOLD}. Current is #{properties_size}. #{msg}")
|
|
193
|
+
return false
|
|
194
|
+
end
|
|
195
|
+
return true
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def keys_from_key(key)
|
|
199
|
+
case key
|
|
200
|
+
when Hash
|
|
201
|
+
key.values_at(:bucketing_key, :matching_key).map { |k| k.nil? ? nil : k }
|
|
202
|
+
else
|
|
203
|
+
[nil, key].map { |k| k.nil? ? nil : k }
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def parsed_treatment(treatment_data, multiple = false)
|
|
208
|
+
if multiple
|
|
209
|
+
{
|
|
210
|
+
treatment: treatment_data[:treatment],
|
|
211
|
+
label: treatment_data[:label],
|
|
212
|
+
change_number: treatment_data[:change_number],
|
|
213
|
+
config: treatment_data[:config]
|
|
214
|
+
}
|
|
215
|
+
else
|
|
216
|
+
{
|
|
217
|
+
treatment: treatment_data[:treatment],
|
|
218
|
+
config: treatment_data[:config],
|
|
219
|
+
}
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def sanitize_split_names(calling_method, split_names)
|
|
224
|
+
return nil if !split_names.is_a?(Array)
|
|
225
|
+
|
|
226
|
+
split_names.compact.uniq.select do |split_name|
|
|
227
|
+
if split_name.nil?
|
|
228
|
+
false
|
|
229
|
+
elsif (split_name.is_a?(String) || split_name.is_a?(Symbol)) && !split_name.empty?
|
|
230
|
+
true
|
|
231
|
+
elsif split_name.is_a?(String) && split_name.empty?
|
|
232
|
+
@config.logger.warn("#{calling_method}: you passed an empty feature_flag_name, flag name must be a non-empty String or a Symbol")
|
|
233
|
+
false
|
|
234
|
+
else
|
|
235
|
+
@config.logger.warn("#{calling_method}: you passed an invalid feature_flag_name, flag name must be a non-empty String or a Symbol")
|
|
236
|
+
false
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def validate_properties(properties, method = 'Event')
|
|
242
|
+
properties_count = 0
|
|
243
|
+
size = 0
|
|
244
|
+
|
|
245
|
+
fixed_properties = properties.each_with_object({}) { |(key, value), result|
|
|
246
|
+
|
|
247
|
+
if(key.is_a?(String) || key.is_a?(Symbol))
|
|
248
|
+
properties_count += 1
|
|
249
|
+
size += variable_size(key)
|
|
250
|
+
if value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Numeric) || value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.nil?
|
|
251
|
+
result[key] = value
|
|
252
|
+
size += variable_size(value)
|
|
253
|
+
else
|
|
254
|
+
@config.logger.warn("Property #{key} is of invalid type. Setting value to nil")
|
|
255
|
+
result[key] = nil
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
@config.logger.warn("#{method} has more than 300 properties. Some of them will be trimmed when processed") if properties_count > 300
|
|
261
|
+
|
|
262
|
+
return fixed_properties, size
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def validate_evaluation_options(evaluation_options)
|
|
266
|
+
if !evaluation_options.is_a?(SplitIoClient::Engine::Models::EvaluationOptions)
|
|
267
|
+
@config.logger.warn("Option #{evaluation_options} should be a EvaluationOptions type. Setting value to nil")
|
|
268
|
+
return nil, 0
|
|
269
|
+
end
|
|
270
|
+
evaluation_options.properties = evaluation_options.properties.transform_keys(&:to_sym)
|
|
271
|
+
evaluation_options.properties, size = validate_properties(evaluation_options.properties, 'Treatment')
|
|
272
|
+
return evaluation_options, size
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def valid_client
|
|
276
|
+
if @destroyed
|
|
277
|
+
@config.logger.error('Client has already been destroyed - no calls possible')
|
|
278
|
+
return false
|
|
279
|
+
end
|
|
280
|
+
@config.valid_mode
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def treatments(key, feature_flag_names, attributes = {}, evaluation_options = nil, calling_method = 'get_treatments')
|
|
284
|
+
sanitized_feature_flag_names = sanitize_split_names(calling_method, feature_flag_names)
|
|
285
|
+
|
|
286
|
+
if sanitized_feature_flag_names.nil?
|
|
287
|
+
@config.logger.error("#{calling_method}: feature_flag_names must be a non-empty Array")
|
|
288
|
+
return nil
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
if sanitized_feature_flag_names.empty?
|
|
292
|
+
@config.logger.error("#{calling_method}: feature_flag_names must be a non-empty Array")
|
|
293
|
+
return {}
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
bucketing_key, matching_key = keys_from_key(key)
|
|
297
|
+
bucketing_key = bucketing_key ? bucketing_key.to_s : nil
|
|
298
|
+
matching_key = matching_key ? matching_key.to_s : nil
|
|
299
|
+
attributes = parsed_attributes(attributes)
|
|
300
|
+
|
|
301
|
+
if !@config.split_validator.valid_get_treatments_parameters(calling_method, key, sanitized_feature_flag_names, matching_key, bucketing_key, attributes)
|
|
302
|
+
to_return = Hash.new
|
|
303
|
+
sanitized_feature_flag_names.each {|name|
|
|
304
|
+
to_return[name.to_sym] = check_fallback_treatment(name, '')
|
|
305
|
+
}
|
|
306
|
+
return to_return
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
if !ready?
|
|
310
|
+
impressions = []
|
|
311
|
+
to_return = Hash.new
|
|
312
|
+
sanitized_feature_flag_names.each {|name|
|
|
313
|
+
treatment_data = check_fallback_treatment(name, Engine::Models::Label::NOT_READY)
|
|
314
|
+
to_return[name.to_sym] = treatment_data
|
|
315
|
+
|
|
316
|
+
impressions << { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, name.to_sym,
|
|
317
|
+
get_treatment_without_config(treatment_data), false, { attributes: attributes, time: nil },
|
|
318
|
+
evaluation_options), :disabled => false }
|
|
319
|
+
}
|
|
320
|
+
@impressions_manager.track(impressions)
|
|
321
|
+
return to_return
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
valid_feature_flag_names = []
|
|
325
|
+
sanitized_feature_flag_names.each { |feature_flag_name|
|
|
326
|
+
valid_feature_flag_names << feature_flag_name unless feature_flag_name.nil?
|
|
327
|
+
}
|
|
328
|
+
start = Time.now
|
|
329
|
+
|
|
330
|
+
feature_flags = @splits_repository.splits(valid_feature_flag_names)
|
|
331
|
+
treatments = Hash.new
|
|
332
|
+
invalid_treatments = Hash.new
|
|
333
|
+
feature_flags.each do |key, feature_flag|
|
|
334
|
+
if feature_flag.nil?
|
|
335
|
+
@config.logger.warn("#{calling_method}: you passed #{key} that " \
|
|
336
|
+
'does not exist in this environment, please double check what feature flags exist in the Split user interface')
|
|
337
|
+
invalid_treatments[key] = check_fallback_treatment(key, Engine::Models::Label::NOT_FOUND)
|
|
338
|
+
next
|
|
339
|
+
end
|
|
340
|
+
treatments_labels_change_numbers, impressions = evaluate_treatment(feature_flag, key, bucketing_key, matching_key, attributes, calling_method, false, evaluation_options)
|
|
341
|
+
treatments[key] =
|
|
342
|
+
{
|
|
343
|
+
treatment: treatments_labels_change_numbers[:treatment],
|
|
344
|
+
config: treatments_labels_change_numbers[:config]
|
|
345
|
+
}
|
|
346
|
+
@impressions_manager.track(impressions) unless impressions.empty?
|
|
347
|
+
end
|
|
348
|
+
record_latency(calling_method, start)
|
|
349
|
+
|
|
350
|
+
treatments.merge(invalid_treatments)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
#
|
|
354
|
+
# obtains the treatment for a given feature
|
|
355
|
+
#
|
|
356
|
+
# @param key [String/Hash] user id or hash with matching_key/bucketing_key
|
|
357
|
+
# @param split_name [String/Array] name of the feature that is being validated or array of them
|
|
358
|
+
# @param attributes [Hash] attributes to pass to the treatment class
|
|
359
|
+
# @param split_data [Hash] split data, when provided this method doesn't fetch splits_repository for the data
|
|
360
|
+
# @param store_impressions [Boolean] impressions aren't stored if this flag is false
|
|
361
|
+
# @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
|
|
362
|
+
def treatment(key, feature_flag_name, attributes = {}, split_data = nil, store_impressions = nil,
|
|
363
|
+
calling_method = 'get_treatment', multiple = false, evaluation_options = nil)
|
|
364
|
+
|
|
365
|
+
log_deprecated_warning(calling_method, split_data, 'split_data')
|
|
366
|
+
log_deprecated_warning(calling_method, store_impressions, 'store_impressions')
|
|
367
|
+
|
|
368
|
+
impressions = []
|
|
369
|
+
bucketing_key, matching_key = keys_from_key(key)
|
|
370
|
+
|
|
371
|
+
attributes = parsed_attributes(attributes)
|
|
372
|
+
|
|
373
|
+
return parsed_treatment(check_fallback_treatment(feature_flag_name, ""), multiple) unless valid_client && @config.split_validator.valid_get_treatment_parameters(calling_method, key, feature_flag_name, matching_key, bucketing_key, attributes)
|
|
374
|
+
|
|
375
|
+
bucketing_key = bucketing_key ? bucketing_key.to_s : nil
|
|
376
|
+
matching_key = matching_key.to_s
|
|
377
|
+
sanitized_feature_flag_name = feature_flag_name.to_s.strip
|
|
378
|
+
|
|
379
|
+
if feature_flag_name.to_s != sanitized_feature_flag_name
|
|
380
|
+
@config.logger.warn("#{calling_method}: feature_flag_name #{feature_flag_name} has extra whitespace, trimming")
|
|
381
|
+
feature_flag_name = sanitized_feature_flag_name
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
feature_flag = @splits_repository.get_split(feature_flag_name)
|
|
385
|
+
treatments, impressions_decorator = evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple, evaluation_options)
|
|
386
|
+
|
|
387
|
+
@impressions_manager.track(impressions_decorator) unless impressions_decorator.nil?
|
|
388
|
+
treatments
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def log_deprecated_warning(calling_method, parameter, parameter_name)
|
|
392
|
+
@config.logger.warn("#{calling_method}: detected #{parameter_name} parameter used, this parameter is deprecated and its value is ignored.") unless parameter.nil?
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def evaluate_treatment(feature_flag, feature_flag_name, bucketing_key, matching_key, attributes, calling_method, multiple = false, evaluation_options = nil)
|
|
396
|
+
impressions_decorator = []
|
|
397
|
+
begin
|
|
398
|
+
start = Time.now
|
|
399
|
+
if feature_flag.nil? && ready?
|
|
400
|
+
@config.logger.warn("#{calling_method}: you passed #{feature_flag_name} that " \
|
|
401
|
+
'does not exist in this environment, please double check what feature flags exist in the Split user interface')
|
|
402
|
+
return check_fallback_treatment(feature_flag_name, Engine::Models::Label::NOT_FOUND), nil
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
if !feature_flag.nil? && ready?
|
|
406
|
+
treatment_data = @evaluator.evaluate_feature_flag(
|
|
407
|
+
{ bucketing_key: bucketing_key, matching_key: matching_key }, feature_flag, attributes
|
|
408
|
+
)
|
|
409
|
+
impressions_disabled = feature_flag[:impressionsDisabled]
|
|
410
|
+
else
|
|
411
|
+
@config.logger.error("#{calling_method}: the SDK is not ready, results may be incorrect for feature flag #{feature_flag_name}. Make sure to wait for SDK readiness before using this method.")
|
|
412
|
+
treatment_data = check_fallback_treatment(feature_flag_name, Engine::Models::Label::NOT_READY)
|
|
413
|
+
impressions_disabled = false
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
evaluation_options, size = validate_evaluation_options(evaluation_options) unless evaluation_options.nil?
|
|
417
|
+
evaluation_options.properties = nil unless evaluation_options.nil? or check_properties_size((EVENT_AVERAGE_SIZE + size), "Properties are ignored")
|
|
418
|
+
|
|
419
|
+
record_latency(calling_method, start)
|
|
420
|
+
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, treatment_data, impressions_disabled, { attributes: attributes, time: nil }, evaluation_options), :disabled => impressions_disabled }
|
|
421
|
+
impressions_decorator << impression_decorator unless impression_decorator.nil?
|
|
422
|
+
rescue StandardError => e
|
|
423
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
424
|
+
record_exception(calling_method)
|
|
425
|
+
treatment_data = check_fallback_treatment(feature_flag_name, Engine::Models::Label::EXCEPTION)
|
|
426
|
+
impression_decorator = { :impression => @impressions_manager.build_impression(matching_key, bucketing_key, feature_flag_name, get_treatment_without_config(treatment_data), false, { attributes: attributes, time: nil }, evaluation_options), :disabled => false }
|
|
427
|
+
|
|
428
|
+
impressions_decorator << impression_decorator unless impression_decorator.nil?
|
|
429
|
+
|
|
430
|
+
return parsed_treatment(treatment_data, multiple), impressions_decorator
|
|
431
|
+
end
|
|
432
|
+
return parsed_treatment(treatment_data, multiple), impressions_decorator
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def variable_size(value)
|
|
436
|
+
value.is_a?(String) ? value.length : 0
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def ready?
|
|
440
|
+
return @status_manager.ready? if @status_manager
|
|
441
|
+
true
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def parsed_attributes(attributes)
|
|
445
|
+
return attributes || attributes.to_h
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def record_latency(method, start)
|
|
449
|
+
bucket = BinarySearchLatencyTracker.get_bucket((Time.now - start) * 1000.0)
|
|
450
|
+
|
|
451
|
+
case method
|
|
452
|
+
when GET_TREATMENT
|
|
453
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENT, bucket)
|
|
454
|
+
when GET_TREATMENTS
|
|
455
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS, bucket)
|
|
456
|
+
when GET_TREATMENT_WITH_CONFIG
|
|
457
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENT_WITH_CONFIG, bucket)
|
|
458
|
+
when GET_TREATMENTS_WITH_CONFIG
|
|
459
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG, bucket)
|
|
460
|
+
when GET_TREATMENTS_BY_FLAG_SET
|
|
461
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SET, bucket)
|
|
462
|
+
when GET_TREATMENTS_BY_FLAG_SETS
|
|
463
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SETS, bucket)
|
|
464
|
+
when GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET
|
|
465
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG_BY_FLAG_SET, bucket)
|
|
466
|
+
when GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS
|
|
467
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, bucket)
|
|
468
|
+
when TRACK
|
|
469
|
+
@telemetry_evaluation_producer.record_latency(Telemetry::Domain::Constants::TRACK, bucket)
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def record_exception(method)
|
|
474
|
+
case method
|
|
475
|
+
when GET_TREATMENT
|
|
476
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENT)
|
|
477
|
+
when GET_TREATMENTS
|
|
478
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS)
|
|
479
|
+
when GET_TREATMENT_WITH_CONFIG
|
|
480
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENT_WITH_CONFIG)
|
|
481
|
+
when GET_TREATMENTS_WITH_CONFIG
|
|
482
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG)
|
|
483
|
+
when GET_TREATMENTS_BY_FLAG_SET
|
|
484
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SET)
|
|
485
|
+
when GET_TREATMENTS_BY_FLAG_SETS
|
|
486
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_BY_FLAG_SETS)
|
|
487
|
+
when GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET
|
|
488
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG_BY_FLAG_SET)
|
|
489
|
+
when GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS
|
|
490
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TREATMENTS_WITH_CONFIG_BY_FLAG_SETS)
|
|
491
|
+
when TRACK
|
|
492
|
+
@telemetry_evaluation_producer.record_exception(Telemetry::Domain::Constants::TRACK)
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def check_fallback_treatment(feature_name, label)
|
|
497
|
+
return {
|
|
498
|
+
label: (label != '')? label : nil,
|
|
499
|
+
treatment: Engine::Models::Treatment::CONTROL,
|
|
500
|
+
config: nil,
|
|
501
|
+
change_number: nil
|
|
502
|
+
} unless feature_name.is_a?(Symbol) || feature_name.is_a?(String)
|
|
503
|
+
|
|
504
|
+
fallback_treatment = @fallback_treatment_calculator.resolve(feature_name.to_sym, label)
|
|
505
|
+
|
|
506
|
+
{
|
|
507
|
+
label: (label != '')? fallback_treatment.label : nil,
|
|
508
|
+
treatment: fallback_treatment.treatment,
|
|
509
|
+
config: get_fallback_config(fallback_treatment),
|
|
510
|
+
change_number: nil
|
|
511
|
+
}
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def get_treatment_without_config(treatment)
|
|
515
|
+
{
|
|
516
|
+
label: treatment[:label],
|
|
517
|
+
treatment: treatment[:treatment],
|
|
518
|
+
}
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def get_fallback_config(fallback_treatment)
|
|
522
|
+
if fallback_treatment.config != nil
|
|
523
|
+
return fallback_treatment.config
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
return nil
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
end
|
|
530
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class SplitIoClient::Constants
|
|
4
|
+
EXPIRATION_RATE = 600
|
|
5
|
+
CONTROL_PRI = 'control_pri'
|
|
6
|
+
CONTROL_SEC = 'control_sec'
|
|
7
|
+
OCCUPANCY_CHANNEL_PREFIX = '[?occupancy=metrics.publishers]'
|
|
8
|
+
FETCH_BACK_OFF_BASE_RETRIES = 1
|
|
9
|
+
PUSH_CONNECTED = 'PUSH_CONNECTED'
|
|
10
|
+
PUSH_RETRYABLE_ERROR = 'PUSH_RETRYABLE_ERROR'
|
|
11
|
+
PUSH_NONRETRYABLE_ERROR = 'PUSH_NONRETRYABLE_ERROR'
|
|
12
|
+
PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN'
|
|
13
|
+
PUSH_SUBSYSTEM_READY = 'PUSH_SUBSYSTEM_READY'
|
|
14
|
+
PUSH_SUBSYSTEM_OFF = 'PUSH_SUBSYSTEM_OFF'
|
|
15
|
+
PUSH_FORCED_STOP = 'PUSH_FORCED_STOP'
|
|
16
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Api
|
|
5
|
+
class Client
|
|
6
|
+
def initialize(config)
|
|
7
|
+
@config = config
|
|
8
|
+
|
|
9
|
+
check_faraday_compatibility
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def get_api(url, api_key, params = {}, cache_control_headers = false)
|
|
13
|
+
api_client.options.params_encoder.sort_params = false
|
|
14
|
+
api_client.get(url, params) do |req|
|
|
15
|
+
req.headers = common_headers(api_key).merge('Accept-Encoding' => 'gzip')
|
|
16
|
+
req.headers = req.headers.merge('Cache-Control' => 'no-cache') if cache_control_headers
|
|
17
|
+
|
|
18
|
+
req.options[:timeout] = @config.read_timeout
|
|
19
|
+
req.options[:open_timeout] = @config.connection_timeout
|
|
20
|
+
|
|
21
|
+
@config.split_logger.log_if_debug("GET #{url} proxy: #{api_client.proxy}")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rescue Zlib::GzipFile::Error => e
|
|
25
|
+
@config.logger.warn("Incorrect formatted response exception: #{e}\n")
|
|
26
|
+
SplitIoClient::Engine::Models::SplitHttpResponse.new(400, '', true)
|
|
27
|
+
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
@config.logger.warn("#{e}\nURL:#{url}\nparams:#{params}")
|
|
30
|
+
raise e, 'Split SDK failed to connect to backend to retrieve information', e.backtrace
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def post_api(url, api_key, data, headers = {}, params = {})
|
|
34
|
+
api_client.post(url) do |req|
|
|
35
|
+
req.headers = common_headers(api_key)
|
|
36
|
+
.merge('Content-Type' => 'application/json')
|
|
37
|
+
.merge(headers)
|
|
38
|
+
|
|
39
|
+
machine_ip = @config.machine_ip
|
|
40
|
+
machine_name = @config.machine_name
|
|
41
|
+
|
|
42
|
+
req.headers = req.headers.merge('SplitSDKMachineIP' => machine_ip) unless machine_ip.empty? || machine_ip == 'unknown'
|
|
43
|
+
req.headers = req.headers.merge('SplitSDKMachineName' => machine_name) unless machine_name.empty? || machine_name == 'unknown'
|
|
44
|
+
|
|
45
|
+
req.body = data.to_json
|
|
46
|
+
|
|
47
|
+
req.options[:timeout] = @config.read_timeout
|
|
48
|
+
req.options[:open_timeout] = @config.connection_timeout
|
|
49
|
+
|
|
50
|
+
@config.split_logger.log_if_transport("POST #{url} #{req.body}")
|
|
51
|
+
@config.split_logger.log_if_debug("POST #{url}")
|
|
52
|
+
end
|
|
53
|
+
rescue StandardError => e
|
|
54
|
+
@config.logger.warn("#{e}\nURL:#{url}\ndata:#{data}\nparams:#{params}")
|
|
55
|
+
raise e, 'Split SDK failed to connect to backend to post information', e.backtrace
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def sdk_url_overriden?
|
|
59
|
+
@config.sdk_url_overriden?
|
|
60
|
+
end
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def api_client
|
|
64
|
+
@api_client ||= Faraday.new do |builder|
|
|
65
|
+
builder.use SplitIoClient::FaradayMiddleware::Gzip
|
|
66
|
+
builder.adapter :net_http_persistent
|
|
67
|
+
builder.options.params_encoder = Faraday::FlatParamsEncoder
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def common_headers(api_key)
|
|
72
|
+
{
|
|
73
|
+
'Authorization' => "Bearer #{api_key}",
|
|
74
|
+
'SplitSDKVersion' => "#{@config.language}-#{@config.version}",
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def check_faraday_compatibility
|
|
79
|
+
version = Faraday::VERSION.split('.')[0]
|
|
80
|
+
|
|
81
|
+
require 'faraday/net_http_persistent' if version.to_i >= 2
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
@config.logger.warn(e)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Api
|
|
5
|
+
class Events < Client
|
|
6
|
+
def initialize(api_key, config, telemetry_runtime_producer)
|
|
7
|
+
super(config)
|
|
8
|
+
@api_key = api_key
|
|
9
|
+
@telemetry_runtime_producer = telemetry_runtime_producer
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def post(events)
|
|
13
|
+
if events.empty?
|
|
14
|
+
@config.split_logger.log_if_debug('No events to report')
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
start = Time.now
|
|
19
|
+
|
|
20
|
+
events.each_slice(@config.events_queue_size) do |events_slice|
|
|
21
|
+
response = post_api(
|
|
22
|
+
"#{@config.events_uri}/events/bulk",
|
|
23
|
+
@api_key,
|
|
24
|
+
events_slice.map { |event| formatted_event(event[:e]) }
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if response.success?
|
|
28
|
+
@config.split_logger.log_if_debug("Events reported: #{events_slice.size}")
|
|
29
|
+
|
|
30
|
+
bucket = BinarySearchLatencyTracker.get_bucket((Time.now - start) * 1000.0)
|
|
31
|
+
@telemetry_runtime_producer.record_sync_latency(Telemetry::Domain::Constants::EVENT_SYNC, bucket)
|
|
32
|
+
@telemetry_runtime_producer.record_successful_sync(Telemetry::Domain::Constants::EVENT_SYNC, (Time.now.to_f * 1000.0).to_i)
|
|
33
|
+
else
|
|
34
|
+
@telemetry_runtime_producer.record_sync_error(Telemetry::Domain::Constants::EVENT_SYNC, response.status)
|
|
35
|
+
|
|
36
|
+
@config.logger.error("Unexpected status code while posting events: #{response.status}." \
|
|
37
|
+
' - Check your API key and base URI')
|
|
38
|
+
raise 'Split SDK failed to connect to backend to post events'
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def formatted_event(event)
|
|
46
|
+
{
|
|
47
|
+
key: event[:key],
|
|
48
|
+
trafficTypeName: event[:trafficTypeName],
|
|
49
|
+
eventTypeId: event[:eventTypeId],
|
|
50
|
+
value: event[:value].to_f,
|
|
51
|
+
timestamp: event[:timestamp].to_i,
|
|
52
|
+
properties: event[:properties]
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|