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 +4 -4
- data/e2e/rox_e2e_test.rb +1 -1
- data/lib/rox/core/client/dynamic_api.rb +36 -0
- data/lib/rox/core/consts/environment.rb +11 -0
- data/lib/rox/core/core.rb +39 -4
- data/lib/rox/core/entities/flag.rb +5 -0
- data/lib/rox/core/entities/variant.rb +7 -3
- data/lib/rox/core/notifications/notification_listener.rb +45 -0
- data/lib/rox/core/register/registerer.rb +10 -8
- data/lib/rox/core/roxx/parser.rb +10 -4
- data/lib/rox/server/flags/server_entities_provider.rb +16 -0
- data/lib/rox/server/rox_server.rb +6 -0
- data/lib/rox/version.rb +1 -1
- data/rox.gemspec +2 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4ad60820f71ef8569f15f07fcb1ced8b05e99be
|
4
|
+
data.tar.gz: 327115ab21d5889e4783fcef71d33e1cbc72e7ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0368035eb4d7f508749be64e32e89001af56475e57a9018d45654e854c3ac9e9bae322f800525b70ea606240efc35cb1b7c9d140537114e145d92390416b706b'
|
7
|
+
data.tar.gz: 75afe906a4eb22195d5020fdb546952bea785a80f49df4eb6cb85bd8cdd9cec7c47e25f702d013525a8e4474b4ef8bd24f4e6acfb33553fde4a48a859dbcd6da
|
data/e2e/rox_e2e_test.rb
CHANGED
@@ -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
|
-
|
76
|
-
|
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
|
-
|
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
|
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.
|
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 =
|
25
|
-
if value.is_a?(Variant)
|
26
|
-
|
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
|
data/lib/rox/core/roxx/parser.rb
CHANGED
@@ -127,15 +127,21 @@ module Rox
|
|
127
127
|
|
128
128
|
add_operator('md5') do |parser, stack, context|
|
129
129
|
op1 = stack.pop
|
130
|
-
|
131
|
-
|
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
|
-
|
138
|
-
|
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
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.
|
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:
|
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
|