rox-rollout 4.7.2 → 5.0.3
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 +21 -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/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 +17 -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 +91 -46
- 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 +13 -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 +46 -6
- 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 +2 -2
- 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 +9 -9
- metadata +47 -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,10 @@ 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/error_handling/userspace_unhandled_error_invoker'
|
25
28
|
|
26
29
|
module Rox
|
27
30
|
module Core
|
@@ -31,14 +34,10 @@ module Rox
|
|
31
34
|
@custom_property_repository = CustomPropertyRepository.new
|
32
35
|
@target_group_repository = TargetGroupRepository.new
|
33
36
|
@experiment_repository = ExperimentRepository.new
|
34
|
-
@
|
37
|
+
@user_unhandled_error_invoker = Rox::Core::UserspaceUnhandledErrorInvoker.new
|
38
|
+
@parser = Parser.new(@user_unhandled_error_invoker)
|
35
39
|
|
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
|
40
|
+
@configuration_fetched_invoker = ConfigurationFetchedInvoker.new(@user_unhandled_error_invoker)
|
42
41
|
@registerer = Registerer.new(@flag_repository)
|
43
42
|
|
44
43
|
@sdk_settings = nil
|
@@ -51,65 +50,109 @@ module Rox
|
|
51
50
|
@push_updates_listener = nil
|
52
51
|
end
|
53
52
|
|
54
|
-
def
|
53
|
+
def userspace_unhandled_error_handler=(handler)
|
54
|
+
@user_unhandled_error_invoker.handler = handler
|
55
|
+
end
|
56
|
+
|
57
|
+
def setup(sdk_settings, device_properties)
|
55
58
|
@sdk_settings = sdk_settings
|
59
|
+
@rox_options = device_properties.rox_options
|
56
60
|
|
57
|
-
|
61
|
+
experiments_extensions = ExperimentsExtensions.new(@parser, @target_group_repository, @flag_repository,
|
62
|
+
@experiment_repository)
|
63
|
+
properties_extensions = PropertiesExtensions.new(@parser, @custom_property_repository, @rox_options.dynamic_property_rule_handler)
|
64
|
+
experiments_extensions.extend
|
65
|
+
properties_extensions.extend
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
|
67
|
+
roxy_path = @rox_options.nil? || @rox_options.roxy_url.nil? ? nil : @rox_options.roxy_url
|
68
|
+
|
69
|
+
validate_api_key(sdk_settings&.api_key) if roxy_path.nil?
|
62
70
|
|
63
71
|
# TODO: Analytics.Analytics.Initialize(deviceProperties.RolloutKey, deviceProperties)
|
64
|
-
@internal_flags = InternalFlags.new(@experiment_repository, @parser)
|
72
|
+
@internal_flags = InternalFlags.new(@experiment_repository, @parser, @rox_options)
|
65
73
|
|
66
74
|
# TODO: impressionInvoker = new ImpressionInvoker(internalFlags, customPropertyRepository, deviceProperties, Analytics.Analytics.Client, roxyPath != null);
|
67
|
-
@impression_invoker = ImpressionInvoker.new(@internal_flags, @custom_property_repository, device_properties,
|
75
|
+
@impression_invoker = ImpressionInvoker.new(@internal_flags, @custom_property_repository, device_properties,
|
76
|
+
nil, !roxy_path.nil?, @user_unhandled_error_invoker)
|
68
77
|
@flag_setter = FlagSetter.new(@flag_repository, @parser, @experiment_repository, @impression_invoker)
|
69
78
|
buid = BUID.new(sdk_settings, device_properties, @flag_repository, @custom_property_repository)
|
70
79
|
|
71
|
-
request_config_builder = RequestConfigurationBuilder.new(sdk_settings, buid, device_properties
|
80
|
+
request_config_builder = RequestConfigurationBuilder.new(sdk_settings, buid, device_properties)
|
72
81
|
|
73
82
|
client_request = Request.new
|
74
83
|
err_reporter_request = Request.new
|
75
84
|
|
76
85
|
@error_reporter = ErrorReporter.new(err_reporter_request, device_properties, buid)
|
77
86
|
|
78
|
-
if
|
79
|
-
@configuration_fetcher =
|
80
|
-
|
87
|
+
if @rox_options.self_managed?
|
88
|
+
@configuration_fetcher = ConfigurationFetcherSelfManaged.new(request_config_builder, client_request,
|
89
|
+
@configuration_fetched_invoker)
|
90
|
+
@state_sender = StateSender.new(@sdk_settings, device_properties, @flag_repository,
|
91
|
+
@custom_property_repository)
|
92
|
+
@flag_repository.register_flag_added_handler { @state_sender.delayed_send }
|
93
|
+
@custom_property_repository.register_property_added_handler { @state_sender.delayed_send }
|
94
|
+
elsif roxy_path.nil?
|
95
|
+
@configuration_fetcher = ConfigurationFetcher.new(request_config_builder, client_request,
|
96
|
+
@configuration_fetched_invoker)
|
97
|
+
@state_sender = StateSender.new(@sdk_settings, device_properties, @flag_repository,
|
98
|
+
@custom_property_repository)
|
81
99
|
@flag_repository.register_flag_added_handler { @state_sender.delayed_send }
|
82
100
|
@custom_property_repository.register_property_added_handler { @state_sender.delayed_send }
|
83
101
|
else
|
84
|
-
@configuration_fetcher = ConfigurationFetcherRoxy.new(request_config_builder, client_request,
|
102
|
+
@configuration_fetcher = ConfigurationFetcherRoxy.new(request_config_builder, client_request,
|
103
|
+
@configuration_fetched_invoker)
|
85
104
|
end
|
86
105
|
|
87
106
|
configuration_fetched_handler = nil
|
88
|
-
unless rox_options.nil?
|
89
|
-
configuration_fetched_handler = rox_options.configuration_fetched_handler
|
90
|
-
end
|
107
|
+
configuration_fetched_handler = @rox_options.configuration_fetched_handler unless @rox_options.nil?
|
91
108
|
|
92
|
-
@configuration_fetched_invoker.
|
109
|
+
@configuration_fetched_invoker.register_start_stop_push(proc do |args|
|
110
|
+
start_or_stop_push_updated_listener unless args.fetcher_status == FetcherStatus::ERROR_FETCHED_FAILED
|
111
|
+
end)
|
112
|
+
|
113
|
+
@configuration_fetched_invoker.register_fetched_handler(&configuration_fetched_handler)
|
93
114
|
|
94
|
-
Thread.new do
|
115
|
+
@thread = Thread.new do
|
95
116
|
Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception)
|
96
117
|
fetch
|
97
|
-
@state_sender&.
|
118
|
+
@state_sender&.send
|
98
119
|
|
99
|
-
if
|
100
|
-
@impression_invoker.register_impression_handler(
|
120
|
+
if !@rox_options.nil? && !@rox_options.impression_handler.nil?
|
121
|
+
@impression_invoker.register_impression_handler(&@rox_options.impression_handler)
|
101
122
|
end
|
102
123
|
|
103
|
-
if
|
104
|
-
PeriodicTask.run(rox_options.fetch_interval) { fetch }
|
124
|
+
if !@rox_options.nil? && !@rox_options.fetch_interval.nil?
|
125
|
+
PeriodicTask.run(@rox_options.fetch_interval) { fetch }
|
105
126
|
end
|
106
127
|
end
|
107
128
|
end
|
108
129
|
|
130
|
+
def shutdown
|
131
|
+
return if @thread.nil?
|
132
|
+
|
133
|
+
Thread.kill(@thread)
|
134
|
+
@thread = nil
|
135
|
+
|
136
|
+
unless @push_updates_listener.nil?
|
137
|
+
@push_updates_listener.stop
|
138
|
+
@push_updates_listener = nil
|
139
|
+
end
|
140
|
+
|
141
|
+
#return if @analytics_client.nil?
|
142
|
+
|
143
|
+
#@analytics_client.flush
|
144
|
+
end
|
145
|
+
|
109
146
|
def fetch
|
110
147
|
return if @configuration_fetcher.nil?
|
111
148
|
|
112
|
-
|
149
|
+
signature_verifier = if @rox_options.self_managed?
|
150
|
+
SignatureVerifierMock.new
|
151
|
+
else
|
152
|
+
SignatureVerifier.new
|
153
|
+
end
|
154
|
+
configuration_parser = ConfigurationParser.new(signature_verifier, @error_reporter,
|
155
|
+
@configuration_fetched_invoker)
|
113
156
|
result = @configuration_fetcher.fetch
|
114
157
|
return if result.nil?
|
115
158
|
|
@@ -122,17 +165,22 @@ module Rox
|
|
122
165
|
|
123
166
|
has_changes = @last_configurations.nil? || @last_configurations.data != result.data
|
124
167
|
@last_configurations = result
|
125
|
-
@configuration_fetched_invoker.invoke(FetcherStatus::APPLIED_FROM_NETWORK, configuration.signature_date,
|
168
|
+
@configuration_fetched_invoker.invoke(FetcherStatus::APPLIED_FROM_NETWORK, configuration.signature_date,
|
169
|
+
has_changes)
|
170
|
+
end
|
171
|
+
|
172
|
+
def register_with_namespace(namespace, rox_container)
|
173
|
+
@registerer.register_instance(rox_container, namespace)
|
126
174
|
end
|
127
175
|
|
128
|
-
def register(
|
129
|
-
|
176
|
+
def register(*args)
|
177
|
+
rox_container = args.pop
|
178
|
+
namespace = args.length == 1 ? args.pop : ''
|
179
|
+
@registerer.register_instance(rox_container, namespace)
|
130
180
|
end
|
131
181
|
|
132
182
|
def context=(context)
|
133
|
-
@
|
134
|
-
flag.context = context
|
135
|
-
end
|
183
|
+
@parser.global_context = context
|
136
184
|
end
|
137
185
|
|
138
186
|
def add_custom_property(property)
|
@@ -143,18 +191,11 @@ module Rox
|
|
143
191
|
@custom_property_repository.add_custom_property_if_not_exists(property)
|
144
192
|
end
|
145
193
|
|
146
|
-
def wrap_configuration_fetched_handler(&handler)
|
147
|
-
lambda do |args|
|
148
|
-
start_or_stop_push_updated_listener unless args.fetcher_status == FetcherStatus::ERROR_FETCHED_FAILED
|
149
|
-
handler.call(args) unless handler.nil?
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
194
|
def start_or_stop_push_updated_listener
|
154
195
|
if @internal_flags.enabled?('rox.internal.pushUpdates')
|
155
196
|
if @push_updates_listener.nil?
|
156
197
|
@push_updates_listener = NotificationListener.new(Environment.notifications_path, @sdk_settings.api_key)
|
157
|
-
@push_updates_listener.on 'changed' do |
|
198
|
+
@push_updates_listener.on 'changed' do |_data|
|
158
199
|
fetch
|
159
200
|
end
|
160
201
|
@push_updates_listener.start
|
@@ -171,12 +212,16 @@ module Rox
|
|
171
212
|
Rox::Core::DynamicApi.new(@flag_repository, entities_provider)
|
172
213
|
end
|
173
214
|
|
215
|
+
def dump_state
|
216
|
+
@state_sender.dump_state
|
217
|
+
end
|
218
|
+
|
174
219
|
def validate_api_key(api_key)
|
175
220
|
valid_api_key_pattern = /^[a-f\d]{24}$/
|
176
221
|
if api_key&.strip&.empty?
|
177
|
-
raise ArgumentError
|
222
|
+
raise ArgumentError, 'Blank Rollout api key - must be specified'
|
178
223
|
elsif !valid_api_key_pattern.match(api_key)
|
179
|
-
raise ArgumentError
|
224
|
+
raise ArgumentError, 'Illegal Rollout api key'
|
180
225
|
end
|
181
226
|
end
|
182
227
|
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,13 @@
|
|
1
|
+
module Rox
|
2
|
+
module Core
|
3
|
+
class UserspaceHandlerException < StandardError
|
4
|
+
attr_accessor :exception_source, :exception_trigger, :original_exception
|
5
|
+
def initialize(exception_source, exception_trigger, original_exception)
|
6
|
+
@exception_source = exception_source
|
7
|
+
@exception_trigger = exception_trigger
|
8
|
+
@original_exception = original_exception
|
9
|
+
super('user unhandled exception in roxx expression')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
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,58 @@
|
|
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
|
-
|
18
|
-
|
21
|
+
# TODO: write analytics client and initiate it before using it
|
22
|
+
def call_analytics_gateway(reporting_value, stickiness_property, context)
|
23
|
+
begin
|
24
|
+
analytics_enabled = @internal_flags.enabled?('rox.internal.analytics')
|
25
|
+
if analytics_enabled && !@is_roxy
|
26
|
+
prop = @custom_property_repository.custom_property(stickiness_property) || @custom_property_repository.custom_property("rox.#{Rox::Core::PropertyType::DISTINCT_ID.name}")
|
27
|
+
distinct_id = '(null_distinct_id'
|
28
|
+
unless prop.nil?
|
29
|
+
prop_value = prop.value(context)
|
30
|
+
distinct_id = prop_value if prop_value.instance_of?(String)
|
31
|
+
end
|
19
32
|
|
20
|
-
|
33
|
+
event_time = (Time.now.to_f * 1000.0).to_i
|
34
|
+
begin
|
35
|
+
event_time = ENV['rox.analytics.ms'].to_i
|
36
|
+
rescue StandardError
|
37
|
+
end
|
38
|
+
|
39
|
+
@analytics_client.track({
|
40
|
+
flag: reporting_value.name,
|
41
|
+
value: reporting_value.value,
|
42
|
+
distinctId: distinct_id,
|
43
|
+
experimentVersion: '0',
|
44
|
+
type: 'IMPRESSION',
|
45
|
+
time: event_time
|
46
|
+
})
|
47
|
+
end
|
48
|
+
rescue StandardError => ex
|
49
|
+
Logging.logger.error('Failed to send analytics', ex)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def invoke(reporting_value, stickiness_property, context)
|
54
|
+
#call_analytics_gateway
|
55
|
+
raise_impression_event(ImpressionArgs.new(reporting_value, context))
|
21
56
|
end
|
22
57
|
|
23
58
|
def register_impression_handler(&block)
|
@@ -33,9 +68,14 @@ module Rox
|
|
33
68
|
end
|
34
69
|
|
35
70
|
handlers.each do |handler|
|
36
|
-
|
71
|
+
begin
|
72
|
+
handler.call(args)
|
73
|
+
rescue StandardError => e
|
74
|
+
@user_unhandled_error_invoker.invoke(handler, ExceptionTrigger::IMPRESSION_HANDLER, e)
|
75
|
+
Logging.logger.error('Impresssion handler exception', e)
|
76
|
+
end
|
37
77
|
end
|
38
78
|
end
|
39
79
|
end
|
40
80
|
end
|
41
|
-
end
|
81
|
+
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
|