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
data/example/example.rb CHANGED
@@ -1,4 +1,100 @@
1
- require_relative '../lib/ff/ruby/server/sdk'
1
+ require "logger"
2
+ require "securerandom"
3
+
4
+ require_relative '../lib/ff/ruby/server/sdk/dto/target'
5
+ require_relative '../lib/ff/ruby/server/sdk/api/config'
6
+ require_relative '../lib/ff/ruby/server/sdk/api/cf_client'
7
+ require_relative '../lib/ff/ruby/server/sdk/api/config_builder'
8
+
9
+ flag_b = "flag1"
10
+ flag_n = "flag2"
11
+ flag_s = "flag3"
12
+ flag_j = "flag4"
13
+
14
+ clients = {}
15
+ targets = {}
16
+
17
+ logger = Logger.new(STDOUT)
18
+
19
+ executor = Concurrent::FixedThreadPool.new(100)
20
+
21
+ keys = {
22
+
23
+ "Freemium" => "1f3339b4-e004-457a-91f7-9b5ce173eaaf",
24
+ "Non-Freemium" => "a30cf6aa-67f2-4545-8ac7-f86709f4f3a0"
25
+ }
26
+
27
+ keys.each do |name, key|
28
+
29
+ targets[name] = Target.new("ruby_target_" + name)
30
+
31
+ config = ConfigBuilder.new
32
+ .logger(logger)
33
+ .build
34
+
35
+ client = CfClient.new(key, config)
36
+
37
+ # .config_url("https://config.feature-flags.uat.harness.io/api/1.0")
38
+ # .event_url("https://event.feature-flags.uat.harness.io/api/1.0")
39
+
40
+ client.init
41
+
42
+ config.logger.debug "We will wait for the initialization"
43
+
44
+ client.wait_for_initialization
45
+
46
+ config.logger.debug "Initialization is complete"
47
+
48
+ clients[name] = client
49
+ end
50
+
51
+ iterations = 10
52
+
53
+ counted = 0
54
+ count_to = keys.size * iterations
55
+
56
+ logger.debug "To count: " + count_to.to_s
57
+
58
+ keys.each do |name, key|
59
+
60
+ client = clients[name]
61
+ target = targets[name]
62
+
63
+ executor.post do
64
+
65
+ (1..iterations).each do |iteration|
66
+
67
+ logger.debug name + " :: iteration no: " + iteration.to_s
68
+
69
+ bool_result = client.bool_variation(flag_b, target, false)
70
+ number_result = client.number_variation(flag_n, target, -1)
71
+ string_result = client.string_variation(flag_s, target, "unavailable !!!")
72
+ json_result = client.json_variation(flag_j, target, JSON.parse("{}"))
73
+
74
+ logger.debug name + " :: '" + flag_b.to_s + "' has the value of: " + bool_result.to_s
75
+ logger.debug name + " :: '" + flag_n.to_s + "' has the value of: " + number_result.to_s
76
+ logger.debug name + " :: '" + flag_s.to_s + "' has the value of: " + string_result.to_s
77
+ logger.debug name + " :: '" + flag_j.to_s + "' has the value of: " + json_result.to_s
78
+ logger.debug "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"
79
+
80
+ counted = counted + 1
81
+
82
+ logger.debug "Counted: " + counted.to_s
83
+
84
+ sleep 10
85
+ end
86
+ end
87
+ end
88
+
89
+ while counted != count_to
90
+
91
+ sleep(1)
92
+ end
93
+
94
+ clients.each do |name, client|
95
+
96
+ logger.debug name + " :: closing"
97
+
98
+ client.close
99
+ end
2
100
 
3
- client = CfClient.new
4
- client.hello
@@ -0,0 +1,91 @@
1
+ require_relative "../common/closeable"
2
+
3
+ class AuthService < Closeable
4
+
5
+ def initialize(connector = nil, poll_interval_in_sec = 60, callback = nil, logger = nil)
6
+
7
+ unless connector.kind_of?(Connector)
8
+
9
+ raise "The 'connector' parameter must be of '" + Connector.to_s + "' data type"
10
+ end
11
+
12
+ unless callback.kind_of?(ClientCallback)
13
+
14
+ raise "The 'callback' parameter must be of '" + ClientCallback.to_s + "' data type"
15
+ end
16
+
17
+ @callback = callback
18
+ @connector = connector
19
+ @poll_interval_in_sec = poll_interval_in_sec
20
+
21
+ if logger != nil
22
+
23
+ @logger = logger
24
+ else
25
+
26
+ @logger = Logger.new(STDOUT)
27
+ end
28
+ end
29
+
30
+ def start_async
31
+
32
+ @logger.debug "Async starting: " + self.to_s
33
+
34
+ @ready = true
35
+
36
+ @thread = Thread.new do
37
+
38
+ @logger.debug "Async started: " + self.to_s
39
+
40
+ while @ready do
41
+
42
+ @logger.debug "Async auth iteration"
43
+
44
+ if @connector.authenticate
45
+
46
+ @callback.on_auth_success
47
+ stop_async
48
+ @logger.info "Stopping Auth service"
49
+ else
50
+
51
+ @logger.error "Exception while authenticating, retry in " + @poll_interval_in_sec.to_s + " seconds"
52
+ end
53
+
54
+ sleep(@poll_interval_in_sec)
55
+ end
56
+ end
57
+
58
+ @thread.run
59
+ end
60
+
61
+ def close
62
+
63
+ stop_async
64
+ end
65
+
66
+ def on_auth_success
67
+
68
+ unless @callback == nil
69
+
70
+ unless @callback.kind_of?(ClientCallback)
71
+
72
+ raise "Expected '" + ClientCallback.to_s + "' data type for the callback"
73
+ end
74
+
75
+ @callback.on_auth_success
76
+ end
77
+ end
78
+
79
+ protected
80
+
81
+ def stop_async
82
+
83
+ @ready = false
84
+
85
+ if @thread != nil
86
+
87
+ @thread.exit
88
+ @thread = nil
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,93 @@
1
+ require "openapi_client"
2
+
3
+ require_relative "inner_client"
4
+ require_relative "../common/closeable"
5
+
6
+ class CfClient < Closeable
7
+
8
+ # Static:
9
+ class << self
10
+
11
+ @@instance = CfClient.new
12
+
13
+ def instance
14
+
15
+ @@instance
16
+ end
17
+ end
18
+
19
+ # Static - End
20
+
21
+ def initialize(api_key = nil, config = nil, connector = nil)
22
+
23
+ if config == nil
24
+
25
+ @config = ConfigBuilder.new.build
26
+ else
27
+
28
+ @config = config
29
+ end
30
+
31
+ @client = InnerClient.new(api_key, config, connector)
32
+
33
+ @config.logger.debug "Client (1): " + @client.to_s
34
+ end
35
+
36
+ def init(api_key = nil, config = nil, connector = nil)
37
+
38
+ if @client == nil
39
+
40
+ @config = config
41
+
42
+ @client = InnerClient.new(
43
+
44
+ api_key = api_key,
45
+ config = config,
46
+ connector = connector
47
+ )
48
+
49
+ @config.logger.debug "Client (2): " + @client.to_s
50
+ end
51
+ end
52
+
53
+ def wait_for_initialization
54
+
55
+ if @client != nil
56
+
57
+ @client.wait_for_initialization
58
+ end
59
+ end
60
+
61
+ def bool_variation(identifier, target, default_value)
62
+
63
+ @client.bool_variation(identifier, target, default_value)
64
+ end
65
+
66
+ def string_variation(identifier, target, default_value)
67
+
68
+ @client.string_variation(identifier, target, default_value)
69
+ end
70
+
71
+ def number_variation(identifier, target, default_value)
72
+
73
+ @client.number_variation(identifier, target, default_value)
74
+ end
75
+
76
+ def json_variation(identifier, target, default_value)
77
+
78
+ @client.json_variation(identifier, target, default_value)
79
+ end
80
+
81
+ def destroy
82
+
83
+ close
84
+ end
85
+
86
+ def close
87
+
88
+ if @client != nil
89
+
90
+ @client.close
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,45 @@
1
+ require_relative "../common/closeable"
2
+
3
+ class ClientCallback < Closeable
4
+
5
+ def initialize
6
+ super
7
+
8
+ @tbi = "To be implemented"
9
+ end
10
+
11
+ def on_auth_success
12
+
13
+ raise @tbi
14
+ end
15
+
16
+ def on_authorized
17
+
18
+ raise @tbi
19
+ end
20
+
21
+ def is_closing
22
+
23
+ raise @tbi
24
+ end
25
+
26
+ def on_processor_ready(processor)
27
+
28
+ raise @tbi
29
+ end
30
+
31
+ def on_update_processor_ready
32
+
33
+ raise @tbi
34
+ end
35
+
36
+ def on_metrics_processor_ready
37
+
38
+ raise @tbi
39
+ end
40
+
41
+ def update(message, manual)
42
+
43
+ raise @tbi
44
+ end
45
+ end
@@ -0,0 +1,140 @@
1
+ require "logger"
2
+
3
+ require_relative "default_cache"
4
+
5
+ class Config
6
+
7
+ attr_accessor :config_url, :event_url, :stream_enabled, :poll_interval_in_seconds, :analytics_enabled,
8
+ :frequency, :buffer_size, :all_attributes_private, :private_attributes, :connection_timeout,
9
+ :read_timeout, :write_timeout, :debugging, :metrics_service_acceptable_duration, :cache, :store,
10
+ :logger
11
+
12
+ # Static:
13
+ class << self
14
+
15
+ @@min_frequency = 60
16
+
17
+ def min_frequency
18
+
19
+ @@min_frequency
20
+ end
21
+ end
22
+ # Static - End
23
+
24
+ def initialize
25
+ super
26
+
27
+ @config_url = "https://config.ff.harness.io/api/1.0"
28
+ @event_url = "https://events.ff.harness.io/api/1.0"
29
+
30
+ @stream_enabled = true
31
+
32
+ @poll_interval_in_seconds = @@min_frequency
33
+
34
+ @analytics_enabled = true
35
+
36
+ @frequency = @@min_frequency
37
+
38
+ @buffer_size = 1024
39
+
40
+ @all_attributes_private = false
41
+
42
+ @private_attributes = Set[]
43
+
44
+ @connection_timeout = 10 * 1000
45
+
46
+ @read_timeout = 30 * 1000
47
+
48
+ @write_timeout = 10 * 1000
49
+
50
+ @debugging = false
51
+
52
+ @logger = Logger.new(STDOUT)
53
+
54
+ @metrics_service_acceptable_duration = 10 * 1000
55
+
56
+ @cache = DefaultCache.new(@logger)
57
+
58
+ # TODO: Storage goes here
59
+
60
+ @store = nil
61
+ end
62
+
63
+ def get_frequency
64
+
65
+ [@frequency, @@min_frequency].max
66
+ end
67
+
68
+ def verify_ssl_host
69
+
70
+ true
71
+ end
72
+
73
+ def params_encoding
74
+
75
+ nil
76
+ end
77
+
78
+ def timeout
79
+
80
+ @connection_timeout
81
+ end
82
+
83
+ def verify_ssl
84
+
85
+ nil
86
+ end
87
+
88
+ def cert_file
89
+
90
+ nil
91
+ end
92
+
93
+ def key_file
94
+
95
+ nil
96
+ end
97
+
98
+ def ssl_ca_cert
99
+
100
+ nil
101
+ end
102
+
103
+ def client_side_validation
104
+
105
+ nil
106
+ end
107
+
108
+ def auth_settings
109
+
110
+ {}
111
+ end
112
+
113
+ def base_url(args)
114
+
115
+ @config_url
116
+ end
117
+
118
+ def describe
119
+
120
+ to_s + "\n" +
121
+ "\tmin_frequency = " + @@min_frequency.to_s + "\n" +
122
+ "\tconfig_url = " + @config_url + "\n" +
123
+ "\tevent_url = " + @event_url + "\n" +
124
+ "\tstream_enabled = " + @stream_enabled.to_s + "\n" +
125
+ "\tpoll_interval_in_seconds = " + @poll_interval_in_seconds.to_s + "\n" +
126
+ "\tanalytics_enabled = " + @analytics_enabled.to_s + "\n" +
127
+ "\tfrequency = " + @frequency.to_s + "\n" +
128
+ "\tget_frequency = " + get_frequency.to_s + "\n" +
129
+ "\tbuffer_size = " + @buffer_size.to_s + "\n" +
130
+ "\tall_attributes_private = " + @all_attributes_private.to_s + "\n" +
131
+ "\tprivate_attributes = " + @private_attributes.to_s + "\n" +
132
+ "\tconnection_timeout = " + @connection_timeout.to_s + "\n" +
133
+ "\tread_timeout = " + @read_timeout.to_s + "\n" +
134
+ "\twrite_timeout = " + @write_timeout.to_s + "\n" +
135
+ "\tdebug = " + @debugging.to_s + "\n" +
136
+ "\tmetrics_service_acceptable_duration = " + @metrics_service_acceptable_duration.to_s + "\n" +
137
+ "\tcache = " + @cache.to_s + "\n" +
138
+ "\tstore = " + @store.to_s
139
+ end
140
+ end
@@ -0,0 +1,116 @@
1
+ class ConfigBuilder
2
+
3
+ def build
4
+
5
+ @config
6
+ end
7
+
8
+ def initialize
9
+
10
+ @config = Config.new
11
+ @config.cache = DefaultCache.new(@config.logger)
12
+ end
13
+
14
+ def config_url(config_url)
15
+
16
+ @config.config_url = config_url
17
+ self
18
+ end
19
+
20
+ def event_url(event_url)
21
+
22
+ @config.event_url = event_url
23
+ self
24
+ end
25
+
26
+ def stream_enabled(stream_enabled)
27
+
28
+ @config.stream_enabled = stream_enabled
29
+ self
30
+ end
31
+
32
+ def poll_interval_in_seconds(poll_interval_in_seconds)
33
+
34
+ @config.poll_interval_in_seconds = poll_interval_in_seconds
35
+ self
36
+ end
37
+
38
+ def analytics_enabled(analytics_enabled)
39
+
40
+ @config.analytics_enabled = analytics_enabled
41
+ self
42
+ end
43
+
44
+ def frequency(frequency)
45
+
46
+ @config.frequency = frequency
47
+ self
48
+ end
49
+
50
+ def buffer_size(buffer_size)
51
+
52
+ @config.buffer_size = buffer_size
53
+ self
54
+ end
55
+
56
+ def all_attributes_private(all_attributes_private)
57
+
58
+ @config.all_attributes_private = all_attributes_private
59
+ self
60
+ end
61
+
62
+ def private_attributes(private_attributes)
63
+
64
+ @config.private_attributes = private_attributes
65
+ self
66
+ end
67
+
68
+ def connection_timeout(connection_timeout)
69
+
70
+ @config.connection_timeout = connection_timeout
71
+ self
72
+ end
73
+
74
+ def read_timeout(read_timeout)
75
+
76
+ @config.read_timeout = read_timeout
77
+ self
78
+ end
79
+
80
+ def write_timeout(write_timeout)
81
+
82
+ @config.write_timeout = write_timeout
83
+ self
84
+ end
85
+
86
+ def logger(logger)
87
+
88
+ @config.logger = logger
89
+ @config.cache.logger = logger
90
+ self
91
+ end
92
+
93
+ def debugging(debug)
94
+
95
+ @config.debugging = debug
96
+ self
97
+ end
98
+
99
+ def metrics_service_acceptable_duration(metrics_service_acceptable_duration)
100
+
101
+ @config.metrics_service_acceptable_duration = metrics_service_acceptable_duration
102
+ self
103
+ end
104
+
105
+ def cache(cache)
106
+
107
+ @config.cache = cache
108
+ self
109
+ end
110
+
111
+ def store(store)
112
+
113
+ @config.store = store
114
+ self
115
+ end
116
+ end
@@ -0,0 +1,112 @@
1
+ require "set"
2
+ require "fileutils"
3
+ require "libcache"
4
+
5
+ class DefaultCache < Cache
6
+
7
+ attr_accessor :logger
8
+
9
+ def initialize(logger = nil)
10
+
11
+ if logger != nil
12
+
13
+ @logger = logger
14
+ else
15
+
16
+ @logger = Logger.new(STDOUT)
17
+ end
18
+
19
+ @keys = Set[]
20
+ @capacity = 10 * 1000
21
+
22
+ lambda = lambda { |*key| @logger.debug "Retrieved #{key}" }
23
+
24
+ cache_dir = "./cache"
25
+ unless directory_exists?(cache_dir)
26
+
27
+ FileUtils.mkdir_p cache_dir
28
+ unless directory_exists?(cache_dir)
29
+
30
+ raise "Failed to initialize filesystem cache at: " + cache_dir
31
+ end
32
+ end
33
+
34
+ @filesystem = CacheBuilder.with(FileCache)
35
+ .set_store(cache_dir)
36
+ .set_max(@capacity)
37
+ .set_post_get(lambda)
38
+ .build
39
+
40
+ @in_memory = CacheBuilder.with(Cache)
41
+ .set_max(@capacity)
42
+ .set_post_get(lambda)
43
+ .build
44
+ end
45
+
46
+ def verify
47
+
48
+ @in_memory != nil && @filesystem != nil && @capacity > 0
49
+ end
50
+
51
+ def set(key, value)
52
+
53
+ begin
54
+ @in_memory.put(key, value)
55
+ @filesystem.put(key, value)
56
+ keys.add(key)
57
+
58
+ rescue ArgumentError => e
59
+
60
+ @logger.error "ERROR: " + e.to_s
61
+
62
+ raise "Invalid arguments passed to the 'set' method: key='" + key.to_s + "', value='" + value.to_s + "'"
63
+ end
64
+ end
65
+
66
+ def get(key)
67
+
68
+ value = @in_memory.get(key)
69
+
70
+ if value == nil
71
+
72
+ value = @filesystem.get(key)
73
+ if value != nil
74
+
75
+ @in_memory.put(key, value)
76
+ end
77
+ end
78
+ value
79
+ end
80
+
81
+ def delete(key)
82
+
83
+ if key == nil
84
+
85
+ raise "Key is nil"
86
+ end
87
+
88
+ if @in_memory.exists?(key)
89
+
90
+ @in_memory.invalidate(key)
91
+ end
92
+
93
+ if @filesystem.exists?(key)
94
+
95
+ @filesystem.invalidate(key)
96
+ end
97
+
98
+ @keys.delete(key)
99
+ end
100
+
101
+ def keys
102
+
103
+ @keys
104
+ end
105
+
106
+ private
107
+
108
+ def directory_exists?(directory)
109
+
110
+ File.directory?(directory)
111
+ end
112
+ end