prefab-cloud-ruby 0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -0
  3. data/Gemfile +9 -22
  4. data/Gemfile.lock +88 -160
  5. data/LICENSE.txt +1 -1
  6. data/Rakefile +14 -14
  7. data/VERSION +1 -1
  8. data/lib/prefab/auth_interceptor.rb +25 -0
  9. data/lib/prefab/client.rb +34 -139
  10. data/lib/prefab/config_client.rb +23 -275
  11. data/lib/prefab/config_loader.rb +27 -60
  12. data/lib/prefab/config_resolver.rb +40 -53
  13. data/lib/prefab/noop_cache.rb +13 -0
  14. data/lib/prefab/noop_stats.rb +8 -0
  15. data/lib/prefab/prefab_pb.rb +39 -0
  16. data/lib/prefab/prefab_services_pb.rb +37 -0
  17. data/lib/prefab/ratelimit_client.rb +58 -0
  18. data/lib/prefab/ratelimit_pb.rb +125 -0
  19. data/lib/prefab/store.rb +29 -0
  20. data/lib/prefab_client.rb +35 -0
  21. metadata +36 -198
  22. data/.envrc.sample +0 -3
  23. data/.github/workflows/ruby.yml +0 -46
  24. data/.gitmodules +0 -3
  25. data/.rubocop.yml +0 -13
  26. data/.tool-versions +0 -1
  27. data/CHANGELOG.md +0 -169
  28. data/CODEOWNERS +0 -1
  29. data/README.md +0 -94
  30. data/bin/console +0 -21
  31. data/compile_protos.sh +0 -18
  32. data/lib/prefab/config_client_presenter.rb +0 -18
  33. data/lib/prefab/config_value_unwrapper.rb +0 -115
  34. data/lib/prefab/config_value_wrapper.rb +0 -18
  35. data/lib/prefab/context.rb +0 -179
  36. data/lib/prefab/context_shape.rb +0 -20
  37. data/lib/prefab/context_shape_aggregator.rb +0 -65
  38. data/lib/prefab/criteria_evaluator.rb +0 -136
  39. data/lib/prefab/encryption.rb +0 -65
  40. data/lib/prefab/error.rb +0 -6
  41. data/lib/prefab/errors/env_var_parse_error.rb +0 -11
  42. data/lib/prefab/errors/initialization_timeout_error.rb +0 -13
  43. data/lib/prefab/errors/invalid_api_key_error.rb +0 -19
  44. data/lib/prefab/errors/missing_default_error.rb +0 -13
  45. data/lib/prefab/errors/missing_env_var_error.rb +0 -11
  46. data/lib/prefab/errors/uninitialized_error.rb +0 -13
  47. data/lib/prefab/evaluation.rb +0 -52
  48. data/lib/prefab/evaluation_summary_aggregator.rb +0 -87
  49. data/lib/prefab/example_contexts_aggregator.rb +0 -78
  50. data/lib/prefab/exponential_backoff.rb +0 -21
  51. data/lib/prefab/feature_flag_client.rb +0 -42
  52. data/lib/prefab/http_connection.rb +0 -41
  53. data/lib/prefab/internal_logger.rb +0 -16
  54. data/lib/prefab/local_config_parser.rb +0 -151
  55. data/lib/prefab/log_path_aggregator.rb +0 -69
  56. data/lib/prefab/logger_client.rb +0 -264
  57. data/lib/prefab/murmer3.rb +0 -50
  58. data/lib/prefab/options.rb +0 -208
  59. data/lib/prefab/periodic_sync.rb +0 -69
  60. data/lib/prefab/prefab.rb +0 -56
  61. data/lib/prefab/rate_limit_cache.rb +0 -41
  62. data/lib/prefab/resolved_config_presenter.rb +0 -86
  63. data/lib/prefab/time_helpers.rb +0 -7
  64. data/lib/prefab/weighted_value_resolver.rb +0 -42
  65. data/lib/prefab/yaml_config_parser.rb +0 -34
  66. data/lib/prefab-cloud-ruby.rb +0 -57
  67. data/lib/prefab_pb.rb +0 -93
  68. data/prefab-cloud-ruby.gemspec +0 -155
  69. data/test/.prefab.default.config.yaml +0 -2
  70. data/test/.prefab.unit_tests.config.yaml +0 -28
  71. data/test/integration_test.rb +0 -150
  72. data/test/integration_test_helpers.rb +0 -151
  73. data/test/support/common_helpers.rb +0 -180
  74. data/test/support/mock_base_client.rb +0 -42
  75. data/test/support/mock_config_client.rb +0 -19
  76. data/test/support/mock_config_loader.rb +0 -1
  77. data/test/test_client.rb +0 -444
  78. data/test/test_config_client.rb +0 -109
  79. data/test/test_config_loader.rb +0 -117
  80. data/test/test_config_resolver.rb +0 -430
  81. data/test/test_config_value_unwrapper.rb +0 -224
  82. data/test/test_config_value_wrapper.rb +0 -42
  83. data/test/test_context.rb +0 -203
  84. data/test/test_context_shape.rb +0 -50
  85. data/test/test_context_shape_aggregator.rb +0 -147
  86. data/test/test_criteria_evaluator.rb +0 -726
  87. data/test/test_encryption.rb +0 -16
  88. data/test/test_evaluation_summary_aggregator.rb +0 -162
  89. data/test/test_example_contexts_aggregator.rb +0 -238
  90. data/test/test_exponential_backoff.rb +0 -18
  91. data/test/test_feature_flag_client.rb +0 -48
  92. data/test/test_helper.rb +0 -17
  93. data/test/test_integration.rb +0 -58
  94. data/test/test_local_config_parser.rb +0 -147
  95. data/test/test_log_path_aggregator.rb +0 -62
  96. data/test/test_logger.rb +0 -621
  97. data/test/test_logger_initialization.rb +0 -12
  98. data/test/test_options.rb +0 -75
  99. data/test/test_prefab.rb +0 -12
  100. data/test/test_rate_limit_cache.rb +0 -44
  101. data/test/test_weighted_value_resolver.rb +0 -71
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'periodic_sync'
4
-
5
- module Prefab
6
- class ContextShapeAggregator
7
- include Prefab::PeriodicSync
8
-
9
- LOG = Prefab::InternalLogger.new(ContextShapeAggregator)
10
-
11
- attr_reader :data
12
-
13
- def initialize(client:, max_shapes:, sync_interval:)
14
- @max_shapes = max_shapes
15
- @client = client
16
- @name = 'context_shape_aggregator'
17
-
18
- @data = Concurrent::Set.new
19
-
20
- start_periodic_sync(sync_interval)
21
- end
22
-
23
- def push(context)
24
- return if @data.size >= @max_shapes
25
-
26
- context.contexts.each_pair do |name, name_context|
27
- name_context.to_h.each_pair do |key, value|
28
- @data.add [name, key, Prefab::ContextShape.field_type_number(value)]
29
- end
30
- end
31
- end
32
-
33
- def prepare_data
34
- duped = @data.dup
35
- @data.clear
36
-
37
- duped.inject({}) do |acc, (name, key, type)|
38
- acc[name] ||= {}
39
- acc[name][key] = type
40
- acc
41
- end
42
- end
43
-
44
- private
45
-
46
- def flush(to_ship, _)
47
- pool.post do
48
- LOG.debug "Uploading context shapes for #{to_ship.values.size}"
49
-
50
- shapes = PrefabProto::ContextShapes.new(
51
- shapes: to_ship.map do |name, shape|
52
- PrefabProto::ContextShape.new(
53
- name: name,
54
- field_types: shape
55
- )
56
- end
57
- )
58
-
59
- result = post('/api/v1/context-shapes', shapes)
60
-
61
- LOG.debug "Uploaded #{to_ship.values.size} shapes: #{result.status}"
62
- end
63
- end
64
- end
65
- end
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Naming/MethodName
4
- # We're intentionally keeping the UPCASED method names to match the protobuf
5
- # and avoid wasting CPU cycles lowercasing things
6
- module Prefab
7
- # This class evaluates a config's criteria. `evaluate` returns the value of
8
- # the first match based on the provided properties.
9
- class CriteriaEvaluator
10
- LOG = Prefab::InternalLogger.new(CriteriaEvaluator)
11
- NAMESPACE_KEY = 'NAMESPACE'
12
- NO_MATCHING_ROWS = [].freeze
13
-
14
- def initialize(config, project_env_id:, resolver:, namespace:, base_client:)
15
- @config = config
16
- @project_env_id = project_env_id
17
- @resolver = resolver
18
- @namespace = namespace
19
- @base_client = base_client
20
- end
21
-
22
- def evaluate(properties)
23
- rtn = evaluate_for_env(@project_env_id, properties) ||
24
- evaluate_for_env(0, properties)
25
- LOG.debug "Eval Key #{@config.key} Result #{rtn&.reportable_value} with #{properties.to_h}" unless @config.config_type == :LOG_LEVEL
26
- rtn
27
- end
28
-
29
- def all_criteria_match?(conditional_value, props)
30
- conditional_value.criteria.all? do |criterion|
31
- public_send(criterion.operator, criterion, props)
32
- end
33
- end
34
-
35
- def IN_SEG(criterion, properties)
36
- in_segment?(criterion, properties)
37
- end
38
-
39
- def NOT_IN_SEG(criterion, properties)
40
- !in_segment?(criterion, properties)
41
- end
42
-
43
- def ALWAYS_TRUE(_criterion, _properties)
44
- true
45
- end
46
-
47
- def PROP_IS_ONE_OF(criterion, properties)
48
- matches?(criterion, value_from_properties(criterion, properties), properties)
49
- end
50
-
51
- def PROP_IS_NOT_ONE_OF(criterion, properties)
52
- !matches?(criterion, value_from_properties(criterion, properties), properties)
53
- end
54
-
55
- def PROP_ENDS_WITH_ONE_OF(criterion, properties)
56
- prop_ends_with_one_of?(criterion, value_from_properties(criterion, properties))
57
- end
58
-
59
- def PROP_DOES_NOT_END_WITH_ONE_OF(criterion, properties)
60
- !prop_ends_with_one_of?(criterion, value_from_properties(criterion, properties))
61
- end
62
-
63
- def HIERARCHICAL_MATCH(criterion, properties)
64
- value = value_from_properties(criterion, properties)
65
- value&.start_with?(criterion.value_to_match.string)
66
- end
67
-
68
- def IN_INT_RANGE(criterion, properties)
69
- value = if criterion.property_name == 'prefab.current-time'
70
- Time.now.utc.to_i * 1000
71
- else
72
- value_from_properties(criterion, properties)
73
- end
74
-
75
- value && value >= criterion.value_to_match.int_range.start && value < criterion.value_to_match.int_range.end
76
- end
77
-
78
- def value_from_properties(criterion, properties)
79
- criterion.property_name == NAMESPACE_KEY ? @namespace : properties.get(criterion.property_name)
80
- end
81
-
82
- private
83
-
84
- def evaluate_for_env(env_id, properties)
85
- @config.rows.each_with_index do |row, index|
86
- next unless row.project_env_id == env_id
87
-
88
- row.values.each_with_index do |conditional_value, value_index|
89
- next unless all_criteria_match?(conditional_value, properties)
90
-
91
- return Prefab::Evaluation.new(
92
- config: @config,
93
- value: conditional_value.value,
94
- value_index: value_index,
95
- config_row_index: index,
96
- context: properties,
97
- resolver: @resolver
98
- )
99
- end
100
- end
101
-
102
- nil
103
- end
104
-
105
- def in_segment?(criterion, properties)
106
- segment = @resolver.get(criterion.value_to_match.string, properties)
107
-
108
- @base_client.log.info("Segment #{criterion.value_to_match.string} not found") unless segment
109
-
110
- segment&.report_and_return(@base_client.evaluation_summary_aggregator)
111
- end
112
-
113
- def matches?(criterion, value, properties)
114
- criterion_value_or_values = Prefab::ConfigValueUnwrapper.deepest_value(criterion.value_to_match, @config.key,
115
- properties, @resolver).unwrap
116
-
117
- case criterion_value_or_values
118
- when Google::Protobuf::RepeatedField
119
- # we to_s the value from properties for comparison because the
120
- # criterion_value_or_values is a list of strings
121
- criterion_value_or_values.include?(value.to_s)
122
- else
123
- criterion_value_or_values == value
124
- end
125
- end
126
-
127
- def prop_ends_with_one_of?(criterion, value)
128
- return false unless value
129
-
130
- criterion.value_to_match.string_list.values.any? do |ending|
131
- value.end_with?(ending)
132
- end
133
- end
134
- end
135
- end
136
- # rubocop:enable Naming/MethodName
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class Encryption
5
- CIPHER_TYPE = "aes-256-gcm" # 32/12
6
- SEPARATOR = "--"
7
-
8
- # Hexadecimal format ensures that generated keys are representable with
9
- # plain text
10
- #
11
- # To convert back to the original string with the desired length:
12
- # [ value ].pack("H*")
13
- def self.generate_new_hex_key
14
- generate_random_key.unpack("H*")[0]
15
- end
16
-
17
- def initialize(key_string_hex)
18
- @key = [key_string_hex].pack("H*")
19
- end
20
-
21
- def encrypt(clear_text)
22
- cipher = OpenSSL::Cipher.new(CIPHER_TYPE)
23
- cipher.encrypt
24
- iv = cipher.random_iv
25
-
26
- # load them into the cipher
27
- cipher.key = @key
28
- cipher.iv = iv
29
- cipher.auth_data = ""
30
-
31
- # encrypt the message
32
- encrypted = cipher.update(clear_text)
33
- encrypted << cipher.final
34
- tag = cipher.auth_tag
35
-
36
- # pack and join
37
- [encrypted, iv, tag].map { |p| p.unpack("H*")[0] }.join(SEPARATOR)
38
- end
39
-
40
- def decrypt(encrypted_string)
41
- unpacked_parts = encrypted_string.split(SEPARATOR).map { |p| [p].pack("H*") }
42
-
43
- cipher = OpenSSL::Cipher.new(CIPHER_TYPE)
44
- cipher.decrypt
45
- cipher.key = @key
46
- cipher.iv = unpacked_parts[1]
47
- cipher.auth_tag = unpacked_parts[2]
48
-
49
- # and decrypt it
50
- decrypted = cipher.update(unpacked_parts[0])
51
- decrypted << cipher.final
52
- decrypted
53
- end
54
-
55
- private
56
-
57
- def self.generate_random_key
58
- SecureRandom.random_bytes(key_length)
59
- end
60
-
61
- def self.key_length
62
- OpenSSL::Cipher.new(CIPHER_TYPE).key_len
63
- end
64
- end
65
- end
data/lib/prefab/error.rb DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class Error < StandardError
5
- end
6
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class EnvVarParseError < Prefab::Error
6
- def initialize(env_var, config, env_var_name)
7
- super("Evaluating #{config.key} couldn't coerce #{env_var_name} of #{env_var} to #{config.value_type}")
8
- end
9
- end
10
- end
11
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class InitializationTimeoutError < Prefab::Error
6
- def initialize(timeout_sec, key)
7
- message = "Prefab couldn't initialize in #{timeout_sec} second timeout. Trying to fetch key `#{key}`."
8
-
9
- super(message)
10
- end
11
- end
12
- end
13
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class InvalidApiKeyError < Prefab::Error
6
- def initialize(key)
7
- if key.nil? || key.empty?
8
- message = 'No API key. Set PREFAB_API_KEY env var or use PREFAB_DATASOURCES=LOCAL_ONLY'
9
-
10
- super(message)
11
- else
12
- message = "Your API key format is invalid. Expecting something like 123-development-yourapikey-SDK. You provided `#{key}`"
13
-
14
- super(message)
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class MissingDefaultError < Prefab::Error
6
- def initialize(key)
7
- message = "No value found for key '#{key}' and no default was provided.\n\nIf you'd prefer returning `nil` rather than raising when this occurs, modify the `on_no_default` value you provide in your Prefab::Options."
8
-
9
- super(message)
10
- end
11
- end
12
- end
13
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class MissingEnvVarError < Prefab::Error
6
- def initialize(message)
7
- super(message)
8
- end
9
- end
10
- end
11
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- module Errors
5
- class UninitializedError < Prefab::Error
6
- def initialize(key=nil)
7
- message = "Use Prefab.initialize before calling Prefab.get #{key}"
8
-
9
- super(message)
10
- end
11
- end
12
- end
13
- end
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- # Records the result of evaluating a config's criteria and forensics for reporting
5
- class Evaluation
6
- attr_reader :value
7
-
8
- def initialize(config:, value:, value_index:, config_row_index:, context:, resolver:)
9
- @config = config
10
- @value = value
11
- @value_index = value_index
12
- @config_row_index = config_row_index
13
- @context = context
14
- @resolver = resolver
15
- end
16
-
17
- def unwrapped_value
18
- deepest_value.unwrap
19
- end
20
-
21
- def reportable_value
22
- deepest_value.reportable_value
23
- end
24
-
25
- def report_and_return(evaluation_summary_aggregator)
26
- report(evaluation_summary_aggregator)
27
-
28
- unwrapped_value
29
- end
30
-
31
- private
32
-
33
- def report(evaluation_summary_aggregator)
34
- return if @config.config_type == :LOG_LEVEL
35
- evaluation_summary_aggregator&.record(
36
- config_key: @config.key,
37
- config_type: @config.config_type,
38
- counter: {
39
- config_id: @config.id,
40
- config_row_index: @config_row_index,
41
- conditional_value_index: @value_index,
42
- selected_value: deepest_value.reportable_wrapped_value,
43
- weighted_value_index: deepest_value.weighted_value_index,
44
- selected_index: nil # TODO
45
- })
46
- end
47
-
48
- def deepest_value
49
- @deepest_value ||= Prefab::ConfigValueUnwrapper.deepest_value(@value, @config, @context, @resolver)
50
- end
51
- end
52
- end
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'periodic_sync'
4
-
5
- module Prefab
6
- # This class aggregates the number of times each config is evaluated, and
7
- # details about how the config is evaluated This data is reported to the
8
- # server at a regular interval defined by `sync_interval`.
9
- class EvaluationSummaryAggregator
10
- include Prefab::PeriodicSync
11
-
12
- LOG = Prefab::InternalLogger.new(EvaluationSummaryAggregator)
13
-
14
- attr_reader :data
15
-
16
- def initialize(client:, max_keys:, sync_interval:)
17
- @client = client
18
- @max_keys = max_keys
19
- @name = 'evaluation_summary_aggregator'
20
-
21
- @data = Concurrent::Hash.new
22
-
23
- start_periodic_sync(sync_interval)
24
- end
25
-
26
- def record(config_key:, config_type:, counter:)
27
- return if @data.size >= @max_keys
28
-
29
- key = [config_key, config_type]
30
- @data[key] ||= Concurrent::Hash.new
31
-
32
- @data[key][counter] ||= 0
33
- @data[key][counter] += 1
34
- end
35
-
36
- private
37
-
38
- def counter_proto(counter, count)
39
- PrefabProto::ConfigEvaluationCounter.new(
40
- config_id: counter[:config_id],
41
- selected_index: counter[:selected_index],
42
- config_row_index: counter[:config_row_index],
43
- conditional_value_index: counter[:conditional_value_index],
44
- weighted_value_index: counter[:weighted_value_index],
45
- selected_value: counter[:selected_value],
46
- count: count
47
- )
48
- end
49
-
50
- def flush(to_ship, start_at_was)
51
- pool.post do
52
- LOG.debug "Flushing #{to_ship.size} summaries"
53
-
54
- summaries_proto = PrefabProto::ConfigEvaluationSummaries.new(
55
- start: start_at_was,
56
- end: Prefab::TimeHelpers.now_in_ms,
57
- summaries: summaries(to_ship)
58
- )
59
-
60
- result = post('/api/v1/telemetry', events(summaries_proto))
61
-
62
- LOG.debug "Uploaded #{to_ship.size} summaries: #{result.status}"
63
- end
64
- end
65
-
66
- def events(summaries)
67
- event = PrefabProto::TelemetryEvent.new(summaries: summaries)
68
-
69
- PrefabProto::TelemetryEvents.new(
70
- instance_hash: @client.instance_hash,
71
- events: [event]
72
- )
73
- end
74
-
75
- def summaries(data)
76
- data.map do |(config_key, config_type), counters|
77
- counter_protos = counters.map { |counter, count| counter_proto(counter, count) }
78
-
79
- PrefabProto::ConfigEvaluationSummary.new(
80
- key: config_key,
81
- type: config_type,
82
- counters: counter_protos
83
- )
84
- end
85
- end
86
- end
87
- end
@@ -1,78 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'periodic_sync'
4
-
5
- module Prefab
6
- # This class aggregates example contexts. It dedupes based on the
7
- # concatenation of the keys of the contexts.
8
- #
9
- # It shouldn't send the same context more than once per hour.
10
- class ExampleContextsAggregator
11
- include Prefab::PeriodicSync
12
-
13
- LOG = Prefab::InternalLogger.new(ExampleContextsAggregator)
14
-
15
- attr_reader :data, :cache
16
-
17
- ONE_HOUR = 60 * 60
18
-
19
- def initialize(client:, max_contexts:, sync_interval:)
20
- @client = client
21
- @max_contexts = max_contexts
22
- @name = 'example_contexts_aggregator'
23
-
24
- @data = Concurrent::Array.new
25
- @cache = Prefab::RateLimitCache.new(ONE_HOUR)
26
-
27
- start_periodic_sync(sync_interval)
28
- end
29
-
30
- def record(contexts)
31
- key = contexts.grouped_key
32
-
33
- return unless @data.size < @max_contexts && !@cache.fresh?(key)
34
-
35
- @cache.set(key)
36
-
37
- @data.push(contexts)
38
- end
39
-
40
- private
41
-
42
- def on_prepare_data
43
- @cache.prune
44
- end
45
-
46
- def flush(to_ship, _)
47
- pool.post do
48
- LOG.debug "Flushing #{to_ship.size} examples"
49
-
50
- result = post('/api/v1/telemetry', events(to_ship))
51
-
52
- LOG.debug "Uploaded #{to_ship.size} examples: #{result.status}"
53
- end
54
- end
55
-
56
- def example_contexts(to_ship)
57
- to_ship.map do |contexts|
58
- PrefabProto::ExampleContext.new(
59
- timestamp: contexts.seen_at * 1000,
60
- contextSet: contexts.slim_proto
61
- )
62
- end
63
- end
64
-
65
- def events(to_ship)
66
- event = PrefabProto::TelemetryEvent.new(
67
- example_contexts: PrefabProto::ExampleContexts.new(
68
- examples: example_contexts(to_ship)
69
- )
70
- )
71
-
72
- PrefabProto::TelemetryEvents.new(
73
- instance_hash: @client.instance_hash,
74
- events: [event]
75
- )
76
- end
77
- end
78
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- # This class implements exponential backoff with a maximum delay.
5
- #
6
- # This is the default sync interval for aggregators.
7
- class ExponentialBackoff
8
- def initialize(max_delay:, initial_delay: 2, multiplier: 2)
9
- @initial_delay = initial_delay
10
- @max_delay = max_delay
11
- @multiplier = multiplier
12
- @delay = initial_delay
13
- end
14
-
15
- def call
16
- delay = @delay
17
- @delay = [@delay * @multiplier, @max_delay].min
18
- delay
19
- end
20
- end
21
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class FeatureFlagClient
5
- def initialize(base_client)
6
- @base_client = base_client
7
- end
8
-
9
- def feature_is_on?(feature_name)
10
- feature_is_on_for?(feature_name, {})
11
- end
12
-
13
- def feature_is_on_for?(feature_name, properties)
14
- variant = @base_client.config_client.get(feature_name, false, properties)
15
-
16
- is_on?(variant)
17
- end
18
-
19
- def get(feature_name, properties, default: false)
20
- value = _get(feature_name, properties)
21
-
22
- value.nil? ? default : value
23
- end
24
-
25
- private
26
-
27
- def _get(feature_name, properties)
28
- @base_client.config_client.get(feature_name, nil, properties)
29
- end
30
-
31
- def is_on?(variant)
32
- return false if variant.nil?
33
-
34
- return variant if variant == !!variant
35
-
36
- variant.bool
37
- rescue StandardError
38
- @base_client.log.info("is_on? methods only work for boolean feature flags variants. This feature flags variant is '#{variant}'. Returning false")
39
- false
40
- end
41
- end
42
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Prefab
4
- class HttpConnection
5
- AUTH_USER = 'authuser'
6
- PROTO_HEADERS = {
7
- 'Content-Type' => 'application/x-protobuf',
8
- 'Accept' => 'application/x-protobuf',
9
- 'X-PrefabCloud-Client-Version' => "prefab-cloud-ruby-#{Prefab::VERSION}"
10
- }.freeze
11
-
12
- def initialize(api_root, api_key)
13
- @api_root = api_root
14
- @api_key = api_key
15
- end
16
-
17
- def get(path)
18
- connection(PROTO_HEADERS).get(path)
19
- end
20
-
21
- def post(path, body)
22
- connection(PROTO_HEADERS).post(path, body.to_proto)
23
- end
24
-
25
- def connection(headers = {})
26
- if Faraday::VERSION[0].to_i >= 2
27
- Faraday.new(@api_root) do |conn|
28
- conn.request :authorization, :basic, AUTH_USER, @api_key
29
-
30
- conn.headers.merge!(headers)
31
- end
32
- else
33
- Faraday.new(@api_root) do |conn|
34
- conn.request :basic_auth, AUTH_USER, @api_key
35
-
36
- conn.headers.merge!(headers)
37
- end
38
- end
39
- end
40
- end
41
- end