prefab-cloud-ruby 0 → 0.0.1

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.
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