rox-rollout 4.0.0 → 4.1.0

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
  SHA1:
3
- metadata.gz: 83830a53d34aa5c51ae4d620b3e21789f5eb2cbd
4
- data.tar.gz: 419c4a400b030dc5e539003bee949b3776a31bbe
3
+ metadata.gz: f4ad60820f71ef8569f15f07fcb1ced8b05e99be
4
+ data.tar.gz: 327115ab21d5889e4783fcef71d33e1cbc72e7ab
5
5
  SHA512:
6
- metadata.gz: a1e076439bc9fb0ffccc6d8f9b3a096b9bc4baa3d22994a9b24c762922ee018105ce4ee3a4f096e111e2bc4a535b661f71559fa7dbe192669c9d7b2f4fb77ecf
7
- data.tar.gz: 4f65eb77eed230bab5fe58fe6f14fb56e9c319650d60d892596a845461bea00dbedfd3bb3a36114edb15488325e3e24589a110d70c26dbae93de980a7134f64f
6
+ metadata.gz: '0368035eb4d7f508749be64e32e89001af56475e57a9018d45654e854c3ac9e9bae322f800525b70ea606240efc35cb1b7c9d140537114e145d92390416b706b'
7
+ data.tar.gz: 75afe906a4eb22195d5020fdb546952bea785a80f49df4eb6cb85bd8cdd9cec7c47e25f702d013525a8e4474b4ef8bd24f4e6acfb33553fde4a48a859dbcd6da
data/e2e/rox_e2e_test.rb CHANGED
@@ -12,7 +12,7 @@ module E2E
12
12
  end
13
13
 
14
14
  def error(message, ex = nil)
15
- puts 'Before Rox.Setup', message
15
+ puts 'Before Rox.Setup', message, ex
16
16
  end
17
17
 
18
18
  def warn(message, ex = nil)
@@ -0,0 +1,36 @@
1
+ require 'rox/core/entities/flag'
2
+
3
+ module Rox
4
+ module Core
5
+ class DynamicApi
6
+ def initialize(flag_repository, entities_provider)
7
+ @flag_repository = flag_repository
8
+ @entities_provider = entities_provider
9
+ end
10
+
11
+ def enabled?(name, default_value, context = nil)
12
+ variant = @flag_repository.flag(name)
13
+ if variant.nil?
14
+ variant = @entities_provider.create_flag(default_value)
15
+ @flag_repository.add_flag(variant, name)
16
+ end
17
+
18
+ return default_value unless variant.is_a?(Flag)
19
+
20
+ is_enabled = variant.internal_enabled?(context, nil_instead_of_default: true)
21
+ is_enabled.nil? ? default_value : is_enabled
22
+ end
23
+
24
+ def value(name, default_value, context = nil, options = [])
25
+ variant = @flag_repository.flag(name)
26
+ if variant.nil?
27
+ variant = @entities_provider.create_variant(default_value, options)
28
+ @flag_repository.add_flag(variant, name)
29
+ end
30
+
31
+ value = variant.internal_value(context, nil_instead_of_default: true)
32
+ value.nil? ? default_value : value
33
+ end
34
+ end
35
+ end
36
+ end
@@ -37,6 +37,17 @@ module Rox
37
37
  'https://analytic.rollout.io'
38
38
  end
39
39
  end
40
+
41
+ def self.notifications_path
42
+ case ENV['ROLLOUT_MODE']
43
+ when 'QA'
44
+ 'https://qax-push.rollout.io/sse'
45
+ when 'LOCAL'
46
+ 'http://127.0.0.1:8887/sse'
47
+ else
48
+ 'https://push.rollout.io/sse'
49
+ end
50
+ end
40
51
  end
41
52
  end
42
53
  end
data/lib/rox/core/core.rb CHANGED
@@ -11,6 +11,7 @@ require 'rox/core/network/request_configuration_builder'
11
11
  require 'rox/core/network/request'
12
12
  require 'rox/core/network/configuration_fetcher'
13
13
  require 'rox/core/network/configuration_fetcher_roxy'
14
+ require 'rox/core/notifications/notification_listener'
14
15
  require 'rox/core/register/registerer'
15
16
  require 'rox/core/client/internal_flags'
16
17
  require 'rox/core/client/buid'
@@ -19,6 +20,7 @@ require 'rox/core/impression/impression_invoker'
19
20
  require 'rox/core/reporting/error_reporter'
20
21
  require 'rox/core/security/signature_verifier'
21
22
  require 'rox/core/utils/periodic_task'
23
+ require 'rox/core/client/dynamic_api'
22
24
 
23
25
  module Rox
24
26
  module Core
@@ -44,6 +46,8 @@ module Rox
44
46
  @error_reporter = nil
45
47
  @configuration_fetcher = nil
46
48
  @last_configurations = nil
49
+ @internal_flags = nil
50
+ @push_updates_listener = nil
47
51
  end
48
52
 
49
53
  def setup(sdk_settings, device_properties, rox_options)
@@ -52,10 +56,10 @@ module Rox
52
56
  roxy_path = rox_options.nil? || rox_options.roxy_url.nil? ? nil : rox_options.roxy_url
53
57
 
54
58
  # TODO: Analytics.Analytics.Initialize(deviceProperties.RolloutKey, deviceProperties)
55
- internal_flags = InternalFlags.new(@experiment_repository, @parser)
59
+ @internal_flags = InternalFlags.new(@experiment_repository, @parser)
56
60
 
57
61
  # TODO: impressionInvoker = new ImpressionInvoker(internalFlags, customPropertyRepository, deviceProperties, Analytics.Analytics.Client, roxyPath != null);
58
- @impression_invoker = ImpressionInvoker.new(internal_flags, @custom_property_repository, device_properties, nil, !roxy_path.nil?)
62
+ @impression_invoker = ImpressionInvoker.new(@internal_flags, @custom_property_repository, device_properties, nil, !roxy_path.nil?)
59
63
  @flag_setter = FlagSetter.new(@flag_repository, @parser, @experiment_repository, @impression_invoker)
60
64
  buid = BUID.new(sdk_settings, device_properties, @flag_repository, @custom_property_repository)
61
65
 
@@ -72,10 +76,13 @@ module Rox
72
76
  @configuration_fetcher = ConfigurationFetcherRoxy.new(request_config_builder, client_request, @configuration_fetched_invoker)
73
77
  end
74
78
 
75
- if !rox_options.nil? && !rox_options.configuration_fetched_handler.nil?
76
- @configuration_fetched_invoker.register_fetched_handler(&rox_options.configuration_fetched_handler)
79
+ configuration_fetched_handler = nil
80
+ unless rox_options.nil?
81
+ configuration_fetched_handler = rox_options.configuration_fetched_handler
77
82
  end
78
83
 
84
+ @configuration_fetched_invoker.register_fetched_handler(&wrap_configuration_fetched_handler(&configuration_fetched_handler))
85
+
79
86
  Thread.new do
80
87
  Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception)
81
88
  fetch
@@ -126,6 +133,34 @@ module Rox
126
133
  def add_custom_property_if_not_exists(property)
127
134
  @custom_property_repository.add_custom_property_if_not_exists(property)
128
135
  end
136
+
137
+ def wrap_configuration_fetched_handler(&handler)
138
+ lambda do |args|
139
+ start_or_stop_push_updated_listener unless args.fetcher_status == FetcherStatus::ERROR_FETCHED_FAILED
140
+ handler.call(args) unless handler.nil?
141
+ end
142
+ end
143
+
144
+ def start_or_stop_push_updated_listener
145
+ if @internal_flags.enabled?('rox.internal.pushUpdates')
146
+ if @push_updates_listener.nil?
147
+ @push_updates_listener = NotificationListener.new(Environment.notifications_path, @sdk_settings.api_key)
148
+ @push_updates_listener.on 'changed' do |data|
149
+ fetch
150
+ end
151
+ @push_updates_listener.start
152
+ end
153
+ else
154
+ unless @push_updates_listener.nil?
155
+ @push_updates_listener.stop
156
+ @push_updates_listener = nil
157
+ end
158
+ end
159
+ end
160
+
161
+ def dynamic_api(entities_provider)
162
+ Rox::Core::DynamicApi.new(@flag_repository, entities_provider)
163
+ end
129
164
  end
130
165
  end
131
166
  end
@@ -14,6 +14,11 @@ module Rox
14
14
  value(context) == Flag::FLAG_TRUE_VALUE
15
15
  end
16
16
 
17
+ def internal_enabled?(context, nil_instead_of_default)
18
+ val = internal_value(context, nil_instead_of_default)
19
+ nil_instead_of_default && val.nil? ? nil : (val == Flag::FLAG_TRUE_VALUE)
20
+ end
21
+
17
22
  def enabled(context)
18
23
  yield if enabled?(context)
19
24
  end
@@ -7,7 +7,7 @@ module Rox
7
7
  class Variant
8
8
  attr_accessor :default_value, :options, :name, :context, :condition, :parser, :impression_invoker, :client_experiment
9
9
 
10
- def initialize(default_value, options)
10
+ def initialize(default_value, options = [])
11
11
  @default_value = default_value
12
12
  @options = options.clone
13
13
  @options << default_value unless options.include?(default_value)
@@ -34,7 +34,11 @@ module Rox
34
34
  end
35
35
 
36
36
  def value(context = nil)
37
- return_value = @default_value
37
+ internal_value(context, false)
38
+ end
39
+
40
+ def internal_value(context, nil_instead_of_default)
41
+ return_value = nil_instead_of_default ? nil : @default_value
38
42
  merged_context = MergedContext.new(@context, context)
39
43
 
40
44
  if !@parser.nil? && !@condition.nil? && !@condition.empty?
@@ -42,7 +46,7 @@ module Rox
42
46
  unless evaluation_result.nil?
43
47
  value = evaluation_result.string_value
44
48
  if !value.nil? && !value.empty?
45
- return_value = value if @options.include?(value)
49
+ return_value = value
46
50
  end
47
51
  end
48
52
  end
@@ -0,0 +1,45 @@
1
+ require "em-eventsource"
2
+
3
+ module Rox
4
+ module Core
5
+ class NotificationListener
6
+ def initialize(listen_url, app_key)
7
+ @listen_url = listen_url
8
+ @app_key = app_key
9
+ @handlers = {}
10
+ end
11
+
12
+ def on(event_name, &handler)
13
+ @handlers[event_name] ||= []
14
+ @handlers[event_name] << handler
15
+ end
16
+
17
+ def start
18
+ sse_url = @listen_url.chomp('/') + '/' + @app_key
19
+ @thread = Thread.new do
20
+ EM.run do
21
+ source = EventMachine::EventSource.new(sse_url)
22
+ @handlers.each do |event_name, event_handlers|
23
+ event_handlers.each do |handler|
24
+ source.on event_name do |data|
25
+ # Start new thread to allow the handler to stop the Listener (terminate the current thread)
26
+ # and continue handler code execution without interruption
27
+ handler_thread = Thread.new do
28
+ handler.call(data)
29
+ end
30
+ handler_thread.join
31
+ end
32
+ end
33
+ end
34
+ source.start
35
+ end
36
+ end
37
+ end
38
+
39
+ def stop
40
+ @thread.terminate unless @thread.nil?
41
+ @thread = nil
42
+ end
43
+ end
44
+ end
45
+ end
@@ -16,14 +16,16 @@ module Rox
16
16
 
17
17
  @namespaces << ns
18
18
 
19
- container.methods.each do |method_name|
20
- method = container.method(method_name)
21
- next unless method.arity.zero?
22
-
19
+ container.instance_variables().each do |attribute_name|
23
20
  begin
24
- value = method.call
25
- if value.is_a?(Variant)
26
- @flag_repository.add_flag(value, ns == '' ? method_name.to_s : "#{ns}.#{method_name}")
21
+ value = container.instance_variable_get(attribute_name)
22
+ if value != nil && value.is_a?(Variant)
23
+ var_name = attribute_name.to_s();
24
+ # removing the attribute starting @ (if [always] exists)
25
+ if (var_name[0] == '@')
26
+ var_name[0] = '';
27
+ end
28
+ @flag_repository.add_flag(value, ns == '' ? var_name : "#{ns}.#{var_name}")
27
29
  end
28
30
  rescue StandardError
29
31
  next
@@ -32,4 +34,4 @@ module Rox
32
34
  end
33
35
  end
34
36
  end
35
- end
37
+ end
@@ -127,15 +127,21 @@ module Rox
127
127
 
128
128
  add_operator('md5') do |parser, stack, context|
129
129
  op1 = stack.pop
130
- raise ArgumentError, 'should be string' unless op1.is_a?(String)
131
- stack.push(Digest::MD5.hexdigest(op1))
130
+ if op1.is_a?(String)
131
+ stack.push(Digest::MD5.hexdigest(op1))
132
+ else
133
+ stack.push(TokenType::UNDEFINED)
134
+ end
132
135
  end
133
136
 
134
137
  add_operator('concat') do |parser, stack, context|
135
138
  op1 = stack.pop
136
139
  op2 = stack.pop
137
- raise ArgumentError, 'should be string' unless op1.is_a?(String) && op2.is_a?(String)
138
- stack.push("#{op1}#{op2}")
140
+ if op1.is_a?(String) && op2.is_a?(String)
141
+ stack.push("#{op1}#{op2}")
142
+ else
143
+ stack.push(TokenType::UNDEFINED)
144
+ end
139
145
  end
140
146
  end
141
147
  end
@@ -0,0 +1,16 @@
1
+ require 'rox/server/flags/rox_flag'
2
+ require 'rox/server/flags/rox_variant'
3
+
4
+ module Rox
5
+ module Server
6
+ class ServerEntitiesProvider
7
+ def create_flag(default_value)
8
+ Rox::Server::RoxFlag.new(default_value)
9
+ end
10
+
11
+ def create_variant(default_value, options)
12
+ Rox::Server::RoxVariant.new(default_value, options)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -8,6 +8,8 @@ require 'rox/core/properties/device_property'
8
8
  require 'rox/core/properties/custom_property'
9
9
  require 'rox/core/properties/custom_property_type'
10
10
  require 'rox/core/consts/property_type'
11
+ require 'rox/server/flags/server_entities_provider'
12
+
11
13
 
12
14
  module Rox
13
15
  module Server
@@ -78,6 +80,10 @@ module Rox
78
80
  def self.set_custom_semver_property(name, value = nil, &block)
79
81
  @core.add_custom_property(Rox::Core::CustomProperty.new(name, Rox::Core::CustomPropertyType::SEMVER, value, &block))
80
82
  end
83
+
84
+ def self.dynamic_api
85
+ @core.dynamic_api(Rox::Server::ServerEntitiesProvider.new)
86
+ end
81
87
  end
82
88
  end
83
89
  end
data/lib/rox/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rox
2
- VERSION = "4.0.0"
2
+ VERSION = "4.1.0"
3
3
  end
data/rox.gemspec CHANGED
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
23
23
 
24
24
  spec.required_ruby_version = '>= 2.3'
25
25
 
26
+ spec.add_runtime_dependency 'em-eventsource', '~> 0.3.0'
27
+
26
28
  spec.add_development_dependency 'bundler', '~> 1.16'
27
29
  spec.add_development_dependency 'minitest', '~> 5.0'
28
30
  spec.add_development_dependency 'rake', '~> 12.3'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rox-rollout
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rollout.io
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-07 00:00:00.000000000 Z
11
+ date: 2019-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: em-eventsource
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.3.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -91,6 +105,7 @@ files:
91
105
  - lib/rox.rb
92
106
  - lib/rox/core/client/buid.rb
93
107
  - lib/rox/core/client/device_properties.rb
108
+ - lib/rox/core/client/dynamic_api.rb
94
109
  - lib/rox/core/client/internal_flags.rb
95
110
  - lib/rox/core/client/sdk_settings.rb
96
111
  - lib/rox/core/configuration/configuration.rb
@@ -124,6 +139,7 @@ files:
124
139
  - lib/rox/core/network/request_configuration_builder.rb
125
140
  - lib/rox/core/network/request_data.rb
126
141
  - lib/rox/core/network/response.rb
142
+ - lib/rox/core/notifications/notification_listener.rb
127
143
  - lib/rox/core/properties/custom_property.rb
128
144
  - lib/rox/core/properties/custom_property_type.rb
129
145
  - lib/rox/core/properties/device_property.rb
@@ -152,6 +168,7 @@ files:
152
168
  - lib/rox/server/client/server_properties.rb
153
169
  - lib/rox/server/flags/rox_flag.rb
154
170
  - lib/rox/server/flags/rox_variant.rb
171
+ - lib/rox/server/flags/server_entities_provider.rb
155
172
  - lib/rox/server/logging/server_logger.rb
156
173
  - lib/rox/server/rox_options.rb
157
174
  - lib/rox/server/rox_server.rb