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.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +34 -13
  3. data/.editorconfig +12 -0
  4. data/.github/workflows/ruby.yml +20 -0
  5. data/.rubocop.yml +17 -0
  6. data/Gemfile +2 -2
  7. data/README.md +32 -0
  8. data/Rakefile +12 -9
  9. data/bin/console +3 -4
  10. data/e2e-server/run_server.sh +12 -0
  11. data/e2e-server/server.rb +158 -0
  12. data/e2e/container.rb +11 -14
  13. data/e2e/custom_props.rb +9 -9
  14. data/e2e/rox_e2e_test.rb +17 -19
  15. data/e2e/test_vars.rb +3 -6
  16. data/example/local.rb +40 -0
  17. data/lib/rox/core/analytics.rb +18 -0
  18. data/lib/rox/core/client/buid.rb +8 -8
  19. data/lib/rox/core/client/device_properties.rb +7 -1
  20. data/lib/rox/core/client/dynamic_api.rb +53 -15
  21. data/lib/rox/core/client/internal_flags.rb +14 -2
  22. data/lib/rox/core/client/sdk_settings.rb +1 -1
  23. data/lib/rox/core/configuration/configuration.rb +1 -1
  24. data/lib/rox/core/configuration/configuration_fetched_args.rb +2 -2
  25. data/lib/rox/core/configuration/configuration_fetched_invoker.rb +8 -3
  26. data/lib/rox/core/configuration/configuration_parser.rb +38 -37
  27. data/lib/rox/core/configuration/fetcher_error.rb +1 -1
  28. data/lib/rox/core/configuration/fetcher_status.rb +1 -1
  29. data/lib/rox/core/configuration/models/experiment_model.rb +1 -1
  30. data/lib/rox/core/configuration/models/target_group_model.rb +1 -1
  31. data/lib/rox/core/consts/build.rb +2 -2
  32. data/lib/rox/core/consts/environment.rb +10 -8
  33. data/lib/rox/core/consts/property_type.rb +16 -17
  34. data/lib/rox/core/context/merged_context.rb +2 -1
  35. data/lib/rox/core/core.rb +88 -38
  36. data/lib/rox/core/entities/default_flag_values.rb +10 -0
  37. data/lib/rox/core/entities/flag.rb +22 -7
  38. data/lib/rox/core/entities/flag_setter.rb +6 -6
  39. data/lib/rox/core/entities/rox_double.rb +11 -0
  40. data/lib/rox/core/entities/rox_int.rb +11 -0
  41. data/lib/rox/core/entities/{variant.rb → rox_string.rb} +20 -13
  42. data/lib/rox/core/error_handling/exception_trigger.rb +10 -0
  43. data/lib/rox/core/error_handling/userspace_handler_exception.rb +12 -0
  44. data/lib/rox/core/error_handling/userspace_unhandled_error_invoker.rb +41 -0
  45. data/lib/rox/core/impression/impression_args.rb +2 -2
  46. data/lib/rox/core/impression/impression_invoker.rb +41 -7
  47. data/lib/rox/core/impression/models/experiment.rb +1 -1
  48. data/lib/rox/core/impression/models/reporting_value.rb +10 -2
  49. data/lib/rox/core/logging/logging.rb +3 -3
  50. data/lib/rox/core/logging/no_op_logger.rb +1 -1
  51. data/lib/rox/core/network/configuration_fetcher.rb +2 -2
  52. data/lib/rox/core/network/configuration_fetcher_roxy.rb +2 -2
  53. data/lib/rox/core/network/configuration_fetcher_self_managed.rb +30 -0
  54. data/lib/rox/core/network/configuration_source.rb +1 -1
  55. data/lib/rox/core/network/request.rb +1 -1
  56. data/lib/rox/core/network/request_configuration_builder.rb +5 -5
  57. data/lib/rox/core/network/request_data.rb +1 -1
  58. data/lib/rox/core/network/response.rb +6 -8
  59. data/lib/rox/core/network/state_sender.rb +61 -18
  60. data/lib/rox/core/notifications/notification_listener.rb +4 -4
  61. data/lib/rox/core/properties/custom_property.rb +1 -1
  62. data/lib/rox/core/properties/custom_property_type.rb +1 -1
  63. data/lib/rox/core/properties/device_property.rb +2 -2
  64. data/lib/rox/core/properties/property_factory.rb +132 -0
  65. data/lib/rox/core/register/registerer.rb +13 -12
  66. data/lib/rox/core/reporting/error_reporter.rb +14 -13
  67. data/lib/rox/core/repositories/experiment_repository.rb +2 -4
  68. data/lib/rox/core/repositories/flag_repository.rb +7 -7
  69. data/lib/rox/core/repositories/roxx/experiments_extensions.rb +8 -8
  70. data/lib/rox/core/repositories/roxx/properties_extensions.rb +32 -7
  71. data/lib/rox/core/repositories/target_group_repository.rb +2 -4
  72. data/lib/rox/core/roxx/evaluation_result.rb +2 -0
  73. data/lib/rox/core/roxx/node.rb +1 -1
  74. data/lib/rox/core/roxx/parser.rb +35 -21
  75. data/lib/rox/core/roxx/regular_expression_extensions.rb +6 -3
  76. data/lib/rox/core/roxx/string_tokenizer.rb +3 -1
  77. data/lib/rox/core/roxx/symbols.rb +1 -1
  78. data/lib/rox/core/roxx/token_type.rb +1 -1
  79. data/lib/rox/core/roxx/tokenized_expression.rb +16 -12
  80. data/lib/rox/core/roxx/value_compare_extensions.rb +49 -29
  81. data/lib/rox/core/security/signature_verifier.rb +3 -3
  82. data/lib/rox/core/security/signature_verifier_mock.rb +12 -0
  83. data/lib/rox/core/utils/type_utils.rb +1 -1
  84. data/lib/rox/server/client/server_properties.rb +1 -1
  85. data/lib/rox/server/flags/normalize_flag_type.rb +25 -0
  86. data/lib/rox/server/flags/rox_double.rb +8 -0
  87. data/lib/rox/server/flags/rox_flag.rb +1 -1
  88. data/lib/rox/server/flags/rox_int.rb +8 -0
  89. data/lib/rox/server/flags/rox_string.rb +8 -0
  90. data/lib/rox/server/flags/server_entities_provider.rb +14 -4
  91. data/lib/rox/server/logging/server_logger.rb +2 -2
  92. data/lib/rox/server/rox_options.rb +39 -8
  93. data/lib/rox/server/rox_server.rb +80 -59
  94. data/lib/rox/version.rb +1 -1
  95. data/rox.gemspec +11 -9
  96. metadata +66 -37
  97. data/CODEOWNERS +0 -1
  98. data/README_DEVELOP.md +0 -25
  99. data/_archive/.document +0 -5
  100. data/_archive/.rspec +0 -1
  101. data/_archive/Gemfile +0 -15
  102. data/_archive/README.md +0 -32
  103. data/_archive/README.rdoc +0 -19
  104. data/_archive/Rakefile +0 -50
  105. data/_archive/lib/expr_function_definition.rb +0 -52
  106. data/_archive/lib/function_definition.rb +0 -48
  107. data/_archive/lib/function_token.rb +0 -12
  108. data/_archive/lib/object_extends.rb +0 -12
  109. data/_archive/lib/ruby_interpreter.rb +0 -292
  110. data/_archive/lib/stack.rb +0 -48
  111. data/_archive/lib/string_extends.rb +0 -14
  112. data/_archive/spec/ruby_interpreter_spec.rb +0 -203
  113. data/_archive/spec/spec_helper.rb +0 -30
  114. data/_archive/spec/stack_spec.rb +0 -77
  115. data/lib/rox/server/flags/rox_variant.rb +0 -8
@@ -0,0 +1,11 @@
1
+ require 'rox/core/entities/rox_string'
2
+
3
+ module Rox
4
+ module Core
5
+ class RoxDouble < RoxString
6
+ def value(context = nil)
7
+ internal_value(context, false, Float)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'rox/core/entities/rox_string'
2
+
3
+ module Rox
4
+ module Core
5
+ class RoxInt < RoxString
6
+ def value(context = nil)
7
+ internal_value(context, false, Integer)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,22 +4,31 @@ require 'rox/core/impression/models/reporting_value'
4
4
 
5
5
  module Rox
6
6
  module Core
7
- class Variant
8
- attr_accessor :default_value, :options, :name, :context, :condition, :parser, :impression_invoker, :client_experiment
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(@context, context)
51
+ merged_context = MergedContext.new(@parser&.global_context, context)
43
52
 
44
- if !@parser.nil? && !@condition.nil? && !@condition.empty?
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
- if !value.nil? && !value.empty?
49
- return_value = value
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
- @impression_invoker.invoke(ReportingValue.new(@name, return_value), @client_experiment, merged_context) if @impression_invoker != nil
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,10 @@
1
+ module Rox
2
+ module Core
3
+ module ExceptionTrigger
4
+ DYNAMIC_PROPERTIES_RULE = 1
5
+ CONFIGURATION_FETCHED_HANDLER = 2
6
+ IMPRESSION_HANDLER = 3
7
+ CUSTOM_PROPERTY_GENERATOR = 4
8
+ end
9
+ end
10
+ 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,5 +1,5 @@
1
1
  module Rox
2
2
  module Core
3
- ImpressionArgs = Struct.new(:reporting_value, :experiment, :context)
3
+ ImpressionArgs = Struct.new(:reporting_value, :context)
4
4
  end
5
- end
5
+ 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, client_experiment, context)
18
- # TODO: Implement analytics logic
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
- raise_impression_event(ImpressionArgs.new(reporting_value, client_experiment, context))
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
- handlers.each do |handler|
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
@@ -11,4 +11,4 @@ module Rox
11
11
  end
12
12
  end
13
13
  end
14
- end
14
+ end
@@ -1,5 +1,13 @@
1
1
  module Rox
2
2
  module Core
3
- ReportingValue = Struct.new(:name, :value)
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
@@ -5,8 +5,8 @@ module Rox
5
5
  class Logging
6
6
  @logger = nil
7
7
 
8
- def self.logger=(logger)
9
- @logger = logger
8
+ class << self
9
+ attr_writer :logger
10
10
  end
11
11
 
12
12
  def self.logger
@@ -14,4 +14,4 @@ module Rox
14
14
  end
15
15
  end
16
16
  end
17
- end
17
+ end
@@ -8,4 +8,4 @@ module Rox
8
8
  def warn(message, ex = nil); end
9
9
  end
10
10
  end
11
- end
11
+ 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 => ex
23
- write_fetch_exception_to_log_and_invoke_fetch_handler(source, ex)
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 => ex
18
- write_fetch_exception_to_log_and_invoke_fetch_handler(source, ex)
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
@@ -6,4 +6,4 @@ module Rox
6
6
  ROXY = 'Roxy'.freeze
7
7
  end
8
8
  end
9
- end
9
+ 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, :use_ssl => uri.scheme == 'https') do |http|
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, roxy_url)
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
- @roxy_url = roxy_url
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
- Rox::Core::PropertyType::DISTINCT_ID.name => @device_properties.distinct_id)
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
@@ -2,4 +2,4 @@ module Rox
2
2
  module Core
3
3
  RequestData = Struct.new(:url, :query_params)
4
4
  end
5
- end
5
+ end
@@ -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
- begin
25
- @status_code == 200 && @content_type == 'application/json' &&
26
- [404, "404"].include?(JSON.parse(text)['result'])
27
- rescue JSON::ParserError
28
- Logging.logger.error("Failed to parse JSON response: #{text}")
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, Proc.new { self.send })
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, @device_properties, @sdk_settings.dev_mode_secret)
34
- md5_signature = StateSender.md5_signature(serialized_feature_flags, serialized_custom_properties, @device_properties, @sdk_settings.dev_mode_secret)
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
- response = get_state_from_CDN(rollout_key, md5_signature)
37
- if !response.success?
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("Successfully sent state to API.")
41
+ Rox::Core::Logging.logger.debug('Successfully fetched state from CDN')
42
+ return
42
43
  else
43
- Rox::Core::Logging.logger.debug("Failed to send state to API.")
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
- @request.send_post("#{Rox::Core::Environment.state_api_path}/#{rollout_key}/#{md5_signature}", state_payload)
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 = self.state_payload(serialized_feature_flags, serialized_custom_properties, device_properties, dev_mode_secret).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["devModeSecret"] = dev_mode_secret
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{|flag| flag.name }.each do |f|
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{|prop| prop.name }.each do |p|
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
  }