rox-rollout 5.1.0 → 5.1.1

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