rox-rollout 5.1.0 → 5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17c6afc3c1c54a584ecec20f9213d1bf3d90fa603e4d3f378fd4014fafe7fbbe
4
- data.tar.gz: cc43890c658daf69fa6e3307ea00a78a6e6d16d94e7f1efc1f7aa8ede5466b2b
3
+ metadata.gz: ae34336f9f49d2ba7a2f77003d3f537eb53bbf4cde40fff04846a4c71d9fe177
4
+ data.tar.gz: d168174f3d6b72a482b89868d950e4bd8c30c908c0df6d0c11d276aceb8c229d
5
5
  SHA512:
6
- metadata.gz: 715c0071fb927d9f8947e47135e1c60036a19ac88ead19d2d8c482821291e04acb4c0adc57027b5e7a6ec305718c68ef64ffe2a5238835045855daf48d1a3075
7
- data.tar.gz: 2c56942cdf66b5a809970fa9ebabaa20b09e0fedf4a42f824234af9618eab0dd41be7b3c557fe3c597fa3175b35b29440cd74a4f8261d0ef0ffa91d1af651aee
6
+ metadata.gz: be4f8985f0289f5becb88a46f13578d83982ad0b5d471c3241dd8127493cdca9d1479eae63bc87e6c13c81092a37ae63e48bedaa40b806020a497a091e0996f2
7
+ data.tar.gz: f5c134a9692176e0495bbb3805799a439c14a08b5c160a61148c4affdb7e39f2699d1586e7f853e6bdd7fc43103bd9a66d17fc61b93a9b228627a92a7a6217ba
@@ -0,0 +1,52 @@
1
+ name: E2E tests
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+
11
+ jobs:
12
+ build:
13
+
14
+ runs-on: macos-latest
15
+
16
+ steps:
17
+ - uses: actions/checkout@v3
18
+ with:
19
+ path: rox-ruby
20
+ - name: Checkout e2e tests
21
+ uses: actions/checkout@v3
22
+ with:
23
+ repository: rollout/sdk-end-2-end-tests
24
+ ref: master
25
+ token: ${{ secrets.E2E_PAT }}
26
+ path: sdk-end-2-end-tests
27
+ - name: link driver
28
+ working-directory: ./sdk-end-2-end-tests/drivers
29
+ run: ln -s $GITHUB_WORKSPACE/rox-ruby/e2e-server ruby
30
+ - name: build e2e node driver
31
+ working-directory: ./sdk-end-2-end-tests/drivers/nodejs
32
+ run: |
33
+ yarn install --frozen-lockfile
34
+ - name: build and run e2e
35
+ working-directory: ./sdk-end-2-end-tests
36
+ run: |
37
+ yarn install --frozen-lockfile
38
+ QA_E2E_BEARER=$QA_E2E_BEARER API_HOST=https://api.test.rollout.io CD_API_ENDPOINT=https://api.test.rollout.io/device/get_configuration CD_S3_ENDPOINT=https://rox-conf.test.rollout.io/ SS_API_ENDPOINT=https://api.test.rollout.io/device/update_state_store/ SS_S3_ENDPOINT=https://rox-state.test.rollout.io/ CLIENT_DATA_CACHE_KEY=client_data ANALYTICS_ENDPOINT=https://analytic.test.rollout.io/ NOTIFICATIONS_ENDPOINT=https://push.test.rollout.io/sse SDK_LANG=ruby NODE_ENV=container yarn test:env
39
+ env:
40
+ QA_E2E_BEARER: ${{ secrets.QA_E2E_BEARER }}
41
+ - name: Show e2e server driver logs
42
+ if: ${{ always() }}
43
+ run: cat ./sdk-end-2-end-tests/drivers/ruby/log_1234.out || echo "no log file"
44
+ - name: Show e2e server sync driver logs
45
+ if: ${{ always() }}
46
+ run: cat ./sdk-end-2-end-tests/drivers/nodejs/log_1233.out || echo "no log file"
47
+ - name: Show e2e server init ClientData driver logs
48
+ if: ${{ always() }}
49
+ run: cat ./sdk-end-2-end-tests/drivers/ruby/log_2234.out || echo "no log file"
50
+ - name: Show e2e server init ClientData sync driver logs
51
+ if: ${{ always() }}
52
+ run: cat ./sdk-end-2-end-tests/drivers/nodejs/log_2233.out || echo "no log file"
@@ -1,4 +1,4 @@
1
- name: Ruby
1
+ name: Unit Tests
2
2
 
3
3
  on: [push]
4
4
 
@@ -8,14 +8,13 @@ jobs:
8
8
  fail-fast: false
9
9
  matrix:
10
10
  os: [ubuntu-latest]
11
- ruby: [2.4, 2.5, 2.6, 2.7, '3.0', 3.1]
11
+ ruby: [2.6, 2.7, '3.0', 3.1, 3.2]
12
12
  runs-on: ${{ matrix.os }}
13
13
  steps:
14
- - uses: actions/checkout@v2
14
+ - uses: actions/checkout@v3
15
15
  - uses: ruby/setup-ruby@v1
16
16
  with:
17
17
  ruby-version: ${{ matrix.ruby }}
18
- bundler: 2.3.3
18
+ bundler: 2.4.6
19
19
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
20
20
  - run: bundle exec rake test
21
- - run: bundle exec rake e2e
data/Rakefile CHANGED
@@ -8,12 +8,6 @@ Rake::TestTask.new(:test) do |t|
8
8
  t.test_files = FileList['test/**/*_test.rb']
9
9
  end
10
10
 
11
- Rake::TestTask.new(:e2e) do |t|
12
- t.libs << 'lib'
13
- t.libs << 'e2e'
14
- t.test_files = FileList['e2e/**/*_test.rb']
15
- end
16
-
17
11
  RuboCop::RakeTask.new(:lint)
18
12
 
19
13
  task default: :test
@@ -2,9 +2,13 @@
2
2
  DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
3
3
  cd -P "$DIR"
4
4
  DIR=`pwd`
5
- gem install sinatra
6
- gem install sinatra-contrib
7
- gem install json
5
+
6
+ # --conservative will save us some time (hopefully gem install just once)
7
+ gem install sinatra --conservative
8
+ gem install sinatra-contrib --conservative
9
+ gem install json --conservative
10
+ gem install em-eventsource --conservative
11
+
8
12
  nohup ruby ./server.rb $1 1>"$DIR"/log_"$1".out 2>&1 &
9
13
  while true ; do
10
14
  curl -p http://127.0.0.1:$1/status-check && exit
data/e2e-server/server.rb CHANGED
@@ -1,18 +1,20 @@
1
+ # comment the following line and install a rox-ruby gem to test a released version
2
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
3
+
1
4
  require 'sinatra'
2
5
  require 'sinatra/namespace'
3
6
  require 'json'
4
7
  require 'rox/server/flags/rox_flag'
5
8
  require 'rox/server/rox_server'
6
9
  require 'rox/server/rox_options'
10
+ require 'rox/server/network_configurations_options'
7
11
 
8
12
  class Container
9
- attr_accessor :first_flag
10
- attr_reader :bool_default_false, :bool_default_true
13
+ attr_reader :boolDefaultFalse, :boolDefaultTrue
11
14
 
12
15
  def initialize
13
- @bool_default_false = Rox::Server::RoxFlag.new(false)
14
- @bool_default_true = Rox::Server::RoxFlag.new(true)
15
- @first_flag = Rox::Server::RoxFlag.new(true)
16
+ @boolDefaultFalse = Rox::Server::RoxFlag.new(false)
17
+ @boolDefaultTrue = Rox::Server::RoxFlag.new(true)
16
18
  end
17
19
  end
18
20
 
@@ -58,95 +60,63 @@ namespace '/' do
58
60
  [200, '']
59
61
  end
60
62
 
61
- get 'api/values/:id' do |id|
62
- if container.first_flag.enabled?
63
- [200, JSON.generate({ value: id })]
64
- else
65
- [200, 'Eladddddd']
66
- end
67
- end
68
-
69
- put 'api/values/:id' do
70
- [200, '']
71
- end
72
-
73
- delete 'api/values/:id' do
74
- [200, '']
75
- end
76
-
77
63
  post '' do
78
64
  data = JSON.parse(request.body.read)
79
65
  action = data['action']
80
66
  payload = data['payload']
81
- puts data
82
-
67
+ puts "data => #{data}"
83
68
  case action
84
69
  when 'staticFlagIsEnabled'
85
70
  flag = payload['flag']
86
71
  result = container.send(flag.to_sym).enabled?(payload['context'])
87
72
  [200, JSON.generate({ result: result })]
88
73
  when 'registerStaticContainers'
89
- Rox::Server::RoxServer.register(container)
74
+ Rox::Server::RoxServer.register_with_namespace('namespace', container)
90
75
  [200, JSON.generate({ result: 'done' })]
91
76
  when 'setCustomPropertyToThrow'
92
77
  def raise_(ex)
93
78
  raise ex
94
79
  end
95
- Rox::Server::RoxServer.set_custom_string_property(payload['key'], raise_(Exception('error')))
80
+ Rox::Server::RoxServer.set_custom_string_property(payload['key']) do |_context|
81
+ raise_(StandardError.new "custom property generator error")
82
+ end
96
83
  [200, JSON.generate({ result: 'done' })]
97
84
  when 'setCustomStringProperty'
98
85
  Rox::Server::RoxServer.set_custom_string_property(payload['key'], payload['value'])
99
86
  [200, JSON.generate({ result: 'done' })]
100
87
  when 'dynamicFlagValue'
101
- if payload['context']
102
- puts "There is a payload[context] => #{payload['context']}"
103
- payload['context'].each do |context|
104
- key = context[0]
105
- value = context[1]
106
- Rox::Server::RoxServer.set_custom_string_property(key, value) if value.instance_of? String
107
- if value.instance_of? TrueClass
108
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
109
- elsif value.instance_of? FalseClass
110
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
111
- end
112
- Rox::Server::RoxServer.set_custom_int_property(key, value) if value.instance_of? Integer
113
- Rox::Server::RoxServer.set_custom_float_property(key, value) if value.instance_of? Float
114
- end
115
- end
116
- result = Rox::Server::RoxServer.dynamic_api.value(payload['flag'], payload['defaultValue'], payload['context'],
117
- [])
88
+ result = Rox::Server::RoxServer.dynamic_api.value(payload['flag'], payload['defaultValue'], payload['context'], [])
118
89
  [200, JSON.generate({ result: result })]
119
90
  when 'dynamicFlagIsEnabled'
120
- payload['context']&.each do |context|
121
- key = context[0]
122
- value = context[1]
123
- Rox::Server::RoxServer.set_custom_string_property(key, value) if value.instance_of? String
124
- if value.instance_of? TrueClass
125
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
126
- elsif value.instance_of? FalseClass
127
- Rox::Server::RoxServer.set_custom_boolean_property(key, value)
128
- end
129
- Rox::Server::RoxServer.set_custom_int_property(key, value) if value.instance_of? Integer
130
- Rox::Server::RoxServer.set_custom_float_property(key, value) if value.instance_of? Float
131
- end
132
91
  result = Rox::Server::RoxServer.dynamic_api.enabled?(payload['flag'], payload['defaultValue'], payload['context'])
133
92
  [200, JSON.generate({ result: result })]
134
93
  when 'setupAndAwait'
135
94
  env = 'stam'
95
+ fetch_interval = nil
136
96
  if payload['options']
137
97
  options = payload['options']
98
+ env = options['env']
99
+ fetch_interval = options['fetchInterval']
138
100
  if options['configuration']
139
101
  configuration = options['configuration']
140
- env = configuration['env']
141
102
  end
142
103
  end
104
+ network_config = nil
143
105
  case env
106
+ when 'container'
107
+ network_config = Rox::Server::NetworkConfigurationsOptions.new(
108
+ get_config_api_endpoint: configuration['CD_API_ENDPOINT']&.chomp('/'),
109
+ get_config_cloud_endpoint: configuration['CD_S3_ENDPOINT']&.chomp('/'),
110
+ send_state_api_endpoint: configuration['SS_API_ENDPOINT']&.chomp('/'),
111
+ send_state_cloud_endpoint: configuration['SS_S3_ENDPOINT']&.chomp('/'),
112
+ analytics_endpoint: configuration['ANALYTICS_ENDPOINT']&.chomp('/'),
113
+ push_notification_endpoint: configuration['NOTIFICATIONS_ENDPOINT']&.chomp('/'))
144
114
  when 'qa'
145
115
  ENV['ROLLOUT_MODE'] = 'QA'
146
116
  when 'localhost'
147
117
  ENV['ROLLOUT_MODE'] = 'LOCAL'
148
118
  end
149
- options = Rox::Server::RoxOptions.new(logger: ServerLogger.new)
119
+ options = Rox::Server::RoxOptions.new(fetch_interval: fetch_interval,logger: ServerLogger.new, network_configurations_options: network_config)
150
120
  puts "options => #{options}"
151
121
  Rox::Server::RoxServer.setup(payload['key'], options).value
152
122
  [200, JSON.generate({ result: 'done' })]
@@ -18,6 +18,7 @@ module Rox
18
18
  def initialize(device_properties)
19
19
  @device_properties = device_properties
20
20
  uri = URI.parse(Rox::Core::Environment.analytics_path)
21
+ logger.debug("Using Analytics url #{uri}")
21
22
  @headers = {
22
23
  'Accept' => 'application/json',
23
24
  'Content-Type' => 'application/json',
@@ -1,77 +1,85 @@
1
1
  module Rox
2
2
  module Core
3
3
  module Environment
4
+ def self.reset(rox_options = nil)
5
+ @roxy_internal_path = 'device/request_configuration'
6
+ if !rox_options&.network_configurations_options.nil?
7
+ @cdn_path = rox_options&.network_configurations_options&.get_config_cloud_endpoint
8
+ @api_path = rox_options&.network_configurations_options&.get_config_api_endpoint
9
+ @state_cdn_path = rox_options&.network_configurations_options&.send_state_cloud_endpoint
10
+ @state_api_path = rox_options&.network_configurations_options&.send_state_api_endpoint
11
+ @analytics_path = rox_options&.network_configurations_options&.analytics_endpoint
12
+ @notifications_path = rox_options&.network_configurations_options&.push_notification_endpoint
13
+ else
14
+ case ENV['ROLLOUT_MODE']
15
+ when 'QA'
16
+ setQA
17
+ when 'LOCAL'
18
+ setLOCAL
19
+ else
20
+ setDefault(rox_options)
21
+ end
22
+ end
23
+ end
24
+
4
25
  def self.roxy_internal_path
5
- 'device/request_configuration'
26
+ @roxy_internal_path
6
27
  end
7
28
 
8
29
  def self.cdn_path
9
- case ENV['ROLLOUT_MODE']
10
- when 'QA'
11
- 'https://qa-conf.rollout.io'
12
- when 'LOCAL'
13
- 'https://development-conf.rollout.io'
14
- else
15
- 'https://conf.rollout.io'
16
- end
30
+ @cdn_path
17
31
  end
18
32
 
19
- def self.api_path(api_url = nil)
20
- case ENV['ROLLOUT_MODE']
21
- when 'QA'
22
- 'https://qa-api.rollout.io/device/get_configuration'
23
- when 'LOCAL'
24
- 'http://127.0.0.1:8557/device/get_configuration'
25
- else
26
- api_url ||= 'https://x-api.rollout.io'
27
- "#{api_url}/device/get_configuration"
28
- end
33
+ def self.api_path
34
+ @api_path
29
35
  end
30
36
 
31
37
  def self.state_cdn_path
32
- case ENV['ROLLOUT_MODE']
33
- when 'QA'
34
- 'https://qa-statestore.rollout.io'
35
- when 'LOCAL'
36
- 'https://development-statestore.rollout.io'
37
- else
38
- 'https://statestore.rollout.io'
39
- end
38
+ @state_cdn_path
40
39
  end
41
40
 
42
- def self.state_api_path(api_url = nil)
43
- case ENV['ROLLOUT_MODE']
44
- when 'QA'
45
- 'https://qa-api.rollout.io/device/update_state_store'
46
- when 'LOCAL'
47
- 'http://127.0.0.1:8557/device/update_state_store'
48
- else
49
- api_url ||= 'https://x-api.rollout.io'
50
- "#{api_url}/device/update_state_store"
51
- end
41
+ def self.state_api_path
42
+ @state_api_path
52
43
  end
53
44
 
54
- def self.analytics_path(analytics_url = 'https://analytic.rollout.io')
55
- case ENV['ROLLOUT_MODE']
56
- when 'QA'
57
- 'https://qaanalytic.rollout.io'
58
- when 'LOCAL'
59
- 'http://127.0.0.1:8787'
60
- else
61
- analytics_url
62
- end
45
+ def self.analytics_path
46
+ @analytics_path
63
47
  end
64
48
 
65
49
  def self.notifications_path
66
- case ENV['ROLLOUT_MODE']
67
- when 'QA'
68
- 'https://qax-push.rollout.io/sse'
69
- when 'LOCAL'
70
- 'http://127.0.0.1:8887/sse'
71
- else
72
- 'https://push.rollout.io/sse'
73
- end
50
+ @notifications_path
51
+ end
52
+
53
+ private
54
+ def self.setQA()
55
+ @cdn_path = 'https://qa-conf.rollout.io'
56
+ @api_path = 'https://qa-api.rollout.io/device/get_configuration'
57
+ @state_cdn_path = 'https://qa-statestore.rollout.io'
58
+ @state_api_path = 'https://qa-api.rollout.io/device/update_state_store'
59
+ @analytics_path = 'https://qaanalytic.rollout.io'
60
+ @notifications_path = 'https://qax-push.rollout.io/sse'
61
+ end
62
+
63
+ def self.setLOCAL()
64
+ @cdn_path = 'https://development-conf.rollout.io'
65
+ @api_path = 'http://127.0.0.1:8557/device/get_configuration'
66
+ @state_cdn_path = 'https://development-statestore.rollout.io'
67
+ @state_api_path = 'http://127.0.0.1:8557/device/update_state_store'
68
+ @analytics_path = 'http://127.0.0.1:8787'
69
+ @notifications_path = 'http://127.0.0.1:8887/sse'
70
+ end
71
+
72
+ def self.setDefault(rox_options)
73
+ @cdn_path = 'https://conf.rollout.io'
74
+ @state_cdn_path = 'https://statestore.rollout.io'
75
+ alternative_api_url = rox_options&.self_managed_options&.server_url || 'https://x-api.rollout.io'
76
+ @api_path = "#{alternative_api_url}/device/get_configuration"
77
+ @state_api_path = "#{alternative_api_url}/device/update_state_store"
78
+ @analytics_path = rox_options&.self_managed_options&.analytics_url ||'https://analytic.rollout.io'
79
+ @notifications_path = 'https://push.rollout.io/sse'
74
80
  end
75
81
  end
76
82
  end
77
83
  end
84
+
85
+ Rox::Core::Environment.reset
data/lib/rox/core/core.rb CHANGED
@@ -59,6 +59,7 @@ module Rox
59
59
  def setup(sdk_settings, device_properties)
60
60
  @sdk_settings = sdk_settings
61
61
  @rox_options = device_properties.rox_options
62
+ Rox::Core::Environment.reset(@rox_options)
62
63
 
63
64
  experiments_extensions = ExperimentsExtensions.new(@parser, @target_group_repository, @flag_repository,
64
65
  @experiment_repository)
@@ -70,14 +71,12 @@ module Rox
70
71
 
71
72
  validate_api_key(sdk_settings&.api_key) if roxy_path.nil?
72
73
 
73
- # TODO: Analytics.Analytics.Initialize(deviceProperties.RolloutKey, deviceProperties)
74
74
  @internal_flags = InternalFlags.new(@experiment_repository, @parser, @rox_options)
75
75
 
76
- if !@rox_options.self_managed? && roxy_path.nil?
76
+ if roxy_path.nil?
77
77
  @analytics_client = create_analytics_client(device_properties)
78
78
  end
79
79
 
80
- # TODO: impressionInvoker = new ImpressionInvoker(internalFlags, customPropertyRepository, deviceProperties, Analytics.Analytics.Client, roxyPath != null);
81
80
  @impression_invoker = ImpressionInvoker.new(@internal_flags, @custom_property_repository, device_properties,
82
81
  @analytics_client, !roxy_path.nil?, @user_unhandled_error_invoker)
83
82
  @flag_setter = FlagSetter.new(@flag_repository, @parser, @experiment_repository, @impression_invoker)
@@ -109,14 +108,13 @@ module Rox
109
108
  @configuration_fetched_invoker)
110
109
  end
111
110
 
112
- configuration_fetched_handler = nil
113
- configuration_fetched_handler = @rox_options.configuration_fetched_handler unless @rox_options.nil?
114
-
115
111
  @configuration_fetched_invoker.register_start_stop_push(proc do |args|
116
112
  start_or_stop_push_updated_listener unless args.fetcher_status == FetcherStatus::ERROR_FETCHED_FAILED
117
113
  end)
118
114
 
119
- @configuration_fetched_invoker.register_fetched_handler(&configuration_fetched_handler)
115
+ if !@rox_options.nil? && !@rox_options.configuration_fetched_handler.nil?
116
+ @configuration_fetched_invoker.register_fetched_handler(&@rox_options.configuration_fetched_handler)
117
+ end
120
118
 
121
119
  @thread = Thread.new do
122
120
  Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception)
@@ -4,7 +4,7 @@ module Rox
4
4
  module Core
5
5
  class RoxDouble < RoxString
6
6
  def value(context = nil)
7
- return_value = internal_value(context, false, Float)
7
+ return_value = Rox::Server::NormalizeFlagType.normalize_float(internal_value(context, false, Float))
8
8
  send_impressions(return_value, context)
9
9
  return_value
10
10
  end
@@ -4,7 +4,7 @@ module Rox
4
4
  module Core
5
5
  class RoxInt < RoxString
6
6
  def value(context = nil)
7
- return_value = internal_value(context, false, Integer)
7
+ return_value = Rox::Server::NormalizeFlagType.normalize_int(internal_value(context, false, Integer))
8
8
  send_impressions(return_value, context)
9
9
  return_value
10
10
  end
@@ -23,7 +23,7 @@ module Rox
23
23
  end
24
24
 
25
25
  def build_for_api
26
- build_request_with_full_params("#{Rox::Core::Environment.api_path(@rox_options.self_managed_options&.server_url)}/#{relative_path}")
26
+ build_request_with_full_params("#{Rox::Core::Environment.api_path}/#{relative_path}")
27
27
  end
28
28
 
29
29
  def relative_path
@@ -21,8 +21,8 @@ module Rox
21
21
  end
22
22
 
23
23
  def missing_via_response_body?
24
- @status_code == 200 && @content_type == 'application/json' &&
25
- [404, '404'].include?(JSON.parse(text)['result'])
24
+ # api send state respons returns 200 'OK', application/json filters it
25
+ @status_code == 200 && @content_type == 'application/json' && @text && [404, '404'].include?(JSON.parse(text)['result'])
26
26
  rescue JSON::ParserError
27
27
  Logging.logger.error("Failed to parse JSON response: #{text}")
28
28
  false
@@ -69,7 +69,7 @@ module Rox
69
69
 
70
70
  def send_state_to_API(rollout_key, md5_signature, state_payload)
71
71
  @request.send_post(
72
- "#{Rox::Core::Environment.state_api_path(@device_properties.rox_options.self_managed_options&.server_url)}/#{rollout_key}/#{md5_signature}", state_payload
72
+ "#{Rox::Core::Environment.state_api_path}/#{rollout_key}/#{md5_signature}", state_payload
73
73
  )
74
74
  end
75
75
 
@@ -0,0 +1,20 @@
1
+ module Rox
2
+ module Server
3
+ class NetworkConfigurationsOptions
4
+ attr_reader :get_config_api_endpoint, :get_config_cloud_endpoint,
5
+ :send_state_api_endpoint, :send_state_cloud_endpoint,
6
+ :analytics_endpoint, :push_notification_endpoint
7
+
8
+ def initialize(get_config_api_endpoint:, get_config_cloud_endpoint:,
9
+ send_state_api_endpoint:, send_state_cloud_endpoint:,
10
+ analytics_endpoint:, push_notification_endpoint:)
11
+ @get_config_api_endpoint = get_config_api_endpoint
12
+ @get_config_cloud_endpoint = get_config_cloud_endpoint
13
+ @send_state_api_endpoint = send_state_api_endpoint
14
+ @send_state_cloud_endpoint = send_state_cloud_endpoint
15
+ @analytics_endpoint = analytics_endpoint
16
+ @push_notification_endpoint = push_notification_endpoint
17
+ end
18
+ end
19
+ end
20
+ end
@@ -3,20 +3,12 @@ require 'rox/server/logging/server_logger'
3
3
 
4
4
  module Rox
5
5
  module Server
6
- class SelfManagedOptions
7
- attr_reader :server_url, :analytics_url
8
-
9
- def initialize(server_url:, analytics_url:)
10
- @server_url = server_url
11
- @analytics_url = analytics_url
12
- end
13
- end
14
-
15
6
  class RoxOptions
16
7
  attr_reader :dev_mode_key, :version, :fetch_interval,
17
8
  :logger, :impression_handler,
18
9
  :configuration_fetched_handler,
19
10
  :roxy_url, :self_managed_options,
11
+ :network_configurations_options,
20
12
  :dynamic_property_rule_handler
21
13
 
22
14
  def initialize(
@@ -28,6 +20,7 @@ module Rox
28
20
  configuration_fetched_handler: nil,
29
21
  roxy_url: nil,
30
22
  self_managed_options: nil,
23
+ network_configurations_options: nil,
31
24
  dynamic_property_rule_handler: nil
32
25
  )
33
26
  @dev_mode_key = dev_mode_key || 'stam'
@@ -45,6 +38,7 @@ module Rox
45
38
  @configuration_fetched_handler = configuration_fetched_handler
46
39
  @roxy_url = roxy_url
47
40
  @self_managed_options = self_managed_options
41
+ @network_configurations_options = network_configurations_options
48
42
  @dynamic_property_rule_handler = dynamic_property_rule_handler || proc do |prop_name, context|
49
43
  context ? context[prop_name] : nil
50
44
  end
@@ -0,0 +1,12 @@
1
+ module Rox
2
+ module Server
3
+ class SelfManagedOptions
4
+ attr_reader :server_url, :analytics_url
5
+
6
+ def initialize(server_url:, analytics_url:)
7
+ @server_url = server_url
8
+ @analytics_url = analytics_url
9
+ end
10
+ end
11
+ end
12
+ end
data/lib/rox/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rox
2
- VERSION = '5.1.0'.freeze
2
+ VERSION = '5.1.1'.freeze
3
3
  end
data/rox.gemspec CHANGED
@@ -20,11 +20,11 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ['lib']
22
22
 
23
- spec.required_ruby_version = '>= 2.3'
23
+ spec.required_ruby_version = '>= 2.5'
24
24
 
25
25
  spec.add_runtime_dependency 'em-eventsource', '~> 0.3.2'
26
26
 
27
- spec.add_development_dependency 'bundler', '~> 2.3.3'
27
+ spec.add_development_dependency 'bundler', '~> 2.4.6'
28
28
  spec.add_development_dependency 'minitest', '~> 5.11'
29
29
  spec.add_development_dependency 'pry-byebug', '~> 3.7.0'
30
30
  spec.add_development_dependency 'rake', '~> 12.3'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rox-rollout
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - CloudBees
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-26 00:00:00.000000000 Z
11
+ date: 2023-05-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-eventsource
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.3.3
33
+ version: 2.4.6
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.3.3
40
+ version: 2.4.6
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -108,16 +108,16 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: 3.7.5
111
- description:
111
+ description:
112
112
  email:
113
113
  - support@rollout.io
114
114
  executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
- - ".circleci/config.yml"
119
118
  - ".editorconfig"
120
- - ".github/workflows/ruby.yml"
119
+ - ".github/workflows/e2e_tests.yaml"
120
+ - ".github/workflows/unit_tests.yaml"
121
121
  - ".gitignore"
122
122
  - ".rubocop.yml"
123
123
  - Gemfile
@@ -129,10 +129,6 @@ files:
129
129
  - e2e-server/.gitignore
130
130
  - e2e-server/run_server.sh
131
131
  - e2e-server/server.rb
132
- - e2e/container.rb
133
- - e2e/custom_props.rb
134
- - e2e/rox_e2e_test.rb
135
- - e2e/test_vars.rb
136
132
  - example/local.rb
137
133
  - lib/rox.rb
138
134
  - lib/rox/core/analytics/backoff_policy.rb
@@ -226,15 +222,17 @@ files:
226
222
  - lib/rox/server/flags/rox_string.rb
227
223
  - lib/rox/server/flags/server_entities_provider.rb
228
224
  - lib/rox/server/logging/server_logger.rb
225
+ - lib/rox/server/network_configurations_options.rb
229
226
  - lib/rox/server/rox_options.rb
230
227
  - lib/rox/server/rox_server.rb
228
+ - lib/rox/server/self_managed_options.rb
231
229
  - lib/rox/version.rb
232
230
  - rox.gemspec
233
231
  homepage: https://github.com/rollout/rox-ruby
234
232
  licenses:
235
233
  - Nonstandard
236
234
  metadata: {}
237
- post_install_message:
235
+ post_install_message:
238
236
  rdoc_options: []
239
237
  require_paths:
240
238
  - lib
@@ -242,15 +240,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
242
240
  requirements:
243
241
  - - ">="
244
242
  - !ruby/object:Gem::Version
245
- version: '2.3'
243
+ version: '2.5'
246
244
  required_rubygems_version: !ruby/object:Gem::Requirement
247
245
  requirements:
248
246
  - - ">="
249
247
  - !ruby/object:Gem::Version
250
248
  version: '0'
251
249
  requirements: []
252
- rubygems_version: 3.0.8
253
- signing_key:
250
+ rubygems_version: 3.4.7
251
+ signing_key:
254
252
  specification_version: 4
255
253
  summary: Feature Management ROX Ruby SDK
256
254
  test_files: []
data/.circleci/config.yml DELETED
@@ -1,73 +0,0 @@
1
- version: 2.1
2
- jobs:
3
- test_2-6: &test-template
4
- docker:
5
- - image: circleci/ruby:2.6.4
6
-
7
- working_directory: ~/repo
8
-
9
- steps:
10
- - checkout
11
-
12
- - run:
13
- name: install ssh
14
- command: |
15
- sudo apt-get update
16
- sudo apt-get install libssl-dev
17
-
18
- - run:
19
- name: install dependencies
20
- command: |
21
- bundle install --jobs=4 --retry=3 --path vendor/bundle
22
-
23
- - run:
24
- name: run tests
25
- command: |
26
- bundle exec rake test
27
-
28
- - run:
29
- name: run tests
30
- command: |
31
- bundle exec rake e2e
32
-
33
- test_2-5:
34
- <<: *test-template
35
- docker:
36
- - image: circleci/ruby:2.5.6
37
- test_2-4:
38
- <<: *test-template
39
- docker:
40
- - image: circleci/ruby:2.4.7
41
- test_2-3:
42
- <<: *test-template
43
- docker:
44
- - image: circleci/ruby:2.3.8
45
-
46
- sdk-integration-test:
47
- <<: *test-template
48
- steps:
49
- - run:
50
- name: trigger sdk integration test workflow
51
- command: >-
52
- curl -X POST https://circleci.com/api/v2/project/gh/rollout/sdk-integration-tests/pipeline
53
- --header 'Content-Type: application/json'
54
- --header 'Accept: application/json'
55
- --header 'x-attribution-login: rox-ruby'
56
- --header 'x-attribution-actor-id: rox-ruby'
57
- --header 'Circle-Token: '$CircleApiToken
58
- --data '{"parameters": {"sdk_triggered_run": true, "repo_name": "'$CIRCLE_PROJECT_REPONAME'", "repo_owner": "'$CIRCLE_PROJECT_USERNAME'", "sha": "'$CIRCLE_SHA1'" }}'
59
-
60
- workflows:
61
- version: 2
62
- test:
63
- jobs:
64
- - test_2-6
65
- - test_2-5
66
- - test_2-4
67
- - test_2-3
68
- - sdk-integration-test:
69
- requires:
70
- - test_2-6
71
- - test_2-5
72
- - test_2-4
73
- - test_2-3
data/e2e/container.rb DELETED
@@ -1,35 +0,0 @@
1
- require 'rox/server/flags/rox_flag'
2
- require 'rox/server/flags/rox_string'
3
-
4
- module E2E
5
- class Container
6
- attr_accessor :simple_flag, :simple_flag_overwritten, :flag_for_impression,
7
- :flag_for_impression_with_experiment_and_context, :flag_custom_properties,
8
- :flag_target_groups_all, :flag_target_groups_any, :flag_target_groups_none,
9
- :variant_with_context, :variant, :variant_overwritten, :flag_for_dependency,
10
- :flag_dependent, :flag_color_dependent_with_context
11
-
12
- def initialize
13
- @simple_flag = Rox::Server::RoxFlag.new(true)
14
- @simple_flag_overwritten = Rox::Server::RoxFlag.new(true)
15
-
16
- @flag_for_impression = Rox::Server::RoxFlag.new(false)
17
- @flag_for_impression_with_experiment_and_context = Rox::Server::RoxFlag.new(false)
18
-
19
- @flag_custom_properties = Rox::Server::RoxFlag.new
20
-
21
- @flag_target_groups_all = Rox::Server::RoxFlag.new
22
- @flag_target_groups_any = Rox::Server::RoxFlag.new
23
- @flag_target_groups_none = Rox::Server::RoxFlag.new
24
-
25
- @variant_with_context = Rox::Server::RoxString.new('red', %w[red blue green])
26
-
27
- @variant = Rox::Server::RoxString.new('red', %w[red blue green])
28
- @variant_overwritten = Rox::Server::RoxString.new('red', %w[red blue green])
29
-
30
- @flag_for_dependency = Rox::Server::RoxFlag.new(false)
31
- @flag_dependent = Rox::Server::RoxFlag.new(false)
32
- @flag_color_dependent_with_context = Rox::Server::RoxString.new('White', %w[White Blue Green Yellow])
33
- end
34
- end
35
- end
data/e2e/custom_props.rb DELETED
@@ -1,55 +0,0 @@
1
- module E2E
2
- class CustomProps
3
- def self.create_custom_props
4
- Rox::Server::RoxServer.set_custom_string_property('string_prop1', 'Hello')
5
- Rox::Server::RoxServer.set_custom_string_property('string_prop2') do |_context|
6
- TestVars.is_computed_string_prop_called = true
7
- 'World'
8
- end
9
-
10
- Rox::Server::RoxServer.set_custom_boolean_property('bool_prop1', true)
11
- Rox::Server::RoxServer.set_custom_boolean_property('bool_prop2') do |_context|
12
- TestVars.is_computed_boolean_prop_called = true
13
- false
14
- end
15
-
16
- Rox::Server::RoxServer.set_custom_int_property('int_prop1', 6)
17
- Rox::Server::RoxServer.set_custom_int_property('int_prop2') do |_context|
18
- TestVars.is_computed_int_prop_called = true
19
- 28
20
- end
21
-
22
- Rox::Server::RoxServer.set_custom_float_property('float_prop1', 3.14)
23
- Rox::Server::RoxServer.set_custom_float_property('float_prop2') do |_context|
24
- TestVars.is_computed_float_prop_called = true
25
- 1.618
26
- end
27
-
28
- Rox::Server::RoxServer.set_custom_semver_property('smvr_prop1', '9.11.2001')
29
- Rox::Server::RoxServer.set_custom_semver_property('smvr_prop2') do |_context|
30
- TestVars.is_computed_semver_prop_called = true
31
- '20.7.1969'
32
- end
33
-
34
- Rox::Server::RoxServer.set_custom_boolean_property('bool_prop_target_group_for_variant') do |context|
35
- context['isDuckAndCover']
36
- end
37
-
38
- Rox::Server::RoxServer.set_custom_boolean_property('bool_prop_target_group_operand1') do |_context|
39
- TestVars.target_group1
40
- end
41
-
42
- Rox::Server::RoxServer.set_custom_boolean_property('bool_prop_target_group_operand2') do |_context|
43
- TestVars.target_group2
44
- end
45
-
46
- Rox::Server::RoxServer.set_custom_boolean_property('bool_prop_target_group_for_dependency') do |_context|
47
- TestVars.is_prop_for_target_group_for_dependency
48
- end
49
-
50
- Rox::Server::RoxServer.set_custom_boolean_property('bool_prop_target_group_for_variant_dependency') do |context|
51
- context['isDuckAndCover']
52
- end
53
- end
54
- end
55
- end
data/e2e/rox_e2e_test.rb DELETED
@@ -1,202 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rox/server/rox_options'
3
- require 'rox/server/rox_server'
4
- require 'rox/server/logging/server_logger'
5
- require_relative 'container'
6
- require_relative 'custom_props'
7
- require_relative 'test_vars'
8
-
9
- module E2E
10
- class Logger
11
- def debug(message, _ex = nil)
12
- puts 'Before Rox.Setup', message
13
- end
14
-
15
- def error(message, ex = nil)
16
- puts 'Before Rox.Setup', message, ex
17
- end
18
-
19
- def warn(message, _ex = nil)
20
- puts 'Before Rox.Setup', message
21
- end
22
- end
23
-
24
- class RoxE2ETest < Minitest::Test
25
- ENV['ROLLOUT_MODE'] = 'QA'
26
-
27
- @@setupComplete = false
28
- @@testsRun = 0
29
-
30
- def setup
31
- @@testsRun += 1
32
- if !@@setupComplete
33
-
34
- configuration_fetched_handler = proc do |e|
35
- if !e.nil? && e.fetcher_status == Rox::Core::FetcherStatus::APPLIED_FROM_NETWORK
36
- TestVars.configuration_fetched_count += 1
37
- end
38
- end
39
-
40
- impression_handler = proc do |e|
41
- puts "targeting: #{e.reporting_value.targeting}"
42
- if e.reporting_value.targeting
43
- puts "#{Time.now}: flag #{e.reporting_value.name} value is #{e.reporting_value.value}"
44
- else
45
- puts "#{Time.now}: flag #{e.reporting_value.name} has no conditions, or targeting is off. default value #{!!e.reporting_value.value == e.reporting_value.value ? (e.reporting_value.value ? 'true' : 'false') : e.reporting_value.value} was used"
46
- end
47
- if !e.nil? && !e.reporting_value.nil? && (e.reporting_value.name == 'flag_for_impression')
48
- TestVars.is_impression_raised = true
49
- TestVars.impression_raised_times += 1
50
- end
51
- TestVars.impression_returned_args = e
52
- end
53
-
54
- option = Rox::Server::RoxOptions.new(
55
- configuration_fetched_handler: configuration_fetched_handler,
56
- impression_handler: impression_handler,
57
- dev_mode_key: '67e39e708444aa953414e444',
58
- logger: Rox::Server::ServerLogger.new
59
- )
60
-
61
- @@container = Container.new
62
- Rox::Server::RoxServer.register(@@container)
63
- CustomProps.create_custom_props
64
- Rox::Server::RoxServer.setup('5b82864ebc3aec37aff1fdd5', option).join
65
- @@setupComplete = true
66
- end
67
- end
68
-
69
- def teardown
70
- if @@testsRun == RoxE2ETest.runnable_methods.length
71
- Rox::Server::RoxServer.shutdown
72
- end
73
- end
74
-
75
- def before_setup
76
- TestVars.is_impression_raised = false
77
- TestVars.impression_raised_times = 0
78
- end
79
-
80
- def test_simple_flag
81
- assert_equal true, @@container.simple_flag.enabled?
82
- end
83
-
84
- def test_simple_flag_overwritten
85
- assert_equal false, @@container.simple_flag_overwritten.enabled?
86
- end
87
-
88
- def test_string
89
- assert_equal 'red', @@container.variant.value
90
- end
91
-
92
- def test_variant_overwritten
93
- assert_equal 'green', @@container.variant_overwritten.value
94
- end
95
-
96
- def test_all_custom_properties
97
- assert_equal true, @@container.flag_custom_properties.enabled?
98
-
99
- assert_equal true, TestVars.is_computed_boolean_prop_called
100
- assert_equal true, TestVars.is_computed_float_prop_called
101
- assert_equal true, TestVars.is_computed_int_prop_called
102
- assert_equal true, TestVars.is_computed_semver_prop_called
103
- assert_equal true, TestVars.is_computed_string_prop_called
104
- end
105
-
106
- def test_fetch_within_timeout
107
- number_of_config_fetches = TestVars.configuration_fetched_count
108
- timeout = 5
109
- thread = Rox::Server::RoxServer.fetch
110
- res = thread.join(timeout)
111
-
112
- if res.nil?
113
- flunk('timeout')
114
- else
115
- assert number_of_config_fetches < TestVars.configuration_fetched_count
116
- end
117
- end
118
-
119
- def test_string_with_context
120
- some_positive_context = { 'isDuckAndCover' => true }
121
- some_negative_context = { 'isDuckAndCover' => false }
122
-
123
- assert_equal 'red', @@container.variant_with_context.value
124
-
125
- assert_equal 'blue', @@container.variant_with_context.value(some_positive_context)
126
- assert_equal 'red', @@container.variant_with_context.value(some_negative_context)
127
- end
128
-
129
- def test_target_groups_all_any_none
130
- TestVars.target_group1 = true
131
- TestVars.target_group2 = true
132
-
133
- assert_equal true, @@container.flag_target_groups_all.enabled?
134
- assert_equal true, @@container.flag_target_groups_any.enabled?
135
- assert_equal false, @@container.flag_target_groups_none.enabled?
136
-
137
- TestVars.target_group1 = false
138
- assert_equal false, @@container.flag_target_groups_all.enabled?
139
- assert_equal true, @@container.flag_target_groups_any.enabled?
140
- assert_equal false, @@container.flag_target_groups_none.enabled?
141
-
142
- TestVars.target_group2 = false
143
- assert_equal false, @@container.flag_target_groups_all.enabled?
144
- assert_equal false, @@container.flag_target_groups_any.enabled?
145
- assert_equal true, @@container.flag_target_groups_none.enabled?
146
- end
147
-
148
- def test_impression_handler
149
- @@container.flag_for_impression.enabled?
150
- assert_equal true, TestVars.is_impression_raised
151
- TestVars.is_impression_raised = false
152
-
153
- context = { 'var' => 'val' }
154
- flag_impression_value = @@container.flag_for_impression_with_experiment_and_context.enabled?(context)
155
- refute_nil TestVars.impression_returned_args
156
- refute_nil TestVars.impression_returned_args.reporting_value
157
- assert_equal true, TestVars.impression_returned_args.reporting_value.value
158
- assert_equal true, flag_impression_value
159
- assert_equal 'flag_for_impression_with_experiment_and_context',
160
- TestVars.impression_returned_args.reporting_value.name
161
-
162
- refute_nil TestVars.impression_returned_args
163
-
164
- assert_equal 'val', TestVars.impression_returned_args.context['var']
165
- end
166
-
167
- def test_static_flag_impression_raised_once
168
- assert_equal false, TestVars.is_impression_raised
169
- assert_equal 0, TestVars.impression_raised_times
170
- @@container.flag_for_impression.enabled?
171
- assert_equal true, TestVars.is_impression_raised
172
- assert_equal 1, TestVars.impression_raised_times
173
- end
174
-
175
- def test_dynamic_flag_impression_raised_once
176
- assert_equal false, TestVars.is_impression_raised
177
- assert_equal 0, TestVars.impression_raised_times
178
- Rox::Server::RoxServer::dynamic_api.enabled?('flag_for_impression', true)
179
- assert_equal true, TestVars.is_impression_raised
180
- assert_equal 1, TestVars.impression_raised_times
181
- end
182
-
183
- def test_flag_dependency
184
- TestVars.is_prop_for_target_group_for_dependency = true
185
- assert_equal true, @@container.flag_for_dependency.enabled?
186
- assert_equal false, @@container.flag_dependent.enabled?
187
-
188
- TestVars.is_prop_for_target_group_for_dependency = false
189
- assert_equal true, @@container.flag_dependent.enabled?
190
- assert_equal false, @@container.flag_for_dependency.enabled?
191
- end
192
-
193
- def test_string_dependency_with_context
194
- some_positive_context = { 'isDuckAndCover' => true }
195
- some_negative_context = { 'isDuckAndCover' => false }
196
-
197
- assert_equal 'White', @@container.flag_color_dependent_with_context.value
198
- assert_equal 'White', @@container.flag_color_dependent_with_context.value(some_negative_context)
199
- assert_equal 'Yellow', @@container.flag_color_dependent_with_context.value(some_positive_context)
200
- end
201
- end
202
- end
data/e2e/test_vars.rb DELETED
@@ -1,22 +0,0 @@
1
- module E2E
2
- module TestVars
3
- class << self
4
- attr_accessor :is_computed_boolean_prop_called, :is_computed_string_prop_called, :is_computed_int_prop_called,
5
- :is_computed_float_prop_called, :is_computed_semver_prop_called, :target_group1, :target_group2, :is_impression_raised, :impression_raised_times, :is_prop_for_target_group_for_dependency, :configuration_fetched_count, :impression_returned_args
6
- end
7
-
8
- @is_computed_boolean_prop_called = false
9
- @is_computed_string_prop_called = false
10
- @is_computed_int_prop_called = false
11
- @is_computed_float_prop_called = false
12
- @is_computed_semver_prop_called = false
13
- @target_group1 = false
14
- @target_group2 = false
15
- @is_impression_raised = false
16
- @impression_raised_times = 0
17
- @is_prop_for_target_group_for_dependency = false
18
-
19
- @configuration_fetched_count = 0
20
- @impression_returned_args = nil
21
- end
22
- end