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,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'jwt'
|
|
4
|
+
require 'cgi'
|
|
5
|
+
|
|
6
|
+
module SplitIoClient
|
|
7
|
+
module Engine
|
|
8
|
+
class AuthApiClient
|
|
9
|
+
def initialize(config, telemetry_runtime_producer)
|
|
10
|
+
@config = config
|
|
11
|
+
@api_client = SplitIoClient::Api::Client.new(@config)
|
|
12
|
+
@telemetry_runtime_producer = telemetry_runtime_producer
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def authenticate(api_key)
|
|
16
|
+
start = Time.now
|
|
17
|
+
response = @api_client.get_api("#{@config.auth_service_url}?s=#{SplitIoClient::Spec::FeatureFlags::SPEC_VERSION}", api_key)
|
|
18
|
+
|
|
19
|
+
return process_success(response, start) if response.success?
|
|
20
|
+
|
|
21
|
+
return process_error(response) if response.status >= 400 && response.status < 500
|
|
22
|
+
|
|
23
|
+
@telemetry_runtime_producer.record_sync_error(Telemetry::Domain::Constants::TOKEN_SYNC, response.status.to_i)
|
|
24
|
+
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}") if @config.debug_enabled
|
|
25
|
+
{ push_enabled: false, retry: true }
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
@config.logger.debug("AuthApiClient error: #{e.inspect}.") if @config.debug_enabled
|
|
28
|
+
{ push_enabled: false, retry: false }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def expiration(token_decoded)
|
|
34
|
+
exp = token_decoded[0]['exp']
|
|
35
|
+
issued_at = token_decoded[0]['iat']
|
|
36
|
+
|
|
37
|
+
exp - issued_at - SplitIoClient::Constants::EXPIRATION_RATE
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def channels(token_decoded)
|
|
41
|
+
capability = token_decoded[0]['x-ably-capability']
|
|
42
|
+
channels_hash = JSON.parse(capability)
|
|
43
|
+
channels_string = channels_hash.keys.join(',')
|
|
44
|
+
channels_string = control_channels(channels_string)
|
|
45
|
+
@config.logger.debug("Channels #{channels_string}") if @config.debug_enabled
|
|
46
|
+
CGI.escape(channels_string)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def decode_token(token)
|
|
50
|
+
JWT.decode token, nil, false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def process_error(response)
|
|
54
|
+
@config.logger.debug("Error connecting to: #{@config.auth_service_url}. Response status: #{response.status}") if @config.debug_enabled
|
|
55
|
+
@telemetry_runtime_producer.record_auth_rejections if response.status == 401
|
|
56
|
+
|
|
57
|
+
{ push_enabled: false, retry: false }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def process_success(response, start)
|
|
61
|
+
@config.logger.debug("Success connection to: #{@config.auth_service_url}") if @config.debug_enabled
|
|
62
|
+
record_telemetry(start)
|
|
63
|
+
|
|
64
|
+
body_json = JSON.parse(response.body, symbolize_names: true)
|
|
65
|
+
push_enabled = body_json[:pushEnabled]
|
|
66
|
+
token = body_json[:token]
|
|
67
|
+
|
|
68
|
+
if push_enabled
|
|
69
|
+
decoded_token = decode_token(token)
|
|
70
|
+
channels = channels(decoded_token)
|
|
71
|
+
exp = expiration(decoded_token)
|
|
72
|
+
|
|
73
|
+
@telemetry_runtime_producer.record_token_refreshes
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
{ push_enabled: push_enabled, token: token, channels: channels, exp: exp, retry: true }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def control_channels(channels_string)
|
|
80
|
+
prefix = SplitIoClient::Constants::OCCUPANCY_CHANNEL_PREFIX
|
|
81
|
+
control_pri = SplitIoClient::Constants::CONTROL_PRI
|
|
82
|
+
control_sec = SplitIoClient::Constants::CONTROL_SEC
|
|
83
|
+
channels_string = channels_string.gsub(control_pri, "#{prefix}#{control_pri}")
|
|
84
|
+
|
|
85
|
+
channels_string.gsub(control_sec, "#{prefix}#{control_sec}")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def record_telemetry(start)
|
|
89
|
+
bucket = BinarySearchLatencyTracker.get_bucket((Time.now - start) * 1000.0)
|
|
90
|
+
@telemetry_runtime_producer.record_sync_latency(Telemetry::Domain::Constants::TOKEN_SYNC, bucket)
|
|
91
|
+
timestamp = (Time.now.to_f * 1000.0).to_i
|
|
92
|
+
@telemetry_runtime_producer.record_successful_sync(Telemetry::Domain::Constants::TOKEN_SYNC, timestamp)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
BACKOFF_MAX_ALLOWED = 1800
|
|
6
|
+
class BackOff
|
|
7
|
+
def initialize(back_off_base, attempt = 0, max_allowed = BACKOFF_MAX_ALLOWED)
|
|
8
|
+
@attempt = attempt
|
|
9
|
+
@back_off_base = back_off_base
|
|
10
|
+
@max_allowed = max_allowed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def interval
|
|
14
|
+
interval = 0
|
|
15
|
+
interval = (@back_off_base * (2**@attempt)) if @attempt.positive?
|
|
16
|
+
@attempt += 1
|
|
17
|
+
|
|
18
|
+
interval >= @max_allowed ? @max_allowed : interval
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def reset
|
|
22
|
+
@attempt = 0
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'concurrent'
|
|
4
|
+
|
|
5
|
+
module SplitIoClient
|
|
6
|
+
module Engine
|
|
7
|
+
module Common
|
|
8
|
+
TIME_INTERVAL_MS = 3600 * 1000
|
|
9
|
+
|
|
10
|
+
class ImpressionCounter
|
|
11
|
+
DEFAULT_AMOUNT = 1
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@cache = Concurrent::Hash.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def inc(split_name, time_frame)
|
|
18
|
+
key = make_key(split_name, time_frame)
|
|
19
|
+
|
|
20
|
+
current_amount = @cache[key]
|
|
21
|
+
@cache[key] = current_amount.nil? ? DEFAULT_AMOUNT : (current_amount + DEFAULT_AMOUNT)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def pop_all
|
|
25
|
+
to_return = Concurrent::Hash.new
|
|
26
|
+
|
|
27
|
+
@cache.each do |key, value|
|
|
28
|
+
to_return[key] = value
|
|
29
|
+
end
|
|
30
|
+
@cache.clear
|
|
31
|
+
|
|
32
|
+
to_return
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def make_key(split_name, time_frame)
|
|
36
|
+
"#{split_name}::#{ImpressionCounter.truncate_time_frame(time_frame)}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.truncate_time_frame(timestamp_ms)
|
|
40
|
+
timestamp_ms - (timestamp_ms % TIME_INTERVAL_MS)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
module Common
|
|
6
|
+
class ImpressionManager
|
|
7
|
+
def initialize(config,
|
|
8
|
+
impressions_repository,
|
|
9
|
+
impression_counter,
|
|
10
|
+
telemetry_runtime_producer,
|
|
11
|
+
impression_observer,
|
|
12
|
+
unique_keys_tracker)
|
|
13
|
+
@config = config
|
|
14
|
+
@impressions_repository = impressions_repository
|
|
15
|
+
@impression_counter = impression_counter
|
|
16
|
+
@impression_observer = impression_observer
|
|
17
|
+
@telemetry_runtime_producer = telemetry_runtime_producer
|
|
18
|
+
@unique_keys_tracker = unique_keys_tracker
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def build_impression(matching_key, bucketing_key, split_name, treatment_data, impressions_disabled, params = {},
|
|
22
|
+
evaluation_options = nil)
|
|
23
|
+
properties = get_properties(evaluation_options)
|
|
24
|
+
impression_data = impression_data(matching_key, bucketing_key, split_name, treatment_data, params[:time], properties)
|
|
25
|
+
return impression(impression_data, params[:attributes]) if check_return_conditions(properties)
|
|
26
|
+
|
|
27
|
+
begin
|
|
28
|
+
if check_none_mode(impressions_disabled)
|
|
29
|
+
@impression_counter.inc(split_name, impression_data[:m])
|
|
30
|
+
@unique_keys_tracker.track(split_name, matching_key)
|
|
31
|
+
end
|
|
32
|
+
if check_observe_impressions
|
|
33
|
+
# In DEBUG mode we should calculate the pt only.
|
|
34
|
+
impression_data[:pt] = @impression_observer.test_and_set(impression_data)
|
|
35
|
+
end
|
|
36
|
+
if check_impression_counter(impression_data)
|
|
37
|
+
# In OPTIMIZED mode we should track the total amount of evaluations and deduplicate the impressions.
|
|
38
|
+
@impression_counter.inc(split_name, impression_data[:m])
|
|
39
|
+
end
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
impression(impression_data, params[:attributes])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def track(impressions_decorator)
|
|
48
|
+
return if impressions_decorator.empty?
|
|
49
|
+
|
|
50
|
+
impressions_decorator.each do |impression_decorator|
|
|
51
|
+
impression_router.add_bulk([impression_decorator[:impression]])
|
|
52
|
+
stats = { dropped: 0, queued: 0, dedupe: 0 }
|
|
53
|
+
begin
|
|
54
|
+
next if @config.impressions_mode == :none || impression_decorator[:disabled]
|
|
55
|
+
|
|
56
|
+
if @config.impressions_mode == :debug
|
|
57
|
+
track_debug_mode([impression_decorator[:impression]], stats)
|
|
58
|
+
else
|
|
59
|
+
track_optimized_mode([impression_decorator[:impression]], stats)
|
|
60
|
+
end
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
63
|
+
ensure
|
|
64
|
+
record_stats(stats)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def check_return_conditions(properties)
|
|
72
|
+
return (@config.impressions_mode == :debug || @config.impressions_mode == :optimized) && !properties.nil?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def check_none_mode(impressions_disabled)
|
|
76
|
+
return @config.impressions_mode == :none || impressions_disabled
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def check_observe_impressions
|
|
80
|
+
return @config.impressions_mode == :debug || @config.impressions_mode == :optimized
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def check_impression_counter(impression_data)
|
|
84
|
+
return @config.impressions_mode == :optimized && !impression_data[:pt].nil?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def get_properties(evaluation_options)
|
|
88
|
+
return evaluation_options.nil? ? nil : evaluation_options.properties
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def impression_router
|
|
92
|
+
@impression_router ||= SplitIoClient::ImpressionRouter.new(@config)
|
|
93
|
+
rescue StandardError => e
|
|
94
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def record_stats(stats)
|
|
98
|
+
return if redis?
|
|
99
|
+
|
|
100
|
+
imp_queued = Telemetry::Domain::Constants::IMPRESSIONS_QUEUED
|
|
101
|
+
imp_dropped = Telemetry::Domain::Constants::IMPRESSIONS_DROPPED
|
|
102
|
+
imp_dedupe = Telemetry::Domain::Constants::IMPRESSIONS_DEDUPE
|
|
103
|
+
@telemetry_runtime_producer.record_impressions_stats(imp_queued, stats[:queued]) unless stats[:queued].zero?
|
|
104
|
+
@telemetry_runtime_producer.record_impressions_stats(imp_dropped, stats[:dropped]) unless stats[:dropped].zero?
|
|
105
|
+
@telemetry_runtime_producer.record_impressions_stats(imp_dedupe, stats[:dedupe]) unless stats[:dedupe].zero?
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# added param time for test
|
|
109
|
+
def impression_data(matching_key, bucketing_key, split_name, treatment, time = nil,
|
|
110
|
+
properties = nil)
|
|
111
|
+
{
|
|
112
|
+
k: matching_key,
|
|
113
|
+
b: bucketing_key,
|
|
114
|
+
f: split_name,
|
|
115
|
+
t: treatment[:treatment],
|
|
116
|
+
r: applied_rule(treatment[:label]),
|
|
117
|
+
c: treatment[:change_number],
|
|
118
|
+
m: time || (Time.now.to_f * 1000.0).to_i,
|
|
119
|
+
pt: nil,
|
|
120
|
+
properties: properties
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def metadata
|
|
125
|
+
{
|
|
126
|
+
s: "#{@config.language}-#{@config.version}",
|
|
127
|
+
i: @config.machine_ip,
|
|
128
|
+
n: @config.machine_name
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def applied_rule(label)
|
|
133
|
+
@config.labels_enabled ? label : nil
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def should_queue_impression?(impression)
|
|
137
|
+
impression[:pt].nil? ||
|
|
138
|
+
(ImpressionCounter.truncate_time_frame(impression[:pt]) != ImpressionCounter.truncate_time_frame(impression[:m]))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def impression(impression_data, attributes)
|
|
142
|
+
{ m: metadata, i: impression_data, attributes: attributes }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def redis?
|
|
146
|
+
@config.impressions_adapter.class.to_s == 'SplitIoClient::Cache::Adapters::RedisAdapter'
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def track_debug_mode(impressions, stats)
|
|
150
|
+
stats[:dropped] = @impressions_repository.add_bulk(impressions)
|
|
151
|
+
stats[:queued] = impressions.length - stats[:dropped]
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def track_optimized_mode(impressions, stats)
|
|
155
|
+
optimized_impressions = impressions.select { |imp| should_queue_impression?(imp[:i]) }
|
|
156
|
+
stats[:dedupe] = impressions.length - optimized_impressions.length
|
|
157
|
+
return if optimized_impressions.empty?
|
|
158
|
+
|
|
159
|
+
stats[:dropped] = @impressions_repository.add_bulk(optimized_impressions)
|
|
160
|
+
stats[:queued] = optimized_impressions.length - stats[:dropped]
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'concurrent'
|
|
4
|
+
|
|
5
|
+
module SplitIoClient
|
|
6
|
+
module Engine
|
|
7
|
+
module Common
|
|
8
|
+
class NoopImpressionCounter
|
|
9
|
+
def inc(split_name, time_frame)
|
|
10
|
+
# no-op
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def pop_all
|
|
14
|
+
# no-op
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def make_key(split_name, time_frame)
|
|
18
|
+
# no-op
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.truncate_time_frame(timestamp_ms)
|
|
22
|
+
# no-op
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
module Events
|
|
6
|
+
class EventsDelivery
|
|
7
|
+
def initialize(config)
|
|
8
|
+
@config = config
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def deliver(sdk_event, event_metadata, event_handler)
|
|
12
|
+
event_handler.call(event_metadata)
|
|
13
|
+
rescue StandardError => e
|
|
14
|
+
@config.logger.error("Exception when calling handler for Sdk Event #{sdk_event}")
|
|
15
|
+
@config.log_found_exception(__method__.to_s, e)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
module Events
|
|
6
|
+
class EventsManager
|
|
7
|
+
def initialize(events_manager_config, events_delivery, config)
|
|
8
|
+
@manager_config = events_manager_config
|
|
9
|
+
@events_delivery = events_delivery
|
|
10
|
+
@active_subscriptions = {}
|
|
11
|
+
@internal_events_status = {}
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
@config = config
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def register(sdk_event, event_handler)
|
|
17
|
+
return if @active_subscriptions.key?(sdk_event) && !get_event_handler(sdk_event).nil?
|
|
18
|
+
|
|
19
|
+
@mutex.synchronize do
|
|
20
|
+
# SDK ready already fired
|
|
21
|
+
if sdk_event == SplitIoClient::Engine::Models::SdkEvent::SDK_READY && event_already_triggered(sdk_event)
|
|
22
|
+
@active_subscriptions[sdk_event] = SplitIoClient::Engine::Models::EventActiveSubscriptions.new(true, event_handler)
|
|
23
|
+
@config.logger.debug('EventsManager: Firing SDK_READY event for new subscription') if @config.debug_enabled
|
|
24
|
+
fire_sdk_event(sdk_event, nil)
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
@config.logger.debug("EventsManager: Register event: #{sdk_event}") if @config.debug_enabled
|
|
29
|
+
@active_subscriptions[sdk_event] = SplitIoClient::Engine::Models::EventActiveSubscriptions.new(false, event_handler)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def unregister(sdk_event)
|
|
34
|
+
return unless @active_subscriptions.key?(sdk_event)
|
|
35
|
+
|
|
36
|
+
@mutex.synchronize do
|
|
37
|
+
@active_subscriptions.delete(sdk_event)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def notify_internal_event(sdk_internal_event, event_metadata)
|
|
42
|
+
@mutex.synchronize do
|
|
43
|
+
update_internal_event_status(sdk_internal_event, true)
|
|
44
|
+
@manager_config.evaluation_order.each do |sorted_event|
|
|
45
|
+
if get_sdk_event_if_applicable(sdk_internal_event).include?(sorted_event) &&
|
|
46
|
+
!get_event_handler(sorted_event).nil?
|
|
47
|
+
fire_sdk_event(sorted_event, event_metadata)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# if client is not subscribed to SDK_READY
|
|
51
|
+
if check_if_register_needed(sorted_event)
|
|
52
|
+
@config.logger.debug('EventsManager: Registering SDK_READY event as fired') if @config.debug_enabled
|
|
53
|
+
@active_subscriptions[Engine::Models::SdkEvent::SDK_READY] = Engine::Models::EventActiveSubscriptions.new(true, nil)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def destroy
|
|
60
|
+
@mutex.synchronize do
|
|
61
|
+
@active_subscriptions = {}
|
|
62
|
+
@internal_events_status = {}
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def check_if_register_needed(sorted_event)
|
|
69
|
+
sorted_event == SplitIoClient::Engine::Models::SdkEvent::SDK_READY &&
|
|
70
|
+
get_event_handler(sorted_event).nil? &&
|
|
71
|
+
!@active_subscriptions.include?(sorted_event)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def fire_sdk_event(sdk_event, event_metadata)
|
|
75
|
+
@config.logger.debug("EventsManager: Firing Sdk event: #{sdk_event}") if @config.debug_enabled
|
|
76
|
+
@config.threads[:sdk_event_notify] = Thread.new do
|
|
77
|
+
@events_delivery.deliver(sdk_event, event_metadata, get_event_handler(sdk_event))
|
|
78
|
+
end
|
|
79
|
+
sdk_event_triggered(sdk_event)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def event_already_triggered(sdk_event)
|
|
83
|
+
return @active_subscriptions[sdk_event].triggered if @active_subscriptions.key?(sdk_event)
|
|
84
|
+
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def get_internal_event_status(sdk_internal_event)
|
|
89
|
+
return @internal_events_status[sdk_internal_event] if @internal_events_status.key?(sdk_internal_event)
|
|
90
|
+
|
|
91
|
+
false
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def update_internal_event_status(sdk_internal_event, status)
|
|
95
|
+
@internal_events_status[sdk_internal_event] = status
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def sdk_event_triggered(sdk_event)
|
|
99
|
+
return unless @active_subscriptions.key?(sdk_event)
|
|
100
|
+
|
|
101
|
+
return if @active_subscriptions[sdk_event].triggered
|
|
102
|
+
|
|
103
|
+
@active_subscriptions[sdk_event].triggered = true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def get_event_handler(sdk_event)
|
|
107
|
+
return nil unless @active_subscriptions.key?(sdk_event)
|
|
108
|
+
|
|
109
|
+
@active_subscriptions[sdk_event].handler
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def get_sdk_event_if_applicable(sdk_internal_event)
|
|
113
|
+
final_sdk_event = SplitIoClient::Engine::Models::ValidSdkEvent.new(nil, false)
|
|
114
|
+
|
|
115
|
+
events_to_fire = []
|
|
116
|
+
require_any_sdk_event = check_require_any(sdk_internal_event)
|
|
117
|
+
if require_any_sdk_event.valid
|
|
118
|
+
if (!event_already_triggered(require_any_sdk_event.sdk_event) &&
|
|
119
|
+
execution_limit(require_any_sdk_event.sdk_event) == 1) ||
|
|
120
|
+
execution_limit(require_any_sdk_event.sdk_event) == -1
|
|
121
|
+
final_sdk_event = SplitIoClient::Engine::Models::ValidSdkEvent.new(
|
|
122
|
+
require_any_sdk_event.sdk_event,
|
|
123
|
+
check_prerequisites(require_any_sdk_event.sdk_event) &&
|
|
124
|
+
check_suppressed_by(require_any_sdk_event.sdk_event)
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
events_to_fire.push(final_sdk_event.sdk_event) if final_sdk_event.valid
|
|
128
|
+
end
|
|
129
|
+
check_require_all.each { |sdk_event| events_to_fire.push(sdk_event) }
|
|
130
|
+
|
|
131
|
+
events_to_fire
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def check_require_all
|
|
135
|
+
events = []
|
|
136
|
+
@manager_config.require_all.each do |require_name, require_value|
|
|
137
|
+
final_status = true
|
|
138
|
+
require_value.each { |val| final_status &= get_internal_event_status(val) }
|
|
139
|
+
events.push(require_name) if check_event_eligible_conditions(final_status, require_name, require_value)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
events
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def check_event_eligible_conditions(final_status, require_name, require_value)
|
|
146
|
+
final_status &&
|
|
147
|
+
check_prerequisites(require_name) &&
|
|
148
|
+
((!event_already_triggered(require_name) &&
|
|
149
|
+
execution_limit(require_name) == 1) ||
|
|
150
|
+
execution_limit(require_name) == -1) &&
|
|
151
|
+
require_value.length.positive?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def check_prerequisites(sdk_event)
|
|
155
|
+
@manager_config.prerequisites.each do |name, value|
|
|
156
|
+
value.each do |val|
|
|
157
|
+
return false if name == sdk_event && !event_already_triggered(val)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
true
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def check_suppressed_by(sdk_event)
|
|
165
|
+
@manager_config.suppressed_by.each do |name, value|
|
|
166
|
+
value.each do |val|
|
|
167
|
+
return false if name == sdk_event && event_already_triggered(val)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
true
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def execution_limit(sdk_event)
|
|
175
|
+
return -1 unless @manager_config.execution_limits.key?(sdk_event)
|
|
176
|
+
|
|
177
|
+
@manager_config.execution_limits[sdk_event]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def check_require_any(sdk_internal_event)
|
|
181
|
+
valid_sdk_event = SplitIoClient::Engine::Models::ValidSdkEvent.new(nil, false)
|
|
182
|
+
@manager_config.require_any.each do |name, val|
|
|
183
|
+
if val.include?(sdk_internal_event)
|
|
184
|
+
valid_sdk_event = SplitIoClient::Engine::Models::ValidSdkEvent.new(name, true)
|
|
185
|
+
return valid_sdk_event
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
valid_sdk_event
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SplitIoClient
|
|
4
|
+
module Engine
|
|
5
|
+
module Events
|
|
6
|
+
class EventsManagerConfig
|
|
7
|
+
attr_accessor :require_all, :prerequisites, :require_any, :suppressed_by, :execution_limits, :evaluation_order
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@require_all = construct_require_all
|
|
11
|
+
@prerequisites = construct_prerequisites
|
|
12
|
+
@require_any = construct_require_any
|
|
13
|
+
@suppressed_by = construct_suppressed_by
|
|
14
|
+
@execution_limits = construct_execution_limits
|
|
15
|
+
@evaluation_order = construct_sorted_events
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def construct_require_all
|
|
21
|
+
{
|
|
22
|
+
SplitIoClient::Engine::Models::SdkEvent::SDK_READY => Set.new([SplitIoClient::Engine::Models::SdkInternalEvent::SDK_READY])
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def construct_prerequisites
|
|
27
|
+
{
|
|
28
|
+
SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE => Set.new([SplitIoClient::Engine::Models::SdkEvent::SDK_READY])
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def construct_require_any
|
|
33
|
+
{
|
|
34
|
+
SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE => Set.new(
|
|
35
|
+
[
|
|
36
|
+
SplitIoClient::Engine::Models::SdkInternalEvent::FLAG_KILLED_NOTIFICATION,
|
|
37
|
+
SplitIoClient::Engine::Models::SdkInternalEvent::FLAGS_UPDATED,
|
|
38
|
+
SplitIoClient::Engine::Models::SdkInternalEvent::RB_SEGMENTS_UPDATED,
|
|
39
|
+
SplitIoClient::Engine::Models::SdkInternalEvent::SEGMENTS_UPDATED
|
|
40
|
+
]
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def construct_suppressed_by
|
|
46
|
+
{}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def construct_execution_limits
|
|
50
|
+
{
|
|
51
|
+
SplitIoClient::Engine::Models::SdkEvent::SDK_READY => 1,
|
|
52
|
+
SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE => -1
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def construct_sorted_events
|
|
57
|
+
sorted_events = []
|
|
58
|
+
[SplitIoClient::Engine::Models::SdkEvent::SDK_READY, SplitIoClient::Engine::Models::SdkEvent::SDK_UPDATE].each do |sdk_event|
|
|
59
|
+
sorted_events = dfs_recursive(sdk_event, sorted_events)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
sorted_events
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def dfs_recursive(sdk_event, added)
|
|
66
|
+
return added if added.include?(sdk_event)
|
|
67
|
+
|
|
68
|
+
get_dependencies(sdk_event).each do |dependent_event|
|
|
69
|
+
added = dfs_recursive(dependent_event, added)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
added.push(sdk_event)
|
|
73
|
+
|
|
74
|
+
added
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def get_dependencies(sdk_event)
|
|
78
|
+
dependencies = Set.new
|
|
79
|
+
@prerequisites.each do |prerequisites_event_name, prerequisites_event_value|
|
|
80
|
+
next unless prerequisites_event_name == sdk_event
|
|
81
|
+
|
|
82
|
+
prerequisites_event_value.each do |prereq_event|
|
|
83
|
+
dependencies.add(prereq_event)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
@suppressed_by.each do |suppressed_event_name, suppressed_event_value|
|
|
88
|
+
dependencies.add(suppressed_event_name) if suppressed_event_value.include?(sdk_event)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
dependencies
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|