rox-rollout 4.7.3 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -4,22 +4,31 @@ require 'rox/core/impression/models/reporting_value'
|
|
4
4
|
|
5
5
|
module Rox
|
6
6
|
module Core
|
7
|
-
class
|
8
|
-
attr_accessor :default_value, :options, :name, :
|
7
|
+
class RoxString
|
8
|
+
attr_accessor :default_value, :options, :name, :condition, :parser, :impression_invoker,
|
9
|
+
:client_experiment
|
9
10
|
|
10
11
|
def initialize(default_value, options = [])
|
11
12
|
@default_value = default_value
|
12
13
|
@options = options.clone
|
14
|
+
raise ArgumentError, 'options should be an array' unless options.is_a?(Array)
|
15
|
+
if options.length > 0 && options.any? { |x| x.class != default_value.class }
|
16
|
+
raise ArgumentError, 'options should be the same type as default value'
|
17
|
+
end
|
13
18
|
@options << default_value unless options.include?(default_value)
|
14
19
|
|
15
20
|
@condition = nil
|
16
21
|
@parser = nil
|
17
|
-
@context = nil
|
18
22
|
@impression_invoker = nil
|
19
23
|
@client_experiment = nil
|
20
24
|
@name = nil
|
21
25
|
end
|
22
26
|
|
27
|
+
def send_impressions(return_value, merged_context)
|
28
|
+
reporting_value = ReportingValue.new(@name, return_value, @client_experiment)
|
29
|
+
@impression_invoker&.invoke(reporting_value, @client_experiment, merged_context)
|
30
|
+
end
|
31
|
+
|
23
32
|
def set_for_evaluation(parser, experiment, impression_invoker)
|
24
33
|
if experiment.nil?
|
25
34
|
@client_experiment = nil
|
@@ -37,24 +46,22 @@ module Rox
|
|
37
46
|
internal_value(context, false)
|
38
47
|
end
|
39
48
|
|
40
|
-
def internal_value(context, nil_instead_of_default)
|
49
|
+
def internal_value(context, nil_instead_of_default, evaluated_type = String)
|
41
50
|
return_value = nil_instead_of_default ? nil : @default_value
|
42
|
-
merged_context = MergedContext.new(@
|
51
|
+
merged_context = MergedContext.new(@parser&.global_context, context)
|
43
52
|
|
44
|
-
|
53
|
+
unless @parser.nil? || @condition.nil? || @condition.empty?
|
45
54
|
evaluation_result = @parser.evaluate_expression(@condition, merged_context)
|
46
55
|
unless evaluation_result.nil?
|
47
|
-
value = evaluation_result.string_value
|
48
|
-
|
49
|
-
|
50
|
-
end
|
56
|
+
value = evaluated_type == String ? evaluation_result.string_value : evaluation_result.value
|
57
|
+
is_empty = value.is_a?(String) && value.empty?
|
58
|
+
return_value = value if !value.nil? && !is_empty
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
|
-
|
55
|
-
|
62
|
+
send_impressions(return_value, merged_context)
|
56
63
|
return_value
|
57
64
|
end
|
58
65
|
end
|
59
66
|
end
|
60
|
-
end
|
67
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rox
|
2
|
+
module Core
|
3
|
+
class UserspaceHandlerException < StandardError
|
4
|
+
def initialize(exception_source, exception_trigger, exception)
|
5
|
+
@exception_source = exception_source
|
6
|
+
@exception_trigger = exception_trigger
|
7
|
+
@exception = exception
|
8
|
+
super('user unhandled exception in roxx expression')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rox/core/logging/logging'
|
2
|
+
|
3
|
+
module Rox
|
4
|
+
module Core
|
5
|
+
class UserspaceUnhandledErrorInvoker
|
6
|
+
def initialize(user_unhandler_error_handler = nil)
|
7
|
+
@user_unhandler_error_handler = user_unhandler_error_handler
|
8
|
+
end
|
9
|
+
|
10
|
+
def invoke(exception_source, exception_trigger, exception)
|
11
|
+
unless @user_unhandler_error_handler
|
12
|
+
Logging.logger.error("User Unhandled Error Occurred, no fallback handler was set, exception ignored: #{exception}")
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
userspace_unhandled_error_args = UserspaceUnhandledErrorArgs.new(exception_source, exception_trigger, exception)
|
18
|
+
@user_unhandler_error_handler.call(userspace_unhandled_error_args)
|
19
|
+
rescue StandardError => e
|
20
|
+
Logging.logger.error("User Unhandled Error Handler itself threw an exception. original exception: #{e}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def handler=(handler)
|
25
|
+
@user_unhandler_error_handler = handler
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class UserspaceUnhandledErrorArgs
|
30
|
+
def initialize(exception_source = nil, exception_trigger = nil, exception = nil)
|
31
|
+
@exception_source = exception_source
|
32
|
+
@exception_trigger = exception_trigger
|
33
|
+
@exception = exception
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
"UserspaceUnhandledErrorArgs(#{@exception_source}, #{@exception_trigger}, #{@exception})"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,23 +1,54 @@
|
|
1
1
|
require 'rox/core/impression/impression_args'
|
2
|
+
require 'rox/core/logging/logging'
|
3
|
+
require 'rox/core/error_handling/exception_trigger'
|
4
|
+
require 'rox/core/consts/property_type'
|
2
5
|
|
3
6
|
module Rox
|
4
7
|
module Core
|
5
8
|
class ImpressionInvoker
|
6
|
-
def initialize(internal_flags, custom_property_repository, device_properties, analytics_client, is_roxy)
|
9
|
+
def initialize(internal_flags, custom_property_repository, device_properties, analytics_client, is_roxy, user_unhandled_error_invoker)
|
7
10
|
@internal_flags = internal_flags
|
8
11
|
@custom_property_repository = custom_property_repository
|
9
12
|
@device_properties = device_properties
|
10
13
|
@analytics_client = analytics_client
|
11
14
|
@is_roxy = is_roxy
|
15
|
+
@user_unhandled_error_invoker = user_unhandled_error_invoker
|
12
16
|
|
13
17
|
@impression_handlers = []
|
14
18
|
@mutex = Mutex.new
|
15
19
|
end
|
16
20
|
|
17
|
-
def invoke(reporting_value,
|
18
|
-
|
21
|
+
def invoke(reporting_value, stickiness_property, context)
|
22
|
+
begin
|
23
|
+
analytics_enabled = @internal_flags.enabled?('rox.internal.analytics')
|
24
|
+
if analytics_enabled && !@is_roxy
|
25
|
+
prop = @custom_property_repository.custom_property(stickiness_property) || @custom_property_repository.custom_property("rox.#{Rox::Core::PropertyType::DISTINCT_ID.name}")
|
26
|
+
distinct_id = '(null_distinct_id'
|
27
|
+
unless prop.nil?
|
28
|
+
prop_value = prop.value(context)
|
29
|
+
distinct_id = prop_value if prop_value.instance_of?(String)
|
30
|
+
end
|
19
31
|
|
20
|
-
|
32
|
+
event_time = (Time.now.to_f * 1000.0).to_i
|
33
|
+
begin
|
34
|
+
event_time = ENV['rox.analytics.ms'].to_i
|
35
|
+
rescue StandardError
|
36
|
+
end
|
37
|
+
|
38
|
+
@analytics_client.track({
|
39
|
+
flag: reporting_value.name,
|
40
|
+
value: reporting_value.value,
|
41
|
+
distinctId: distinct_id,
|
42
|
+
experimentVersion: '0',
|
43
|
+
type: 'IMPRESSION',
|
44
|
+
time: event_time
|
45
|
+
})
|
46
|
+
end
|
47
|
+
rescue StandardError => ex
|
48
|
+
Logging.logger.error('Failed to send analytics', ex)
|
49
|
+
end
|
50
|
+
|
51
|
+
raise_impression_event(ImpressionArgs.new(reporting_value, context))
|
21
52
|
end
|
22
53
|
|
23
54
|
def register_impression_handler(&block)
|
@@ -32,10 +63,13 @@ module Rox
|
|
32
63
|
handlers = @impression_handlers.clone
|
33
64
|
end
|
34
65
|
|
35
|
-
|
36
|
-
handler.call(args)
|
66
|
+
begin
|
67
|
+
handlers.each { |handler| handler.call(args) }
|
68
|
+
rescue StandardError => e
|
69
|
+
user_unhandled_error_invoker.invoke(handler, ExceptionTrigger::IMPRESSION_HANDLER, e)
|
70
|
+
Logging.logger.error('Impresssion handler exception', ex)
|
37
71
|
end
|
38
72
|
end
|
39
73
|
end
|
40
74
|
end
|
41
|
-
end
|
75
|
+
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module Rox
|
2
2
|
module Core
|
3
|
-
ReportingValue
|
3
|
+
class ReportingValue
|
4
|
+
attr_reader :name, :value, :targeting
|
5
|
+
|
6
|
+
def initialize(name, value, experiment = nil)
|
7
|
+
@name = name
|
8
|
+
@value = value
|
9
|
+
@targeting = !experiment.nil?
|
10
|
+
end
|
11
|
+
end
|
4
12
|
end
|
5
|
-
end
|
13
|
+
end
|
@@ -19,8 +19,8 @@ module Rox
|
|
19
19
|
end
|
20
20
|
|
21
21
|
write_fetch_error_to_log_and_invoke_fetch_handler(source, fetch_result)
|
22
|
-
rescue StandardError =>
|
23
|
-
write_fetch_exception_to_log_and_invoke_fetch_handler(source,
|
22
|
+
rescue StandardError => e
|
23
|
+
write_fetch_exception_to_log_and_invoke_fetch_handler(source, e)
|
24
24
|
end
|
25
25
|
|
26
26
|
nil
|
@@ -14,8 +14,8 @@ module Rox
|
|
14
14
|
else
|
15
15
|
write_fetch_error_to_log_and_invoke_fetch_handler(source, fetch_roxy)
|
16
16
|
end
|
17
|
-
rescue StandardError =>
|
18
|
-
write_fetch_exception_to_log_and_invoke_fetch_handler(source,
|
17
|
+
rescue StandardError => e
|
18
|
+
write_fetch_exception_to_log_and_invoke_fetch_handler(source, e)
|
19
19
|
end
|
20
20
|
|
21
21
|
nil
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rox/core/network/configuration_source'
|
2
|
+
require 'rox/core/network/configuration_fetch_result'
|
3
|
+
require 'rox/core/network/configuration_fetcher_base'
|
4
|
+
|
5
|
+
module Rox
|
6
|
+
module Core
|
7
|
+
class ConfigurationFetcherSelfManaged < ConfigurationFetcherBase
|
8
|
+
def fetch
|
9
|
+
source = ConfigurationSource::API
|
10
|
+
begin
|
11
|
+
fetch_result = fetch_from_api
|
12
|
+
if fetch_result.success?
|
13
|
+
return ConfigurationFetchResult.new(fetch_result.text, source)
|
14
|
+
else
|
15
|
+
write_fetch_error_to_log_and_invoke_fetch_handler(source, fetch_result)
|
16
|
+
end
|
17
|
+
rescue StandardError => e
|
18
|
+
write_fetch_exception_to_log_and_invoke_fetch_handler(source, e)
|
19
|
+
end
|
20
|
+
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_from_api
|
25
|
+
api_request = @request_configuration_builder.build_for_api
|
26
|
+
@request.send_post(api_request.url, api_request.query_params)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -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
|
@@ -69,15 +86,15 @@ module Rox
|
|
69
86
|
values[PropertyType::APP_KEY.name] = device_properties.all_properties[PropertyType::APP_KEY.name]
|
70
87
|
values[PropertyType::PLATFORM.name] = device_properties.all_properties[PropertyType::PLATFORM.name]
|
71
88
|
values.merge!(device_properties.all_properties)
|
72
|
-
values.merge!({feature_flags: seralized_flag_repository})
|
73
|
-
values.merge!({custom_properties: serialized_custom_property_repository})
|
74
|
-
values[
|
89
|
+
values.merge!({ feature_flags: seralized_flag_repository })
|
90
|
+
values.merge!({ custom_properties: serialized_custom_property_repository })
|
91
|
+
values['devModeSecret'] = dev_mode_secret
|
75
92
|
values
|
76
93
|
end
|
77
94
|
|
78
95
|
def self.seralize_flag_repository(flag_repository)
|
79
96
|
flags = []
|
80
|
-
flag_repository.all_flags.sort_by
|
97
|
+
flag_repository.all_flags.sort_by(&:name).each do |f|
|
81
98
|
flags << {
|
82
99
|
name: f.name,
|
83
100
|
defaultValue: f.default_value,
|
@@ -87,11 +104,37 @@ module Rox
|
|
87
104
|
flags
|
88
105
|
end
|
89
106
|
|
107
|
+
def self.seralize_flag_repository_with_values(flag_repository)
|
108
|
+
flags = []
|
109
|
+
flag_repository.all_flags.sort_by(&:name).each do |f|
|
110
|
+
flags << {
|
111
|
+
name: f.name,
|
112
|
+
enabled: f.enabled?,
|
113
|
+
defaultValue: f.default_value,
|
114
|
+
options: f.options
|
115
|
+
}
|
116
|
+
end
|
117
|
+
flags
|
118
|
+
end
|
119
|
+
|
90
120
|
def self.serialize_custom_properties_repository(custom_property_repository)
|
91
121
|
properties = []
|
92
|
-
custom_property_repository.all_custom_properties.sort_by
|
122
|
+
custom_property_repository.all_custom_properties.sort_by(&:name).each do |p|
|
123
|
+
properties << {
|
124
|
+
name: p.name,
|
125
|
+
type: p.type.type,
|
126
|
+
externalType: p.type.external_type
|
127
|
+
}
|
128
|
+
end
|
129
|
+
properties
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.serialize_custom_properties_repository_with_values(custom_property_repository)
|
133
|
+
properties = []
|
134
|
+
custom_property_repository.all_custom_properties.sort_by(&:name).each do |p|
|
93
135
|
properties << {
|
94
136
|
name: p.name,
|
137
|
+
value: p.value(nil),
|
95
138
|
type: p.type.type,
|
96
139
|
externalType: p.type.external_type
|
97
140
|
}
|