rox-rollout 4.7.1 → 5.0.2
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/.circleci/config.yml +34 -13
- data/.editorconfig +12 -0
- data/.github/workflows/ruby.yml +20 -0
- data/.rubocop.yml +17 -0
- data/Gemfile +2 -2
- data/README.md +32 -0
- data/Rakefile +12 -9
- data/bin/console +3 -4
- data/e2e/container.rb +11 -14
- data/e2e/custom_props.rb +9 -9
- data/e2e/rox_e2e_test.rb +17 -19
- data/e2e/test_vars.rb +3 -6
- data/e2e-server/run_server.sh +12 -0
- data/e2e-server/server.rb +158 -0
- data/example/local.rb +40 -0
- data/lib/rox/core/analytics.rb +18 -0
- data/lib/rox/core/client/buid.rb +8 -8
- data/lib/rox/core/client/device_properties.rb +7 -1
- data/lib/rox/core/client/dynamic_api.rb +53 -15
- data/lib/rox/core/client/internal_flags.rb +14 -2
- data/lib/rox/core/client/sdk_settings.rb +1 -1
- data/lib/rox/core/configuration/configuration.rb +1 -1
- data/lib/rox/core/configuration/configuration_fetched_args.rb +2 -2
- data/lib/rox/core/configuration/configuration_fetched_invoker.rb +8 -3
- data/lib/rox/core/configuration/configuration_parser.rb +38 -37
- data/lib/rox/core/configuration/fetcher_error.rb +1 -1
- data/lib/rox/core/configuration/fetcher_status.rb +1 -1
- data/lib/rox/core/configuration/models/experiment_model.rb +1 -1
- data/lib/rox/core/configuration/models/target_group_model.rb +1 -1
- data/lib/rox/core/consts/build.rb +2 -2
- data/lib/rox/core/consts/environment.rb +10 -8
- data/lib/rox/core/consts/property_type.rb +16 -17
- data/lib/rox/core/context/merged_context.rb +2 -1
- data/lib/rox/core/core.rb +89 -38
- data/lib/rox/core/entities/default_flag_values.rb +10 -0
- data/lib/rox/core/entities/flag.rb +22 -7
- data/lib/rox/core/entities/flag_setter.rb +6 -6
- data/lib/rox/core/entities/rox_double.rb +11 -0
- data/lib/rox/core/entities/rox_int.rb +11 -0
- data/lib/rox/core/entities/{variant.rb → rox_string.rb} +20 -13
- data/lib/rox/core/error_handling/exception_trigger.rb +10 -0
- data/lib/rox/core/error_handling/userspace_handler_exception.rb +12 -0
- data/lib/rox/core/error_handling/userspace_unhandled_error_invoker.rb +41 -0
- data/lib/rox/core/impression/impression_args.rb +2 -2
- data/lib/rox/core/impression/impression_invoker.rb +41 -7
- data/lib/rox/core/impression/models/experiment.rb +1 -1
- data/lib/rox/core/impression/models/reporting_value.rb +10 -2
- data/lib/rox/core/logging/logging.rb +3 -3
- data/lib/rox/core/logging/no_op_logger.rb +1 -1
- data/lib/rox/core/network/configuration_fetcher.rb +2 -2
- data/lib/rox/core/network/configuration_fetcher_roxy.rb +2 -2
- data/lib/rox/core/network/configuration_fetcher_self_managed.rb +30 -0
- data/lib/rox/core/network/configuration_source.rb +1 -1
- data/lib/rox/core/network/request.rb +1 -1
- data/lib/rox/core/network/request_configuration_builder.rb +5 -5
- data/lib/rox/core/network/request_data.rb +1 -1
- data/lib/rox/core/network/response.rb +6 -8
- data/lib/rox/core/network/state_sender.rb +63 -19
- data/lib/rox/core/notifications/notification_listener.rb +4 -4
- data/lib/rox/core/properties/custom_property.rb +1 -1
- data/lib/rox/core/properties/custom_property_type.rb +1 -1
- data/lib/rox/core/properties/device_property.rb +2 -2
- data/lib/rox/core/properties/property_factory.rb +132 -0
- data/lib/rox/core/register/registerer.rb +13 -12
- data/lib/rox/core/reporting/error_reporter.rb +14 -13
- data/lib/rox/core/repositories/experiment_repository.rb +2 -4
- data/lib/rox/core/repositories/flag_repository.rb +7 -7
- data/lib/rox/core/repositories/roxx/experiments_extensions.rb +8 -8
- data/lib/rox/core/repositories/roxx/properties_extensions.rb +32 -7
- data/lib/rox/core/repositories/target_group_repository.rb +2 -4
- data/lib/rox/core/roxx/evaluation_result.rb +2 -0
- data/lib/rox/core/roxx/node.rb +1 -1
- data/lib/rox/core/roxx/parser.rb +35 -21
- data/lib/rox/core/roxx/regular_expression_extensions.rb +6 -3
- data/lib/rox/core/roxx/string_tokenizer.rb +3 -1
- data/lib/rox/core/roxx/symbols.rb +1 -1
- data/lib/rox/core/roxx/token_type.rb +1 -1
- data/lib/rox/core/roxx/tokenized_expression.rb +16 -12
- data/lib/rox/core/roxx/value_compare_extensions.rb +49 -29
- data/lib/rox/core/security/signature_verifier.rb +3 -3
- data/lib/rox/core/security/signature_verifier_mock.rb +12 -0
- data/lib/rox/core/utils/type_utils.rb +1 -1
- data/lib/rox/server/client/server_properties.rb +1 -1
- data/lib/rox/server/flags/normalize_flag_type.rb +25 -0
- data/lib/rox/server/flags/rox_double.rb +8 -0
- data/lib/rox/server/flags/rox_flag.rb +1 -1
- data/lib/rox/server/flags/rox_int.rb +8 -0
- data/lib/rox/server/flags/rox_string.rb +8 -0
- data/lib/rox/server/flags/server_entities_provider.rb +14 -4
- data/lib/rox/server/logging/server_logger.rb +2 -2
- data/lib/rox/server/rox_options.rb +39 -8
- data/lib/rox/server/rox_server.rb +84 -59
- data/lib/rox/version.rb +1 -1
- data/rox.gemspec +11 -9
- metadata +62 -33
- data/CODEOWNERS +0 -1
- data/README_DEVELOP.md +0 -25
- data/_archive/.document +0 -5
- data/_archive/.rspec +0 -1
- data/_archive/Gemfile +0 -15
- data/_archive/Gemfile.lock +0 -87
- data/_archive/README.md +0 -32
- data/_archive/README.rdoc +0 -19
- data/_archive/Rakefile +0 -50
- data/_archive/lib/expr_function_definition.rb +0 -52
- data/_archive/lib/function_definition.rb +0 -48
- data/_archive/lib/function_token.rb +0 -12
- data/_archive/lib/object_extends.rb +0 -12
- data/_archive/lib/ruby_interpreter.rb +0 -292
- data/_archive/lib/stack.rb +0 -48
- data/_archive/lib/string_extends.rb +0 -14
- data/_archive/spec/ruby_interpreter_spec.rb +0 -203
- data/_archive/spec/spec_helper.rb +0 -30
- data/_archive/spec/stack_spec.rb +0 -77
- data/lib/rox/server/flags/rox_variant.rb +0 -8
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/network/configuration_fetcher_self_managed'
|
14
15
|
require 'rox/core/network/state_sender'
|
15
16
|
require 'rox/core/notifications/notification_listener'
|
16
17
|
require 'rox/core/register/registerer'
|
@@ -20,8 +21,11 @@ require 'rox/core/entities/flag_setter'
|
|
20
21
|
require 'rox/core/impression/impression_invoker'
|
21
22
|
require 'rox/core/reporting/error_reporter'
|
22
23
|
require 'rox/core/security/signature_verifier'
|
24
|
+
require 'rox/core/security/signature_verifier_mock'
|
23
25
|
require 'rox/core/utils/periodic_task'
|
24
26
|
require 'rox/core/client/dynamic_api'
|
27
|
+
require 'rox/core/analytics'
|
28
|
+
require 'rox/core/error_handling/userspace_unhandled_error_invoker'
|
25
29
|
|
26
30
|
module Rox
|
27
31
|
module Core
|
@@ -31,14 +35,10 @@ module Rox
|
|
31
35
|
@custom_property_repository = CustomPropertyRepository.new
|
32
36
|
@target_group_repository = TargetGroupRepository.new
|
33
37
|
@experiment_repository = ExperimentRepository.new
|
34
|
-
@
|
38
|
+
@user_unhandled_error_invoker = Rox::Core::UserspaceUnhandledErrorInvoker.new
|
39
|
+
@parser = Parser.new(@user_unhandled_error_invoker)
|
35
40
|
|
36
|
-
|
37
|
-
properties_extensions = PropertiesExtensions.new(@parser, @custom_property_repository)
|
38
|
-
experiments_extensions.extend
|
39
|
-
properties_extensions.extend
|
40
|
-
|
41
|
-
@configuration_fetched_invoker = ConfigurationFetchedInvoker.new
|
41
|
+
@configuration_fetched_invoker = ConfigurationFetchedInvoker.new(@user_unhandled_error_invoker)
|
42
42
|
@registerer = Registerer.new(@flag_repository)
|
43
43
|
|
44
44
|
@sdk_settings = nil
|
@@ -51,64 +51,106 @@ module Rox
|
|
51
51
|
@push_updates_listener = nil
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
54
|
+
def userspace_unhandled_error_handler=(handler)
|
55
|
+
@user_unhandled_error_invoker.handler = handler
|
56
|
+
end
|
57
|
+
|
58
|
+
def setup(sdk_settings, device_properties)
|
55
59
|
@sdk_settings = sdk_settings
|
60
|
+
@rox_options = device_properties.rox_options
|
61
|
+
|
62
|
+
experiments_extensions = ExperimentsExtensions.new(@parser, @target_group_repository, @flag_repository,
|
63
|
+
@experiment_repository)
|
64
|
+
properties_extensions = PropertiesExtensions.new(@parser, @custom_property_repository, @rox_options.dynamic_property_rule_handler)
|
65
|
+
experiments_extensions.extend
|
66
|
+
properties_extensions.extend
|
56
67
|
|
57
|
-
roxy_path = rox_options.nil? || rox_options.roxy_url.nil? ? nil : rox_options.roxy_url
|
68
|
+
roxy_path = @rox_options.nil? || @rox_options.roxy_url.nil? ? nil : @rox_options.roxy_url
|
58
69
|
|
59
|
-
if roxy_path.nil?
|
60
|
-
validate_api_key(sdk_settings&.api_key)
|
61
|
-
end
|
70
|
+
validate_api_key(sdk_settings&.api_key) if roxy_path.nil?
|
62
71
|
|
63
72
|
# TODO: Analytics.Analytics.Initialize(deviceProperties.RolloutKey, deviceProperties)
|
64
|
-
@internal_flags = InternalFlags.new(@experiment_repository, @parser)
|
73
|
+
@internal_flags = InternalFlags.new(@experiment_repository, @parser, @rox_options)
|
65
74
|
|
75
|
+
@analytics_client = Analytics.new(sdk_settings.api_key).client
|
66
76
|
# TODO: impressionInvoker = new ImpressionInvoker(internalFlags, customPropertyRepository, deviceProperties, Analytics.Analytics.Client, roxyPath != null);
|
67
|
-
@impression_invoker = ImpressionInvoker.new(@internal_flags, @custom_property_repository, device_properties,
|
77
|
+
@impression_invoker = ImpressionInvoker.new(@internal_flags, @custom_property_repository, device_properties,
|
78
|
+
@analytics_client, !roxy_path.nil?, @user_unhandled_error_invoker)
|
68
79
|
@flag_setter = FlagSetter.new(@flag_repository, @parser, @experiment_repository, @impression_invoker)
|
69
80
|
buid = BUID.new(sdk_settings, device_properties, @flag_repository, @custom_property_repository)
|
70
81
|
|
71
|
-
request_config_builder = RequestConfigurationBuilder.new(sdk_settings, buid, device_properties
|
82
|
+
request_config_builder = RequestConfigurationBuilder.new(sdk_settings, buid, device_properties)
|
72
83
|
|
73
84
|
client_request = Request.new
|
74
85
|
err_reporter_request = Request.new
|
75
86
|
|
76
87
|
@error_reporter = ErrorReporter.new(err_reporter_request, device_properties, buid)
|
77
88
|
|
78
|
-
if
|
79
|
-
@configuration_fetcher =
|
80
|
-
|
89
|
+
if @rox_options.self_managed?
|
90
|
+
@configuration_fetcher = ConfigurationFetcherSelfManaged.new(request_config_builder, client_request,
|
91
|
+
@configuration_fetched_invoker)
|
92
|
+
@state_sender = StateSender.new(@sdk_settings, device_properties, @flag_repository,
|
93
|
+
@custom_property_repository)
|
94
|
+
@flag_repository.register_flag_added_handler { @state_sender.delayed_send }
|
95
|
+
@custom_property_repository.register_property_added_handler { @state_sender.delayed_send }
|
96
|
+
elsif roxy_path.nil?
|
97
|
+
@configuration_fetcher = ConfigurationFetcher.new(request_config_builder, client_request,
|
98
|
+
@configuration_fetched_invoker)
|
99
|
+
@state_sender = StateSender.new(@sdk_settings, device_properties, @flag_repository,
|
100
|
+
@custom_property_repository)
|
81
101
|
@flag_repository.register_flag_added_handler { @state_sender.delayed_send }
|
82
102
|
@custom_property_repository.register_property_added_handler { @state_sender.delayed_send }
|
83
103
|
else
|
84
|
-
@configuration_fetcher = ConfigurationFetcherRoxy.new(request_config_builder, client_request,
|
104
|
+
@configuration_fetcher = ConfigurationFetcherRoxy.new(request_config_builder, client_request,
|
105
|
+
@configuration_fetched_invoker)
|
85
106
|
end
|
86
107
|
|
87
108
|
configuration_fetched_handler = nil
|
88
|
-
unless rox_options.nil?
|
89
|
-
configuration_fetched_handler = rox_options.configuration_fetched_handler
|
90
|
-
end
|
109
|
+
configuration_fetched_handler = @rox_options.configuration_fetched_handler unless @rox_options.nil?
|
91
110
|
|
92
111
|
@configuration_fetched_invoker.register_fetched_handler(&wrap_configuration_fetched_handler(&configuration_fetched_handler))
|
93
112
|
|
94
|
-
Thread.new do
|
113
|
+
@thread = Thread.new do
|
95
114
|
Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception)
|
96
115
|
fetch
|
116
|
+
@state_sender&.send
|
97
117
|
|
98
|
-
if
|
99
|
-
@impression_invoker.register_impression_handler(
|
118
|
+
if !@rox_options.nil? && !@rox_options.impression_handler.nil?
|
119
|
+
@impression_invoker.register_impression_handler(&@rox_options.impression_handler)
|
100
120
|
end
|
101
121
|
|
102
|
-
if
|
103
|
-
PeriodicTask.run(rox_options.fetch_interval) { fetch }
|
122
|
+
if !@rox_options.nil? && !@rox_options.fetch_interval.nil?
|
123
|
+
PeriodicTask.run(@rox_options.fetch_interval) { fetch }
|
104
124
|
end
|
105
125
|
end
|
106
126
|
end
|
107
127
|
|
128
|
+
def shutdown
|
129
|
+
return if @thread.nil?
|
130
|
+
|
131
|
+
Thread.kill(@thread)
|
132
|
+
@thread = nil
|
133
|
+
|
134
|
+
unless @push_updates_listener.nil?
|
135
|
+
@push_updates_listener.stop
|
136
|
+
@push_updates_listener = nil
|
137
|
+
end
|
138
|
+
|
139
|
+
return if @analytics_client.nil?
|
140
|
+
|
141
|
+
@analytics_client.flush
|
142
|
+
end
|
143
|
+
|
108
144
|
def fetch
|
109
145
|
return if @configuration_fetcher.nil?
|
110
146
|
|
111
|
-
|
147
|
+
signature_verifier = if @rox_options.self_managed?
|
148
|
+
SignatureVerifierMock.new
|
149
|
+
else
|
150
|
+
SignatureVerifier.new
|
151
|
+
end
|
152
|
+
configuration_parser = ConfigurationParser.new(signature_verifier, @error_reporter,
|
153
|
+
@configuration_fetched_invoker)
|
112
154
|
result = @configuration_fetcher.fetch
|
113
155
|
return if result.nil?
|
114
156
|
|
@@ -121,17 +163,22 @@ module Rox
|
|
121
163
|
|
122
164
|
has_changes = @last_configurations.nil? || @last_configurations.data != result.data
|
123
165
|
@last_configurations = result
|
124
|
-
@configuration_fetched_invoker.invoke(FetcherStatus::APPLIED_FROM_NETWORK, configuration.signature_date,
|
166
|
+
@configuration_fetched_invoker.invoke(FetcherStatus::APPLIED_FROM_NETWORK, configuration.signature_date,
|
167
|
+
has_changes)
|
168
|
+
end
|
169
|
+
|
170
|
+
def register_with_namespace(namespace, rox_container)
|
171
|
+
@registerer.register_instance(rox_container, namespace)
|
125
172
|
end
|
126
173
|
|
127
|
-
def register(
|
128
|
-
|
174
|
+
def register(*args)
|
175
|
+
rox_container = args.pop
|
176
|
+
namespace = args.length == 1 ? args.pop : ''
|
177
|
+
@registerer.register_instance(rox_container, namespace)
|
129
178
|
end
|
130
179
|
|
131
180
|
def context=(context)
|
132
|
-
@
|
133
|
-
flag.context = context
|
134
|
-
end
|
181
|
+
@parser.global_context = context
|
135
182
|
end
|
136
183
|
|
137
184
|
def add_custom_property(property)
|
@@ -145,7 +192,7 @@ module Rox
|
|
145
192
|
def wrap_configuration_fetched_handler(&handler)
|
146
193
|
lambda do |args|
|
147
194
|
start_or_stop_push_updated_listener unless args.fetcher_status == FetcherStatus::ERROR_FETCHED_FAILED
|
148
|
-
handler
|
195
|
+
handler&.call(args)
|
149
196
|
end
|
150
197
|
end
|
151
198
|
|
@@ -153,7 +200,7 @@ module Rox
|
|
153
200
|
if @internal_flags.enabled?('rox.internal.pushUpdates')
|
154
201
|
if @push_updates_listener.nil?
|
155
202
|
@push_updates_listener = NotificationListener.new(Environment.notifications_path, @sdk_settings.api_key)
|
156
|
-
@push_updates_listener.on 'changed' do |
|
203
|
+
@push_updates_listener.on 'changed' do |_data|
|
157
204
|
fetch
|
158
205
|
end
|
159
206
|
@push_updates_listener.start
|
@@ -170,12 +217,16 @@ module Rox
|
|
170
217
|
Rox::Core::DynamicApi.new(@flag_repository, entities_provider)
|
171
218
|
end
|
172
219
|
|
220
|
+
def dump_state
|
221
|
+
@state_sender.dump_state
|
222
|
+
end
|
223
|
+
|
173
224
|
def validate_api_key(api_key)
|
174
225
|
valid_api_key_pattern = /^[a-f\d]{24}$/
|
175
226
|
if api_key&.strip&.empty?
|
176
|
-
raise ArgumentError
|
227
|
+
raise ArgumentError, 'Blank Rollout api key - must be specified'
|
177
228
|
elsif !valid_api_key_pattern.match(api_key)
|
178
|
-
raise ArgumentError
|
229
|
+
raise ArgumentError, 'Illegal Rollout api key'
|
179
230
|
end
|
180
231
|
end
|
181
232
|
end
|
@@ -1,20 +1,35 @@
|
|
1
|
-
require 'rox/core/entities/
|
1
|
+
require 'rox/core/entities/rox_string'
|
2
|
+
require 'rox/core/entities/default_flag_values'
|
2
3
|
|
3
4
|
module Rox
|
4
5
|
module Core
|
5
|
-
class Flag <
|
6
|
+
class Flag < RoxString
|
6
7
|
FLAG_TRUE_VALUE = 'true'.freeze
|
7
8
|
FLAG_FALSE_VALUE = 'false'.freeze
|
8
9
|
|
9
|
-
def initialize(default_value =
|
10
|
-
super(default_value ? Flag::FLAG_TRUE_VALUE : Flag::FLAG_FALSE_VALUE, [Flag::FLAG_FALSE_VALUE,
|
10
|
+
def initialize(default_value = DefaultFlagValues::BOOLEAN)
|
11
|
+
super(default_value ? Flag::FLAG_TRUE_VALUE : Flag::FLAG_FALSE_VALUE, [Flag::FLAG_FALSE_VALUE,
|
12
|
+
Flag::FLAG_TRUE_VALUE])
|
11
13
|
end
|
12
14
|
|
13
15
|
def enabled?(context)
|
14
|
-
|
16
|
+
merged_context = MergedContext.new(@parser&.global_context, context)
|
17
|
+
value = internal_enabled?(merged_context)
|
18
|
+
if [true, false].include? value
|
19
|
+
send_impressions(value, merged_context)
|
20
|
+
return value
|
21
|
+
end
|
22
|
+
|
23
|
+
send_impressions(DefaultFlagValues::BOOLEAN, merged_context)
|
24
|
+
DefaultFlagValues.BOOLEAN
|
25
|
+
end
|
26
|
+
|
27
|
+
def value(context = nil)
|
28
|
+
merged_context = MergedContext.new(@parser&.global_context, context)
|
29
|
+
internal_value(merged_context, false)
|
15
30
|
end
|
16
31
|
|
17
|
-
def internal_enabled?(context, nil_instead_of_default)
|
32
|
+
def internal_enabled?(context, nil_instead_of_default = false)
|
18
33
|
val = internal_value(context, nil_instead_of_default)
|
19
34
|
nil_instead_of_default && val.nil? ? nil : (val == Flag::FLAG_TRUE_VALUE)
|
20
35
|
end
|
@@ -28,4 +43,4 @@ module Rox
|
|
28
43
|
end
|
29
44
|
end
|
30
45
|
end
|
31
|
-
end
|
46
|
+
end
|
@@ -7,9 +7,9 @@ module Rox
|
|
7
7
|
@experiment_repository = experiment_repository
|
8
8
|
@impression_invoker = impression_invoker
|
9
9
|
|
10
|
-
@flag_repository.register_flag_added_handler do |
|
11
|
-
exp = @experiment_repository.experiment_by_flag(
|
12
|
-
set_flag_data(
|
10
|
+
@flag_repository.register_flag_added_handler do |string|
|
11
|
+
exp = @experiment_repository.experiment_by_flag(string.name)
|
12
|
+
set_flag_data(string, exp)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -31,9 +31,9 @@ module Rox
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def set_flag_data(
|
35
|
-
|
34
|
+
def set_flag_data(string, experiment = nil)
|
35
|
+
string.set_for_evaluation(@parser, experiment, @impression_invoker)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
39
|
-
end
|
39
|
+
end
|
@@ -4,22 +4,31 @@ require 'rox/core/impression/models/reporting_value'
|
|
4
4
|
|
5
5
|
module Rox
|
6
6
|
module Core
|
7
|
-
class
|
8
|
-
attr_accessor :default_value, :options, :name, :
|
7
|
+
class RoxString
|
8
|
+
attr_accessor :default_value, :options, :name, :condition, :parser, :impression_invoker,
|
9
|
+
:client_experiment
|
9
10
|
|
10
11
|
def initialize(default_value, options = [])
|
11
12
|
@default_value = default_value
|
12
13
|
@options = options.clone
|
14
|
+
raise ArgumentError, 'options should be an array' unless options.is_a?(Array)
|
15
|
+
if options.length > 0 && options.any? { |x| x.class != default_value.class }
|
16
|
+
raise ArgumentError, 'options should be the same type as default value'
|
17
|
+
end
|
13
18
|
@options << default_value unless options.include?(default_value)
|
14
19
|
|
15
20
|
@condition = nil
|
16
21
|
@parser = nil
|
17
|
-
@context = nil
|
18
22
|
@impression_invoker = nil
|
19
23
|
@client_experiment = nil
|
20
24
|
@name = nil
|
21
25
|
end
|
22
26
|
|
27
|
+
def send_impressions(return_value, merged_context)
|
28
|
+
reporting_value = ReportingValue.new(@name, return_value, @client_experiment)
|
29
|
+
@impression_invoker&.invoke(reporting_value, @client_experiment, merged_context)
|
30
|
+
end
|
31
|
+
|
23
32
|
def set_for_evaluation(parser, experiment, impression_invoker)
|
24
33
|
if experiment.nil?
|
25
34
|
@client_experiment = nil
|
@@ -37,24 +46,22 @@ module Rox
|
|
37
46
|
internal_value(context, false)
|
38
47
|
end
|
39
48
|
|
40
|
-
def internal_value(context, nil_instead_of_default)
|
49
|
+
def internal_value(context, nil_instead_of_default, evaluated_type = String)
|
41
50
|
return_value = nil_instead_of_default ? nil : @default_value
|
42
|
-
merged_context = MergedContext.new(@
|
51
|
+
merged_context = MergedContext.new(@parser&.global_context, context)
|
43
52
|
|
44
|
-
|
53
|
+
unless @parser.nil? || @condition.nil? || @condition.empty?
|
45
54
|
evaluation_result = @parser.evaluate_expression(@condition, merged_context)
|
46
55
|
unless evaluation_result.nil?
|
47
|
-
value = evaluation_result.string_value
|
48
|
-
|
49
|
-
|
50
|
-
end
|
56
|
+
value = evaluated_type == String ? evaluation_result.string_value : evaluation_result.value
|
57
|
+
is_empty = value.is_a?(String) && value.empty?
|
58
|
+
return_value = value if !value.nil? && !is_empty
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
|
-
|
55
|
-
|
62
|
+
send_impressions(return_value, merged_context)
|
56
63
|
return_value
|
57
64
|
end
|
58
65
|
end
|
59
66
|
end
|
60
|
-
end
|
67
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rox
|
2
|
+
module Core
|
3
|
+
class UserspaceHandlerException < StandardError
|
4
|
+
def initialize(exception_source, exception_trigger, exception)
|
5
|
+
@exception_source = exception_source
|
6
|
+
@exception_trigger = exception_trigger
|
7
|
+
@exception = exception
|
8
|
+
super('user unhandled exception in roxx expression')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rox/core/logging/logging'
|
2
|
+
|
3
|
+
module Rox
|
4
|
+
module Core
|
5
|
+
class UserspaceUnhandledErrorInvoker
|
6
|
+
def initialize(user_unhandler_error_handler = nil)
|
7
|
+
@user_unhandler_error_handler = user_unhandler_error_handler
|
8
|
+
end
|
9
|
+
|
10
|
+
def invoke(exception_source, exception_trigger, exception)
|
11
|
+
unless @user_unhandler_error_handler
|
12
|
+
Logging.logger.error("User Unhandled Error Occurred, no fallback handler was set, exception ignored: #{exception}")
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
userspace_unhandled_error_args = UserspaceUnhandledErrorArgs.new(exception_source, exception_trigger, exception)
|
18
|
+
@user_unhandler_error_handler.call(userspace_unhandled_error_args)
|
19
|
+
rescue StandardError => e
|
20
|
+
Logging.logger.error("User Unhandled Error Handler itself threw an exception. original exception: #{e}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def handler=(handler)
|
25
|
+
@user_unhandler_error_handler = handler
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class UserspaceUnhandledErrorArgs
|
30
|
+
def initialize(exception_source = nil, exception_trigger = nil, exception = nil)
|
31
|
+
@exception_source = exception_source
|
32
|
+
@exception_trigger = exception_trigger
|
33
|
+
@exception = exception
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"UserspaceUnhandledErrorArgs(#{@exception_source}, #{@exception_trigger}, #{@exception})"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,23 +1,54 @@
|
|
1
1
|
require 'rox/core/impression/impression_args'
|
2
|
+
require 'rox/core/logging/logging'
|
3
|
+
require 'rox/core/error_handling/exception_trigger'
|
4
|
+
require 'rox/core/consts/property_type'
|
2
5
|
|
3
6
|
module Rox
|
4
7
|
module Core
|
5
8
|
class ImpressionInvoker
|
6
|
-
def initialize(internal_flags, custom_property_repository, device_properties, analytics_client, is_roxy)
|
9
|
+
def initialize(internal_flags, custom_property_repository, device_properties, analytics_client, is_roxy, user_unhandled_error_invoker)
|
7
10
|
@internal_flags = internal_flags
|
8
11
|
@custom_property_repository = custom_property_repository
|
9
12
|
@device_properties = device_properties
|
10
13
|
@analytics_client = analytics_client
|
11
14
|
@is_roxy = is_roxy
|
15
|
+
@user_unhandled_error_invoker = user_unhandled_error_invoker
|
12
16
|
|
13
17
|
@impression_handlers = []
|
14
18
|
@mutex = Mutex.new
|
15
19
|
end
|
16
20
|
|
17
|
-
def invoke(reporting_value,
|
18
|
-
|
21
|
+
def invoke(reporting_value, stickiness_property, context)
|
22
|
+
begin
|
23
|
+
analytics_enabled = @internal_flags.enabled?('rox.internal.analytics')
|
24
|
+
if analytics_enabled && !@is_roxy
|
25
|
+
prop = @custom_property_repository.custom_property(stickiness_property) || @custom_property_repository.custom_property("rox.#{Rox::Core::PropertyType::DISTINCT_ID.name}")
|
26
|
+
distinct_id = '(null_distinct_id'
|
27
|
+
unless prop.nil?
|
28
|
+
prop_value = prop.value(context)
|
29
|
+
distinct_id = prop_value if prop_value.instance_of?(String)
|
30
|
+
end
|
19
31
|
|
20
|
-
|
32
|
+
event_time = (Time.now.to_f * 1000.0).to_i
|
33
|
+
begin
|
34
|
+
event_time = ENV['rox.analytics.ms'].to_i
|
35
|
+
rescue StandardError
|
36
|
+
end
|
37
|
+
|
38
|
+
@analytics_client.track({
|
39
|
+
flag: reporting_value.name,
|
40
|
+
value: reporting_value.value,
|
41
|
+
distinctId: distinct_id,
|
42
|
+
experimentVersion: '0',
|
43
|
+
type: 'IMPRESSION',
|
44
|
+
time: event_time
|
45
|
+
})
|
46
|
+
end
|
47
|
+
rescue StandardError => ex
|
48
|
+
Logging.logger.error('Failed to send analytics', ex)
|
49
|
+
end
|
50
|
+
|
51
|
+
raise_impression_event(ImpressionArgs.new(reporting_value, context))
|
21
52
|
end
|
22
53
|
|
23
54
|
def register_impression_handler(&block)
|
@@ -32,10 +63,13 @@ module Rox
|
|
32
63
|
handlers = @impression_handlers.clone
|
33
64
|
end
|
34
65
|
|
35
|
-
|
36
|
-
handler.call(args)
|
66
|
+
begin
|
67
|
+
handlers.each { |handler| handler.call(args) }
|
68
|
+
rescue StandardError => e
|
69
|
+
user_unhandled_error_invoker.invoke(handler, ExceptionTrigger::IMPRESSION_HANDLER, e)
|
70
|
+
Logging.logger.error('Impresssion handler exception', ex)
|
37
71
|
end
|
38
72
|
end
|
39
73
|
end
|
40
74
|
end
|
41
|
-
end
|
75
|
+
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module Rox
|
2
2
|
module Core
|
3
|
-
ReportingValue
|
3
|
+
class ReportingValue
|
4
|
+
attr_reader :name, :value, :targeting
|
5
|
+
|
6
|
+
def initialize(name, value, experiment = nil)
|
7
|
+
@name = name
|
8
|
+
@value = value
|
9
|
+
@targeting = !experiment.nil?
|
10
|
+
end
|
11
|
+
end
|
4
12
|
end
|
5
|
-
end
|
13
|
+
end
|
@@ -19,8 +19,8 @@ module Rox
|
|
19
19
|
end
|
20
20
|
|
21
21
|
write_fetch_error_to_log_and_invoke_fetch_handler(source, fetch_result)
|
22
|
-
rescue StandardError =>
|
23
|
-
write_fetch_exception_to_log_and_invoke_fetch_handler(source,
|
22
|
+
rescue StandardError => e
|
23
|
+
write_fetch_exception_to_log_and_invoke_fetch_handler(source, e)
|
24
24
|
end
|
25
25
|
|
26
26
|
nil
|
@@ -14,8 +14,8 @@ module Rox
|
|
14
14
|
else
|
15
15
|
write_fetch_error_to_log_and_invoke_fetch_handler(source, fetch_roxy)
|
16
16
|
end
|
17
|
-
rescue StandardError =>
|
18
|
-
write_fetch_exception_to_log_and_invoke_fetch_handler(source,
|
17
|
+
rescue StandardError => e
|
18
|
+
write_fetch_exception_to_log_and_invoke_fetch_handler(source, e)
|
19
19
|
end
|
20
20
|
|
21
21
|
nil
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rox/core/network/configuration_source'
|
2
|
+
require 'rox/core/network/configuration_fetch_result'
|
3
|
+
require 'rox/core/network/configuration_fetcher_base'
|
4
|
+
|
5
|
+
module Rox
|
6
|
+
module Core
|
7
|
+
class ConfigurationFetcherSelfManaged < ConfigurationFetcherBase
|
8
|
+
def fetch
|
9
|
+
source = ConfigurationSource::API
|
10
|
+
begin
|
11
|
+
fetch_result = fetch_from_api
|
12
|
+
if fetch_result.success?
|
13
|
+
return ConfigurationFetchResult.new(fetch_result.text, source)
|
14
|
+
else
|
15
|
+
write_fetch_error_to_log_and_invoke_fetch_handler(source, fetch_result)
|
16
|
+
end
|
17
|
+
rescue StandardError => e
|
18
|
+
write_fetch_exception_to_log_and_invoke_fetch_handler(source, e)
|
19
|
+
end
|
20
|
+
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_from_api
|
25
|
+
api_request = @request_configuration_builder.build_for_api
|
26
|
+
@request.send_post(api_request.url, api_request.query_params)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|