ff-ruby-server-sdk 0.0.2 → 1.0.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.run/build.sh.run.xml +17 -0
  3. data/.run/install.sh.run.xml +17 -0
  4. data/.run/openapi.sh.run.xml +17 -0
  5. data/.run/publish.sh.run.xml +17 -0
  6. data/.run/sdk_test.rb.run.xml +3 -3
  7. data/.run/unpublish.sh.run.xml +17 -0
  8. data/CHANGELOG.md +1 -1
  9. data/Gemfile +20 -3
  10. data/README.md +155 -7
  11. data/api.yaml +736 -0
  12. data/example/example.rb +99 -3
  13. data/lib/ff/ruby/server/sdk/api/auth_service.rb +91 -0
  14. data/lib/ff/ruby/server/sdk/api/cf_client.rb +93 -0
  15. data/lib/ff/ruby/server/sdk/api/client_callback.rb +45 -0
  16. data/lib/ff/ruby/server/sdk/api/config.rb +140 -0
  17. data/lib/ff/ruby/server/sdk/api/config_builder.rb +116 -0
  18. data/lib/ff/ruby/server/sdk/api/default_cache.rb +112 -0
  19. data/lib/ff/ruby/server/sdk/api/evaluation.rb +29 -0
  20. data/lib/ff/ruby/server/sdk/api/evaluator.rb +526 -0
  21. data/lib/ff/ruby/server/sdk/api/file_map_store.rb +60 -0
  22. data/lib/ff/ruby/server/sdk/api/flag_evaluate_callback.rb +13 -0
  23. data/lib/ff/ruby/server/sdk/api/inner_client.rb +311 -0
  24. data/lib/ff/ruby/server/sdk/api/inner_client_flag_evaluate_callback.rb +30 -0
  25. data/lib/ff/ruby/server/sdk/api/inner_client_metrics_callback.rb +33 -0
  26. data/lib/ff/ruby/server/sdk/api/inner_client_repository_callback.rb +44 -0
  27. data/lib/ff/ruby/server/sdk/api/inner_client_updater.rb +63 -0
  28. data/lib/ff/ruby/server/sdk/api/metrics_callback.rb +19 -0
  29. data/lib/ff/ruby/server/sdk/api/metrics_event.rb +16 -0
  30. data/lib/ff/ruby/server/sdk/api/metrics_processor.rb +297 -0
  31. data/lib/ff/ruby/server/sdk/api/operators.rb +20 -0
  32. data/lib/ff/ruby/server/sdk/api/polling_processor.rb +164 -0
  33. data/lib/ff/ruby/server/sdk/api/repository_callback.rb +28 -0
  34. data/lib/ff/ruby/server/sdk/api/storage_repository.rb +263 -0
  35. data/lib/ff/ruby/server/sdk/api/summary_metrics.rb +16 -0
  36. data/lib/ff/ruby/server/sdk/api/update_processor.rb +149 -0
  37. data/lib/ff/ruby/server/sdk/common/cache.rb +27 -0
  38. data/lib/ff/ruby/server/sdk/common/closeable.rb +7 -0
  39. data/lib/ff/ruby/server/sdk/common/destroyable.rb +12 -0
  40. data/lib/ff/ruby/server/sdk/common/repository.rb +45 -0
  41. data/lib/ff/ruby/server/sdk/common/storage.rb +29 -0
  42. data/lib/ff/ruby/server/sdk/connector/connector.rb +44 -0
  43. data/lib/ff/ruby/server/sdk/connector/events.rb +118 -0
  44. data/lib/ff/ruby/server/sdk/connector/harness_connector.rb +236 -0
  45. data/lib/ff/ruby/server/sdk/connector/service.rb +19 -0
  46. data/lib/ff/ruby/server/sdk/connector/updater.rb +32 -0
  47. data/lib/ff/ruby/server/sdk/dto/message.rb +13 -0
  48. data/lib/ff/ruby/server/sdk/dto/target.rb +24 -0
  49. data/lib/ff/ruby/server/sdk/version.rb +2 -1
  50. data/lib/ff/ruby/server/sdk.rb +39 -3
  51. data/openapitools.json +7 -0
  52. data/scripts/openapi.sh +35 -0
  53. data/scripts/sdk_specs.sh +1 -1
  54. metadata +46 -3
  55. data/lib/ff/ruby/server/sdk/cf_client.rb +0 -6
@@ -0,0 +1,297 @@
1
+ require "time"
2
+ require "concurrent-ruby"
3
+
4
+ require_relative "../dto/target"
5
+ require_relative "../../sdk/version"
6
+ require_relative "../common/closeable"
7
+ require_relative "../api/metrics_event"
8
+ require_relative "../api/summary_metrics"
9
+
10
+ class MetricsProcessor < Closeable
11
+
12
+ def initialize(
13
+
14
+ connector,
15
+ config,
16
+ callback
17
+ )
18
+
19
+ unless connector.kind_of?(Connector)
20
+
21
+ raise "The 'connector' must be of '" + Connector.to_s + "' data type"
22
+ end
23
+
24
+ unless callback.kind_of?(MetricsCallback)
25
+
26
+ raise "The 'callback' must be of '" + MetricsCallback.to_s + "' data type"
27
+ end
28
+
29
+ unless config.kind_of?(Config)
30
+
31
+ raise "The 'config' must be of '" + Config.to_s + "' data type"
32
+ end
33
+
34
+ @config = config
35
+ @callback = callback
36
+ @connector = connector
37
+
38
+ @sdk_type = "SDK_TYPE"
39
+ @global_target_set = Set[]
40
+ @staging_target_set = Set[]
41
+ @target_attribute = "target"
42
+ @global_target = "__global__cf_target" # <--- This target identifier is used to aggregate and send data for all
43
+ # targets as a summary
44
+
45
+ @ready = false
46
+ @jar_version = ""
47
+ @server = "server"
48
+ @sdk_version = "SDK_VERSION"
49
+ @sdk_language = "SDK_LANGUAGE"
50
+ @global_target_name = "Global Target"
51
+ @feature_name_attribute = "featureName"
52
+ @variation_identifier_attribute = "variationIdentifier"
53
+
54
+ @queue = SizedQueue.new(@config.buffer_size)
55
+ @executor = Concurrent::FixedThreadPool.new(100)
56
+
57
+ @callback.on_metrics_ready
58
+ end
59
+
60
+ def start
61
+
62
+ @config.logger.info "Starting metrics processor with request interval: " + @config.frequency.to_s
63
+ start_async
64
+ end
65
+
66
+ def stop
67
+
68
+ @config.logger.info "Stopping metrics processor"
69
+ stop_async
70
+ end
71
+
72
+ def close
73
+
74
+ stop
75
+ @config.logger.info "Closing metrics processor"
76
+ end
77
+
78
+ def push_to_queue(
79
+
80
+ target,
81
+ feature_config,
82
+ variation
83
+ )
84
+
85
+ @executor.post do
86
+
87
+ @config.logger.debug "Pushing to the metrics queue: START"
88
+
89
+ event = MetricsEvent.new(feature_config, target, variation)
90
+ @queue.push(event)
91
+
92
+ @config.logger.debug "Pushing to the metrics queue: END, queue size: " + @queue.size.to_s
93
+
94
+ end
95
+ end
96
+
97
+ def send_data_and_reset_cache(data)
98
+
99
+ @config.logger.debug "Reading from queue and building cache"
100
+
101
+ @jar_version = get_version
102
+
103
+ unless data.empty?
104
+
105
+ map = {}
106
+
107
+ data.each do |event|
108
+
109
+ new_value = 1
110
+ current = map[event]
111
+
112
+ if current != nil
113
+
114
+ new_value = current + 1
115
+ end
116
+
117
+ map[event] = new_value
118
+ end
119
+
120
+ metrics = prepare_summary_metrics_body(map)
121
+
122
+ if !metrics.metrics_data.empty? && !metrics.target_data.empty?
123
+
124
+ start_time = (Time.now.to_f * 1000).to_i
125
+
126
+ @connector.post_metrics(metrics)
127
+
128
+ end_time = (Time.now.to_f * 1000).to_i
129
+
130
+ if end_time - start_time > @config.metrics_service_acceptable_duration
131
+
132
+ @config.logger.debug "Metrics service API duration=[" + (end_time - start_time).to_s + "]"
133
+ end
134
+ end
135
+
136
+ @global_target_set.merge(@staging_target_set)
137
+ @staging_target_set.clear
138
+ end
139
+ end
140
+
141
+ protected
142
+
143
+ def run_one_iteration
144
+
145
+ @config.logger.debug "Async metrics iteration: " + @thread.to_s
146
+
147
+ data = []
148
+
149
+ until @queue.empty?
150
+
151
+ item = @queue.pop
152
+ data.push(item)
153
+ end
154
+
155
+ send_data_and_reset_cache(data)
156
+ end
157
+
158
+ def prepare_summary_metrics_body(data)
159
+
160
+ summary_metrics_data = {}
161
+ metrics = OpenapiClient::Metrics.new({ :target_data => [], :metrics_data => [] })
162
+
163
+ add_target_data(
164
+
165
+ metrics,
166
+ Target.new(
167
+
168
+ name = @global_target_name,
169
+ identifier = @global_target
170
+ )
171
+ )
172
+
173
+ data.each do |key, value|
174
+
175
+ target = key.target
176
+
177
+ add_target_data(metrics, target)
178
+
179
+ summary_metrics = prepare_summary_metrics_key(key)
180
+
181
+ summary_metrics_data[summary_metrics] = value
182
+ end
183
+
184
+ summary_metrics_data.each do |key, value|
185
+
186
+ metrics_data = OpenapiClient::MetricsData.new({ :attributes => [] })
187
+ metrics_data.timestamp = (Time.now.to_f * 1000).to_i
188
+ metrics_data.count = value
189
+ metrics_data.metrics_type = "FFMETRICS"
190
+ metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @feature_name_attribute, :value => key.feature_name }))
191
+ metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @variation_identifier_attribute, :value => key.variation_identifier }))
192
+ metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @target_attribute, :value => @global_target }))
193
+ metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @sdk_type, :value => @server }))
194
+ metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @sdk_language, :value => "ruby" }))
195
+ metrics_data.attributes.push(OpenapiClient::KeyValue.new({ :key => @sdk_version, :value => @jar_version }))
196
+
197
+ metrics.metrics_data.push(metrics_data)
198
+ end
199
+
200
+ metrics
201
+ end
202
+
203
+ private
204
+
205
+ def start_async
206
+
207
+ @config.logger.debug "Async starting: " + self.to_s
208
+
209
+ @ready = true
210
+
211
+ @thread = Thread.new do
212
+
213
+ @config.logger.debug "Async started: " + self.to_s
214
+
215
+ while @ready do
216
+
217
+ unless @initialized
218
+
219
+ @initialized = true
220
+ @config.logger.info "Metrics processor initialized"
221
+ end
222
+
223
+ sleep(@config.frequency)
224
+
225
+ run_one_iteration
226
+ end
227
+ end
228
+
229
+ @thread.run
230
+ end
231
+
232
+ def stop_async
233
+
234
+ @queue.clear
235
+
236
+ @ready = false
237
+ @initialized = false
238
+ end
239
+
240
+ def prepare_summary_metrics_key(key)
241
+
242
+ SummaryMetrics.new(
243
+
244
+ feature_name = key.feature_config.feature,
245
+ variation_identifier = key.variation.identifier,
246
+ variation_value = key.variation.value
247
+ )
248
+ end
249
+
250
+ def add_target_data(metrics, target)
251
+
252
+ target_data = OpenapiClient::TargetData.new({ :attributes => [] })
253
+ private_attributes = target.private_attributes
254
+
255
+ if !@staging_target_set.include?(target) && !@global_target_set.include?(target) && !target.is_private
256
+
257
+ @staging_target_set.add(target)
258
+
259
+ attributes = target.attributes
260
+
261
+ attributes.each do |k, v|
262
+
263
+ key_value = OpenapiClient::KeyValue.new
264
+
265
+ if !private_attributes.empty?
266
+
267
+ unless private_attributes.include?(k)
268
+
269
+ key_value = OpenapiClient::KeyValue.new({ :key => k, :value => v.to_s })
270
+ end
271
+ else
272
+
273
+ key_value = OpenapiClient::KeyValue.new({ :key => k, :value => v.to_s })
274
+ end
275
+
276
+ target_data.attributes.push(key_value)
277
+ end
278
+
279
+ target_data.identifier = target.identifier
280
+
281
+ if target.name == nil || target.name == ""
282
+
283
+ target_data.name = target.identifier
284
+ else
285
+
286
+ target_data.name = target.name
287
+ end
288
+
289
+ metrics.target_data.push(target_data)
290
+ end
291
+ end
292
+
293
+ def get_version
294
+
295
+ Ff::Ruby::Server::Sdk::VERSION
296
+ end
297
+ end
@@ -0,0 +1,20 @@
1
+ class Operators
2
+
3
+ def initialize
4
+ super
5
+
6
+ raise "Abstract"
7
+ end
8
+
9
+ # Static:
10
+ class << self
11
+
12
+ @@SEGMENT_MATCH = "SEGMENT_MATCH"
13
+
14
+ def SEGMENT_MATCH
15
+
16
+ @@SEGMENT_MATCH
17
+ end
18
+
19
+ end # Static - End
20
+ end
@@ -0,0 +1,164 @@
1
+ require_relative "../common/closeable"
2
+
3
+ class PollingProcessor < Closeable
4
+
5
+ def initialize(
6
+
7
+ connector,
8
+ repository,
9
+ poll_interval_in_sec,
10
+ callback,
11
+ logger = nil
12
+ )
13
+
14
+ @callback = callback
15
+ @connector = connector
16
+ @repository = repository
17
+ @poll_interval_in_sec = poll_interval_in_sec
18
+
19
+ if logger != nil
20
+
21
+ @logger = logger
22
+ else
23
+
24
+ @logger = Logger.new(STDOUT)
25
+ end
26
+ end
27
+
28
+ def retrieve_flags
29
+
30
+ flags = []
31
+
32
+ @logger.info "Fetching flags started"
33
+
34
+ result = @connector.get_flags
35
+
36
+ if result != nil
37
+
38
+ @logger.info "Flags are fetched"
39
+
40
+ result.each { |fc|
41
+
42
+ if fc != nil
43
+
44
+ @repository.set_flag(fc.feature, fc)
45
+ flags.push(fc)
46
+ end
47
+ }
48
+ end
49
+
50
+ @logger.info "Fetching flags finished"
51
+
52
+ flags
53
+ end
54
+
55
+ def retrieve_segments
56
+
57
+ segments = []
58
+
59
+ @logger.info "Fetching segments started"
60
+
61
+ result = @connector.get_segments
62
+
63
+ if result != nil
64
+
65
+ @logger.info "Segments are fetched"
66
+
67
+ result.each { |s|
68
+
69
+ if s != nil
70
+
71
+ @repository.set_flag(s.identifier, s)
72
+ flags.push(s)
73
+ end
74
+ }
75
+ end
76
+
77
+ @logger.info "Fetching segments finished"
78
+
79
+ segments
80
+ end
81
+
82
+ def start_async
83
+
84
+ @logger.debug "Async starting: " + self.to_s
85
+
86
+ @ready = true
87
+
88
+ @thread = Thread.new do
89
+
90
+ @logger.debug "Async started: " + self.to_s
91
+
92
+ while @ready do
93
+
94
+ @logger.debug "Async poll iteration"
95
+
96
+ if @callback != nil
97
+
98
+ @callback.on_poller_iteration(self)
99
+ end
100
+
101
+ begin
102
+
103
+ retrieve_flags
104
+ retrieve_segments
105
+
106
+ unless @initialized
107
+
108
+ @initialized = true
109
+ @logger.info "PollingProcessor initialized"
110
+
111
+ if @callback != nil
112
+
113
+ @callback.on_poller_ready(self)
114
+ end
115
+ end
116
+
117
+ rescue OpenapiClient::ApiError => e
118
+
119
+ if @callback != nil
120
+
121
+ @callback.on_poller_error(e)
122
+ end
123
+ end
124
+
125
+ sleep(@poll_interval_in_sec)
126
+ end
127
+ end
128
+
129
+ @thread.run
130
+ end
131
+
132
+ def stop_async
133
+
134
+ @ready = false
135
+ @initialized = false
136
+ end
137
+
138
+ def start
139
+
140
+ @logger.info "Starting PollingProcessor with request interval: " + @poll_interval_in_sec.to_s
141
+ start_async
142
+ end
143
+
144
+ def stop
145
+
146
+ @logger.info "Stopping PollingProcessor"
147
+ stop_async
148
+ unless @ready
149
+
150
+ @logger.info "PollingProcessor stopped"
151
+ end
152
+ end
153
+
154
+ def close
155
+
156
+ stop
157
+ @logger.info "Closing PollingProcessor"
158
+ end
159
+
160
+ def is_ready
161
+
162
+ @ready && @initialized
163
+ end
164
+ end
@@ -0,0 +1,28 @@
1
+ class RepositoryCallback
2
+
3
+ def initialize
4
+ super
5
+
6
+ @tbi = "To be implemented"
7
+ end
8
+
9
+ def on_flag_stored(identifier)
10
+
11
+ raise @tbi
12
+ end
13
+
14
+ def on_flag_deleted(identifier)
15
+
16
+ raise @tbi
17
+ end
18
+
19
+ def on_segment_stored(identifier)
20
+
21
+ raise @tbi
22
+ end
23
+
24
+ def on_segment_deleted(identifier)
25
+
26
+ raise @tbi
27
+ end
28
+ end