ff-ruby-server-sdk 1.0.6 → 1.1.1

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.
@@ -0,0 +1,102 @@
1
+
2
+ class SdkCodes
3
+ def self.raise_missing_sdk_key(logger)
4
+ msg = SdkCodes.sdk_err_msg(1002)
5
+ logger.error msg
6
+ raise msg
7
+ end
8
+
9
+ def self.info_poll_started(logger, durationSec)
10
+ logger.info SdkCodes.sdk_err_msg(4000, durationSec*1000)
11
+ end
12
+
13
+ def self.info_sdk_init_ok(logger)
14
+ logger.info SdkCodes.sdk_err_msg(1000)
15
+ end
16
+
17
+ def self.info_sdk_auth_ok(logger)
18
+ logger.info SdkCodes.sdk_err_msg(2000)
19
+ end
20
+
21
+ def self.info_polling_stopped(logger)
22
+ logger.info SdkCodes.sdk_err_msg(4001)
23
+ end
24
+
25
+ def self.info_stream_connected(logger)
26
+ logger.info SdkCodes.sdk_err_msg(5000)
27
+ end
28
+
29
+ def self.info_stream_event_received(logger, event_json)
30
+ logger.info SdkCodes.sdk_err_msg(5002, event_json)
31
+ end
32
+
33
+ def self.info_metrics_thread_started(logger)
34
+ logger.info SdkCodes.sdk_err_msg(7000)
35
+ end
36
+
37
+ def self.warn_auth_failed_srv_defaults(logger)
38
+ logger.warn SdkCodes.sdk_err_msg(2001)
39
+ end
40
+
41
+ def self.warn_auth_retying(logger, attempt)
42
+ logger.warn SdkCodes.sdk_err_msg(2003, ", attempt #{attempt}")
43
+ end
44
+
45
+ def self.warn_stream_disconnected(logger, reason)
46
+ logger.warn SdkCodes.sdk_err_msg(5001, reason)
47
+ end
48
+
49
+ def self.warn_post_metrics_failed(logger, reason)
50
+ logger.warn SdkCodes.sdk_err_msg(7002, reason)
51
+ end
52
+
53
+ def self.warn_default_variation_served(logger, identifier, target, default)
54
+ logger.warn SdkCodes.sdk_err_msg(6001, "identifier=%s, target=%s, default=%s" % [identifier, target.identifier, default])
55
+ end
56
+
57
+ private
58
+
59
+ @map =
60
+ {
61
+ # SDK_INIT_1xxx
62
+ 1000 => "The SDK has successfully initialized",
63
+ 1001 => "The SDK has failed to initialize due to the following authentication error:",
64
+ 1002 => "The SDK has failed to initialize due to a missing or empty API key",
65
+ # SDK_AUTH_2xxx
66
+ 2000 => "Authenticated ok",
67
+ 2001 => "Authentication failed with a non-recoverable error - defaults will be served",
68
+ 2003 => "Retrying to authenticate",
69
+ # SDK_POLL_4xxx
70
+ 4000 => "Polling started, intervalMs:",
71
+ 4001 => "Polling stopped",
72
+ # SDK_STREAM_5xxx
73
+ 5000 => "SSE stream connected ok",
74
+ 5001 => "SSE stream disconnected, reason:",
75
+ 5002 => "SSE event received: ",
76
+ 5003 => "SSE retrying to connect in",
77
+ # SDK_EVAL_6xxx
78
+ 6000 => "Evaluated variation successfully",
79
+ 6001 => "Default variation was served",
80
+ # SDK_METRICS_7xxx
81
+ 7000 => "Metrics thread started",
82
+ 7001 => "Metrics thread exited",
83
+ 7002 => "Posting metrics failed, reason:",
84
+ }
85
+
86
+ def self.sdk_err_msg(error_code, append_text = "")
87
+ "SDKCODE(%s:%s): %s %s" % [(get_err_class error_code), error_code, @map[error_code], append_text]
88
+ end
89
+
90
+ def self.get_err_class(error_code)
91
+ if error_code >= 1000 and error_code <= 1999
92
+ return "init"
93
+ elsif error_code >= 2000 and error_code <= 2999 then return "auth"
94
+ elsif error_code >= 4000 and error_code <= 4999 then return "poll"
95
+ elsif error_code >= 5000 and error_code <= 5999 then return "stream"
96
+ elsif error_code >= 6000 and error_code <= 6999 then return "eval"
97
+ elsif error_code >= 7000 and error_code <= 7999 then return "metric"
98
+ end
99
+ ""
100
+ end
101
+
102
+ end
@@ -1,8 +1,8 @@
1
1
  require "json"
2
- require "sse-client"
2
+ require 'restclient'
3
3
 
4
4
  require_relative './service'
5
-
5
+ require_relative "../common/sdk_codes"
6
6
  class Events < Service
7
7
 
8
8
  def initialize(
@@ -10,109 +10,105 @@ class Events < Service
10
10
  url,
11
11
  headers,
12
12
  updater,
13
- logger = nil
13
+ config
14
14
  )
15
15
 
16
- if @updater != nil
16
+ @url = url
17
+ @headers = headers
18
+ @headers['params'] = {}
19
+ @updater = updater
20
+ @config = config
17
21
 
22
+ if @updater != nil
18
23
  unless @updater.kind_of?(Updater)
19
-
20
24
  raise "The 'callback' parameter must be of '" + Updater.to_s + "' data type"
21
25
  end
22
26
  end
23
27
 
24
- @updater = updater
25
-
26
- if logger != nil
27
-
28
- @logger = logger
28
+ if @config.logger != nil
29
+ @logger = @config.logger
29
30
  else
30
-
31
31
  @logger = Logger.new(STDOUT)
32
32
  end
33
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
34
  @updater.on_ready
67
35
  end
68
36
 
69
37
  def start
70
-
71
- @logger.info "Starting EventSource service"
72
-
73
- @sse.start
38
+ @logger.debug "Starting EventSource service"
39
+ begin
40
+ conn = RestClient::Request.execute(method: :get,
41
+ url: @url,
42
+ headers: @headers,
43
+ block_response: proc { |response| response_handler response },
44
+ before_execution_proc: nil,
45
+ log: false,
46
+ read_timeout: 60,
47
+ ssl_ca_file: @config.ssl_ca_cert)
48
+
49
+ rescue => e
50
+ on_error e.message
51
+ end
74
52
  end
75
53
 
76
54
  def stop
77
-
78
- @logger.info "Stopping EventSource service"
79
-
55
+ @logger.debug "Stopping EventSource service"
80
56
  on_closed
81
57
  end
82
58
 
83
59
  def close
84
-
85
60
  stop
86
61
  end
87
62
 
88
63
  def on_open
89
-
90
- @logger.info "EventSource connected"
91
-
64
+ SdkCodes::info_stream_connected @logger
92
65
  @updater.on_connected
93
66
  end
94
67
 
95
- def on_error
96
-
97
- @logger.error "EventSource error"
98
-
68
+ def on_error(reason="")
69
+ SdkCodes::warn_stream_disconnected @logger, reason
99
70
  @updater.on_error
100
-
101
71
  stop
102
72
  end
103
73
 
104
74
  def on_closed
105
-
106
- @logger.info "EventSource disconnected"
107
-
75
+ SdkCodes::warn_stream_disconnected @logger, "on_closed called"
108
76
  @updater.on_disconnected
109
77
  end
110
78
 
111
79
  def on_message(message)
112
-
113
- @logger.debug "EventSource message received " + message.to_s
80
+ SdkCodes::info_stream_event_received @logger, message.to_s
114
81
 
115
82
  msg = JSON.parse(message)
116
83
  @updater.update(msg)
117
84
  end
85
+
86
+ private
87
+
88
+ def emit_line(line)
89
+ if line.start_with?("data:")
90
+ @logger.debug "SSE emit line: " + line
91
+ on_message line[line.index("{")..-1]
92
+ end
93
+ end
94
+
95
+ def response_handler(response)
96
+ on_open
97
+ case response.code
98
+ when "200"
99
+ line = ""
100
+ response.read_body do |chunk|
101
+ line << chunk
102
+ while line.sub!(/^(.*)\n/,"")
103
+ emit_line $1
104
+ end
105
+ end
106
+ close
107
+ else
108
+ msg = "SSE ERROR: http_code=%d body=%d" % [response.code, response.body]
109
+ @logger.warn msg
110
+ on_error msg
111
+ end
112
+ end
113
+
118
114
  end
@@ -13,6 +13,7 @@ class HarnessConnector < Connector
13
13
  @config = config
14
14
  @on_unauthorized = on_unauthorized
15
15
  @user_agent = "RubySDK " + Ff::Ruby::Server::Sdk::VERSION
16
+ @sdk_info = "Ruby #{Ff::Ruby::Server::Sdk::VERSION} Server"
16
17
 
17
18
  @api = OpenapiClient::ClientApi.new(make_api_client)
18
19
  @metrics_api = OpenapiClient::MetricsApi.new(make_metrics_api_client)
@@ -34,16 +35,23 @@ class HarnessConnector < Connector
34
35
  response = @api.authenticate(opts = options)
35
36
  @token = response.auth_token
36
37
 
37
- @config.logger.info "Token has been obtained"
38
+ @config.logger.debug "Token has been obtained"
38
39
  process_token
39
- return true
40
+ return 200
40
41
 
41
42
  rescue OpenapiClient::ApiError => e
42
43
 
43
- log_error(e)
44
- end
44
+ if e.message.include? "the server returns an error"
45
+ # NOTE openapi-generator 5.2.1 has a bug where exceptions don't contain any useful information and we can't
46
+ # determine if a timeout has occurred. This is fixed in 6.3.0 but requires Ruby version to be increased to 2.7
47
+ # https://github.com/OpenAPITools/openapi-generator/releases/tag/v6.3.0
48
+ @config.logger.warn "OpenapiClient::ApiError [\n\n#{e}\n]"
49
+ return -1
50
+ end
45
51
 
46
- false
52
+ log_error("auth", e)
53
+ return e.code
54
+ end
47
55
  end
48
56
 
49
57
  def get_flags
@@ -58,7 +66,8 @@ class HarnessConnector < Connector
58
66
 
59
67
  rescue OpenapiClient::ApiError => e
60
68
 
61
- log_error(e)
69
+ log_error("get_feature_config", e)
70
+ return nil
62
71
  end
63
72
  end
64
73
 
@@ -74,7 +83,8 @@ class HarnessConnector < Connector
74
83
 
75
84
  rescue OpenapiClient::ApiError => e
76
85
 
77
- log_error(e)
86
+ log_error("get_all_segments", e)
87
+ return nil
78
88
  end
79
89
  end
80
90
 
@@ -117,12 +127,11 @@ class HarnessConnector < Connector
117
127
  opts = options
118
128
  )
119
129
 
120
- @config.logger.info "Successfully sent analytics data to the server"
130
+ @config.logger.debug "Successfully sent analytics data to the server"
121
131
 
122
132
  rescue OpenapiClient::ApiError => e
123
-
124
- log_error(e)
125
- @config.logger.error "Exception while posting metrics to the event server"
133
+ log_error("post_metrics", e)
134
+ SdkCodes.warn_post_metrics_failed @config.logger, e.message
126
135
  end
127
136
  end
128
137
 
@@ -139,15 +148,19 @@ class HarnessConnector < Connector
139
148
  headers = {
140
149
 
141
150
  "Authorization" => "Bearer " + @token,
142
- "API-Key" => @api_key
143
- }
151
+ "API-Key" => @api_key,
152
+ "User-Agent" => @user_agent,
153
+ "Harness-SDK-Info" => @sdk_info,
154
+ "Harness-AccountID" => @account_id,
155
+ "Harness-EnvironmentID" => @environment_id
156
+ }.compact
144
157
 
145
158
  @event_source = Events.new(
146
159
 
147
160
  url,
148
161
  headers,
149
162
  updater,
150
- @config.logger
163
+ @config
151
164
  )
152
165
 
153
166
  @event_source
@@ -169,7 +182,10 @@ class HarnessConnector < Connector
169
182
  api_client = OpenapiClient::ApiClient.new
170
183
 
171
184
  api_client.config = @config
185
+ api_client.config.connection_timeout = @config.read_timeout / 1000
186
+ api_client.config.read_timeout = @config.read_timeout / 1000
172
187
  api_client.user_agent = @user_agent
188
+ api_client.default_headers['Harness-SDK-Info'] = @sdk_info
173
189
 
174
190
  api_client
175
191
  end
@@ -189,30 +205,32 @@ class HarnessConnector < Connector
189
205
 
190
206
  api_client.config = config
191
207
  api_client.user_agent = @user_agent
208
+ api_client.default_headers['Harness-SDK-Info'] = @sdk_info
192
209
 
193
210
  api_client
194
211
  end
195
212
 
196
213
  def process_token
197
-
198
- headers = {
199
-
200
- "Authorization" => "Bearer " + @token
201
- }
202
-
203
- @api.api_client.default_headers = @api.api_client.default_headers.merge(headers)
204
- @metrics_api.api_client.default_headers = @metrics_api.api_client.default_headers.merge(headers)
205
-
206
214
  decoded_token = JWT.decode @token, nil, false
207
215
 
208
216
  if decoded_token != nil && !decoded_token.empty?
209
217
 
210
218
  @environment = decoded_token[0]["environment"]
211
219
  @cluster = decoded_token[0]["clusterIdentifier"]
220
+ @environment_id = decoded_token[0]["environmentIdentifier"]
221
+ @account_id = decoded_token[0]["accountID"]
222
+
223
+ headers = {
224
+ "Authorization" => "Bearer " + @token,
225
+ "Harness-AccountID" => @account_id,
226
+ "Harness-EnvironmentID" => @environment_id
227
+ }.compact
228
+
229
+ @api.api_client.default_headers = @api.api_client.default_headers.merge(headers)
230
+ @metrics_api.api_client.default_headers = @metrics_api.api_client.default_headers.merge(headers)
212
231
 
213
232
  @config.logger.debug "Token has been processed: environment='" + @environment.to_s + "', cluster='" + @cluster.to_s + "'"
214
233
  else
215
-
216
234
  @config.logger.error "ERROR: Could not obtain the environment and cluster data from the token"
217
235
  end
218
236
  end
@@ -229,8 +247,14 @@ class HarnessConnector < Connector
229
247
  }
230
248
  end
231
249
 
232
- def log_error(e)
250
+ def log_error(prefix, e)
251
+
252
+ if e.code == 0
253
+ type = "typhoeus/libcurl"
254
+ else
255
+ type = "HTTP code #{e.code}"
256
+ end
233
257
 
234
- @config.logger.error "ERROR - Start\n\n" + e.to_s + "\nERROR - End"
258
+ @config.logger.warn "%s: OpenapiClient::ApiError (%s) [\n\n%s\n]" % [prefix, type, e.to_s]
235
259
  end
236
260
  end
@@ -5,7 +5,7 @@ module Ff
5
5
  module Server
6
6
  module Sdk
7
7
 
8
- VERSION = "1.0.6"
8
+ VERSION = "1.1.1"
9
9
  end
10
10
  end
11
11
  end
data/scripts/openapi.sh CHANGED
@@ -57,12 +57,9 @@ if which openapi-generator-cli; then
57
57
  gem install concurrent-ruby -v 1.1.10 && \
58
58
  gem install murmurhash3 -v 0.1.6 && \
59
59
  cd "$dir_path/.." && \
60
- openapi-generator-cli generate -i api.yaml -g ruby -o "$generated_path" && \
61
- cd "$generated_path" && gem build openapi_client.gemspec && \
62
- test -e "openapi_client-1.0.0.gem" && \
63
- gem install --dev "openapi_client-1.0.0.gem"; then
60
+ openapi-generator-cli generate -i api.yaml -g ruby -o "$generated_path"; then
64
61
 
65
- echo "Generated API has been installed with success: $generated_path"
62
+ echo "API has been generated with success: $generated_path"
66
63
  else
67
64
 
68
65
  echo "ERROR: 'openapi-generator-cli' is not installed [1] 😬"
data/scripts/sdk_specs.sh CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/bin/bash
2
2
 
3
3
  export ff_ruby_sdk="ff-ruby-server-sdk"
4
- export ff_ruby_sdk_version="1.0.6"
4
+ export ff_ruby_sdk_version="1.1.1"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ff-ruby-server-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - 'Miloš Vasić, cyr.: Милош Васић'
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-19 00:00:00.000000000 Z
11
+ date: 2023-05-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -122,20 +122,6 @@ dependencies:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
124
  version: 2.1.0
125
- - !ruby/object:Gem::Dependency
126
- name: sse-client
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - '='
130
- - !ruby/object:Gem::Version
131
- version: 1.1.0
132
- type: :runtime
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - '='
137
- - !ruby/object:Gem::Version
138
- version: 1.1.0
139
125
  - !ruby/object:Gem::Dependency
140
126
  name: concurrent-ruby
141
127
  requirement: !ruby/object:Gem::Requirement
@@ -165,19 +151,25 @@ dependencies:
165
151
  - !ruby/object:Gem::Version
166
152
  version: 0.1.6
167
153
  - !ruby/object:Gem::Dependency
168
- name: openapi_client
154
+ name: typhoeus
169
155
  requirement: !ruby/object:Gem::Requirement
170
156
  requirements:
171
157
  - - "~>"
172
158
  - !ruby/object:Gem::Version
173
- version: 1.0.0
159
+ version: '1.0'
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 1.0.1
174
163
  type: :runtime
175
164
  prerelease: false
176
165
  version_requirements: !ruby/object:Gem::Requirement
177
166
  requirements:
178
167
  - - "~>"
179
168
  - !ruby/object:Gem::Version
180
- version: 1.0.0
169
+ version: '1.0'
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: 1.0.1
181
173
  description: Harness is a feature management platform that helps teams to build better
182
174
  software and to test features quicker.
183
175
  email:
@@ -198,6 +190,7 @@ files:
198
190
  - api.yaml
199
191
  - bin/console
200
192
  - bin/setup
193
+ - buildInContainer.sh
201
194
  - docs/build.md
202
195
  - docs/further_reading.md
203
196
  - docs/images/ff-gui.png
@@ -207,6 +200,7 @@ files:
207
200
  - example/number_variation_example/number_variation_example.rb
208
201
  - example/simple_example/example.rb
209
202
  - example/string_variation_example/string_variation_example.rb
203
+ - example/tls_example/tls_example.rb
210
204
  - example/url_change_example/url_change_example.rb
211
205
  - lib/ff/ruby/server/generated/lib/openapi_client.rb
212
206
  - lib/ff/ruby/server/generated/lib/openapi_client/api/client_api.rb
@@ -268,6 +262,7 @@ files:
268
262
  - lib/ff/ruby/server/sdk/common/closeable.rb
269
263
  - lib/ff/ruby/server/sdk/common/destroyable.rb
270
264
  - lib/ff/ruby/server/sdk/common/repository.rb
265
+ - lib/ff/ruby/server/sdk/common/sdk_codes.rb
271
266
  - lib/ff/ruby/server/sdk/common/storage.rb
272
267
  - lib/ff/ruby/server/sdk/connector/connector.rb
273
268
  - lib/ff/ruby/server/sdk/connector/events.rb
@@ -291,7 +286,7 @@ metadata:
291
286
  homepage_uri: https://www.harness.io/
292
287
  source_code_uri: https://github.com/harness/ff-ruby-server-sdk
293
288
  changelog_uri: https://github.com/harness/ff-ruby-server-sdk/blob/main/CHANGELOG.md
294
- post_install_message:
289
+ post_install_message:
295
290
  rdoc_options: []
296
291
  require_paths:
297
292
  - lib
@@ -307,8 +302,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
307
302
  - !ruby/object:Gem::Version
308
303
  version: '0'
309
304
  requirements: []
310
- rubygems_version: 3.1.6
311
- signing_key:
305
+ rubygems_version: 3.4.10
306
+ signing_key:
312
307
  specification_version: 4
313
308
  summary: Harness is a feature management platform that helps teams to build better
314
309
  software and to test features quicker.