rox-rollout 5.0.0 → 5.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/.github/workflows/ruby.yml +2 -1
- data/.gitignore +1 -0
- data/e2e/rox_e2e_test.rb +63 -18
- data/e2e/test_vars.rb +2 -1
- data/e2e-server/.gitignore +1 -0
- data/lib/rox/core/analytics/backoff_policy.rb +51 -0
- data/lib/rox/core/analytics/client.rb +187 -0
- data/lib/rox/core/analytics/defaults.rb +31 -0
- data/lib/rox/core/analytics/logging.rb +62 -0
- data/lib/rox/core/analytics/message_batch.rb +74 -0
- data/lib/rox/core/analytics/response.rb +17 -0
- data/lib/rox/core/analytics/test_queue.rb +58 -0
- data/lib/rox/core/analytics/transport.rb +143 -0
- data/lib/rox/core/analytics/utils.rb +89 -0
- data/lib/rox/core/analytics/worker.rb +67 -0
- data/lib/rox/core/configuration/configuration_fetched_invoker.rb +10 -1
- data/lib/rox/core/core.rb +17 -10
- data/lib/rox/core/entities/flag.rb +3 -1
- data/lib/rox/core/entities/rox_double.rb +3 -1
- data/lib/rox/core/entities/rox_int.rb +3 -1
- data/lib/rox/core/entities/rox_string.rb +3 -2
- data/lib/rox/core/error_handling/userspace_handler_exception.rb +3 -2
- data/lib/rox/core/impression/impression_invoker.rb +13 -9
- data/lib/rox/core/network/state_sender.rb +2 -1
- data/lib/rox/core/properties/custom_property.rb +1 -1
- data/lib/rox/core/repositories/roxx/properties_extensions.rb +2 -2
- data/lib/rox/core/roxx/parser.rb +1 -1
- data/lib/rox/server/rox_server.rb +4 -0
- data/lib/rox/version.rb +1 -1
- data/rox.gemspec +1 -3
- metadata +20 -24
- data/lib/rox/core/analytics.rb +0 -18
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'rox/core/analytics/defaults'
|
2
|
+
require 'rox/core/analytics/utils'
|
3
|
+
require 'rox/core/analytics/response'
|
4
|
+
require 'rox/core/analytics/logging'
|
5
|
+
require 'rox/core/analytics/backoff_policy'
|
6
|
+
require 'net/http'
|
7
|
+
require 'net/https'
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
module Rox
|
11
|
+
module Core
|
12
|
+
class Analytics
|
13
|
+
class Transport
|
14
|
+
include Rox::Core::Analytics::Defaults::Request
|
15
|
+
include Rox::Core::Analytics::Utils
|
16
|
+
include Rox::Core::Analytics::Logging
|
17
|
+
|
18
|
+
def initialize(device_properties)
|
19
|
+
@device_properties = device_properties
|
20
|
+
uri = URI.parse(Rox::Core::Environment.analytics_path)
|
21
|
+
@headers = {
|
22
|
+
'Accept' => 'application/json',
|
23
|
+
'Content-Type' => 'application/json',
|
24
|
+
'User-Agent' => "ruby/#{device_properties.lib_version}"
|
25
|
+
}
|
26
|
+
@path = uri.path + '/impression/' + device_properties.rollout_key
|
27
|
+
@retries = RETRIES
|
28
|
+
@backoff_policy = Rox::Core::Analytics::BackoffPolicy.new
|
29
|
+
|
30
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
31
|
+
http.use_ssl = uri.scheme == 'https'
|
32
|
+
http.read_timeout = 8
|
33
|
+
http.open_timeout = 4
|
34
|
+
|
35
|
+
@http = http
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sends a batch of messages to the API
|
39
|
+
#
|
40
|
+
# @return [Response] API response
|
41
|
+
def send(batch)
|
42
|
+
logger.debug("Sending request for #{batch.length} items")
|
43
|
+
|
44
|
+
last_response, exception = retry_with_backoff(@retries) do
|
45
|
+
status_code, body = send_request(batch)
|
46
|
+
should_retry = should_retry_request?(status_code, body)
|
47
|
+
logger.debug("Response status code: #{status_code}")
|
48
|
+
logger.debug("Response error: #{body}") if status_code != 200
|
49
|
+
|
50
|
+
[Response.new(status_code, body), should_retry]
|
51
|
+
end
|
52
|
+
|
53
|
+
if exception
|
54
|
+
logger.error(exception.message)
|
55
|
+
exception.backtrace.each { |line| logger.error(line) }
|
56
|
+
Response.new(-1, exception.to_s)
|
57
|
+
else
|
58
|
+
last_response
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Closes a persistent connection if it exists
|
63
|
+
def shutdown
|
64
|
+
@http.finish if @http.started?
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def should_retry_request?(status_code, body)
|
70
|
+
if status_code >= 500
|
71
|
+
true # Server error
|
72
|
+
elsif status_code == 429
|
73
|
+
true # Rate limited
|
74
|
+
elsif status_code >= 400
|
75
|
+
logger.error(body)
|
76
|
+
false # Client error. Do not retry, but log
|
77
|
+
else
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Takes a block that returns [result, should_retry].
|
83
|
+
#
|
84
|
+
# Retries upto `retries_remaining` times, if `should_retry` is false or
|
85
|
+
# an exception is raised. `@backoff_policy` is used to determine the
|
86
|
+
# duration to sleep between attempts
|
87
|
+
#
|
88
|
+
# Returns [last_result, raised_exception]
|
89
|
+
def retry_with_backoff(retries_remaining, &block)
|
90
|
+
result, caught_exception = nil
|
91
|
+
should_retry = false
|
92
|
+
|
93
|
+
begin
|
94
|
+
result, should_retry = yield
|
95
|
+
return [result, nil] unless should_retry
|
96
|
+
rescue StandardError => e
|
97
|
+
should_retry = true
|
98
|
+
caught_exception = e
|
99
|
+
end
|
100
|
+
|
101
|
+
if should_retry && (retries_remaining > 1)
|
102
|
+
logger.debug("Retrying request, #{retries_remaining} retries left")
|
103
|
+
sleep(@backoff_policy.next_interval.to_f / 1000)
|
104
|
+
retry_with_backoff(retries_remaining - 1, &block)
|
105
|
+
else
|
106
|
+
[result, caught_exception]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Sends a request for the batch, returns [status_code, body]
|
111
|
+
def send_request(batch)
|
112
|
+
payload = JSON.generate(
|
113
|
+
:analyticsVersion => '1.0.0',
|
114
|
+
:sdkVersion => @device_properties.lib_version,
|
115
|
+
:time => DateTime.now.strftime('%Q').to_i,
|
116
|
+
:platform => @device_properties.all_properties[PropertyType::PLATFORM.name],
|
117
|
+
:rolloutKey => @device_properties.rollout_key,
|
118
|
+
:events => batch
|
119
|
+
)
|
120
|
+
request = Net::HTTP::Post.new(@path, @headers)
|
121
|
+
|
122
|
+
if self.class.stub
|
123
|
+
logger.debug "stubbed request to #{@path}: #{JSON.generate(batch)}"
|
124
|
+
|
125
|
+
[200, '{}']
|
126
|
+
else
|
127
|
+
@http.start unless @http.started? # Maintain a persistent connection
|
128
|
+
response = @http.request(request, payload)
|
129
|
+
[response.code.to_i, response.body]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class << self
|
134
|
+
attr_writer :stub
|
135
|
+
|
136
|
+
def stub
|
137
|
+
@stub || ENV['STUB']
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Rox
|
4
|
+
module Core
|
5
|
+
class Analytics
|
6
|
+
module Utils
|
7
|
+
extend self
|
8
|
+
|
9
|
+
# public: Return a new hash with keys converted from strings to symbols
|
10
|
+
#
|
11
|
+
def symbolize_keys(hash)
|
12
|
+
hash.each_with_object({}) do |(k, v), memo|
|
13
|
+
memo[k.to_sym] = v
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# public: Convert hash keys from strings to symbols in place
|
18
|
+
#
|
19
|
+
def symbolize_keys!(hash)
|
20
|
+
hash.replace symbolize_keys hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# public: Return a new hash with keys as strings
|
24
|
+
#
|
25
|
+
def stringify_keys(hash)
|
26
|
+
hash.each_with_object({}) do |(k, v), memo|
|
27
|
+
memo[k.to_s] = v
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# public: Returns a new hash with all the date values in the into iso8601
|
32
|
+
# strings
|
33
|
+
#
|
34
|
+
def isoify_dates(hash)
|
35
|
+
hash.each_with_object({}) do |(k, v), memo|
|
36
|
+
memo[k] = datetime_in_iso8601(v)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# public: Converts all the date values in the into iso8601 strings in place
|
41
|
+
#
|
42
|
+
def isoify_dates!(hash)
|
43
|
+
hash.replace isoify_dates hash
|
44
|
+
end
|
45
|
+
|
46
|
+
# public: Returns a uid string
|
47
|
+
#
|
48
|
+
def uid
|
49
|
+
arr = SecureRandom.random_bytes(16).unpack('NnnnnN')
|
50
|
+
arr[2] = (arr[2] & 0x0fff) | 0x4000
|
51
|
+
arr[3] = (arr[3] & 0x3fff) | 0x8000
|
52
|
+
'%08x-%04x-%04x-%04x-%04x%08x' % arr
|
53
|
+
end
|
54
|
+
|
55
|
+
def datetime_in_iso8601(datetime)
|
56
|
+
case datetime
|
57
|
+
when Time
|
58
|
+
time_in_iso8601 datetime
|
59
|
+
when DateTime
|
60
|
+
time_in_iso8601 datetime.to_time
|
61
|
+
when Date
|
62
|
+
date_in_iso8601 datetime
|
63
|
+
else
|
64
|
+
datetime
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def time_in_iso8601(time)
|
69
|
+
"#{time.strftime('%Y-%m-%dT%H:%M:%S.%6N')}#{formatted_offset(time, true, 'Z')}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def date_in_iso8601(date)
|
73
|
+
date.strftime('%F')
|
74
|
+
end
|
75
|
+
|
76
|
+
def formatted_offset(time, colon = true, alternate_utc_string = nil)
|
77
|
+
time.utc? && alternate_utc_string || seconds_to_utc_offset(time.utc_offset, colon)
|
78
|
+
end
|
79
|
+
|
80
|
+
def seconds_to_utc_offset(seconds, colon = true)
|
81
|
+
(colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON) % [(seconds < 0 ? '-' : '+'), (seconds.abs / 3600), ((seconds.abs % 3600) / 60)]
|
82
|
+
end
|
83
|
+
|
84
|
+
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
|
85
|
+
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'rox/core/analytics/defaults'
|
2
|
+
require 'rox/core/analytics/message_batch'
|
3
|
+
require 'rox/core/analytics/transport'
|
4
|
+
require 'rox/core/analytics/utils'
|
5
|
+
|
6
|
+
module Rox
|
7
|
+
module Core
|
8
|
+
class Analytics
|
9
|
+
class Worker
|
10
|
+
include Rox::Core::Analytics::Utils
|
11
|
+
include Rox::Core::Analytics::Defaults
|
12
|
+
include Rox::Core::Analytics::Logging
|
13
|
+
|
14
|
+
# public: Creates a new worker
|
15
|
+
#
|
16
|
+
# The worker continuously takes messages off the queue
|
17
|
+
# and makes requests to the segment.io api
|
18
|
+
#
|
19
|
+
# queue - Queue synchronized between client and worker
|
20
|
+
# @param [Rox::Core::DeviceProperties] device_properties
|
21
|
+
#
|
22
|
+
def initialize(queue, device_properties)
|
23
|
+
@queue = queue
|
24
|
+
@device_properties = device_properties
|
25
|
+
@on_error = proc { |status, error| }
|
26
|
+
batch_size = Defaults::MessageBatch::MAX_SIZE
|
27
|
+
@batch = MessageBatch.new(batch_size)
|
28
|
+
@lock = Mutex.new
|
29
|
+
@transport = Transport.new(device_properties)
|
30
|
+
end
|
31
|
+
|
32
|
+
# public: Continuously runs the loop to check for new events
|
33
|
+
#
|
34
|
+
def run
|
35
|
+
until Thread.current[:should_exit]
|
36
|
+
return if @queue.empty?
|
37
|
+
|
38
|
+
@lock.synchronize do
|
39
|
+
consume_message_from_queue! until @batch.full? || @queue.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
res = @transport.send @batch
|
43
|
+
@on_error.call(res.status, res.error) unless res.status == 200
|
44
|
+
|
45
|
+
@lock.synchronize { @batch.clear }
|
46
|
+
end
|
47
|
+
ensure
|
48
|
+
@transport.shutdown
|
49
|
+
end
|
50
|
+
|
51
|
+
# public: Check whether we have outstanding requests.
|
52
|
+
#
|
53
|
+
def is_requesting?
|
54
|
+
@lock.synchronize { !@batch.empty? }
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def consume_message_from_queue!
|
60
|
+
@batch << @queue.pop
|
61
|
+
rescue MessageBatch::JSONGenerationError => e
|
62
|
+
@on_error.call(-1, e.to_s)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rox/core/configuration/configuration_fetched_args'
|
2
|
+
require 'rox/core/error_handling/exception_trigger'
|
2
3
|
|
3
4
|
module Rox
|
4
5
|
module Core
|
@@ -6,6 +7,7 @@ module Rox
|
|
6
7
|
def initialize(user_unhandled_error_invoker)
|
7
8
|
@fetched_handlers = []
|
8
9
|
@mutex = Mutex.new
|
10
|
+
@internal_handler = nil
|
9
11
|
@user_unhandled_error_invoker = user_unhandled_error_invoker
|
10
12
|
end
|
11
13
|
|
@@ -17,6 +19,10 @@ module Rox
|
|
17
19
|
raise_fetched_event(Rox::Core::ConfigurationFetchedArgs.error(error_details))
|
18
20
|
end
|
19
21
|
|
22
|
+
def register_start_stop_push(block)
|
23
|
+
@internal_handler = block
|
24
|
+
end
|
25
|
+
|
20
26
|
def register_fetched_handler(&block)
|
21
27
|
@mutex.synchronize do
|
22
28
|
@fetched_handlers << block
|
@@ -24,6 +30,9 @@ module Rox
|
|
24
30
|
end
|
25
31
|
|
26
32
|
def raise_fetched_event(args)
|
33
|
+
unless @internal_handler.nil?
|
34
|
+
@internal_handler.call(args)
|
35
|
+
end
|
27
36
|
handlers = []
|
28
37
|
@mutex.synchronize do
|
29
38
|
handlers = @fetched_handlers.clone
|
@@ -33,7 +42,7 @@ module Rox
|
|
33
42
|
begin
|
34
43
|
handler.call(args)
|
35
44
|
rescue StandardError => e
|
36
|
-
user_unhandled_error_invoker.invoke(handler,
|
45
|
+
@user_unhandled_error_invoker.invoke(handler, ExceptionTrigger::CONFIGURATION_FETCHED_HANDLER, e)
|
37
46
|
end
|
38
47
|
end
|
39
48
|
end
|
data/lib/rox/core/core.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'rox/core/analytics/client'
|
1
2
|
require 'rox/core/repositories/flag_repository'
|
2
3
|
require 'rox/core/repositories/custom_property_repository'
|
3
4
|
require 'rox/core/repositories/target_group_repository'
|
@@ -24,7 +25,6 @@ require 'rox/core/security/signature_verifier'
|
|
24
25
|
require 'rox/core/security/signature_verifier_mock'
|
25
26
|
require 'rox/core/utils/periodic_task'
|
26
27
|
require 'rox/core/client/dynamic_api'
|
27
|
-
require 'rox/core/analytics'
|
28
28
|
require 'rox/core/error_handling/userspace_unhandled_error_invoker'
|
29
29
|
|
30
30
|
module Rox
|
@@ -49,6 +49,7 @@ module Rox
|
|
49
49
|
@last_configurations = nil
|
50
50
|
@internal_flags = nil
|
51
51
|
@push_updates_listener = nil
|
52
|
+
@analytics_client = nil
|
52
53
|
end
|
53
54
|
|
54
55
|
def userspace_unhandled_error_handler=(handler)
|
@@ -72,7 +73,10 @@ module Rox
|
|
72
73
|
# TODO: Analytics.Analytics.Initialize(deviceProperties.RolloutKey, deviceProperties)
|
73
74
|
@internal_flags = InternalFlags.new(@experiment_repository, @parser, @rox_options)
|
74
75
|
|
75
|
-
|
76
|
+
if !@rox_options.self_managed? && roxy_path.nil?
|
77
|
+
@analytics_client = create_analytics_client(device_properties)
|
78
|
+
end
|
79
|
+
|
76
80
|
# TODO: impressionInvoker = new ImpressionInvoker(internalFlags, customPropertyRepository, deviceProperties, Analytics.Analytics.Client, roxyPath != null);
|
77
81
|
@impression_invoker = ImpressionInvoker.new(@internal_flags, @custom_property_repository, device_properties,
|
78
82
|
@analytics_client, !roxy_path.nil?, @user_unhandled_error_invoker)
|
@@ -108,7 +112,11 @@ module Rox
|
|
108
112
|
configuration_fetched_handler = nil
|
109
113
|
configuration_fetched_handler = @rox_options.configuration_fetched_handler unless @rox_options.nil?
|
110
114
|
|
111
|
-
@configuration_fetched_invoker.
|
115
|
+
@configuration_fetched_invoker.register_start_stop_push(proc do |args|
|
116
|
+
start_or_stop_push_updated_listener unless args.fetcher_status == FetcherStatus::ERROR_FETCHED_FAILED
|
117
|
+
end)
|
118
|
+
|
119
|
+
@configuration_fetched_invoker.register_fetched_handler(&configuration_fetched_handler)
|
112
120
|
|
113
121
|
@thread = Thread.new do
|
114
122
|
Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception)
|
@@ -189,13 +197,6 @@ module Rox
|
|
189
197
|
@custom_property_repository.add_custom_property_if_not_exists(property)
|
190
198
|
end
|
191
199
|
|
192
|
-
def wrap_configuration_fetched_handler(&handler)
|
193
|
-
lambda do |args|
|
194
|
-
start_or_stop_push_updated_listener unless args.fetcher_status == FetcherStatus::ERROR_FETCHED_FAILED
|
195
|
-
handler&.call(args)
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
200
|
def start_or_stop_push_updated_listener
|
200
201
|
if @internal_flags.enabled?('rox.internal.pushUpdates')
|
201
202
|
if @push_updates_listener.nil?
|
@@ -229,6 +230,12 @@ module Rox
|
|
229
230
|
raise ArgumentError, 'Illegal Rollout api key'
|
230
231
|
end
|
231
232
|
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def create_analytics_client(device_properties)
|
237
|
+
Rox::Core::Analytics::Client.new(device_properties)
|
238
|
+
end
|
232
239
|
end
|
233
240
|
end
|
234
241
|
end
|
@@ -26,7 +26,9 @@ module Rox
|
|
26
26
|
|
27
27
|
def value(context = nil)
|
28
28
|
merged_context = MergedContext.new(@parser&.global_context, context)
|
29
|
-
internal_value(merged_context, false)
|
29
|
+
return_value = internal_value(merged_context, false)
|
30
|
+
send_impressions(return_value, merged_context)
|
31
|
+
return_value
|
30
32
|
end
|
31
33
|
|
32
34
|
def internal_enabled?(context, nil_instead_of_default = false)
|
@@ -43,7 +43,9 @@ module Rox
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def value(context = nil)
|
46
|
-
internal_value(context, false)
|
46
|
+
return_value = internal_value(context, false)
|
47
|
+
send_impressions(return_value, context)
|
48
|
+
return_value
|
47
49
|
end
|
48
50
|
|
49
51
|
def internal_value(context, nil_instead_of_default, evaluated_type = String)
|
@@ -59,7 +61,6 @@ module Rox
|
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
62
|
-
send_impressions(return_value, merged_context)
|
63
64
|
return_value
|
64
65
|
end
|
65
66
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
module Rox
|
2
2
|
module Core
|
3
3
|
class UserspaceHandlerException < StandardError
|
4
|
-
|
4
|
+
attr_accessor :exception_source, :exception_trigger, :original_exception
|
5
|
+
def initialize(exception_source, exception_trigger, original_exception)
|
5
6
|
@exception_source = exception_source
|
6
7
|
@exception_trigger = exception_trigger
|
7
|
-
@
|
8
|
+
@original_exception = original_exception
|
8
9
|
super('user unhandled exception in roxx expression')
|
9
10
|
end
|
10
11
|
end
|
@@ -18,7 +18,7 @@ module Rox
|
|
18
18
|
@mutex = Mutex.new
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def call_analytics_gateway(reporting_value, stickiness_property, context)
|
22
22
|
begin
|
23
23
|
analytics_enabled = @internal_flags.enabled?('rox.internal.analytics')
|
24
24
|
if analytics_enabled && !@is_roxy
|
@@ -29,9 +29,9 @@ module Rox
|
|
29
29
|
distinct_id = prop_value if prop_value.instance_of?(String)
|
30
30
|
end
|
31
31
|
|
32
|
-
event_time =
|
32
|
+
event_time = DateTime.now.strftime('%Q').to_i
|
33
33
|
begin
|
34
|
-
event_time = ENV['rox.analytics.ms'].to_i
|
34
|
+
event_time = ENV['rox.analytics.ms'].to_i if ENV['rox.analytics.ms']
|
35
35
|
rescue StandardError
|
36
36
|
end
|
37
37
|
|
@@ -39,7 +39,6 @@ module Rox
|
|
39
39
|
flag: reporting_value.name,
|
40
40
|
value: reporting_value.value,
|
41
41
|
distinctId: distinct_id,
|
42
|
-
experimentVersion: '0',
|
43
42
|
type: 'IMPRESSION',
|
44
43
|
time: event_time
|
45
44
|
})
|
@@ -47,7 +46,10 @@ module Rox
|
|
47
46
|
rescue StandardError => ex
|
48
47
|
Logging.logger.error('Failed to send analytics', ex)
|
49
48
|
end
|
49
|
+
end
|
50
50
|
|
51
|
+
def invoke(reporting_value, stickiness_property, context)
|
52
|
+
call_analytics_gateway(reporting_value, stickiness_property, context)
|
51
53
|
raise_impression_event(ImpressionArgs.new(reporting_value, context))
|
52
54
|
end
|
53
55
|
|
@@ -63,11 +65,13 @@ module Rox
|
|
63
65
|
handlers = @impression_handlers.clone
|
64
66
|
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
handlers.each do |handler|
|
69
|
+
begin
|
70
|
+
handler.call(args)
|
71
|
+
rescue StandardError => e
|
72
|
+
@user_unhandled_error_invoker.invoke(handler, ExceptionTrigger::IMPRESSION_HANDLER, e)
|
73
|
+
Logging.logger.error('Impresssion handler exception', e)
|
74
|
+
end
|
71
75
|
end
|
72
76
|
end
|
73
77
|
end
|
@@ -83,9 +83,10 @@ module Rox
|
|
83
83
|
|
84
84
|
def self.state_payload(seralized_flag_repository, serialized_custom_property_repository, device_properties, dev_mode_secret)
|
85
85
|
values = {}
|
86
|
+
# be careful adding here properties, md5 signature uses all props
|
87
|
+
# to add more payload data, please change the md5_signature to use only the relevant properties (the current ones creating the payload)
|
86
88
|
values[PropertyType::APP_KEY.name] = device_properties.all_properties[PropertyType::APP_KEY.name]
|
87
89
|
values[PropertyType::PLATFORM.name] = device_properties.all_properties[PropertyType::PLATFORM.name]
|
88
|
-
values.merge!(device_properties.all_properties)
|
89
90
|
values.merge!({ feature_flags: seralized_flag_repository })
|
90
91
|
values.merge!({ custom_properties: serialized_custom_property_repository })
|
91
92
|
values['devModeSecret'] = dev_mode_secret
|
@@ -38,13 +38,13 @@ module Rox
|
|
38
38
|
def get_value_from_dynamic_property_rule_handler(prop_name, context)
|
39
39
|
@dynamic_property_rule_handler.call(prop_name, context)
|
40
40
|
rescue StandardError => e
|
41
|
-
raise Rox::Core::UserspaceHandlerException,
|
41
|
+
raise Rox::Core::UserspaceHandlerException.new(@dynamic_property_rule_handler, ExceptionTrigger::DYNAMIC_PROPERTIES_RULE, e)
|
42
42
|
end
|
43
43
|
|
44
44
|
def get_value_from_property(property, context)
|
45
45
|
property.value(context)
|
46
46
|
rescue StandardError => e
|
47
|
-
raise Rox::Core::UserspaceHandlerException,
|
47
|
+
raise Rox::Core::UserspaceHandlerException.new(property.block, ExceptionTrigger::CUSTOM_PROPERTY_GENERATOR, e)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
data/lib/rox/core/roxx/parser.rb
CHANGED
@@ -50,7 +50,7 @@ module Rox
|
|
50
50
|
|
51
51
|
EvaluationResult.new(result)
|
52
52
|
rescue Rox::Core::UserspaceHandlerException => e
|
53
|
-
@user_unhandled_error_invoker.invoke(e.exception_source, e.exception_trigger, e.
|
53
|
+
@user_unhandled_error_invoker.invoke(e.exception_source, e.exception_trigger, e.original_exception)
|
54
54
|
Logging.logger.warn("Roxx Exception: Failed evaluate expression, user unhandled expression: #{e}")
|
55
55
|
EvaluationResult.new(nil)
|
56
56
|
rescue StandardError => e
|
@@ -45,6 +45,10 @@ module Rox
|
|
45
45
|
@core.register(rox_container)
|
46
46
|
end
|
47
47
|
|
48
|
+
def register_with_namespace(namespace, rox_container)
|
49
|
+
@core.register_with_namespace(namespace, rox_container)
|
50
|
+
end
|
51
|
+
|
48
52
|
def use_userspace_unhandled_error_handler(&handler)
|
49
53
|
@core.userspace_unhandled_error_handler = handler
|
50
54
|
end
|
data/lib/rox/version.rb
CHANGED
data/rox.gemspec
CHANGED
@@ -23,10 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.required_ruby_version = '>= 2.3'
|
24
24
|
|
25
25
|
spec.add_runtime_dependency 'em-eventsource', '~> 0.3.2'
|
26
|
-
spec.add_runtime_dependency 'analytics-ruby', '~> 2.0.0'
|
27
26
|
|
28
|
-
|
29
|
-
spec.add_development_dependency 'bundler', '~> 2.2.3'
|
27
|
+
spec.add_development_dependency 'bundler', '~> 2.3.3'
|
30
28
|
spec.add_development_dependency 'minitest', '~> 5.11'
|
31
29
|
spec.add_development_dependency 'pry-byebug', '~> 3.7.0'
|
32
30
|
spec.add_development_dependency 'rake', '~> 12.3'
|