prefab-cloud-ruby 0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc.sample +3 -0
  3. data/.github/workflows/ruby.yml +46 -0
  4. data/.gitmodules +3 -0
  5. data/.rubocop.yml +13 -0
  6. data/.tool-versions +1 -0
  7. data/CHANGELOG.md +169 -0
  8. data/CODEOWNERS +1 -0
  9. data/Gemfile +26 -0
  10. data/Gemfile.lock +188 -0
  11. data/LICENSE.txt +20 -0
  12. data/README.md +94 -0
  13. data/Rakefile +50 -0
  14. data/VERSION +1 -0
  15. data/bin/console +21 -0
  16. data/compile_protos.sh +18 -0
  17. data/lib/prefab/client.rb +153 -0
  18. data/lib/prefab/config_client.rb +292 -0
  19. data/lib/prefab/config_client_presenter.rb +18 -0
  20. data/lib/prefab/config_loader.rb +84 -0
  21. data/lib/prefab/config_resolver.rb +77 -0
  22. data/lib/prefab/config_value_unwrapper.rb +115 -0
  23. data/lib/prefab/config_value_wrapper.rb +18 -0
  24. data/lib/prefab/context.rb +179 -0
  25. data/lib/prefab/context_shape.rb +20 -0
  26. data/lib/prefab/context_shape_aggregator.rb +65 -0
  27. data/lib/prefab/criteria_evaluator.rb +136 -0
  28. data/lib/prefab/encryption.rb +65 -0
  29. data/lib/prefab/error.rb +6 -0
  30. data/lib/prefab/errors/env_var_parse_error.rb +11 -0
  31. data/lib/prefab/errors/initialization_timeout_error.rb +13 -0
  32. data/lib/prefab/errors/invalid_api_key_error.rb +19 -0
  33. data/lib/prefab/errors/missing_default_error.rb +13 -0
  34. data/lib/prefab/errors/missing_env_var_error.rb +11 -0
  35. data/lib/prefab/errors/uninitialized_error.rb +13 -0
  36. data/lib/prefab/evaluation.rb +52 -0
  37. data/lib/prefab/evaluation_summary_aggregator.rb +87 -0
  38. data/lib/prefab/example_contexts_aggregator.rb +78 -0
  39. data/lib/prefab/exponential_backoff.rb +21 -0
  40. data/lib/prefab/feature_flag_client.rb +42 -0
  41. data/lib/prefab/http_connection.rb +41 -0
  42. data/lib/prefab/internal_logger.rb +16 -0
  43. data/lib/prefab/local_config_parser.rb +151 -0
  44. data/lib/prefab/log_path_aggregator.rb +69 -0
  45. data/lib/prefab/logger_client.rb +264 -0
  46. data/lib/prefab/murmer3.rb +50 -0
  47. data/lib/prefab/options.rb +208 -0
  48. data/lib/prefab/periodic_sync.rb +69 -0
  49. data/lib/prefab/prefab.rb +56 -0
  50. data/lib/prefab/rate_limit_cache.rb +41 -0
  51. data/lib/prefab/resolved_config_presenter.rb +86 -0
  52. data/lib/prefab/time_helpers.rb +7 -0
  53. data/lib/prefab/weighted_value_resolver.rb +42 -0
  54. data/lib/prefab/yaml_config_parser.rb +34 -0
  55. data/lib/prefab-cloud-ruby.rb +57 -0
  56. data/lib/prefab_pb.rb +93 -0
  57. data/prefab-cloud-ruby.gemspec +155 -0
  58. data/test/.prefab.default.config.yaml +2 -0
  59. data/test/.prefab.unit_tests.config.yaml +28 -0
  60. data/test/integration_test.rb +150 -0
  61. data/test/integration_test_helpers.rb +151 -0
  62. data/test/support/common_helpers.rb +180 -0
  63. data/test/support/mock_base_client.rb +42 -0
  64. data/test/support/mock_config_client.rb +19 -0
  65. data/test/support/mock_config_loader.rb +1 -0
  66. data/test/test_client.rb +444 -0
  67. data/test/test_config_client.rb +109 -0
  68. data/test/test_config_loader.rb +117 -0
  69. data/test/test_config_resolver.rb +430 -0
  70. data/test/test_config_value_unwrapper.rb +224 -0
  71. data/test/test_config_value_wrapper.rb +42 -0
  72. data/test/test_context.rb +203 -0
  73. data/test/test_context_shape.rb +50 -0
  74. data/test/test_context_shape_aggregator.rb +147 -0
  75. data/test/test_criteria_evaluator.rb +726 -0
  76. data/test/test_encryption.rb +16 -0
  77. data/test/test_evaluation_summary_aggregator.rb +162 -0
  78. data/test/test_example_contexts_aggregator.rb +238 -0
  79. data/test/test_exponential_backoff.rb +18 -0
  80. data/test/test_feature_flag_client.rb +48 -0
  81. data/test/test_helper.rb +17 -0
  82. data/test/test_integration.rb +58 -0
  83. data/test/test_local_config_parser.rb +147 -0
  84. data/test/test_log_path_aggregator.rb +62 -0
  85. data/test/test_logger.rb +621 -0
  86. data/test/test_logger_initialization.rb +12 -0
  87. data/test/test_options.rb +75 -0
  88. data/test/test_prefab.rb +12 -0
  89. data/test/test_rate_limit_cache.rb +44 -0
  90. data/test/test_weighted_value_resolver.rb +71 -0
  91. metadata +337 -0
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestLocalConfigParser < Minitest::Test
6
+ FILE_NAME = 'example-config.yaml'
7
+ DEFAULT_MATCH = 'default'
8
+
9
+ def setup
10
+ super
11
+ @mock_resolver = MockResolver.new
12
+ end
13
+
14
+ def test_parse_int_config
15
+ key = :sample_int
16
+ parsed = Prefab::LocalConfigParser.parse(key, 123, {}, FILE_NAME)[key]
17
+ config = parsed[:config]
18
+
19
+ assert_equal FILE_NAME, parsed[:source]
20
+ assert_equal DEFAULT_MATCH, parsed[:match]
21
+ assert_equal :CONFIG, config.config_type
22
+ assert_equal key.to_s, config.key
23
+ assert_equal 1, config.rows.size
24
+ assert_equal 1, config.rows[0].values.size
25
+ assert_equal 123, config.rows[0].values[0].value.int
26
+ end
27
+
28
+ def test_flag_with_a_value
29
+ key = :flag_with_a_value
30
+ value = stringify_keys({ feature_flag: true, value: 'all-features' })
31
+ parsed = Prefab::LocalConfigParser.parse(key, value, {}, FILE_NAME)[key]
32
+ config = parsed[:config]
33
+
34
+ assert_equal FILE_NAME, parsed[:source]
35
+ assert_equal key, parsed[:match]
36
+ assert_equal :FEATURE_FLAG, config.config_type
37
+ assert_equal key.to_s, config.key
38
+ assert_equal 1, config.rows.size
39
+ assert_equal 1, config.rows[0].values.size
40
+
41
+ value_row = config.rows[0].values[0]
42
+ assert_equal 'all-features', Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, key, {}, @mock_resolver).unwrap
43
+ end
44
+
45
+ def test_flag_in_user_key
46
+ key = :flag_in_user_key
47
+ value = stringify_keys({ 'feature_flag': 'true', value: true,
48
+ criterion: { operator: 'PROP_IS_ONE_OF', property: 'user.key', values: %w[abc123 xyz987] } })
49
+ parsed = Prefab::LocalConfigParser.parse(key, value, {}, FILE_NAME)[key]
50
+ config = parsed[:config]
51
+
52
+ assert_equal FILE_NAME, parsed[:source]
53
+ assert_equal key, parsed[:match]
54
+ assert_equal :FEATURE_FLAG, config.config_type
55
+ assert_equal key.to_s, config.key
56
+ assert_equal 1, config.rows.size
57
+ assert_equal 1, config.rows[0].values.size
58
+ assert_equal 1, config.rows[0].values[0].criteria.size
59
+
60
+ value_row = config.rows[0].values[0]
61
+ assert_equal true, Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, key, {}, @mock_resolver).unwrap
62
+
63
+ assert_equal 'user.key', value_row.criteria[0].property_name
64
+ assert_equal :PROP_IS_ONE_OF, value_row.criteria[0].operator
65
+ assert_equal %w[abc123 xyz987], value_row.criteria[0].value_to_match.string_list.values
66
+ end
67
+
68
+ def test_provided_values
69
+ with_env('LOOKUP_ENV', 'from env') do
70
+ key = :test_provided
71
+ value = stringify_keys({type: 'provided', source: 'ENV_VAR', lookup: 'LOOKUP_ENV'})
72
+ parsed = Prefab::LocalConfigParser.parse(key, value, {}, FILE_NAME)[key]
73
+ config = parsed[:config]
74
+
75
+ assert_equal FILE_NAME, parsed[:source]
76
+ assert_equal 'LOOKUP_ENV', parsed[:match]
77
+ assert_equal :CONFIG, config.config_type
78
+ assert_equal key.to_s, config.key
79
+ assert_equal 1, config.rows.size
80
+ assert_equal 1, config.rows[0].values.size
81
+
82
+ value_row = config.rows[0].values[0]
83
+ provided = value_row.value.provided
84
+ assert_equal :ENV_VAR, provided.source
85
+ assert_equal 'LOOKUP_ENV', provided.lookup
86
+ assert_equal 'from env', Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, config, {}, @mock_resolver).unwrap
87
+ reportable_value = Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, config, {}, @mock_resolver).reportable_value
88
+ assert_equal 'from env', reportable_value
89
+ end
90
+ end
91
+
92
+ def test_confidential_provided_values
93
+ with_env('LOOKUP_ENV', 'from env') do
94
+ key = :test_provided
95
+ value = stringify_keys({type: 'provided', source: 'ENV_VAR', lookup: 'LOOKUP_ENV', confidential: true})
96
+ parsed = Prefab::LocalConfigParser.parse(key, value, {}, FILE_NAME)[key]
97
+ config = parsed[:config]
98
+
99
+ value_row = config.rows[0].values[0]
100
+ provided = value_row.value.provided
101
+ assert_equal :ENV_VAR, provided.source
102
+ assert_equal 'LOOKUP_ENV', provided.lookup
103
+ assert_equal 'from env', Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, config, {}, @mock_resolver).unwrap
104
+ reportable_value = Prefab::ConfigValueUnwrapper.deepest_value(value_row.value, config, {}, @mock_resolver).reportable_value
105
+ assert reportable_value.start_with? Prefab::ConfigValueUnwrapper::CONFIDENTIAL_PREFIX
106
+ end
107
+ end
108
+
109
+ def test_confidential_values
110
+ key = :test_confidential
111
+ value = stringify_keys({value: 'a confidential string', confidential: true})
112
+ parsed = Prefab::LocalConfigParser.parse(key, value, {}, FILE_NAME)[key]
113
+ config = parsed[:config]
114
+
115
+ assert_equal FILE_NAME, parsed[:source]
116
+ assert_equal :CONFIG, config.config_type
117
+ assert_equal key.to_s, config.key
118
+ assert_equal 1, config.rows.size
119
+ assert_equal 1, config.rows[0].values.size
120
+
121
+ value_row = config.rows[0].values[0]
122
+ config_value = value_row.value
123
+ assert_equal 'a confidential string', Prefab::ConfigValueUnwrapper.deepest_value(config_value, key, {}, @mock_resolver).unwrap
124
+ reportable_value = Prefab::ConfigValueUnwrapper.deepest_value(config_value, key, {}, @mock_resolver).reportable_value
125
+ assert reportable_value.start_with? Prefab::ConfigValueUnwrapper::CONFIDENTIAL_PREFIX
126
+ end
127
+
128
+ private
129
+
130
+ def stringify_keys(hash)
131
+ deep_transform_keys(hash, &:to_s)
132
+ end
133
+
134
+ def deep_transform_keys(hash, &block)
135
+ result = {}
136
+ hash.each do |key, value|
137
+ result[yield(key)] = value.is_a?(Hash) ? deep_transform_keys(value, &block) : value
138
+ end
139
+ result
140
+ end
141
+
142
+ class MockResolver
143
+ def get(key)
144
+ raise "unexpected key"
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'timecop'
5
+
6
+ class TestLogPathAggregator < Minitest::Test
7
+ MAX_WAIT = 2
8
+ SLEEP_TIME = 0.01
9
+
10
+ def test_push
11
+ client = new_client
12
+ aggregator = Prefab::LogPathAggregator.new(client: client, max_paths: 2, sync_interval: 1000)
13
+
14
+ aggregator.push('test.test_log_path_aggregator.test_push.1', ::Logger::INFO)
15
+ aggregator.push('test.test_log_path_aggregator.test_push.2', ::Logger::DEBUG)
16
+
17
+ assert_equal 2, aggregator.data.size
18
+
19
+ # we've reached the limit, so no more
20
+ aggregator.push('test.test_log_path_aggregator.test_push.3', ::Logger::INFO)
21
+ assert_equal 2, aggregator.data.size
22
+
23
+ assert_only_expected_logs
24
+ end
25
+
26
+ def test_sync
27
+ Timecop.freeze do
28
+ client = new_client(namespace: 'this.is.a.namespace')
29
+
30
+ 2.times { client.log.info('here is a message') }
31
+ 3.times { client.log.error('here is a message') }
32
+
33
+ requests = wait_for_post_requests(client) do
34
+ client.log_path_aggregator.send(:sync)
35
+ end
36
+
37
+ assert_equal '/api/v1/known-loggers', requests[0][0]
38
+ sent_logger = requests[0][1]
39
+ assert_equal 'this.is.a.namespace', sent_logger.namespace
40
+ assert_equal Prefab::TimeHelpers.now_in_ms, sent_logger.start_at
41
+ assert_equal Prefab::TimeHelpers.now_in_ms, sent_logger.end_at
42
+ assert_equal client.instance_hash, sent_logger.instance_hash
43
+ assert_includes sent_logger.loggers, PrefabProto::Logger.new(logger_name: 'test.test_log_path_aggregator.test_sync', infos: 2, errors: 3)
44
+ assert_includes sent_logger.loggers, PrefabProto::Logger.new(logger_name: 'cloud.prefab.client.client', debugs: 1) # spot test that internal logging is here too
45
+
46
+ assert_logged [
47
+ 'WARN 2023-08-09 15:18:12 -0400: cloud.prefab.client.configclient No success loading checkpoints',
48
+ 'ERROR 2023-08-09 15:18:12 -0400: test.test_log_path_aggregator.test_sync here is a message'
49
+ ]
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def new_client(overrides = {})
56
+ super(**{
57
+ prefab_datasources: Prefab::Options::DATASOURCES::ALL,
58
+ api_key: '123-development-yourapikey-SDK',
59
+ collect_sync_interval: 1000 # we'll trigger sync manually in our test
60
+ }.merge(overrides))
61
+ end
62
+ end