ff-ruby-server-sdk 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,263 @@
1
+ require_relative "operators"
2
+ require_relative "repository_callback"
3
+ require_relative "../common/repository"
4
+
5
+ class StorageRepository < Repository
6
+
7
+ def initialize(cache, callback = nil, store = nil, logger = nil)
8
+
9
+ @cache = cache
10
+ @store = store
11
+ @callback = callback
12
+
13
+ if logger != nil
14
+
15
+ @logger = logger
16
+ else
17
+
18
+ @logger = Logger.new(STDOUT)
19
+ end
20
+ end
21
+
22
+ def get_flag(identifier, cacheable = true)
23
+
24
+ flag_key = format_flag_key(identifier)
25
+ flag = @cache.get(flag_key)
26
+
27
+ if flag != nil
28
+
29
+ return flag
30
+ end
31
+
32
+ if @store != nil
33
+
34
+ flag = @store.get(flag_key)
35
+ if flag != nil && cacheable
36
+
37
+ @cache.set(flag_key, flag)
38
+ end
39
+
40
+ return flag
41
+ end
42
+
43
+ nil
44
+ end
45
+
46
+ def get_segment(identifier, cacheable = true)
47
+
48
+ segment_key = format_segment_key(identifier)
49
+ segment = @cache.get(segment_key)
50
+
51
+ if segment != nil
52
+
53
+ return segment
54
+ end
55
+
56
+ if @store != nil
57
+
58
+ segment = @store.get(segment_key)
59
+ if segment != nil && cacheable
60
+
61
+ @cache.set(segment_key, segment)
62
+ end
63
+
64
+ return segment
65
+ end
66
+
67
+ nil
68
+ end
69
+
70
+ def find_flags_by_segment(identifier)
71
+
72
+ result = []
73
+ keys = @cache.keys
74
+
75
+ if @store != nil
76
+
77
+ keys = @store.keys
78
+ end
79
+
80
+ keys.each do |key|
81
+
82
+ flag = get_flag(key)
83
+
84
+ if flag != nil && !flag.rules.length > 0
85
+
86
+ flag.rules.each do |rule|
87
+
88
+ rule.clauses.each do |clause|
89
+
90
+ if clause.op == Operators.SEGMENT_MATCH && clause.values.include(identifier)
91
+
92
+ result.push(flag.feature)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ result
100
+ end
101
+
102
+ def set_flag(identifier, feature_config)
103
+
104
+ if is_flag_outdated(identifier, feature_config)
105
+
106
+ @logger.debug "Flag " + identifier + " already exists"
107
+ return
108
+ end
109
+
110
+ flag_key = format_flag_key(identifier)
111
+
112
+ if @store != nil
113
+
114
+ @store.set(flag_key, feature_config)
115
+ @cache.delete(flag_key)
116
+
117
+ @logger.debug "Flag " + identifier + " successfully stored and cache invalidated"
118
+ else
119
+
120
+ @cache.set(flag_key, feature_config)
121
+
122
+ @logger.debug "Flag " + identifier + " successfully cached"
123
+ end
124
+
125
+ if @callback != nil
126
+
127
+ unless @callback.kind_of?(RepositoryCallback)
128
+
129
+ raise "The 'callback' parameter must be of '" + RepositoryCallback.to_s + "' data type"
130
+ end
131
+
132
+ @callback.on_flag_stored(identifier)
133
+ end
134
+ end
135
+
136
+ def set_segment(identifier, segment)
137
+
138
+ if is_segment_outdated(identifier, segment)
139
+
140
+ @logger.debug "Segment " + identifier + " already exists"
141
+ return
142
+ end
143
+
144
+ segment_key = format_segment_key(identifier)
145
+
146
+ if @store != nil
147
+
148
+ @store.set(segment_key, segment)
149
+ @cache.delete(segment_key)
150
+
151
+ @logger.debug "Segment " + identifier + " successfully stored and cache invalidated"
152
+ else
153
+
154
+ @cache.set(segment_key, segment)
155
+
156
+ @logger.debug "Segment " + identifier + " successfully cached"
157
+ end
158
+
159
+ if @callback != nil
160
+
161
+ unless @callback.kind_of?(RepositoryCallback)
162
+
163
+ raise "The 'callback' parameter must be of '" + RepositoryCallback.to_s + "' data type"
164
+ end
165
+
166
+ @callback.on_segment_stored(identifier)
167
+ end
168
+ end
169
+
170
+ def delete_flag(identifier)
171
+
172
+ flag_key = format_flag_key(identifier)
173
+
174
+ if @store != nil
175
+
176
+ @store.delete(flag_key)
177
+
178
+ @logger.debug "Flag " + identifier + " successfully deleted from store"
179
+ end
180
+
181
+ @cache.delete(flag_key)
182
+
183
+ @logger.debug "Flag " + identifier + " successfully deleted from cache"
184
+
185
+ if @callback != nil
186
+
187
+ unless @callback.kind_of?(RepositoryCallback)
188
+
189
+ raise "The 'callback' parameter must be of '" + RepositoryCallback.to_s + "' data type"
190
+ end
191
+
192
+ @callback.on_flag_deleted(identifier)
193
+ end
194
+ end
195
+
196
+ def delete_segment(identifier)
197
+
198
+ segment_key = format_segment_key(identifier)
199
+
200
+ if @store != nil
201
+
202
+ @store.delete(segment_key)
203
+
204
+ @logger.debug "Segment " + identifier + " successfully deleted from store"
205
+ end
206
+
207
+ @cache.delete(segment_key)
208
+
209
+ @logger.debug "Segment " + identifier + " successfully deleted from cache"
210
+
211
+ if @callback != nil
212
+
213
+ unless @callback.kind_of?(RepositoryCallback)
214
+
215
+ raise "The 'callback' parameter must be of '" + RepositoryCallback.to_s + "' data type"
216
+ end
217
+
218
+ @callback.on_segment_deleted(identifier)
219
+ end
220
+ end
221
+
222
+ def close
223
+
224
+ if @store != nil
225
+
226
+ @store.close
227
+ end
228
+ end
229
+
230
+ protected
231
+
232
+ def is_flag_outdated(identifier, new_feature_config)
233
+
234
+ flag = get_flag(identifier, false)
235
+ if flag != nil && flag.version != new_feature_config.version
236
+
237
+ return flag.version >= new_feature_config.version
238
+ end
239
+
240
+ false
241
+ end
242
+
243
+ def is_segment_outdated(identifier, new_segment)
244
+
245
+ segment = get_segment(identifier, false)
246
+ if segment != nil && segment.version != new_segment.version
247
+
248
+ return segment.version >= new_segment.version
249
+ end
250
+
251
+ false
252
+ end
253
+
254
+ def format_flag_key(identifier)
255
+
256
+ "flags_" + identifier
257
+ end
258
+
259
+ def format_segment_key(identifier)
260
+
261
+ "segments_" + identifier
262
+ end
263
+ end
@@ -0,0 +1,16 @@
1
+ class SummaryMetrics
2
+
3
+ attr_accessor :feature_name, :variation_identifier, :variation_value
4
+
5
+ def initialize(
6
+
7
+ feature_name,
8
+ variation_identifier,
9
+ variation_value
10
+ )
11
+
12
+ @feature_name = feature_name
13
+ @variation_value = variation_value
14
+ @variation_identifier = variation_identifier
15
+ end
16
+ end
@@ -0,0 +1,149 @@
1
+ require "concurrent-ruby"
2
+
3
+ require_relative "../common/closeable"
4
+
5
+ class UpdateProcessor < Closeable
6
+
7
+ def initialize(
8
+
9
+ connector,
10
+ repository,
11
+ callback,
12
+ logger
13
+ )
14
+
15
+ @connector = connector
16
+ @repository = repository
17
+ @updater = callback
18
+ @executor = Concurrent::FixedThreadPool.new(100)
19
+
20
+ if logger != nil
21
+
22
+ @logger = logger
23
+ else
24
+
25
+ @logger = Logger.new(STDOUT)
26
+ end
27
+ end
28
+
29
+ def start
30
+
31
+ @logger.info "Starting updater (EventSource)"
32
+
33
+ if @updater != nil
34
+
35
+ unless @updater.kind_of?(Updater)
36
+
37
+ raise "The 'callback' parameter must be of '" + Updater.to_s + "' data type"
38
+ end
39
+ end
40
+
41
+ if @connector != nil
42
+
43
+ unless @connector.kind_of?(Connector)
44
+
45
+ raise "The 'connector' must be of '" + Connector.to_s + "' data type"
46
+ end
47
+
48
+ @executor.post do
49
+
50
+ @stream = @connector.stream(@updater)
51
+ @stream.start
52
+ end
53
+ end
54
+ end
55
+
56
+ def stop
57
+
58
+ @logger.info "Stopping updater (EventSource)"
59
+
60
+ if @stream != nil
61
+
62
+ @stream.stop
63
+ end
64
+
65
+ @executor.shutdown
66
+ @executor.wait_for_termination(3)
67
+
68
+ if @executor.shuttingdown?
69
+
70
+ @executor.kill
71
+ end
72
+
73
+ @logger.info "Updater stopped (EventSource)"
74
+ end
75
+
76
+ def close
77
+
78
+ @logger.info "Closing UpdateProcessor"
79
+
80
+ stop
81
+ end
82
+
83
+ def update(message)
84
+
85
+ if message["domain"] == "flag"
86
+
87
+ @executor.post do
88
+
89
+ process_flag(message)
90
+ end
91
+
92
+ return
93
+ end
94
+
95
+ if message["domain"] == "target-segment"
96
+
97
+ @executor.post do
98
+
99
+ process_segment(message)
100
+ end
101
+ end
102
+ end
103
+
104
+ protected
105
+
106
+ def process_flag(message)
107
+
108
+ @logger.info "Processing flag message: " + message.to_s
109
+
110
+ config = @connector.get_flag(message["identifier"])
111
+
112
+ if config != nil
113
+
114
+ if message["event"] == "create" || message["event"] == "patch"
115
+
116
+ @repository.set_flag(message["identifier"], config)
117
+
118
+ else
119
+
120
+ if message["event"] == "delete"
121
+
122
+ @repository.delete_flag(message["identifier"])
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ def process_segment(message)
129
+
130
+ @logger.info "Processing segment message: " + message.to_s
131
+
132
+ segment = @connector.get_segment(message["identifier"])
133
+
134
+ if segment != nil
135
+
136
+ if message["event"] == "create" || message["event"] == "patch"
137
+
138
+ @repository.set_segment(message.identifier, segment)
139
+
140
+ else
141
+
142
+ if message["event"] == "delete"
143
+
144
+ @repository.delete_segment(message["identifier"])
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,27 @@
1
+ class Cache
2
+
3
+ def initialize
4
+
5
+ @tbi = "To be implemented"
6
+ end
7
+
8
+ def set(key, value)
9
+
10
+ raise @tbi
11
+ end
12
+
13
+ def get(key)
14
+
15
+ raise @tbi
16
+ end
17
+
18
+ def delete(key)
19
+
20
+ raise @tbi
21
+ end
22
+
23
+ def keys
24
+
25
+ raise @tbi
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ class Closeable
2
+
3
+ def close
4
+
5
+ raise "Not implemented"
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ class Destroyable
2
+
3
+ def initialize
4
+
5
+ @tbi = "to be implemented"
6
+ end
7
+
8
+ def destroy
9
+
10
+ raise @tbi
11
+ end
12
+ end
@@ -0,0 +1,45 @@
1
+ require_relative "closeable"
2
+
3
+ class Repository < Closeable
4
+
5
+ def initialize
6
+ super
7
+
8
+ @tbi = "To be implemented"
9
+ end
10
+
11
+ def get_flag(identifier, cacheable = true)
12
+
13
+ raise @tbi
14
+ end
15
+
16
+ def get_segment(identifier, cacheable = true)
17
+
18
+ raise @tbi
19
+ end
20
+
21
+ def find_flags_by_segment(identifier)
22
+
23
+ raise @tbi
24
+ end
25
+
26
+ def set_flag(identifier, feature_config)
27
+
28
+ raise @tbi
29
+ end
30
+
31
+ def set_segment(identifier, segment)
32
+
33
+ raise @tbi
34
+ end
35
+
36
+ def delete_flag(identifier)
37
+
38
+ raise @tbi
39
+ end
40
+
41
+ def delete_segment(identifier)
42
+
43
+ raise @tbi
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "closeable"
2
+
3
+ class Storage < Closeable
4
+
5
+ def initialize
6
+
7
+ @tbi = "To be implemented"
8
+ end
9
+
10
+ def set(key, value)
11
+
12
+ raise @tbi
13
+ end
14
+
15
+ def get(key)
16
+
17
+ raise @tbi
18
+ end
19
+
20
+ def delete(key)
21
+
22
+ raise @tbi
23
+ end
24
+
25
+ def keys
26
+
27
+ raise @tbi
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../common/closeable'
2
+
3
+ class Connector < Closeable
4
+
5
+ def initialize
6
+
7
+ @tbi = "To be implemented"
8
+ end
9
+
10
+ def authenticate
11
+
12
+ raise @tbi
13
+ end
14
+
15
+ def get_flags
16
+
17
+ raise @tbi
18
+ end
19
+
20
+ def get_flag(identifier)
21
+
22
+ raise @tbi
23
+ end
24
+
25
+ def get_segments
26
+
27
+ raise @tbi
28
+ end
29
+
30
+ def get_segment(identifier)
31
+
32
+ raise @tbi
33
+ end
34
+
35
+ def post_metrics(metrics)
36
+
37
+ raise @tbi
38
+ end
39
+
40
+ def stream(updater)
41
+
42
+ raise @tbi
43
+ end
44
+ end
@@ -0,0 +1,118 @@
1
+ require "json"
2
+ require "sse-client"
3
+
4
+ require_relative './service'
5
+
6
+ class Events < Service
7
+
8
+ def initialize(
9
+
10
+ url,
11
+ headers,
12
+ updater,
13
+ logger = nil
14
+ )
15
+
16
+ if @updater != nil
17
+
18
+ unless @updater.kind_of?(Updater)
19
+
20
+ raise "The 'callback' parameter must be of '" + Updater.to_s + "' data type"
21
+ end
22
+ end
23
+
24
+ @updater = updater
25
+
26
+ if logger != nil
27
+
28
+ @logger = logger
29
+ else
30
+
31
+ @logger = Logger.new(STDOUT)
32
+ end
33
+
34
+ @sse = SSE::EventSource.new(
35
+
36
+ url = url,
37
+ query = {},
38
+ headers = headers
39
+ )
40
+
41
+ @sse.open do
42
+
43
+ on_open
44
+ end
45
+
46
+ @sse.error do |error|
47
+
48
+ if error != nil
49
+
50
+ @logger.error "SSE ERROR: " + error.body
51
+ end
52
+
53
+ on_error
54
+ end
55
+
56
+ @sse.message do |message|
57
+
58
+ on_message(message)
59
+ end
60
+
61
+ @sse.on("*") do |message|
62
+
63
+ on_message(message)
64
+ end
65
+
66
+ @updater.on_ready
67
+ end
68
+
69
+ def start
70
+
71
+ @logger.info "Starting EventSource service"
72
+
73
+ @sse.start
74
+ end
75
+
76
+ def stop
77
+
78
+ @logger.info "Stopping EventSource service"
79
+
80
+ on_closed
81
+ end
82
+
83
+ def close
84
+
85
+ stop
86
+ end
87
+
88
+ def on_open
89
+
90
+ @logger.info "EventSource connected"
91
+
92
+ @updater.on_connected
93
+ end
94
+
95
+ def on_error
96
+
97
+ @logger.error "EventSource error"
98
+
99
+ @updater.on_error
100
+
101
+ stop
102
+ end
103
+
104
+ def on_closed
105
+
106
+ @logger.info "EventSource disconnected"
107
+
108
+ @updater.on_disconnected
109
+ end
110
+
111
+ def on_message(message)
112
+
113
+ @logger.debug "EventSource message received " + message.to_s
114
+
115
+ msg = JSON.parse(message)
116
+ @updater.update(msg)
117
+ end
118
+ end