ff-ruby-server-sdk 1.0.6 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.