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
@@ -28,7 +28,7 @@ module Rox
|
|
28
28
|
request.body = JSON.dump(payload)
|
29
29
|
end
|
30
30
|
|
31
|
-
resp = Net::HTTP.start(uri.hostname, uri.port, :
|
31
|
+
resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
32
32
|
http.request(request)
|
33
33
|
end
|
34
34
|
|
@@ -5,25 +5,25 @@ require 'rox/core/consts/property_type'
|
|
5
5
|
module Rox
|
6
6
|
module Core
|
7
7
|
class RequestConfigurationBuilder
|
8
|
-
def initialize(sdk_settings, buid, device_properties
|
8
|
+
def initialize(sdk_settings, buid, device_properties)
|
9
9
|
@sdk_settings = sdk_settings
|
10
10
|
@buid = buid
|
11
11
|
@device_properties = device_properties
|
12
|
-
@
|
12
|
+
@rox_options = device_properties.rox_options
|
13
13
|
end
|
14
14
|
|
15
15
|
def build_for_roxy
|
16
|
-
roxy_endpoint = URI.join(@roxy_url, Rox::Core::Environment.roxy_internal_path).to_s
|
16
|
+
roxy_endpoint = URI.join(@rox_options.roxy_url, Rox::Core::Environment.roxy_internal_path).to_s
|
17
17
|
build_request_with_full_params(roxy_endpoint)
|
18
18
|
end
|
19
19
|
|
20
20
|
def build_for_cdn
|
21
21
|
RequestData.new("#{Rox::Core::Environment.cdn_path}/#{relative_path}",
|
22
|
-
|
22
|
+
Rox::Core::PropertyType::DISTINCT_ID.name => @device_properties.distinct_id)
|
23
23
|
end
|
24
24
|
|
25
25
|
def build_for_api
|
26
|
-
build_request_with_full_params("#{Rox::Core::Environment.api_path}/#{relative_path}")
|
26
|
+
build_request_with_full_params("#{Rox::Core::Environment.api_path(@rox_options.self_managed_options&.server_url)}/#{relative_path}")
|
27
27
|
end
|
28
28
|
|
29
29
|
def relative_path
|
@@ -6,7 +6,7 @@ module Rox
|
|
6
6
|
class Response
|
7
7
|
attr_accessor :status_code, :text, :content_type
|
8
8
|
|
9
|
-
def initialize(status_code, text, content_type='application/octet-stream')
|
9
|
+
def initialize(status_code, text, content_type = 'application/octet-stream')
|
10
10
|
@status_code = status_code
|
11
11
|
@text = text
|
12
12
|
@content_type = content_type
|
@@ -21,13 +21,11 @@ module Rox
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def missing_via_response_body?
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
false
|
30
|
-
end
|
24
|
+
@status_code == 200 && @content_type == 'application/json' &&
|
25
|
+
[404, '404'].include?(JSON.parse(text)['result'])
|
26
|
+
rescue JSON::ParserError
|
27
|
+
Logging.logger.error("Failed to parse JSON response: #{text}")
|
28
|
+
false
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
@@ -18,7 +18,7 @@ module Rox
|
|
18
18
|
@flag_repository = flag_repository
|
19
19
|
@custom_property_repository = custom_property_repository
|
20
20
|
@request = Request.new
|
21
|
-
@debouncer = Debouncer.new(1,
|
21
|
+
@debouncer = Debouncer.new(1, proc { send })
|
22
22
|
end
|
23
23
|
|
24
24
|
def delayed_send
|
@@ -30,21 +30,35 @@ module Rox
|
|
30
30
|
serialized_feature_flags = StateSender.seralize_flag_repository(@flag_repository)
|
31
31
|
serialized_custom_properties = StateSender.serialize_custom_properties_repository(@custom_property_repository)
|
32
32
|
|
33
|
-
state_payload = StateSender.state_payload(serialized_feature_flags, serialized_custom_properties,
|
34
|
-
|
33
|
+
state_payload = StateSender.state_payload(serialized_feature_flags, serialized_custom_properties,
|
34
|
+
@device_properties, @sdk_settings.dev_mode_secret)
|
35
|
+
md5_signature = StateSender.md5_signature(serialized_feature_flags, serialized_custom_properties,
|
36
|
+
@device_properties, @sdk_settings.dev_mode_secret)
|
35
37
|
|
36
|
-
|
37
|
-
|
38
|
-
Rox::Core::Logging.logger.debug("Failed to fetch state from CDN. Sending state to API...")
|
39
|
-
response = send_state_to_API(rollout_key, md5_signature, state_payload)
|
38
|
+
unless @device_properties.rox_options.self_managed?
|
39
|
+
response = get_state_from_CDN(rollout_key, md5_signature)
|
40
40
|
if response.success?
|
41
|
-
Rox::Core::Logging.logger.debug(
|
41
|
+
Rox::Core::Logging.logger.debug('Successfully fetched state from CDN')
|
42
|
+
return
|
42
43
|
else
|
43
|
-
Rox::Core::Logging.logger.debug(
|
44
|
+
Rox::Core::Logging.logger.debug('Failed to fetch state from CDN. Sending state to API...')
|
44
45
|
end
|
45
|
-
else
|
46
|
-
Rox::Core::Logging.logger.debug("Successfully fetched state from CDN")
|
47
46
|
end
|
47
|
+
|
48
|
+
response = send_state_to_API(rollout_key, md5_signature, state_payload)
|
49
|
+
message = if response.success?
|
50
|
+
'Successfully sent state to API.'
|
51
|
+
else
|
52
|
+
'Failed to send state to API.'
|
53
|
+
end
|
54
|
+
Rox::Core::Logging.logger.debug(message)
|
55
|
+
end
|
56
|
+
|
57
|
+
def dump_state
|
58
|
+
serialized_feature_flags = StateSender.seralize_flag_repository_with_values(@flag_repository)
|
59
|
+
serialized_custom_properties = StateSender.serialize_custom_properties_repository_with_values(@custom_property_repository)
|
60
|
+
StateSender.state_payload(serialized_feature_flags, serialized_custom_properties, @device_properties,
|
61
|
+
@sdk_settings.dev_mode_secret)
|
48
62
|
end
|
49
63
|
|
50
64
|
def get_state_from_CDN(rollout_key, md5_signature)
|
@@ -54,11 +68,14 @@ module Rox
|
|
54
68
|
end
|
55
69
|
|
56
70
|
def send_state_to_API(rollout_key, md5_signature, state_payload)
|
57
|
-
|
71
|
+
@request.send_post(
|
72
|
+
"#{Rox::Core::Environment.state_api_path(@device_properties.rox_options.self_managed_options&.server_url)}/#{rollout_key}/#{md5_signature}", state_payload
|
73
|
+
)
|
58
74
|
end
|
59
75
|
|
60
76
|
def self.md5_signature(serialized_feature_flags, serialized_custom_properties, device_properties, dev_mode_secret)
|
61
|
-
values =
|
77
|
+
values = state_payload(serialized_feature_flags, serialized_custom_properties, device_properties,
|
78
|
+
dev_mode_secret).values
|
62
79
|
|
63
80
|
hash = Digest::MD5.hexdigest(values.join('|'))
|
64
81
|
hash.upcase
|
@@ -66,18 +83,19 @@ module Rox
|
|
66
83
|
|
67
84
|
def self.state_payload(seralized_flag_repository, serialized_custom_property_repository, device_properties, dev_mode_secret)
|
68
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)
|
69
88
|
values[PropertyType::APP_KEY.name] = device_properties.all_properties[PropertyType::APP_KEY.name]
|
70
89
|
values[PropertyType::PLATFORM.name] = device_properties.all_properties[PropertyType::PLATFORM.name]
|
71
|
-
values.merge!(
|
72
|
-
values.merge!({
|
73
|
-
values
|
74
|
-
values["devModeSecret"] = dev_mode_secret
|
90
|
+
values.merge!({ feature_flags: seralized_flag_repository })
|
91
|
+
values.merge!({ custom_properties: serialized_custom_property_repository })
|
92
|
+
values['devModeSecret'] = dev_mode_secret
|
75
93
|
values
|
76
94
|
end
|
77
95
|
|
78
96
|
def self.seralize_flag_repository(flag_repository)
|
79
97
|
flags = []
|
80
|
-
flag_repository.all_flags.sort_by
|
98
|
+
flag_repository.all_flags.sort_by(&:name).each do |f|
|
81
99
|
flags << {
|
82
100
|
name: f.name,
|
83
101
|
defaultValue: f.default_value,
|
@@ -87,11 +105,37 @@ module Rox
|
|
87
105
|
flags
|
88
106
|
end
|
89
107
|
|
108
|
+
def self.seralize_flag_repository_with_values(flag_repository)
|
109
|
+
flags = []
|
110
|
+
flag_repository.all_flags.sort_by(&:name).each do |f|
|
111
|
+
flags << {
|
112
|
+
name: f.name,
|
113
|
+
enabled: f.enabled?,
|
114
|
+
defaultValue: f.default_value,
|
115
|
+
options: f.options
|
116
|
+
}
|
117
|
+
end
|
118
|
+
flags
|
119
|
+
end
|
120
|
+
|
90
121
|
def self.serialize_custom_properties_repository(custom_property_repository)
|
91
122
|
properties = []
|
92
|
-
custom_property_repository.all_custom_properties.sort_by
|
123
|
+
custom_property_repository.all_custom_properties.sort_by(&:name).each do |p|
|
124
|
+
properties << {
|
125
|
+
name: p.name,
|
126
|
+
type: p.type.type,
|
127
|
+
externalType: p.type.external_type
|
128
|
+
}
|
129
|
+
end
|
130
|
+
properties
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.serialize_custom_properties_repository_with_values(custom_property_repository)
|
134
|
+
properties = []
|
135
|
+
custom_property_repository.all_custom_properties.sort_by(&:name).each do |p|
|
93
136
|
properties << {
|
94
137
|
name: p.name,
|
138
|
+
value: p.value(nil),
|
95
139
|
type: p.type.type,
|
96
140
|
externalType: p.type.external_type
|
97
141
|
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'em-eventsource'
|
2
2
|
|
3
3
|
module Rox
|
4
4
|
module Core
|
@@ -15,7 +15,7 @@ module Rox
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def start
|
18
|
-
sse_url = @listen_url.chomp('/')
|
18
|
+
sse_url = "#{@listen_url.chomp('/')}/#{@app_key}"
|
19
19
|
@thread = Thread.new do
|
20
20
|
EM.run do
|
21
21
|
source = EventMachine::EventSource.new(sse_url)
|
@@ -37,9 +37,9 @@ module Rox
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def stop
|
40
|
-
@thread
|
40
|
+
@thread&.terminate
|
41
41
|
@thread = nil
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
45
|
-
end
|
45
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'rox/server/rox_options'
|
3
|
+
require 'rox/server/client/sdk_settings'
|
4
|
+
require 'rox/server/client/server_properties'
|
5
|
+
require 'rox/core/properties/device_property'
|
6
|
+
require 'rox/core/properties/custom_property'
|
7
|
+
require 'rox/core/properties/custom_property_type'
|
8
|
+
require 'rox/core/consts/property_type'
|
9
|
+
|
10
|
+
module Rox
|
11
|
+
module Core
|
12
|
+
# Consolidates creation of all the internal properties
|
13
|
+
class PropertyFactory
|
14
|
+
def initialize(server_properties)
|
15
|
+
@server_properties = server_properties
|
16
|
+
end
|
17
|
+
|
18
|
+
def platform
|
19
|
+
property = Rox::Core::PropertyType::PLATFORM
|
20
|
+
type = Rox::Core::CustomPropertyType::STRING
|
21
|
+
|
22
|
+
Rox::Core::DeviceProperty.new(
|
23
|
+
property.name,
|
24
|
+
type,
|
25
|
+
@server_properties.get(property)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def app_release
|
30
|
+
property = Rox::Core::PropertyType::APP_RELEASE
|
31
|
+
type = Rox::Core::CustomPropertyType::SEMVER
|
32
|
+
|
33
|
+
Rox::Core::DeviceProperty.new(
|
34
|
+
property.name,
|
35
|
+
type,
|
36
|
+
@server_properties.get(property)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def distinct_id
|
41
|
+
property = Rox::Core::PropertyType::DISTINCT_ID
|
42
|
+
type = Rox::Core::CustomPropertyType::STRING
|
43
|
+
|
44
|
+
Rox::Core::DeviceProperty.new(
|
45
|
+
property.name,
|
46
|
+
type
|
47
|
+
) do |_|
|
48
|
+
SecureRandom.uuid
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def internal_real_platform
|
53
|
+
property = Rox::Core::PropertyType::PLATFORM
|
54
|
+
type = Rox::Core::CustomPropertyType::STRING
|
55
|
+
|
56
|
+
Rox::Core::DeviceProperty.new(
|
57
|
+
'internal.realPlatform',
|
58
|
+
type,
|
59
|
+
@server_properties.get(property)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def internal_custom_platform
|
64
|
+
property = Rox::Core::PropertyType::PLATFORM
|
65
|
+
type = Rox::Core::CustomPropertyType::STRING
|
66
|
+
|
67
|
+
Rox::Core::DeviceProperty.new(
|
68
|
+
'internal.customPlatform',
|
69
|
+
type,
|
70
|
+
@server_properties.get(property)
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def internal_app_key
|
75
|
+
type = Rox::Core::CustomPropertyType::STRING
|
76
|
+
|
77
|
+
Rox::Core::DeviceProperty.new(
|
78
|
+
'internal.appKey',
|
79
|
+
type,
|
80
|
+
@server_properties.rollout_key
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def internal_distinct_id
|
85
|
+
type = Rox::Core::CustomPropertyType::STRING
|
86
|
+
|
87
|
+
Rox::Core::DeviceProperty.new(
|
88
|
+
"internal.#{Rox::Core::PropertyType::DISTINCT_ID.name}",
|
89
|
+
type
|
90
|
+
) do
|
91
|
+
SecureRandom.uuid
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def internal_lib_version
|
96
|
+
type = Rox::Core::CustomPropertyType::SEMVER
|
97
|
+
|
98
|
+
Rox::Core::DeviceProperty.new(
|
99
|
+
'internal.lib_version',
|
100
|
+
type,
|
101
|
+
@server_properties.lib_version
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
def internal_api_version
|
106
|
+
property = Rox::Core::PropertyType::API_VERSION
|
107
|
+
type = Rox::Core::CustomPropertyType::SEMVER
|
108
|
+
|
109
|
+
Rox::Core::DeviceProperty.new(
|
110
|
+
'internal.api_version',
|
111
|
+
type,
|
112
|
+
property
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
# rubocop:disable Metrics/MethodLength
|
117
|
+
def all_properties
|
118
|
+
[
|
119
|
+
platform,
|
120
|
+
app_release,
|
121
|
+
distinct_id,
|
122
|
+
internal_real_platform,
|
123
|
+
internal_custom_platform,
|
124
|
+
internal_app_key,
|
125
|
+
internal_distinct_id,
|
126
|
+
internal_lib_version,
|
127
|
+
internal_api_version
|
128
|
+
]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -7,25 +7,26 @@ module Rox
|
|
7
7
|
@mutex = Mutex.new
|
8
8
|
end
|
9
9
|
|
10
|
-
def register_instance(container,
|
11
|
-
raise ArgumentError, 'A namespace cannot be null' if
|
10
|
+
def register_instance(container, namespace)
|
11
|
+
raise ArgumentError, 'A namespace cannot be null' if namespace.nil?
|
12
12
|
|
13
13
|
@mutex.synchronize do
|
14
|
-
|
14
|
+
if @namespaces.include?(namespace)
|
15
|
+
raise ArgumentError,
|
16
|
+
"A container with the given namespace (#{namespace}) has already been registered"
|
17
|
+
end
|
15
18
|
end
|
16
19
|
|
17
|
-
@namespaces <<
|
20
|
+
@namespaces << namespace
|
18
21
|
|
19
|
-
container.instance_variables
|
22
|
+
container.instance_variables.each do |attribute_name|
|
20
23
|
begin
|
21
24
|
value = container.instance_variable_get(attribute_name)
|
22
|
-
if value
|
23
|
-
var_name = attribute_name.to_s
|
24
|
-
|
25
|
-
if
|
26
|
-
|
27
|
-
end
|
28
|
-
@flag_repository.add_flag(value, ns == '' ? var_name : "#{ns}.#{var_name}")
|
25
|
+
if !value.nil? && value.is_a?(RoxString)
|
26
|
+
var_name = attribute_name.to_s
|
27
|
+
# removing the attribute starting @ (if [always] exists)
|
28
|
+
var_name[0] = '' if var_name[0] == '@'
|
29
|
+
@flag_repository.add_flag(value, namespace == '' ? var_name : "#{namespace}.#{var_name}")
|
29
30
|
end
|
30
31
|
rescue StandardError
|
31
32
|
next
|
@@ -13,6 +13,7 @@ module Rox
|
|
13
13
|
|
14
14
|
def report(message, ex)
|
15
15
|
return if @device_properties.rollout_environment == 'LOCAL'
|
16
|
+
return if @device_properties.rox_options.self_managed?
|
16
17
|
|
17
18
|
Logging.logger.error("Error report: #{message}", ex)
|
18
19
|
|
@@ -33,8 +34,8 @@ module Rox
|
|
33
34
|
begin
|
34
35
|
@request.send_post(ErrorReporter::BUGSNAG_NOTIFY_URL, payload)
|
35
36
|
Logging.logger.debug('Bugsnag error report was sent')
|
36
|
-
rescue StandardError =>
|
37
|
-
Logging.logger.error('Failed to send bugsnag error ',
|
37
|
+
rescue StandardError => e
|
38
|
+
Logging.logger.error('Failed to send bugsnag error ', e)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
@@ -49,13 +50,13 @@ module Rox
|
|
49
50
|
|
50
51
|
def add_metadata(message, ev)
|
51
52
|
inner_data = {
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
'message' => message,
|
54
|
+
'deviceId' => @device_properties.distinct_id,
|
55
|
+
'buid' => @buid.to_s
|
55
56
|
}
|
56
57
|
|
57
58
|
metadata = {
|
58
|
-
|
59
|
+
'data' => inner_data
|
59
60
|
}
|
60
61
|
|
61
62
|
ev['metaData'] = metadata
|
@@ -88,15 +89,15 @@ module Rox
|
|
88
89
|
|
89
90
|
def add_notifier(payload)
|
90
91
|
notifier = {
|
91
|
-
|
92
|
-
|
92
|
+
'name' => 'Rollout Ruby SDK',
|
93
|
+
'version' => @device_properties.lib_version
|
93
94
|
}
|
94
95
|
payload['notifier'] = notifier
|
95
96
|
end
|
96
97
|
|
97
98
|
def add_user(id, rollout_key, ev)
|
98
99
|
user = {
|
99
|
-
|
100
|
+
id => rollout_key
|
100
101
|
}
|
101
102
|
ev['user'] = user
|
102
103
|
end
|
@@ -121,13 +122,13 @@ module Rox
|
|
121
122
|
|
122
123
|
def add_app(ev)
|
123
124
|
app = {
|
124
|
-
|
125
|
-
|
125
|
+
'releaseStage' => @device_properties.rollout_environment,
|
126
|
+
'version' => @device_properties.lib_version
|
126
127
|
}
|
127
128
|
ev['app'] = app
|
128
129
|
end
|
129
130
|
|
130
|
-
STACK_TRACE_LINE_REGEX = /^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')
|
131
|
+
STACK_TRACE_LINE_REGEX = /^((?:[a-zA-Z]:)?[^:]+):(\d+)(?::in `([^']+)')?$/.freeze
|
131
132
|
|
132
133
|
def parse_stack_trace(stack_trace)
|
133
134
|
stack = []
|
@@ -149,4 +150,4 @@ module Rox
|
|
149
150
|
end
|
150
151
|
end
|
151
152
|
end
|
152
|
-
end
|
153
|
+
end
|
@@ -5,9 +5,7 @@ module Rox
|
|
5
5
|
@experiments = []
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
@experiments = experiments
|
10
|
-
end
|
8
|
+
attr_writer :experiments
|
11
9
|
|
12
10
|
def experiment_by_flag(flag_name)
|
13
11
|
@experiments.detect { |e| e.flags.include?(flag_name) }
|
@@ -18,4 +16,4 @@ module Rox
|
|
18
16
|
end
|
19
17
|
end
|
20
18
|
end
|
21
|
-
end
|
19
|
+
end
|
@@ -2,29 +2,29 @@ module Rox
|
|
2
2
|
module Core
|
3
3
|
class FlagRepository
|
4
4
|
def initialize
|
5
|
-
@
|
5
|
+
@strings = {}
|
6
6
|
@flag_added_handlers = []
|
7
7
|
@mutex = Mutex.new
|
8
8
|
@handlers_mutex = Mutex.new
|
9
9
|
end
|
10
10
|
|
11
|
-
def add_flag(
|
12
|
-
|
11
|
+
def add_flag(string, name)
|
12
|
+
string.name = name if string.name.nil? || string.name.empty?
|
13
13
|
@mutex.synchronize do
|
14
|
-
@
|
14
|
+
@strings[name] = string
|
15
15
|
end
|
16
|
-
raise_flag_added_event(
|
16
|
+
raise_flag_added_event(string)
|
17
17
|
end
|
18
18
|
|
19
19
|
def flag(name)
|
20
20
|
@mutex.synchronize do
|
21
|
-
return @
|
21
|
+
return @strings[name]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
def all_flags
|
26
26
|
@mutex.synchronize do
|
27
|
-
return @
|
27
|
+
return @strings.values
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -11,13 +11,13 @@ module Rox
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def extend
|
14
|
-
@parser.add_operator('mergeSeed') do |
|
14
|
+
@parser.add_operator('mergeSeed') do |_parser, stack, _context|
|
15
15
|
seed1 = stack.pop
|
16
16
|
seed2 = stack.pop
|
17
17
|
stack.push("#{seed1}.#{seed2}")
|
18
18
|
end
|
19
19
|
|
20
|
-
@parser.add_operator('isInPercentage') do |
|
20
|
+
@parser.add_operator('isInPercentage') do |_parser, stack, _context|
|
21
21
|
percentage = stack.pop
|
22
22
|
seed = stack.pop
|
23
23
|
|
@@ -27,7 +27,7 @@ module Rox
|
|
27
27
|
stack.push(bucket <= percentage)
|
28
28
|
end
|
29
29
|
|
30
|
-
@parser.add_operator('isInPercentageRange') do |
|
30
|
+
@parser.add_operator('isInPercentageRange') do |_parser, stack, _context|
|
31
31
|
percentage_low = stack.pop
|
32
32
|
percentage_high = stack.pop
|
33
33
|
seed = stack.pop
|
@@ -42,17 +42,17 @@ module Rox
|
|
42
42
|
@parser.add_operator('flagValue') do |parser, stack, context|
|
43
43
|
feature_flag_identifier = stack.pop
|
44
44
|
result = Flag::FLAG_FALSE_VALUE
|
45
|
-
|
45
|
+
string = @flags_repository.flag(feature_flag_identifier)
|
46
46
|
|
47
|
-
if
|
48
|
-
result = variant.value(context)
|
49
|
-
else
|
47
|
+
if string.nil?
|
50
48
|
flags_experiment = @experiment_repository.experiment_by_flag(feature_flag_identifier)
|
51
49
|
|
52
50
|
if !flags_experiment.nil? && !flags_experiment.condition.nil? && !flags_experiment.condition.empty?
|
53
51
|
experiment_eval_result = parser.evaluate_expression(flags_experiment.condition, context).string_value
|
54
52
|
result = experiment_eval_result if !experiment_eval_result.nil? && !experiment_eval_result.empty?
|
55
53
|
end
|
54
|
+
else
|
55
|
+
result = string.value(context)
|
56
56
|
end
|
57
57
|
|
58
58
|
stack.push(result)
|
@@ -79,4 +79,4 @@ module Rox
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
82
|
-
end
|
82
|
+
end
|