rox-rollout 4.7.3 → 5.0.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/.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-server/run_server.sh +12 -0
- data/e2e-server/server.rb +158 -0
- 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/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 +88 -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 +61 -18
- 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 +80 -59
- data/lib/rox/version.rb +1 -1
- data/rox.gemspec +11 -9
- metadata +66 -37
- 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/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/e2e/rox_e2e_test.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'minitest/autorun'
|
|
2
2
|
require 'rox/server/rox_options'
|
|
3
3
|
require 'rox/server/rox_server'
|
|
4
4
|
require_relative 'container'
|
|
@@ -7,7 +7,7 @@ require_relative 'test_vars'
|
|
|
7
7
|
|
|
8
8
|
module E2E
|
|
9
9
|
class Logger
|
|
10
|
-
def debug(message,
|
|
10
|
+
def debug(message, _ex = nil)
|
|
11
11
|
puts 'Before Rox.Setup', message
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@ module E2E
|
|
|
15
15
|
puts 'Before Rox.Setup', message, ex
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def warn(message,
|
|
18
|
+
def warn(message, _ex = nil)
|
|
19
19
|
puts 'Before Rox.Setup', message
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -30,8 +30,8 @@ module E2E
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
impression_handler = proc do |e|
|
|
33
|
-
if !e.nil? && !e.reporting_value.nil?
|
|
34
|
-
TestVars.is_impression_raised = true
|
|
33
|
+
if !e.nil? && !e.reporting_value.nil? && (e.reporting_value.name == 'flag_for_impression')
|
|
34
|
+
TestVars.is_impression_raised = true
|
|
35
35
|
end
|
|
36
36
|
TestVars.impression_returned_args = e
|
|
37
37
|
end
|
|
@@ -44,7 +44,7 @@ module E2E
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
@@container = Container.new
|
|
47
|
-
Rox::Server::RoxServer.register(
|
|
47
|
+
Rox::Server::RoxServer.register(@@container)
|
|
48
48
|
CustomProps.create_custom_props
|
|
49
49
|
Rox::Server::RoxServer.setup('5b82864ebc3aec37aff1fdd5', option).join
|
|
50
50
|
|
|
@@ -56,7 +56,7 @@ module E2E
|
|
|
56
56
|
assert_equal false, @@container.simple_flag_overwritten.enabled?
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
-
def
|
|
59
|
+
def test_string
|
|
60
60
|
assert_equal 'red', @@container.variant.value
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -87,9 +87,9 @@ module E2E
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
def
|
|
91
|
-
some_positive_context = {'isDuckAndCover' => true}
|
|
92
|
-
some_negative_context = {'isDuckAndCover' => false}
|
|
90
|
+
def test_string_with_context
|
|
91
|
+
some_positive_context = { 'isDuckAndCover' => true }
|
|
92
|
+
some_negative_context = { 'isDuckAndCover' => false }
|
|
93
93
|
|
|
94
94
|
assert_equal 'red', @@container.variant_with_context.value
|
|
95
95
|
|
|
@@ -121,18 +121,16 @@ module E2E
|
|
|
121
121
|
assert_equal true, TestVars.is_impression_raised
|
|
122
122
|
TestVars.is_impression_raised = false
|
|
123
123
|
|
|
124
|
-
context = {'var' => 'val'}
|
|
124
|
+
context = { 'var' => 'val' }
|
|
125
125
|
flag_impression_value = @@container.flag_for_impression_with_experiment_and_context.enabled?(context)
|
|
126
126
|
refute_nil TestVars.impression_returned_args
|
|
127
127
|
refute_nil TestVars.impression_returned_args.reporting_value
|
|
128
|
-
assert_equal
|
|
128
|
+
assert_equal true, TestVars.impression_returned_args.reporting_value.value
|
|
129
129
|
assert_equal true, flag_impression_value
|
|
130
|
-
assert_equal 'flag_for_impression_with_experiment_and_context',
|
|
130
|
+
assert_equal 'flag_for_impression_with_experiment_and_context',
|
|
131
|
+
TestVars.impression_returned_args.reporting_value.name
|
|
131
132
|
|
|
132
133
|
refute_nil TestVars.impression_returned_args
|
|
133
|
-
refute_nil TestVars.impression_returned_args.experiment
|
|
134
|
-
assert_equal '5b8f85ecbc3aec37aff20841', TestVars.impression_returned_args.experiment.identifier
|
|
135
|
-
assert_equal 'flag for impression with experiment and context', TestVars.impression_returned_args.experiment.name
|
|
136
134
|
|
|
137
135
|
assert_equal 'val', TestVars.impression_returned_args.context['var']
|
|
138
136
|
end
|
|
@@ -147,9 +145,9 @@ module E2E
|
|
|
147
145
|
assert_equal false, @@container.flag_for_dependency.enabled?
|
|
148
146
|
end
|
|
149
147
|
|
|
150
|
-
def
|
|
151
|
-
some_positive_context = {'isDuckAndCover' => true}
|
|
152
|
-
some_negative_context = {'isDuckAndCover' => false}
|
|
148
|
+
def test_string_dependency_with_context
|
|
149
|
+
some_positive_context = { 'isDuckAndCover' => true }
|
|
150
|
+
some_negative_context = { 'isDuckAndCover' => false }
|
|
153
151
|
|
|
154
152
|
assert_equal 'White', @@container.flag_color_dependent_with_context.value
|
|
155
153
|
assert_equal 'White', @@container.flag_color_dependent_with_context.value(some_negative_context)
|
data/e2e/test_vars.rb
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
module E2E
|
|
2
2
|
module TestVars
|
|
3
3
|
class << self
|
|
4
|
-
attr_accessor :is_computed_boolean_prop_called, :is_computed_string_prop_called, :is_computed_int_prop_called,
|
|
5
|
-
|
|
6
|
-
attr_accessor :is_impression_raised, :is_prop_for_target_group_for_dependency
|
|
7
|
-
|
|
8
|
-
attr_accessor :configuration_fetched_count, :impression_returned_args
|
|
4
|
+
attr_accessor :is_computed_boolean_prop_called, :is_computed_string_prop_called, :is_computed_int_prop_called,
|
|
5
|
+
:is_computed_float_prop_called, :is_computed_semver_prop_called, :target_group1, :target_group2, :is_impression_raised, :is_prop_for_target_group_for_dependency, :configuration_fetched_count, :impression_returned_args
|
|
9
6
|
end
|
|
10
7
|
|
|
11
8
|
@is_computed_boolean_prop_called = false
|
|
@@ -21,4 +18,4 @@ module E2E
|
|
|
21
18
|
@configuration_fetched_count = 0
|
|
22
19
|
@impression_returned_args = nil
|
|
23
20
|
end
|
|
24
|
-
end
|
|
21
|
+
end
|
data/example/local.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
2
|
+
|
|
3
|
+
require 'rox/server/rox_server'
|
|
4
|
+
require 'rox/server/rox_options'
|
|
5
|
+
|
|
6
|
+
API_HOST = 'http://localhost:8557'.freeze
|
|
7
|
+
APP_KEY = '600571e330819d4842999e4f'.freeze
|
|
8
|
+
DEV_MODE_SECRET = 'e56cda16749d8d0a9b91d34c'.freeze
|
|
9
|
+
|
|
10
|
+
class Flags
|
|
11
|
+
attr_accessor :boolean_flag, :string_flag, :int_flag, :double_flag
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
# Define the feature flags
|
|
15
|
+
@boolean_flag = Rox::Server::RoxFlag.new(true)
|
|
16
|
+
@string_flag = Rox::Server::RoxString.new('option 1', ['option 1', 'option 2', 'option 3'])
|
|
17
|
+
@int_flag = Rox::Server::RoxInt.new(1, [1, 2, 3])
|
|
18
|
+
@double_flag = Rox::Server::RoxDouble.new(4.0, [5.0, 6.0])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
flags = Flags.new
|
|
23
|
+
Rox::Server::RoxServer.register(flags)
|
|
24
|
+
|
|
25
|
+
options = Rox::Server::RoxOptions.new(
|
|
26
|
+
self_managed_options: Rox::Server::SelfManagedOptions.new(
|
|
27
|
+
server_url: API_HOST,
|
|
28
|
+
analytics_url: 'http://127.0.0.1:8787'
|
|
29
|
+
),
|
|
30
|
+
dev_mode_key: DEV_MODE_SECRET
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
Rox::Server::RoxServer.setup(APP_KEY, options)
|
|
34
|
+
|
|
35
|
+
puts "boolean_flag is #{flags.boolean_flag.enabled?}"
|
|
36
|
+
puts "boolean_flag is #{Rox::Server::RoxServer.dynamic_api.enabled?('boolean_flag', true)} (dynamic api)"
|
|
37
|
+
puts "boolean_flag is #{Rox::Server::RoxServer.dynamic_api.enabled?('boolean_flag', true)} (dynamic api)"
|
|
38
|
+
puts "string_flag is #{flags.string_flag.value}"
|
|
39
|
+
puts "int_flag is #{flags.int_flag.value}"
|
|
40
|
+
puts "double_flag is #{flags.double_flag.value}"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'segment/analytics'
|
|
2
|
+
|
|
3
|
+
module Rox
|
|
4
|
+
module Core
|
|
5
|
+
# Analytics based on segment client
|
|
6
|
+
# https://segment.com/docs/connections/sources/catalog/libraries/server/ruby/
|
|
7
|
+
class Analytics
|
|
8
|
+
attr_reader :client
|
|
9
|
+
|
|
10
|
+
def initialize(key)
|
|
11
|
+
@client = Segment::Analytics.new({
|
|
12
|
+
write_key: key,
|
|
13
|
+
on_error: proc { |_status, msg| print msg }
|
|
14
|
+
})
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/rox/core/client/buid.rb
CHANGED
|
@@ -6,13 +6,13 @@ module Rox
|
|
|
6
6
|
module Core
|
|
7
7
|
class BUID
|
|
8
8
|
BUID_GENERATORS = [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
PropertyType::PLATFORM,
|
|
10
|
+
PropertyType::APP_KEY,
|
|
11
|
+
PropertyType::LIB_VERSION,
|
|
12
|
+
PropertyType::API_VERSION
|
|
13
13
|
].freeze
|
|
14
14
|
|
|
15
|
-
def initialize(sdk_settings, device_properties,
|
|
15
|
+
def initialize(sdk_settings, device_properties, _flag_repository, _custom_property_repository)
|
|
16
16
|
@sdk_settings = sdk_settings
|
|
17
17
|
@device_properties = device_properties
|
|
18
18
|
@buid = nil
|
|
@@ -31,11 +31,11 @@ module Rox
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def query_string_parts
|
|
34
|
-
generators = BUID::BUID_GENERATORS.map {|pt, _| pt.name}
|
|
34
|
+
generators = BUID::BUID_GENERATORS.map { |pt, _| pt.name }
|
|
35
35
|
|
|
36
36
|
{
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
PropertyType::BUID.name => value,
|
|
38
|
+
PropertyType::BUID_GENERATORS_LIST.name => generators.join(',')
|
|
39
39
|
}
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -4,11 +4,17 @@ require 'rox/core/consts/build'
|
|
|
4
4
|
module Rox
|
|
5
5
|
module Core
|
|
6
6
|
class DeviceProperties
|
|
7
|
+
attr_reader :rox_options
|
|
8
|
+
|
|
7
9
|
def initialize(sdk_settings, rox_options)
|
|
8
10
|
@sdk_settings = sdk_settings
|
|
9
11
|
@rox_options = rox_options
|
|
10
12
|
end
|
|
11
13
|
|
|
14
|
+
def get(property)
|
|
15
|
+
all_properties[property.name]
|
|
16
|
+
end
|
|
17
|
+
|
|
12
18
|
def all_properties
|
|
13
19
|
{
|
|
14
20
|
PropertyType::PACKAGE_NAME.name => @rox_options.version,
|
|
@@ -20,7 +26,7 @@ module Rox
|
|
|
20
26
|
PropertyType::APP_RELEASE.name => @rox_options.version,
|
|
21
27
|
PropertyType::DISTINCT_ID.name => distinct_id,
|
|
22
28
|
PropertyType::APP_KEY.name => @sdk_settings.api_key,
|
|
23
|
-
PropertyType::PLATFORM.name => Build::PLATFORM
|
|
29
|
+
PropertyType::PLATFORM.name => Build::PLATFORM
|
|
24
30
|
}
|
|
25
31
|
end
|
|
26
32
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'rox/core/entities/flag'
|
|
2
|
+
require 'rox/server/flags/normalize_flag_type'
|
|
2
3
|
|
|
3
4
|
module Rox
|
|
4
5
|
module Core
|
|
@@ -9,28 +10,65 @@ module Rox
|
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def enabled?(name, default_value, context = nil)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
variant = @entities_provider.create_flag(default_value)
|
|
15
|
-
@flag_repository.add_flag(variant, name)
|
|
16
|
-
end
|
|
13
|
+
raise ArgumentError, 'flag name should be a string' unless name.is_a?(String)
|
|
14
|
+
raise ArgumentError, 'default value should be boolean' unless [true, false].include?(default_value)
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
string = @flag_repository.flag(name)
|
|
17
|
+
if string.nil?
|
|
18
|
+
string = @entities_provider.create_flag(default_value)
|
|
19
|
+
@flag_repository.add_flag(string, name)
|
|
20
|
+
end
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
merged_context = MergedContext.new(string.parser&.global_context, context)
|
|
23
|
+
return_value = unless string.is_a?(Flag)
|
|
24
|
+
default_value
|
|
25
|
+
else
|
|
26
|
+
is_enabled = string.internal_enabled?(context, nil_instead_of_default: true)
|
|
27
|
+
is_enabled.nil? ? default_value : is_enabled
|
|
28
|
+
end
|
|
29
|
+
string.send_impressions(return_value, merged_context)
|
|
30
|
+
return_value
|
|
22
31
|
end
|
|
23
32
|
|
|
24
33
|
def value(name, default_value, context = nil, options = [])
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
generic_value(name, default_value, context, options)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def int_value(name, default_value, context = nil, options = [])
|
|
38
|
+
create_method = @entities_provider.method(:create_int)
|
|
39
|
+
normalize_method = Rox::Server::NormalizeFlagType.method(:normalize_int)
|
|
40
|
+
generic_value(name, default_value, context, options, Integer, create_method, normalize_method)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def double_value(name, default_value, context = nil, options = [])
|
|
44
|
+
create_method = @entities_provider.method(:create_double)
|
|
45
|
+
normalize_method = Rox::Server::NormalizeFlagType.method(:normalize_float)
|
|
46
|
+
generic_value(name, default_value, context, options, Float, create_method, normalize_method)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def generic_value(name, default_value, context, options, flag_type = String, create_method = nil, normalize_method = Rox::Server::NormalizeFlagType.method(:normalize_string))
|
|
52
|
+
unless name.is_a?(String)
|
|
53
|
+
raise ArgumentError, 'DynamicApi error - name must be string'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
unless default_value.is_a?(flag_type)
|
|
57
|
+
raise ArgumentError, "DynamicApi default value must be of #{flag_type} type. Received #{default_value}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
string = @flag_repository.flag(name)
|
|
61
|
+
if string.nil?
|
|
62
|
+
string = create_method.nil? ? @entities_provider.create_string(default_value, options) : create_method.call(default_value, options)
|
|
63
|
+
@flag_repository.add_flag(string, name)
|
|
29
64
|
end
|
|
30
65
|
|
|
31
|
-
|
|
32
|
-
value
|
|
66
|
+
merged_context = MergedContext.new(string.parser&.global_context, context)
|
|
67
|
+
value = string.internal_value(context, nil_instead_of_default: true)
|
|
68
|
+
return_value = value.nil? ? normalize_method.call(default_value) : normalize_method.call(value)
|
|
69
|
+
string.send_impressions(return_value, merged_context)
|
|
70
|
+
return_value
|
|
33
71
|
end
|
|
34
72
|
end
|
|
35
73
|
end
|
|
36
|
-
end
|
|
74
|
+
end
|
|
@@ -3,12 +3,24 @@ require 'rox/core/entities/flag'
|
|
|
3
3
|
module Rox
|
|
4
4
|
module Core
|
|
5
5
|
class InternalFlags
|
|
6
|
-
|
|
6
|
+
@@defaults_self_managed = {
|
|
7
|
+
'rox.internal.pushUpdates' => false,
|
|
8
|
+
'rox.internal.considerThrottleInPush' => false,
|
|
9
|
+
'rox.internal.throttleFetchInSeconds' => 0
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
def initialize(experiment_repository, parser, rox_options)
|
|
7
13
|
@experiment_repository = experiment_repository
|
|
8
14
|
@parser = parser
|
|
15
|
+
@rox_options = rox_options
|
|
9
16
|
end
|
|
10
17
|
|
|
11
18
|
def enabled?(flag_name)
|
|
19
|
+
if @rox_options.self_managed?
|
|
20
|
+
value = @@defaults_self_managed[flag_name]
|
|
21
|
+
return value unless value.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
12
24
|
internal_experiment = @experiment_repository.experiment_by_flag(flag_name)
|
|
13
25
|
return false if internal_experiment.nil?
|
|
14
26
|
|
|
@@ -17,4 +29,4 @@ module Rox
|
|
|
17
29
|
end
|
|
18
30
|
end
|
|
19
31
|
end
|
|
20
|
-
end
|
|
32
|
+
end
|
|
@@ -14,10 +14,10 @@ module Rox
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.error(error_details)
|
|
17
|
-
args = ConfigurationFetchedArgs.new(FetcherStatus::ERROR_FETCHED_FAILED, nil, false
|
|
17
|
+
args = ConfigurationFetchedArgs.new(FetcherStatus::ERROR_FETCHED_FAILED, nil, false)
|
|
18
18
|
args.error_details = error_details
|
|
19
19
|
args
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
|
-
end
|
|
23
|
+
end
|
|
@@ -3,9 +3,10 @@ require 'rox/core/configuration/configuration_fetched_args'
|
|
|
3
3
|
module Rox
|
|
4
4
|
module Core
|
|
5
5
|
class ConfigurationFetchedInvoker
|
|
6
|
-
def initialize
|
|
6
|
+
def initialize(user_unhandled_error_invoker)
|
|
7
7
|
@fetched_handlers = []
|
|
8
8
|
@mutex = Mutex.new
|
|
9
|
+
@user_unhandled_error_invoker = user_unhandled_error_invoker
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def invoke(fetcher_status, creation_date, has_changes)
|
|
@@ -29,9 +30,13 @@ module Rox
|
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
handlers.each do |handler|
|
|
32
|
-
|
|
33
|
+
begin
|
|
34
|
+
handler.call(args)
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
user_unhandled_error_invoker.invoke(handler, Rox::Core::CONFIGURATION_FETCHED_HANDLER, e)
|
|
37
|
+
end
|
|
33
38
|
end
|
|
34
39
|
end
|
|
35
40
|
end
|
|
36
41
|
end
|
|
37
|
-
end
|
|
42
|
+
end
|
|
@@ -17,50 +17,51 @@ module Rox
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def parse(fetch_result, sdk_settings)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
begin
|
|
28
|
-
json_obj = JSON.parse(fetch_result.data)
|
|
29
|
-
rescue StandardError => ex
|
|
30
|
-
@configuration_fetched_invoker.invoke_error(FetcherError::CORRUPTED_JSON)
|
|
31
|
-
@error_reporter.report('Failed to parse JSON configuration', ex)
|
|
32
|
-
return nil
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
if fetch_result.source != ConfigurationSource::ROXY && !@signature_verifier.verify(json_obj['data'], json_obj['signature_v0'])
|
|
36
|
-
@configuration_fetched_invoker.invoke_error(FetcherError::SIGNATURE_VERIFICATION_ERROR)
|
|
37
|
-
@error_reporter.report('Failed to validate signature', StandardError.new("Data : #{json_obj['data']} Signature : #{json_obj['signature_v0']}"))
|
|
38
|
-
return nil
|
|
39
|
-
end
|
|
20
|
+
if fetch_result.nil? || fetch_result.data.nil? || fetch_result.data.empty?
|
|
21
|
+
@configuration_fetched_invoker.invoke_error(FetcherError::EMPTY_JSON)
|
|
22
|
+
@error_reporter.report('Failed to parse JSON configuration - Null Or Empty', ArgumentError.new('data'))
|
|
23
|
+
return nil
|
|
24
|
+
end
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
begin
|
|
27
|
+
json_obj = JSON.parse(fetch_result.data)
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
@configuration_fetched_invoker.invoke_error(FetcherError::CORRUPTED_JSON)
|
|
30
|
+
@error_reporter.report('Failed to parse JSON configuration', e)
|
|
31
|
+
return nil
|
|
32
|
+
end
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
34
|
+
if fetch_result.source != ConfigurationSource::ROXY && !@signature_verifier.verify(json_obj['data'],
|
|
35
|
+
json_obj['signature_v0'])
|
|
36
|
+
@configuration_fetched_invoker.invoke_error(FetcherError::SIGNATURE_VERIFICATION_ERROR)
|
|
37
|
+
@error_reporter.report('Failed to validate signature',
|
|
38
|
+
StandardError.new("Data : #{json_obj['data']} Signature : #{json_obj['signature_v0']}"))
|
|
39
|
+
return nil
|
|
40
|
+
end
|
|
50
41
|
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
signature_date = DateTime.rfc3339(json_obj['signed_date']).to_time
|
|
43
|
+
internal_data_string = json_obj['data']
|
|
44
|
+
internal_data_object = JSON.parse(internal_data_string)
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
if fetch_result.source != ConfigurationSource::ROXY && internal_data_object['application'] != sdk_settings.api_key
|
|
47
|
+
@configuration_fetched_invoker.invoke_error(FetcherError::MISMATCH_APP_KEY)
|
|
48
|
+
@error_reporter.report('Failed to parse JSON configuration - ',
|
|
49
|
+
StandardError.new("Internal Data: #{internal_data_object['application']} SdkSettings: #{sdk_settings.api_key}"))
|
|
58
50
|
return nil
|
|
59
51
|
end
|
|
52
|
+
|
|
53
|
+
experiments = parse_experiments(internal_data_object)
|
|
54
|
+
target_groups = parse_target_groups(internal_data_object)
|
|
55
|
+
|
|
56
|
+
Configuration.new(experiments, target_groups, signature_date)
|
|
57
|
+
rescue StandardError => e
|
|
58
|
+
Logging.logger.error('Failed to parse configurations', e)
|
|
59
|
+
@configuration_fetched_invoker.invoke_error(FetcherError::UNKNOWN)
|
|
60
|
+
nil
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
def parse_experiments(data)
|
|
63
|
-
data['experiments'].map { |e| parse_experiment(e)}
|
|
64
|
+
data['experiments'].map { |e| parse_experiment(e) }
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
def parse_experiment(data)
|
|
@@ -69,12 +70,12 @@ module Rox
|
|
|
69
70
|
name = data['name']
|
|
70
71
|
id = data['_id']
|
|
71
72
|
labels = data['labels'] || []
|
|
72
|
-
flags = data['featureFlags'].map {|f| f['name']}
|
|
73
|
+
flags = data['featureFlags'].map { |f| f['name'] }
|
|
73
74
|
ExperimentModel.new(id, name, condition, is_archived, flags, labels)
|
|
74
75
|
end
|
|
75
76
|
|
|
76
77
|
def parse_target_groups(data)
|
|
77
|
-
data['targetGroups'].map { |g| parse_target_group(g)}
|
|
78
|
+
data['targetGroups'].map { |g| parse_target_group(g) }
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
def parse_target_group(data)
|