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
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