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.
Files changed (210) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/pull_request_template.md +9 -0
  4. data/.github/workflows/ci.yml +90 -0
  5. data/.github/workflows/update-license-year.yml +45 -0
  6. data/.gitignore +20 -0
  7. data/.rubocop.yml +76 -0
  8. data/.simplecov +1 -0
  9. data/CHANGES.txt +361 -2
  10. data/CONTRIBUTORS-GUIDE.md +49 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE +3 -36
  13. data/NOTICE.txt +5 -0
  14. data/README.md +59 -127
  15. data/Rakefile +27 -3
  16. data/ext/murmurhash/MurmurHash3.java +302 -0
  17. data/gemfiles/faraday_after_0.13.gemfile +7 -0
  18. data/gemfiles/faraday_before_0.13.gemfile +8 -0
  19. data/lib/murmurhash/base.rb +58 -0
  20. data/lib/murmurhash/murmurhash.jar +0 -0
  21. data/lib/murmurhash/murmurhash_mri.rb +3 -0
  22. data/lib/splitclient-rb/cache/adapters/cache_adapter.rb +130 -0
  23. data/lib/splitclient-rb/cache/adapters/memory_adapter.rb +12 -0
  24. data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +137 -0
  25. data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +53 -0
  26. data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +178 -0
  27. data/lib/splitclient-rb/cache/fetchers/segment_fetcher.rb +83 -0
  28. data/lib/splitclient-rb/cache/fetchers/split_fetcher.rb +70 -0
  29. data/lib/splitclient-rb/cache/filter/bloom_filter.rb +67 -0
  30. data/lib/splitclient-rb/cache/filter/filter_adapter.rb +32 -0
  31. data/lib/splitclient-rb/cache/filter/flag_set_filter.rb +40 -0
  32. data/lib/splitclient-rb/cache/hashers/impression_hasher.rb +34 -0
  33. data/lib/splitclient-rb/cache/observers/impression_observer.rb +22 -0
  34. data/lib/splitclient-rb/cache/observers/noop_impression_observer.rb +10 -0
  35. data/lib/splitclient-rb/cache/repositories/events/memory_repository.rb +45 -0
  36. data/lib/splitclient-rb/cache/repositories/events/redis_repository.rb +35 -0
  37. data/lib/splitclient-rb/cache/repositories/events_repository.rb +61 -0
  38. data/lib/splitclient-rb/cache/repositories/flag_sets/memory_repository.rb +40 -0
  39. data/lib/splitclient-rb/cache/repositories/flag_sets/redis_repository.rb +49 -0
  40. data/lib/splitclient-rb/cache/repositories/impressions/memory_repository.rb +56 -0
  41. data/lib/splitclient-rb/cache/repositories/impressions/redis_repository.rb +57 -0
  42. data/lib/splitclient-rb/cache/repositories/impressions_repository.rb +23 -0
  43. data/lib/splitclient-rb/cache/repositories/repository.rb +24 -0
  44. data/lib/splitclient-rb/cache/repositories/rule_based_segments_repository.rb +136 -0
  45. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +124 -0
  46. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +296 -0
  47. data/lib/splitclient-rb/cache/routers/impression_router.rb +60 -0
  48. data/lib/splitclient-rb/cache/senders/events_sender.rb +42 -0
  49. data/lib/splitclient-rb/cache/senders/impressions_adapter/memory_sender.rb +71 -0
  50. data/lib/splitclient-rb/cache/senders/impressions_adapter/redis_sender.rb +69 -0
  51. data/lib/splitclient-rb/cache/senders/impressions_count_sender.rb +43 -0
  52. data/lib/splitclient-rb/cache/senders/impressions_formatter.rb +95 -0
  53. data/lib/splitclient-rb/cache/senders/impressions_sender.rb +52 -0
  54. data/lib/splitclient-rb/cache/senders/impressions_sender_adapter.rb +21 -0
  55. data/lib/splitclient-rb/cache/senders/localhost_repo_cleaner.rb +47 -0
  56. data/lib/splitclient-rb/cache/stores/localhost_split_builder.rb +95 -0
  57. data/lib/splitclient-rb/cache/stores/localhost_split_store.rb +110 -0
  58. data/lib/splitclient-rb/cache/stores/store_utils.rb +13 -0
  59. data/lib/splitclient-rb/clients/split_client.rb +530 -0
  60. data/lib/splitclient-rb/constants.rb +16 -0
  61. data/lib/splitclient-rb/engine/api/client.rb +87 -0
  62. data/lib/splitclient-rb/engine/api/events.rb +57 -0
  63. data/lib/splitclient-rb/engine/api/faraday_middleware/gzip.rb +58 -0
  64. data/lib/splitclient-rb/engine/api/impressions.rb +79 -0
  65. data/lib/splitclient-rb/engine/api/segments.rb +78 -0
  66. data/lib/splitclient-rb/engine/api/splits.rb +139 -0
  67. data/lib/splitclient-rb/engine/api/telemetry_api.rb +47 -0
  68. data/lib/splitclient-rb/engine/auth_api_client.rb +100 -0
  69. data/lib/splitclient-rb/engine/back_off.rb +26 -0
  70. data/lib/splitclient-rb/engine/common/impressions_counter.rb +45 -0
  71. data/lib/splitclient-rb/engine/common/impressions_manager.rb +165 -0
  72. data/lib/splitclient-rb/engine/common/noop_impressions_counter.rb +27 -0
  73. data/lib/{splitclient-engine → splitclient-rb/engine}/evaluator/splitter.rb +35 -22
  74. data/lib/splitclient-rb/engine/events/events_delivery.rb +20 -0
  75. data/lib/splitclient-rb/engine/events/events_manager.rb +194 -0
  76. data/lib/splitclient-rb/engine/events/events_manager_config.rb +96 -0
  77. data/lib/splitclient-rb/engine/events/events_task.rb +50 -0
  78. data/lib/splitclient-rb/engine/events/noop_events_queue.rb +13 -0
  79. data/lib/splitclient-rb/engine/fallback_treatment_calculator.rb +48 -0
  80. data/lib/splitclient-rb/engine/impressions/noop_unique_keys_tracker.rb +17 -0
  81. data/lib/splitclient-rb/engine/impressions/unique_keys_tracker.rb +144 -0
  82. data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/all_keys_matcher.rb +10 -21
  83. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +47 -0
  84. data/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb +33 -0
  85. data/lib/{splitclient-engine → splitclient-rb/engine}/matchers/combiners.rb +4 -6
  86. data/lib/splitclient-rb/engine/matchers/combining_matcher.rb +80 -0
  87. data/lib/splitclient-rb/engine/matchers/contains_all_matcher.rb +20 -0
  88. data/lib/splitclient-rb/engine/matchers/contains_any_matcher.rb +16 -0
  89. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +43 -0
  90. data/lib/splitclient-rb/engine/matchers/dependency_matcher.rb +26 -0
  91. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +42 -0
  92. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +37 -0
  93. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +44 -0
  94. data/lib/splitclient-rb/engine/matchers/equal_to_semver_matcher.rb +28 -0
  95. data/lib/splitclient-rb/engine/matchers/equal_to_set_matcher.rb +16 -0
  96. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +43 -0
  97. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher.rb +28 -0
  98. data/lib/splitclient-rb/engine/matchers/in_list_semver_matcher.rb +36 -0
  99. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +43 -0
  100. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher.rb +28 -0
  101. data/lib/splitclient-rb/engine/matchers/matcher.rb +52 -0
  102. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +29 -0
  103. data/lib/splitclient-rb/engine/matchers/negation_matcher.rb +47 -0
  104. data/lib/splitclient-rb/engine/matchers/part_of_set_matcher.rb +22 -0
  105. data/lib/splitclient-rb/engine/matchers/prerequisites_matcher.rb +31 -0
  106. data/lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb +78 -0
  107. data/lib/splitclient-rb/engine/matchers/semver.rb +201 -0
  108. data/lib/splitclient-rb/engine/matchers/set_matcher.rb +27 -0
  109. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +40 -0
  110. data/lib/splitclient-rb/engine/matchers/user_defined_segment_matcher.rb +28 -0
  111. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +66 -0
  112. data/lib/splitclient-rb/engine/metrics/binary_search_latency_tracker.rb +66 -0
  113. data/lib/splitclient-rb/engine/models/evaluation_options.rb +9 -0
  114. data/lib/splitclient-rb/engine/models/event_active_subscriptions.rb +14 -0
  115. data/lib/splitclient-rb/engine/models/events_metadata.rb +10 -0
  116. data/lib/splitclient-rb/engine/models/fallback_treatment.rb +11 -0
  117. data/lib/splitclient-rb/engine/models/fallback_treatments_configuration.rb +36 -0
  118. data/lib/splitclient-rb/engine/models/label.rb +10 -0
  119. data/lib/splitclient-rb/engine/models/sdk_event.rb +4 -0
  120. data/lib/splitclient-rb/engine/models/sdk_event_type.rb +4 -0
  121. data/lib/splitclient-rb/engine/models/sdk_internal_event.rb +8 -0
  122. data/lib/splitclient-rb/engine/models/sdk_internal_event_notification.rb +16 -0
  123. data/lib/splitclient-rb/engine/models/segment_type.rb +4 -0
  124. data/lib/splitclient-rb/engine/models/split.rb +17 -0
  125. data/lib/splitclient-rb/engine/models/split_http_response.rb +19 -0
  126. data/lib/splitclient-rb/engine/models/treatment.rb +3 -0
  127. data/lib/splitclient-rb/engine/models/valid_sdk_event.rb +14 -0
  128. data/lib/splitclient-rb/engine/parser/condition.rb +271 -0
  129. data/lib/splitclient-rb/engine/parser/evaluator.rb +112 -0
  130. data/lib/{splitclient-engine → splitclient-rb/engine}/parser/partition.rb +2 -4
  131. data/lib/splitclient-rb/engine/push_manager.rb +66 -0
  132. data/lib/splitclient-rb/engine/status_manager.rb +39 -0
  133. data/lib/splitclient-rb/engine/sync_manager.rb +183 -0
  134. data/lib/splitclient-rb/engine/synchronizer.rb +231 -0
  135. data/lib/splitclient-rb/exceptions.rb +26 -0
  136. data/lib/splitclient-rb/helpers/decryption_helper.rb +25 -0
  137. data/lib/splitclient-rb/helpers/evaluator_helper.rb +37 -0
  138. data/lib/splitclient-rb/helpers/repository_helper.rb +61 -0
  139. data/lib/splitclient-rb/helpers/thread_helper.rb +24 -0
  140. data/lib/splitclient-rb/helpers/util.rb +26 -0
  141. data/lib/splitclient-rb/managers/split_manager.rb +121 -0
  142. data/lib/splitclient-rb/spec.rb +9 -0
  143. data/lib/splitclient-rb/split_config.rb +591 -47
  144. data/lib/splitclient-rb/split_factory.rb +284 -0
  145. data/lib/splitclient-rb/split_factory_builder.rb +7 -0
  146. data/lib/splitclient-rb/split_factory_registry.rb +63 -0
  147. data/lib/splitclient-rb/split_logger.rb +23 -0
  148. data/lib/splitclient-rb/sse/event_source/client.rb +264 -0
  149. data/lib/splitclient-rb/sse/event_source/event_parser.rb +65 -0
  150. data/lib/splitclient-rb/sse/event_source/event_types.rb +15 -0
  151. data/lib/splitclient-rb/sse/event_source/stream_data.rb +22 -0
  152. data/lib/splitclient-rb/sse/notification_manager_keeper.rb +84 -0
  153. data/lib/splitclient-rb/sse/notification_processor.rb +48 -0
  154. data/lib/splitclient-rb/sse/sse_handler.rb +44 -0
  155. data/lib/splitclient-rb/sse/workers/segments_worker.rb +62 -0
  156. data/lib/splitclient-rb/sse/workers/splits_worker.rb +153 -0
  157. data/lib/splitclient-rb/telemetry/domain/constants.rb +48 -0
  158. data/lib/splitclient-rb/telemetry/domain/structs.rb +35 -0
  159. data/lib/splitclient-rb/telemetry/evaluation_consumer.rb +14 -0
  160. data/lib/splitclient-rb/telemetry/evaluation_producer.rb +21 -0
  161. data/lib/splitclient-rb/telemetry/init_consumer.rb +14 -0
  162. data/lib/splitclient-rb/telemetry/init_producer.rb +19 -0
  163. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_consumer.rb +32 -0
  164. data/lib/splitclient-rb/telemetry/memory/memory_evaluation_producer.rb +24 -0
  165. data/lib/splitclient-rb/telemetry/memory/memory_init_consumer.rb +28 -0
  166. data/lib/splitclient-rb/telemetry/memory/memory_init_producer.rb +34 -0
  167. data/lib/splitclient-rb/telemetry/memory/memory_runtime_consumer.rb +119 -0
  168. data/lib/splitclient-rb/telemetry/memory/memory_runtime_producer.rb +87 -0
  169. data/lib/splitclient-rb/telemetry/memory/memory_synchronizer.rb +213 -0
  170. data/lib/splitclient-rb/telemetry/redis/redis_evaluation_producer.rb +38 -0
  171. data/lib/splitclient-rb/telemetry/redis/redis_init_producer.rb +37 -0
  172. data/lib/splitclient-rb/telemetry/redis/redis_synchronizer.rb +27 -0
  173. data/lib/splitclient-rb/telemetry/runtime_consumer.rb +25 -0
  174. data/lib/splitclient-rb/telemetry/runtime_producer.rb +25 -0
  175. data/lib/splitclient-rb/telemetry/storages/memory.rb +159 -0
  176. data/lib/splitclient-rb/telemetry/sync_task.rb +36 -0
  177. data/lib/splitclient-rb/telemetry/synchronizer.rb +33 -0
  178. data/lib/splitclient-rb/utilitites.rb +49 -0
  179. data/lib/splitclient-rb/validators.rb +361 -0
  180. data/lib/splitclient-rb/version.rb +1 -1
  181. data/lib/splitclient-rb.rb +177 -25
  182. data/sonar-project.properties +6 -0
  183. data/splitclient-rb.gemspec +53 -26
  184. data/tasks/benchmark_get_treatment.rake +31 -17
  185. data/tasks/irb.rake +6 -0
  186. metadata +411 -86
  187. data/NEWS +0 -9
  188. data/lib/splitclient-cache/local_store.rb +0 -45
  189. data/lib/splitclient-engine/impressions/impressions.rb +0 -79
  190. data/lib/splitclient-engine/matchers/between_matcher.rb +0 -48
  191. data/lib/splitclient-engine/matchers/combining_matcher.rb +0 -94
  192. data/lib/splitclient-engine/matchers/equal_to_matcher.rb +0 -48
  193. data/lib/splitclient-engine/matchers/greater_than_or_equal_to_matcher.rb +0 -48
  194. data/lib/splitclient-engine/matchers/less_than_or_equal_to_matcher.rb +0 -48
  195. data/lib/splitclient-engine/matchers/negation_matcher.rb +0 -54
  196. data/lib/splitclient-engine/matchers/user_defined_segment_matcher.rb +0 -61
  197. data/lib/splitclient-engine/matchers/whitelist_matcher.rb +0 -64
  198. data/lib/splitclient-engine/metrics/binary_search_latency_tracker.rb +0 -122
  199. data/lib/splitclient-engine/metrics/metrics.rb +0 -158
  200. data/lib/splitclient-engine/parser/condition.rb +0 -143
  201. data/lib/splitclient-engine/parser/segment.rb +0 -84
  202. data/lib/splitclient-engine/parser/segment_parser.rb +0 -46
  203. data/lib/splitclient-engine/parser/split.rb +0 -68
  204. data/lib/splitclient-engine/parser/split_adapter.rb +0 -433
  205. data/lib/splitclient-engine/parser/split_parser.rb +0 -139
  206. data/lib/splitclient-engine/partitions/treatments.rb +0 -40
  207. data/lib/splitclient-rb/split_client.rb +0 -156
  208. data/lib/splitclient-rb_utilitites.rb +0 -26
  209. data/tasks/console.rake +0 -4
  210. data/tasks/rspec.rake +0 -3
@@ -0,0 +1,271 @@
1
+ module SplitIoClient
2
+ #
3
+ # acts as dto for a condition structure
4
+ #
5
+ class Condition < NoMethodError
6
+ TYPE_ROLLOUT = 'ROLLOUT'.freeze
7
+ TYPE_WHITELIST = 'WHITELIST'.freeze
8
+ #
9
+ # definition of the condition
10
+ #
11
+ # @returns [object] condition values
12
+ attr_accessor :data
13
+
14
+ def initialize(condition, config)
15
+ @data = condition
16
+ @partitions = set_partitions
17
+ @config = config
18
+ end
19
+
20
+ def create_condition_matcher(matchers)
21
+ CombiningMatcher.new(@config.split_logger, combiner, matchers) if combiner
22
+ end
23
+
24
+ #
25
+ # @return [object] the combiner value for this condition
26
+ def combiner
27
+ @data[:matcherGroup][:combiner]
28
+ end
29
+
30
+ #
31
+ # @return [object] the matcher value for this condition
32
+ def matcher
33
+ @data[:matcherGroup][:matchers].first[:matcherType]
34
+ end
35
+
36
+ #
37
+ # @return [object] the matchers array value for this condition
38
+ def matchers
39
+ @data[:matcherGroup][:matchers]
40
+ end
41
+
42
+ #
43
+ # @return [string] condition type
44
+ def type
45
+ @data[:conditionType]
46
+ end
47
+
48
+ def negation_matcher(matcher)
49
+ NegationMatcher.new(@config.split_logger, matcher)
50
+ end
51
+
52
+ def matcher_all_keys(_params)
53
+ @matcher_all_keys ||= AllKeysMatcher.new(@config.split_logger)
54
+ end
55
+
56
+ # returns UserDefinedSegmentMatcher[object]
57
+ def matcher_in_segment(params)
58
+ matcher = params[:matcher]
59
+ segment_name = matcher[:userDefinedSegmentMatcherData] && matcher[:userDefinedSegmentMatcherData][:segmentName]
60
+
61
+ UserDefinedSegmentMatcher.new(params[:segments_repository], segment_name, @config.split_logger)
62
+ end
63
+
64
+ # returns WhitelistMatcher[object] the whitelist for this condition in case it has a whitelist matcher
65
+ def matcher_whitelist(params)
66
+ result = nil
67
+ matcher = params[:matcher]
68
+ is_user_whitelist = ((matcher[:keySelector]).nil? || (matcher[:keySelector])[:attribute].nil?)
69
+ if is_user_whitelist
70
+ result = (matcher[:whitelistMatcherData])[:whitelist]
71
+ else
72
+ attribute = (matcher[:keySelector])[:attribute]
73
+ white_list = (matcher[:whitelistMatcherData])[:whitelist]
74
+ result = { attribute: attribute, value: white_list }
75
+ end
76
+ WhitelistMatcher.new(result, @config.split_logger, @config.split_validator)
77
+ end
78
+
79
+ def matcher_equal_to(params)
80
+ matcher = params[:matcher]
81
+ attribute = (matcher[:keySelector])[:attribute]
82
+ value = (matcher[:unaryNumericMatcherData])[:value]
83
+ data_type = (matcher[:unaryNumericMatcherData])[:dataType]
84
+ EqualToMatcher.new({attribute: attribute, value: value, data_type: data_type}, @config.split_logger, @config.split_validator)
85
+ end
86
+
87
+ def matcher_greater_than_or_equal_to(params)
88
+ matcher = params[:matcher]
89
+ attribute = (matcher[:keySelector])[:attribute]
90
+ value = (matcher[:unaryNumericMatcherData])[:value]
91
+ data_type = (matcher[:unaryNumericMatcherData])[:dataType]
92
+ GreaterThanOrEqualToMatcher.new({attribute: attribute, value: value, data_type: data_type}, @config.split_logger, @config.split_validator)
93
+ end
94
+
95
+ def matcher_less_than_or_equal_to(params)
96
+ matcher = params[:matcher]
97
+ attribute = (matcher[:keySelector])[:attribute]
98
+ value = (matcher[:unaryNumericMatcherData])[:value]
99
+ data_type = (matcher[:unaryNumericMatcherData])[:dataType]
100
+ LessThanOrEqualToMatcher.new({attribute: attribute, value: value, data_type: data_type}, @config.split_logger, @config.split_validator)
101
+ end
102
+
103
+ def matcher_between(params)
104
+ matcher = params[:matcher]
105
+ attribute = (matcher[:keySelector])[:attribute]
106
+ start_value = (matcher[:betweenMatcherData])[:start]
107
+ end_value = (matcher[:betweenMatcherData])[:end]
108
+ data_type = (matcher[:betweenMatcherData])[:dataType]
109
+ BetweenMatcher.new({attribute: attribute, start_value: start_value, end_value: end_value, data_type: data_type}, @config.split_logger, @config.split_validator)
110
+ end
111
+
112
+ def matcher_equal_to_set(params)
113
+ EqualToSetMatcher.new(
114
+ params[:matcher][:keySelector][:attribute],
115
+ params[:matcher][:whitelistMatcherData][:whitelist],
116
+ @config.split_logger
117
+ )
118
+ end
119
+
120
+ def matcher_contains_any_of_set(params)
121
+ ContainsAnyMatcher.new(
122
+ params[:matcher][:keySelector][:attribute],
123
+ params[:matcher][:whitelistMatcherData][:whitelist],
124
+ @config.split_logger
125
+ )
126
+ end
127
+
128
+ def matcher_contains_all_of_set(params)
129
+ ContainsAllMatcher.new(
130
+ params[:matcher][:keySelector][:attribute],
131
+ params[:matcher][:whitelistMatcherData][:whitelist],
132
+ @config.split_logger
133
+ )
134
+ end
135
+
136
+ def matcher_part_of_set(params)
137
+ PartOfSetMatcher.new(
138
+ params[:matcher][:keySelector][:attribute],
139
+ params[:matcher][:whitelistMatcherData][:whitelist],
140
+ @config.split_logger
141
+ )
142
+ end
143
+
144
+ def matcher_starts_with(params)
145
+ StartsWithMatcher.new(
146
+ params[:matcher][:keySelector][:attribute],
147
+ params[:matcher][:whitelistMatcherData][:whitelist],
148
+ @config.split_logger
149
+ )
150
+ end
151
+
152
+ def matcher_ends_with(params)
153
+ EndsWithMatcher.new(
154
+ params[:matcher][:keySelector][:attribute],
155
+ params[:matcher][:whitelistMatcherData][:whitelist],
156
+ @config.split_logger
157
+ )
158
+ end
159
+
160
+ def matcher_contains_string(params)
161
+ ContainsMatcher.new(
162
+ params[:matcher][:keySelector][:attribute],
163
+ params[:matcher][:whitelistMatcherData][:whitelist],
164
+ @config.split_logger, @config.split_validator
165
+ )
166
+ end
167
+
168
+ def matcher_in_split_treatment(params)
169
+ DependencyMatcher.new(
170
+ params[:matcher][:dependencyMatcherData][:split],
171
+ params[:matcher][:dependencyMatcherData][:treatments],
172
+ @config.split_logger
173
+ )
174
+ end
175
+
176
+ def matcher_equal_to_boolean(params)
177
+ EqualToBooleanMatcher.new(
178
+ params[:matcher][:keySelector][:attribute],
179
+ params[:matcher][:booleanMatcherData],
180
+ @config.split_logger
181
+ )
182
+ end
183
+
184
+ def matcher_matches_string(params)
185
+ MatchesStringMatcher.new(
186
+ params[:matcher][:keySelector][:attribute],
187
+ params[:matcher][:stringMatcherData],
188
+ @config.split_logger
189
+ )
190
+ end
191
+
192
+ def matcher_equal_to_semver(params)
193
+ EqualToSemverMatcher.new(
194
+ params[:matcher][:keySelector][:attribute],
195
+ params[:matcher][:stringMatcherData],
196
+ @config.split_logger, @config.split_validator
197
+ )
198
+ end
199
+
200
+ def matcher_greater_than_or_equal_to_semver(params)
201
+ GreaterThanOrEqualToSemverMatcher.new(
202
+ params[:matcher][:keySelector][:attribute],
203
+ params[:matcher][:stringMatcherData],
204
+ @config.split_logger, @config.split_validator
205
+ )
206
+ end
207
+
208
+ def matcher_less_than_or_equal_to_semver(params)
209
+ LessThanOrEqualToSemverMatcher.new(
210
+ params[:matcher][:keySelector][:attribute],
211
+ params[:matcher][:stringMatcherData],
212
+ @config.split_logger, @config.split_validator
213
+ )
214
+ end
215
+
216
+ def matcher_between_semver(params)
217
+ BetweenSemverMatcher.new(
218
+ params[:matcher][:keySelector][:attribute],
219
+ params[:matcher][:betweenStringMatcherData][:start],
220
+ params[:matcher][:betweenStringMatcherData][:end],
221
+ @config.split_logger, @config.split_validator
222
+ )
223
+ end
224
+
225
+ def matcher_in_list_semver(params)
226
+ InListSemverMatcher.new(
227
+ params[:matcher][:keySelector][:attribute],
228
+ params[:matcher][:whitelistMatcherData][:whitelist],
229
+ @config.split_logger, @config.split_validator
230
+ )
231
+ end
232
+
233
+ def matcher_in_rule_based_segment(params)
234
+ matcher = params[:matcher]
235
+ segment_name = matcher[:userDefinedSegmentMatcherData] && matcher[:userDefinedSegmentMatcherData][:segmentName]
236
+
237
+ RuleBasedSegmentMatcher.new(params[:segments_repository], params[:rule_based_segments_repository], segment_name, @config)
238
+ end
239
+
240
+ #
241
+ # @return [object] the negate value for this condition
242
+ def negate
243
+ @data[:matcherGroup][:matchers].first[:negate]
244
+ end
245
+
246
+ #
247
+ # @return [object] the array of partitions for this condition
248
+ attr_reader :partitions
249
+
250
+ #
251
+ # converts the partitions hash for this condition into an array of partition objects
252
+ #
253
+ # @return [void]
254
+ def set_partitions
255
+ partitions_list = []
256
+ return partitions_list unless @data.key?(:partitions) or @data.key?('partitions')
257
+
258
+ @data[:partitions].each do |p|
259
+ partition = SplitIoClient::Partition.new(p)
260
+ partitions_list << partition
261
+ end
262
+ partitions_list
263
+ end
264
+
265
+ #
266
+ # @return [boolean] true if the condition is empty false otherwise
267
+ def empty?
268
+ @data.empty?
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,112 @@
1
+ module SplitIoClient
2
+ module Engine
3
+ module Parser
4
+ class Evaluator
5
+ def initialize(segments_repository, splits_repository, rb_segment_repository, config)
6
+ @splits_repository = splits_repository
7
+ @segments_repository = segments_repository
8
+ @rb_segment_repository = rb_segment_repository
9
+ @config = config
10
+ end
11
+
12
+ def evaluate_feature_flag(keys, feature_flag, attributes = nil)
13
+ # DependencyMatcher here, split is actually a split_name in this case
14
+ cache_result = feature_flag.is_a? String
15
+ feature_flag = @splits_repository.get_split(feature_flag) if cache_result
16
+ evaluate_treatment(keys, feature_flag, attributes)
17
+ end
18
+
19
+ private
20
+
21
+ def evaluate_treatment(keys, feature_flag, attributes)
22
+ if Models::Split.archived?(feature_flag)
23
+ return treatment_hash(Models::Label::ARCHIVED, Models::Treatment::CONTROL, feature_flag[:changeNumber])
24
+ end
25
+
26
+ treatment = if Models::Split.matchable?(feature_flag)
27
+ match(feature_flag, keys, attributes)
28
+ else
29
+ treatment_hash(Models::Label::KILLED, feature_flag[:defaultTreatment], feature_flag[:changeNumber], split_configurations(feature_flag[:defaultTreatment], feature_flag))
30
+ end
31
+
32
+ treatment
33
+ end
34
+
35
+ def split_configurations(treatment, split)
36
+ return nil if split[:configurations].nil?
37
+ split[:configurations][treatment.to_sym]
38
+ end
39
+
40
+ def match(split, keys, attributes)
41
+
42
+ return treatment_hash(Models::Label::PREREQUISITES_NOT_MET, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split)) unless check_prerequisites_matcher(split, keys, attributes)
43
+
44
+ in_rollout = false
45
+ key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key]
46
+ legacy_algo = (split[:algo] == 1 || split[:algo] == nil) ? true : false
47
+ splitter = Splitter.new
48
+
49
+ split[:conditions].each do |c|
50
+ condition = SplitIoClient::Condition.new(c, @config)
51
+
52
+ next if condition.empty?
53
+
54
+ if !in_rollout && condition.type == SplitIoClient::Condition::TYPE_ROLLOUT
55
+ if split[:trafficAllocation] < 100
56
+ bucket = splitter.bucket(splitter.count_hash(key, split[:trafficAllocationSeed].to_i, legacy_algo))
57
+
58
+ if bucket > split[:trafficAllocation]
59
+ return treatment_hash(Models::Label::NOT_IN_SPLIT, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split))
60
+ end
61
+ end
62
+
63
+ in_rollout = true
64
+ end
65
+ condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository, @rb_segment_repository).match?(
66
+ matching_key: keys[:matching_key],
67
+ bucketing_key: keys[:bucketing_key],
68
+ evaluator: self,
69
+ attributes: attributes
70
+ )
71
+
72
+ next unless condition_matched
73
+
74
+ result = splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
75
+
76
+ return treatment_from_result(result, split, c)
77
+ end
78
+
79
+ treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split))
80
+ end
81
+
82
+ private
83
+
84
+ def treatment_from_result(result, split, condition)
85
+ if result.nil?
86
+ return treatment_hash(Models::Label::NO_RULE_MATCHED, split[:defaultTreatment], split[:changeNumber], split_configurations(split[:defaultTreatment], split))
87
+ else
88
+ return treatment_hash(condition[:label], result, split[:changeNumber],split_configurations(result, split))
89
+ end
90
+ end
91
+
92
+ def treatment_hash(label, treatment, change_number = nil, configurations = nil)
93
+ { label: label, treatment: treatment, change_number: change_number, config: configurations }
94
+ end
95
+
96
+ def check_prerequisites_matcher(split, keys, attributes)
97
+ if split.key?(:prerequisites) && !split[:prerequisites].nil?
98
+ prerequisites_matcher = SplitIoClient::PrerequisitesMatcher.new(split[:prerequisites], @config.split_logger)
99
+ return prerequisites_matcher.match?(
100
+ matching_key: keys[:matching_key],
101
+ bucketing_key: keys[:bucketing_key],
102
+ evaluator: self,
103
+ attributes: attributes
104
+ )
105
+ end
106
+
107
+ true
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -1,5 +1,4 @@
1
1
  module SplitIoClient
2
-
3
2
  #
4
3
  # acts as dto for a partition structure
5
4
  #
@@ -30,8 +29,7 @@ module SplitIoClient
30
29
  #
31
30
  # @return [boolean] true if the partition is empty false otherwise
32
31
  def is_empty?
33
- @data.empty? ? true : false
32
+ @data.empty?
34
33
  end
35
34
  end
36
-
37
- end
35
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ class PushManager
6
+ def initialize(config, sse_handler, api_key, telemetry_runtime_producer)
7
+ @config = config
8
+ @sse_handler = sse_handler
9
+ @auth_api_client = AuthApiClient.new(@config, telemetry_runtime_producer)
10
+ @api_key = api_key
11
+ @back_off = Engine::BackOff.new(@config.auth_retry_back_off_base, 1)
12
+ @telemetry_runtime_producer = telemetry_runtime_producer
13
+ end
14
+
15
+ def start_sse
16
+ response = @auth_api_client.authenticate(@api_key)
17
+ @config.logger.debug("Auth service response push_enabled: #{response[:push_enabled]}") if @config.debug_enabled
18
+
19
+ if response[:push_enabled] && @sse_handler.start(response[:token], response[:channels])
20
+ schedule_next_token_refresh(response[:exp])
21
+ @back_off.reset
22
+ record_telemetry(response[:exp])
23
+
24
+ return true
25
+ end
26
+
27
+ stop_sse
28
+ schedule_next_token_refresh(@back_off.interval) if response[:retry]
29
+ false
30
+ rescue StandardError => e
31
+ @config.logger.error("start_sse: #{e.inspect}")
32
+ end
33
+
34
+ def stop_sse
35
+ @sse_handler.stop
36
+ SplitIoClient::Helpers::ThreadHelper.stop(:schedule_next_token_refresh, @config)
37
+ rescue StandardError => e
38
+ @config.logger.error(e.inspect)
39
+ end
40
+
41
+ private
42
+
43
+ def schedule_next_token_refresh(time)
44
+ @config.threads[:schedule_next_token_refresh] = Thread.new { refresh_token_task(time) }
45
+ end
46
+
47
+ def refresh_token_task(time)
48
+ @config.logger.debug("schedule_next_token_refresh refresh in #{time} seconds.") if @config.debug_enabled
49
+
50
+ sleep(time)
51
+
52
+ @config.logger.debug('schedule_next_token_refresh starting ...') if @config.debug_enabled
53
+ @sse_handler.stop
54
+
55
+ start_sse
56
+ rescue StandardError => e
57
+ @config.logger.debug("schedule_next_token_refresh error: #{e.inspect}") if @config.debug_enabled
58
+ end
59
+
60
+ def record_telemetry(time)
61
+ data = (Time.now.to_f * 1000.0).to_i + (time * 1000.0).to_i
62
+ @telemetry_runtime_producer.record_streaming_event(Telemetry::Domain::Constants::TOKEN_REFRESH, data)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ class StatusManager
6
+ def initialize(config, internal_events_queue)
7
+ @config = config
8
+ @sdk_ready = Concurrent::CountDownLatch.new(1)
9
+ @internal_events_queue = internal_events_queue
10
+ end
11
+
12
+ def ready?
13
+ return true if @config.consumer?
14
+
15
+ @sdk_ready.wait(0)
16
+ end
17
+
18
+ def ready!
19
+ return if ready?
20
+
21
+ @sdk_ready.count_down
22
+ @config.logger.info('SplitIO SDK is ready')
23
+ @internal_events_queue.push(
24
+ SplitIoClient::Engine::Models::SdkInternalEventNotification.new(
25
+ SplitIoClient::Engine::Models::SdkInternalEvent::SDK_READY, nil
26
+ )
27
+ )
28
+ end
29
+
30
+ def wait_until_ready(seconds = nil)
31
+ return if @config.consumer?
32
+
33
+ timeout = seconds || @config.block_until_ready
34
+
35
+ raise SDKBlockerTimeoutExpiredException, 'SDK start up timeout expired' unless @sdk_ready.wait(timeout)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SplitIoClient
4
+ module Engine
5
+ class SyncManager
6
+ SYNC_MODE_STREAMING = 0
7
+ SYNC_MODE_POLLING = 1
8
+
9
+ def initialize(config, synchronizer, telemetry_runtime_producer, telemetry_synchronizer,
10
+ status_manager, sse_handler, push_manager, status_queue)
11
+ @config = config
12
+ @synchronizer = synchronizer
13
+ @telemetry_runtime_producer = telemetry_runtime_producer
14
+ @telemetry_synchronizer = telemetry_synchronizer
15
+ @status_manager = status_manager
16
+ @sse_handler = sse_handler
17
+ @push_manager = push_manager
18
+ @status_queue = status_queue
19
+ @sse_connected = Concurrent::AtomicBoolean.new(false)
20
+ @back_off = Engine::BackOff.new(1, 3)
21
+ end
22
+
23
+ def start
24
+ start_thread
25
+ PhusionPassenger.on_event(:starting_worker_process) { |forked| start_thread if forked } if defined?(PhusionPassenger)
26
+ end
27
+
28
+ def start_consumer
29
+ start_consumer_thread
30
+ PhusionPassenger.on_event(:starting_worker_process) { |forked| start_consumer_thread if forked } if defined?(PhusionPassenger)
31
+ end
32
+
33
+ private
34
+
35
+ def start_thread
36
+ @config.threads[:start_sdk] = Thread.new do
37
+ sleep(0.5) until @synchronizer.sync_all(false)
38
+
39
+ @status_manager.ready!
40
+ @telemetry_synchronizer.synchronize_config
41
+ @synchronizer.start_periodic_data_recording
42
+ connected = false
43
+
44
+ if @config.streaming_enabled
45
+ @config.logger.debug('Starting Streaming mode ...') if @config.debug_enabled
46
+ start_push_status_monitor
47
+ connected = @push_manager.start_sse
48
+ end
49
+
50
+ unless connected
51
+ @config.logger.debug('Starting Polling mode ...') if @config.debug_enabled
52
+ @synchronizer.start_periodic_fetch
53
+ record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
54
+ end
55
+ end
56
+ end
57
+
58
+ def start_consumer_thread
59
+ @config.threads[:start_sdk_consumer] = Thread.new do
60
+ @status_manager.ready!
61
+ @telemetry_synchronizer.synchronize_config
62
+ @synchronizer.start_periodic_data_recording
63
+ end
64
+ end
65
+
66
+ def process_subsystem_ready
67
+ @synchronizer.stop_periodic_fetch
68
+ @synchronizer.sync_all
69
+ @sse_handler.start_workers
70
+ record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_STREAMING)
71
+ end
72
+
73
+ def process_subsystem_down
74
+ @sse_handler.stop_workers
75
+ @synchronizer.start_periodic_fetch
76
+ record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
77
+ end
78
+
79
+ def process_push_shutdown
80
+ @push_manager.stop_sse
81
+ @sse_handler.stop_workers
82
+ @synchronizer.start_periodic_fetch
83
+ record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
84
+ rescue StandardError => e
85
+ @config.logger.error("process_push_shutdown error: #{e.inspect}")
86
+ end
87
+
88
+ def process_connected
89
+ if @sse_connected.value
90
+ @config.logger.debug('Streaming already connected.') if @config.debug_enabled
91
+ return
92
+ end
93
+
94
+ @sse_connected.make_true
95
+ @synchronizer.stop_periodic_fetch
96
+ @synchronizer.sync_all
97
+ @sse_handler.start_workers
98
+ record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_STREAMING)
99
+ rescue StandardError => e
100
+ @config.logger.error("process_connected error: #{e.inspect}")
101
+ end
102
+
103
+ def process_forced_stop
104
+ unless @sse_connected.value
105
+ @config.logger.debug('Streaming already disconnected.') if @config.debug_enabled
106
+ return
107
+ end
108
+
109
+ @sse_connected.make_false
110
+ @synchronizer.start_periodic_fetch
111
+ record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
112
+ rescue StandardError => e
113
+ @config.logger.error("process_connected error: #{e.inspect}")
114
+ end
115
+
116
+ def process_disconnect(reconnect)
117
+ unless @sse_connected.value || reconnect
118
+ @config.logger.debug('Streaming already disconnected.') if @config.debug_enabled
119
+ return
120
+ end
121
+
122
+ @sse_connected.make_false
123
+ @sse_handler.stop_workers
124
+ @synchronizer.start_periodic_fetch
125
+ record_telemetry(Telemetry::Domain::Constants::SYNC_MODE, SYNC_MODE_POLLING)
126
+
127
+ if reconnect
128
+ wait_interval = @back_off.interval
129
+ @config.logger.debug("Retrying streaming connection in: #{wait_interval} seconds")
130
+ sleep(wait_interval)
131
+ @push_manager.stop_sse
132
+ @synchronizer.sync_all
133
+ @push_manager.start_sse
134
+ end
135
+ rescue StandardError => e
136
+ @config.logger.error("process_disconnect error: #{e.inspect}")
137
+ end
138
+
139
+ def record_telemetry(type, data)
140
+ @telemetry_runtime_producer.record_streaming_event(type, data)
141
+ end
142
+
143
+ def start_push_status_monitor
144
+ @config.threads[:push_status_handler] = Thread.new do
145
+ @config.logger.debug('Starting push status handler ...') if @config.debug_enabled
146
+ incoming_push_status_handler
147
+ end
148
+ end
149
+
150
+ def incoming_push_status_handler
151
+ while (status = @status_queue.pop)
152
+ @config.logger.debug("Push status handler dequeue #{status}") if @config.debug_enabled
153
+
154
+ case status
155
+ when Constants::PUSH_CONNECTED
156
+ @back_off.reset
157
+ process_connected
158
+ when Constants::PUSH_RETRYABLE_ERROR
159
+ process_disconnect(true)
160
+ when Constants::PUSH_FORCED_STOP
161
+ process_forced_stop
162
+ when Constants::PUSH_NONRETRYABLE_ERROR
163
+ process_disconnect(false)
164
+ when Constants::PUSH_SUBSYSTEM_DOWN
165
+ process_subsystem_down
166
+ when Constants::PUSH_SUBSYSTEM_READY
167
+ process_subsystem_ready
168
+ when Constants::PUSH_SUBSYSTEM_OFF
169
+ process_push_shutdown
170
+ else
171
+ log_if_debug('Incorrect push status type.')
172
+ end
173
+ end
174
+ rescue StandardError => e
175
+ @config.logger.error("Push status handler error: #{e.inspect}")
176
+ end
177
+ end
178
+
179
+ def log_if_debug(msg)
180
+ @config.logger.debug(msg) if @config.debug_enabled
181
+ end
182
+ end
183
+ end