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