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
@@ -1,4 +1,4 @@
1
- require "em-eventsource"
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('/') + '/' + @app_key
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.terminate unless @thread.nil?
40
+ @thread&.terminate
41
41
  @thread = nil
42
42
  end
43
43
  end
44
44
  end
45
- end
45
+ end
@@ -15,4 +15,4 @@ module Rox
15
15
  end
16
16
  end
17
17
  end
18
- end
18
+ end
@@ -15,4 +15,4 @@ module Rox
15
15
  SEMVER = CustomPropertyType.new('semver', 'Semver')
16
16
  end
17
17
  end
18
- end
18
+ end
@@ -4,8 +4,8 @@ module Rox
4
4
  module Core
5
5
  class DeviceProperty < CustomProperty
6
6
  def initialize(name, type, value = nil)
7
- super('rox.' + name, type, value)
7
+ super("rox.#{name}", type, value)
8
8
  end
9
9
  end
10
10
  end
11
- end
11
+ 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, ns)
11
- raise ArgumentError, 'A namespace cannot be null' if ns.nil?
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
- raise ArgumentError, "A container with the given namespace (#{ns}) has already been registered" if @namespaces.include?(ns)
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 << ns
20
+ @namespaces << namespace
18
21
 
19
- container.instance_variables().each do |attribute_name|
22
+ container.instance_variables.each do |attribute_name|
20
23
  begin
21
24
  value = container.instance_variable_get(attribute_name)
22
- if value != nil && value.is_a?(Variant)
23
- var_name = attribute_name.to_s();
24
- # removing the attribute starting @ (if [always] exists)
25
- if (var_name[0] == '@')
26
- var_name[0] = '';
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 => ex
37
- Logging.logger.error('Failed to send bugsnag error ', ex)
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
- 'message' => message,
53
- 'deviceId' => @device_properties.distinct_id,
54
- 'buid' => @buid.to_s
53
+ 'message' => message,
54
+ 'deviceId' => @device_properties.distinct_id,
55
+ 'buid' => @buid.to_s
55
56
  }
56
57
 
57
58
  metadata = {
58
- 'data' => inner_data
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
- 'name' => 'Rollout Ruby SDK',
92
- 'version' => @device_properties.lib_version
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
- id => rollout_key
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
- 'releaseStage' => @device_properties.rollout_environment,
125
- 'version' => @device_properties.lib_version
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
- def experiments=(experiments)
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
- @variants = {}
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(variant, name)
12
- variant.name = name if variant.name.nil? || variant.name.empty?
11
+ def add_flag(string, name)
12
+ string.name = name if string.name.nil? || string.name.empty?
13
13
  @mutex.synchronize do
14
- @variants[name] = variant
14
+ @strings[name] = string
15
15
  end
16
- raise_flag_added_event(variant)
16
+ raise_flag_added_event(string)
17
17
  end
18
18
 
19
19
  def flag(name)
20
20
  @mutex.synchronize do
21
- return @variants[name]
21
+ return @strings[name]
22
22
  end
23
23
  end
24
24
 
25
25
  def all_flags
26
26
  @mutex.synchronize do
27
- return @variants.values
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 |parser, stack, context|
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 |parser, stack, context|
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 |parser, stack, context|
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
- variant = @flags_repository.flag(feature_flag_identifier)
45
+ string = @flags_repository.flag(feature_flag_identifier)
46
46
 
47
- if !variant.nil?
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
@@ -1,26 +1,51 @@
1
1
  require 'rox/core/roxx/token_type'
2
+ require 'rox/core/error_handling/exception_trigger'
3
+ require 'rox/core/error_handling/userspace_handler_exception'
2
4
 
3
5
  module Rox
4
6
  module Core
5
7
  class PropertiesExtensions
6
- def initialize(parser, properties_repository)
8
+ def initialize(parser, properties_repository, dynamic_property_rule_handler = nil)
7
9
  @parser = parser
8
10
  @properties_repository = properties_repository
11
+ @dynamic_property_rule_handler = dynamic_property_rule_handler
9
12
  end
10
13
 
11
14
  def extend
12
- @parser.add_operator('property') do |parser, stack, context|
15
+ @parser.add_operator('property') do |_parser, stack, context|
13
16
  prop_name = stack.pop.to_s
14
17
  property = @properties_repository.custom_property(prop_name)
18
+ value = get_value(prop_name, property, context)
15
19
 
16
- if property.nil?
17
- stack.push(TokenType::UNDEFINED)
20
+ stack.push(value.nil? ? TokenType::UNDEFINED : value)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def get_value(prop_name, property, context)
27
+ if property.nil?
28
+ if @dynamic_property_rule_handler.nil?
29
+ TokenType::UNDEFINED
18
30
  else
19
- value = property.value(context)
20
- stack.push(value.nil? ? TokenType::UNDEFINED : value)
31
+ get_value_from_dynamic_property_rule_handler(prop_name, context)
21
32
  end
33
+ else
34
+ get_value_from_property(property, context)
22
35
  end
23
36
  end
37
+
38
+ def get_value_from_dynamic_property_rule_handler(prop_name, context)
39
+ @dynamic_property_rule_handler.call(prop_name, context)
40
+ rescue StandardError => e
41
+ raise Rox::Core::UserspaceHandlerException, handler, ExceptionTrigger::DYNAMIC_PROPERTIES_RULE, e
42
+ end
43
+
44
+ def get_value_from_property(property, context)
45
+ property.value(context)
46
+ rescue StandardError => e
47
+ raise Rox::Core::UserspaceHandlerException, handler, ExceptionTrigger::CUSTOM_PROPERTY_GENERATOR, e
48
+ end
24
49
  end
25
50
  end
26
- end
51
+ end