prefab-cloud-ruby 1.2.1 → 1.3.0

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.
@@ -0,0 +1,54 @@
1
+ # Modified from https://github.com/reidmorrison/rails_semantic_logger/blob/master/lib/rails_semantic_logger/action_controller/log_subscriber.rb
2
+ #
3
+ # Copyright 2012, 2013, 2014, 2015 Reid Morrison
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ module Prefab
18
+ module LogSubscribers
19
+ class ActionControllerSubscriber < ActiveSupport::LogSubscriber
20
+
21
+ INTERNAL_PARAMS = %w[controller action format _method only_path].freeze
22
+
23
+ LOG = Prefab::StaticLogger.new("rails.controller.request")
24
+
25
+ # With great debt to https://github.com/reidmorrison/rails_semantic_logger/blob/master/lib/rails_semantic_logger/action_controller/log_subscriber.rb
26
+ def process_action(event)
27
+ payload = event.payload.dup
28
+ payload.delete(:headers)
29
+ payload.delete(:request)
30
+ payload.delete(:response)
31
+ params = payload[:params]
32
+
33
+ if params.kind_of?(Hash) || params.kind_of?(::ActionController::Parameters)
34
+ payload[:params] = params.to_unsafe_h unless params.is_a?(Hash)
35
+ payload[:params] = params.except(*INTERNAL_PARAMS)
36
+
37
+ if payload[:params].empty?
38
+ payload.delete(:params)
39
+ elsif params["file"]
40
+ # When logging to JSON the entire tempfile is logged, so convert it to a string.
41
+ payload[:params]["file"] = params["file"].inspect
42
+ end
43
+ end
44
+
45
+ # Rounds off the runtimes. For example, :view_runtime, :mongo_runtime, etc.
46
+ payload.keys.each do |key|
47
+ payload[key] = payload[key].to_f.round(2) if key.to_s =~ /(.*)_runtime/
48
+ end
49
+
50
+ LOG.info "#{payload[:status]} #{payload[:controller]}##{payload[:action]}", **payload
51
+ end
52
+ end
53
+ end
54
+ end
@@ -5,7 +5,6 @@ module Prefab
5
5
  SEP = '.'
6
6
  BASE_KEY = 'log-level'
7
7
  UNKNOWN_PATH = 'unknown.'
8
- INTERNAL_PREFIX = 'cloud.prefab.client'
9
8
 
10
9
  LOG_LEVEL_LOOKUPS = {
11
10
  PrefabProto::LogLevel::NOT_SET_LOG_LEVEL => ::Logger::DEBUG,
@@ -65,12 +64,6 @@ module Prefab
65
64
  def log_internal(severity, message, path, log_context={}, &block)
66
65
  return if @recurse_check[local_log_id]
67
66
  @recurse_check[local_log_id] = true
68
-
69
- path = if path
70
- "#{INTERNAL_PREFIX}.#{path}"
71
- else
72
- INTERNAL_PREFIX
73
- end
74
67
  begin
75
68
  log(message, path, nil, severity, log_context, &block)
76
69
  ensure
@@ -17,6 +17,8 @@ module Prefab
17
17
  attr_reader :prefab_envs
18
18
  attr_reader :collect_sync_interval
19
19
  attr_reader :use_local_cache
20
+ attr_reader :datafile
21
+ attr_reader :disable_action_controller_logging
20
22
  attr_accessor :is_fork
21
23
 
22
24
  DEFAULT_LOG_FORMATTER = proc { |data|
@@ -82,7 +84,9 @@ module Prefab
82
84
  collect_evaluation_summaries: true,
83
85
  collect_max_evaluation_summaries: DEFAULT_MAX_EVAL_SUMMARIES,
84
86
  allow_telemetry_in_local_mode: false,
85
- x_use_local_cache: false
87
+ x_datafile: ENV['PREFAB_DATAFILE'],
88
+ x_use_local_cache: false,
89
+ disable_action_controller_logging: false
86
90
  )
87
91
  @api_key = api_key
88
92
  @logdev = logdev
@@ -94,6 +98,7 @@ module Prefab
94
98
  @initialization_timeout_sec = initialization_timeout_sec
95
99
  @on_init_failure = on_init_failure
96
100
  @prefab_datasources = prefab_datasources
101
+ @datafile = x_datafile
97
102
  @prefab_config_classpath_dir = prefab_config_classpath_dir
98
103
  @prefab_config_override_dir = prefab_config_override_dir
99
104
  @prefab_envs = Array(prefab_envs)
@@ -104,6 +109,7 @@ module Prefab
104
109
  @collect_max_evaluation_summaries = collect_max_evaluation_summaries
105
110
  @allow_telemetry_in_local_mode = allow_telemetry_in_local_mode
106
111
  @use_local_cache = x_use_local_cache
112
+ @disable_action_controller_logging = disable_action_controller_logging
107
113
  @is_fork = false
108
114
 
109
115
  # defaults that may be overridden by context_upload_mode
@@ -134,6 +140,10 @@ module Prefab
134
140
  @prefab_datasources == DATASOURCES::LOCAL_ONLY
135
141
  end
136
142
 
143
+ def datafile?
144
+ !@datafile.nil?
145
+ end
146
+
137
147
  def collect_max_paths
138
148
  return 0 unless telemetry_allowed?(@collect_logger_counts)
139
149
 
@@ -44,7 +44,7 @@ module Prefab
44
44
  if v.nil?
45
45
  hash[k] = ConfigRow.new(k, nil, nil, nil)
46
46
  else
47
- value = @resolver.evaluate(v[:config])&.unwrapped_value
47
+ value = @resolver.evaluate(v[:config])&.reportable_value
48
48
  hash[k] = ConfigRow.new(k, value, v[:match], v[:source])
49
49
  end
50
50
  end
@@ -65,7 +65,7 @@ module Prefab
65
65
  if v.nil?
66
66
  elements << 'tombstone'
67
67
  else
68
- value = @resolver.evaluate(v[:config])&.unwrapped_value
68
+ value = @resolver.evaluate(v[:config])&.reportable_value
69
69
  elements << value.to_s.slice(0..34).ljust(35)
70
70
  elements << value.class.to_s.slice(0..6).ljust(7)
71
71
  elements << "Match: #{v[:match]}".slice(0..29).ljust(30)
@@ -1,30 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prefab
4
- class SseLogger < ::Logger
4
+ class SseLogger < InternalLogger
5
5
  def initialize()
6
- @path = "sse"
7
- end
8
-
9
- def debug(progname = nil, &block)
10
- Prefab::LoggerClient.instance.log_internal ::Logger::DEBUG, progname, @path, &block
11
- end
12
-
13
- def info(progname = nil, &block)
14
- Prefab::LoggerClient.instance.log_internal ::Logger::INFO, progname, @path, &block
6
+ super("sse")
15
7
  end
16
8
 
17
9
  # The SSE::Client warns on a perfectly normal stream disconnect, recast to info
18
- def warn(progname = nil, &block)
19
- Prefab::LoggerClient.instance.log_internal ::Logger::INFO, progname, @path, &block
20
- end
21
-
22
- def error(progname = nil, &block)
23
- Prefab::LoggerClient.instance.log_internal ::Logger::ERROR, progname, @path, &block
24
- end
25
-
26
- def fatal(progname = nil, &block)
27
- Prefab::LoggerClient.instance.log_internal ::Logger::FATAL, progname, @path, &block
10
+ def warn(msg = nil,**log_context, &block)
11
+ Prefab::LoggerClient.instance.log_internal ::Logger::INFO, msg, @path, log_context, &block
28
12
  end
29
13
  end
30
14
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prefab
4
+ class StaticLogger < ::Logger
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def debug(msg = nil, **log_context, &block)
10
+ Prefab::LoggerClient.instance.log_internal ::Logger::DEBUG, msg, @path, log_context, &block
11
+ end
12
+
13
+ def info(msg = nil, **log_context, &block)
14
+ Prefab::LoggerClient.instance.log_internal ::Logger::INFO, msg, @path, log_context, &block
15
+ end
16
+
17
+ def warn(msg = nil, **log_context, &block)
18
+ Prefab::LoggerClient.instance.log_internal ::Logger::WARN, msg, @path, log_context, &block
19
+ end
20
+
21
+ def error(msg = nil, **log_context, &block)
22
+ Prefab::LoggerClient.instance.log_internal ::Logger::ERROR, msg, @path, log_context, &block
23
+ end
24
+
25
+ def fatal(msg = nil, **log_context, &block)
26
+ Prefab::LoggerClient.instance.log_internal ::Logger::FATAL, msg, @path, log_context, &block
27
+ end
28
+ end
29
+ end
@@ -14,11 +14,14 @@ require 'prefab_pb'
14
14
  require 'prefab/time_helpers'
15
15
  require 'prefab/error'
16
16
  require 'prefab/evaluation'
17
+ require 'prefab/encryption'
17
18
  require 'prefab/exponential_backoff'
18
19
  require 'prefab/errors/initialization_timeout_error'
19
20
  require 'prefab/errors/invalid_api_key_error'
20
21
  require 'prefab/errors/missing_default_error'
22
+ require 'prefab/errors/env_var_parse_error'
21
23
  require 'prefab/options'
24
+ require 'prefab/static_logger'
22
25
  require 'prefab/internal_logger'
23
26
  require 'prefab/rate_limit_cache'
24
27
  require 'prefab/context_shape_aggregator'
@@ -39,6 +42,9 @@ require 'prefab/config_resolver'
39
42
  require 'prefab/http_connection'
40
43
  require 'prefab/context'
41
44
  require 'prefab/logger_client'
45
+ require 'active_support/deprecation'
46
+ require 'active_support'
47
+ require 'prefab/log_subscribers/action_controller_subscriber'
42
48
  require 'prefab/client'
43
49
  require 'prefab/config_client_presenter'
44
50
  require 'prefab/config_client'
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: prefab-cloud-ruby 1.2.1 ruby lib
5
+ # stub: prefab-cloud-ruby 1.3.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "prefab-cloud-ruby".freeze
9
- s.version = "1.2.1"
9
+ s.version = "1.3.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Jeff Dwyer".freeze]
14
- s.date = "2023-11-01"
14
+ s.date = "2023-11-13"
15
15
  s.description = "Feature Flags, Live Config, and Dynamic Log Levels as a service".freeze
16
16
  s.email = "jdwyer@prefab.cloud".freeze
17
17
  s.executables = ["console".freeze]
@@ -48,7 +48,9 @@ Gem::Specification.new do |s|
48
48
  "lib/prefab/context_shape.rb",
49
49
  "lib/prefab/context_shape_aggregator.rb",
50
50
  "lib/prefab/criteria_evaluator.rb",
51
+ "lib/prefab/encryption.rb",
51
52
  "lib/prefab/error.rb",
53
+ "lib/prefab/errors/env_var_parse_error.rb",
52
54
  "lib/prefab/errors/initialization_timeout_error.rb",
53
55
  "lib/prefab/errors/invalid_api_key_error.rb",
54
56
  "lib/prefab/errors/missing_default_error.rb",
@@ -61,6 +63,7 @@ Gem::Specification.new do |s|
61
63
  "lib/prefab/internal_logger.rb",
62
64
  "lib/prefab/local_config_parser.rb",
63
65
  "lib/prefab/log_path_aggregator.rb",
66
+ "lib/prefab/log_subscribers/action_controller_subscriber.rb",
64
67
  "lib/prefab/logger_client.rb",
65
68
  "lib/prefab/murmer3.rb",
66
69
  "lib/prefab/options.rb",
@@ -69,6 +72,7 @@ Gem::Specification.new do |s|
69
72
  "lib/prefab/rate_limit_cache.rb",
70
73
  "lib/prefab/resolved_config_presenter.rb",
71
74
  "lib/prefab/sse_logger.rb",
75
+ "lib/prefab/static_logger.rb",
72
76
  "lib/prefab/time_helpers.rb",
73
77
  "lib/prefab/weighted_value_resolver.rb",
74
78
  "lib/prefab/yaml_config_parser.rb",
@@ -82,6 +86,7 @@ Gem::Specification.new do |s|
82
86
  "test/support/mock_base_client.rb",
83
87
  "test/support/mock_config_client.rb",
84
88
  "test/support/mock_config_loader.rb",
89
+ "test/test_action_controller.rb",
85
90
  "test/test_client.rb",
86
91
  "test/test_config_client.rb",
87
92
  "test/test_config_loader.rb",
@@ -92,6 +97,7 @@ Gem::Specification.new do |s|
92
97
  "test/test_context_shape.rb",
93
98
  "test/test_context_shape_aggregator.rb",
94
99
  "test/test_criteria_evaluator.rb",
100
+ "test/test_encryption.rb",
95
101
  "test/test_evaluation_summary_aggregator.rb",
96
102
  "test/test_example_contexts_aggregator.rb",
97
103
  "test/test_exponential_backoff.rb",
@@ -123,6 +129,7 @@ Gem::Specification.new do |s|
123
129
  s.add_runtime_dependency(%q<google-protobuf>.freeze, [">= 0"])
124
130
  s.add_runtime_dependency(%q<ld-eventsource>.freeze, [">= 0"])
125
131
  s.add_runtime_dependency(%q<uuid>.freeze, [">= 0"])
132
+ s.add_runtime_dependency(%q<activesupport>.freeze, [">= 4"])
126
133
  s.add_development_dependency(%q<benchmark-ips>.freeze, [">= 0"])
127
134
  s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
128
135
  s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
@@ -135,6 +142,7 @@ Gem::Specification.new do |s|
135
142
  s.add_dependency(%q<google-protobuf>.freeze, [">= 0"])
136
143
  s.add_dependency(%q<ld-eventsource>.freeze, [">= 0"])
137
144
  s.add_dependency(%q<uuid>.freeze, [">= 0"])
145
+ s.add_dependency(%q<activesupport>.freeze, [">= 4"])
138
146
  s.add_dependency(%q<benchmark-ips>.freeze, [">= 0"])
139
147
  s.add_dependency(%q<bundler>.freeze, [">= 0"])
140
148
  s.add_dependency(%q<juwelier>.freeze, ["~> 2.4.9"])
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestActionController < Minitest::Test
6
+ def setup
7
+ super
8
+ new_client(config: log_level_config("log-level.rails.controller", "INFO"))
9
+ @subscriber = Prefab::LogSubscribers::ActionControllerSubscriber.new
10
+ end
11
+
12
+ def test_load
13
+ @subscriber.process_action(event)
14
+ assert_logged ['INFO 2023-08-09 15:18:12 -0400: rails.controller.request 200 MyController#index action=index controller=MyController db_runtime=0.05 format=application/html method=GET params={"p1"=>"v1"} path=/my?p1=v1 status=200 view_runtime=0.02']
15
+ end
16
+
17
+
18
+ def event
19
+ ActiveSupport::Notifications::Event.new(
20
+ 'process_action.action_controller',
21
+ Time.now,
22
+ Time.now,
23
+ 99,
24
+ status: 200,
25
+ controller: 'MyController',
26
+ action: 'index',
27
+ format: 'application/html',
28
+ method: 'GET',
29
+ path: '/my?p1=v1',
30
+ params: { 'p1' => 'v1' },
31
+ db_runtime: 0.051123,
32
+ view_runtime: 0.024555
33
+ )
34
+ end
35
+
36
+ def log_level_config(path, level)
37
+ PrefabProto::Config.new(
38
+ id: 123,
39
+ key: path,
40
+ config_type: PrefabProto::ConfigType::LOG_LEVEL,
41
+ rows: [
42
+ PrefabProto::ConfigRow.new(
43
+ values: [
44
+ PrefabProto::ConditionalValue.new(
45
+ value: PrefabProto::ConfigValue.new(log_level: level)
46
+ )
47
+ ]
48
+ )
49
+ ]
50
+ )
51
+ end
52
+ end
@@ -3,72 +3,219 @@
3
3
  require 'test_helper'
4
4
 
5
5
  class TestConfigValueUnwrapper < Minitest::Test
6
- CONFIG_KEY = 'config_key'
6
+ CONFIG = PrefabProto::Config.new(
7
+ key: 'config_key'
8
+ )
7
9
  EMPTY_CONTEXT = Prefab::Context.new()
10
+ DECRYPTION_KEY_NAME = "decryption.key"
11
+ DECRYPTION_KEY_VALUE = Prefab::Encryption.generate_new_hex_key
12
+
13
+ def setup
14
+ super
15
+ @mock_resolver = MockResolver.new
16
+ end
8
17
 
9
18
  def test_unwrapping_int
10
19
  config_value = PrefabProto::ConfigValue.new(int: 123)
11
- assert_equal 123, unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
20
+ assert_equal 123, unwrap(config_value, CONFIG, EMPTY_CONTEXT)
12
21
  end
13
22
 
14
23
  def test_unwrapping_string
15
24
  config_value = PrefabProto::ConfigValue.new(string: 'abc')
16
- assert_equal 'abc', unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
25
+ assert_equal 'abc', unwrap(config_value, CONFIG, EMPTY_CONTEXT)
26
+ assert_equal 'abc', reportable_value(config_value, CONFIG, EMPTY_CONTEXT)
17
27
  end
18
28
 
19
29
  def test_unwrapping_double
20
30
  config_value = PrefabProto::ConfigValue.new(double: 1.23)
21
- assert_equal 1.23, unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
31
+ assert_equal 1.23, unwrap(config_value, CONFIG, EMPTY_CONTEXT)
22
32
  end
23
33
 
24
34
  def test_unwrapping_bool
25
35
  config_value = PrefabProto::ConfigValue.new(bool: true)
26
- assert_equal true, unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
36
+ assert_equal true, unwrap(config_value, CONFIG, EMPTY_CONTEXT)
27
37
 
28
38
  config_value = PrefabProto::ConfigValue.new(bool: false)
29
- assert_equal false, unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
39
+ assert_equal false, unwrap(config_value, CONFIG, EMPTY_CONTEXT)
30
40
  end
31
41
 
32
42
  def test_unwrapping_log_level
33
43
  config_value = PrefabProto::ConfigValue.new(log_level: :INFO)
34
- assert_equal :INFO, unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
44
+ assert_equal :INFO, unwrap(config_value, CONFIG, EMPTY_CONTEXT)
35
45
  end
36
46
 
37
47
  def test_unwrapping_string_list
38
48
  config_value = PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: %w[a b c]))
39
- assert_equal %w[a b c], unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
49
+ assert_equal %w[a b c], unwrap(config_value, CONFIG, EMPTY_CONTEXT)
40
50
  end
41
51
 
42
52
  def test_unwrapping_weighted_values
43
53
  # single value
44
54
  config_value = PrefabProto::ConfigValue.new(weighted_values: weighted_values([['abc', 1]]))
45
55
 
46
- assert_equal 'abc', unwrap(config_value, CONFIG_KEY, EMPTY_CONTEXT)
56
+ assert_equal 'abc', unwrap(config_value, CONFIG, EMPTY_CONTEXT)
47
57
 
48
58
  # multiple values, evenly distributed
49
59
  config_value = PrefabProto::ConfigValue.new(weighted_values: weighted_values([['abc', 1], ['def', 1], ['ghi', 1]]))
50
- assert_equal 'def', unwrap(config_value, CONFIG_KEY, context_with_key('user:000'))
51
- assert_equal 'ghi', unwrap(config_value, CONFIG_KEY, context_with_key('user:456'))
52
- assert_equal 'abc', unwrap(config_value, CONFIG_KEY, context_with_key('user:789'))
53
- assert_equal 'ghi', unwrap(config_value, CONFIG_KEY, context_with_key('user:888'))
60
+ assert_equal 'def', unwrap(config_value, CONFIG, context_with_key('user:000'))
61
+ assert_equal 'ghi', unwrap(config_value, CONFIG, context_with_key('user:456'))
62
+ assert_equal 'abc', unwrap(config_value, CONFIG, context_with_key('user:789'))
63
+ assert_equal 'ghi', unwrap(config_value, CONFIG, context_with_key('user:888'))
54
64
 
55
65
  # multiple values, unevenly distributed
56
66
  config_value = PrefabProto::ConfigValue.new(weighted_values: weighted_values([['abc', 1], ['def', 99], ['ghi', 1]]))
57
- assert_equal 'def', unwrap(config_value, CONFIG_KEY, context_with_key('user:123'))
58
- assert_equal 'def', unwrap(config_value, CONFIG_KEY, context_with_key('user:456'))
59
- assert_equal 'def', unwrap(config_value, CONFIG_KEY, context_with_key('user:789'))
60
- assert_equal 'def', unwrap(config_value, CONFIG_KEY, context_with_key('user:012'))
61
- assert_equal 'ghi', unwrap(config_value, CONFIG_KEY, context_with_key('user:428'))
62
- assert_equal 'abc', unwrap(config_value, CONFIG_KEY, context_with_key('user:548'))
67
+ assert_equal 'def', unwrap(config_value, CONFIG, context_with_key('user:123'))
68
+ assert_equal 'def', unwrap(config_value, CONFIG, context_with_key('user:456'))
69
+ assert_equal 'def', unwrap(config_value, CONFIG, context_with_key('user:789'))
70
+ assert_equal 'def', unwrap(config_value, CONFIG, context_with_key('user:012'))
71
+ assert_equal 'ghi', unwrap(config_value, CONFIG, context_with_key('user:428'))
72
+ assert_equal 'abc', unwrap(config_value, CONFIG, context_with_key('user:548'))
73
+ end
74
+
75
+ def test_unwrapping_provided_values
76
+ with_env('ENV_VAR_NAME', 'unit test value')do
77
+ value = PrefabProto::Provided.new(
78
+ source: :ENV_VAR,
79
+ lookup: "ENV_VAR_NAME"
80
+ )
81
+ config_value = PrefabProto::ConfigValue.new(provided: value)
82
+ assert_equal 'unit test value', unwrap(config_value, CONFIG, EMPTY_CONTEXT)
83
+ end
84
+ end
85
+
86
+ def test_unwrapping_provided_values_of_type_string_list
87
+ with_env('ENV_VAR_NAME', '["bob","cary"]')do
88
+ value = PrefabProto::Provided.new(
89
+ source: :ENV_VAR,
90
+ lookup: "ENV_VAR_NAME"
91
+ )
92
+ config_value = PrefabProto::ConfigValue.new(provided: value)
93
+ assert_equal ["bob", "cary"], unwrap(config_value, CONFIG, EMPTY_CONTEXT)
94
+ end
95
+ end
96
+
97
+ def test_unwrapping_provided_values_coerces_to_int
98
+ with_env('ENV_VAR_NAME', '42')do
99
+ value = PrefabProto::Provided.new(
100
+ source: :ENV_VAR,
101
+ lookup: "ENV_VAR_NAME"
102
+ )
103
+ config_value = PrefabProto::ConfigValue.new(provided: value)
104
+ assert_equal 42, unwrap(config_value, config_of(PrefabProto::Config::ValueType::INT), EMPTY_CONTEXT)
105
+ end
106
+ end
107
+
108
+ def test_unwrapping_provided_values_when_value_type_mismatch
109
+ with_env('ENV_VAR_NAME', 'not an int')do
110
+ value = PrefabProto::Provided.new(
111
+ source: :ENV_VAR,
112
+ lookup: "ENV_VAR_NAME"
113
+ )
114
+ config_value = PrefabProto::ConfigValue.new(provided: value)
115
+
116
+ assert_raises Prefab::Errors::EnvVarParseError do
117
+ unwrap(config_value, config_of(PrefabProto::Config::ValueType::INT), EMPTY_CONTEXT)
118
+ end
119
+ end
120
+ end
121
+
122
+ def test_coerce
123
+ assert_equal "string", Prefab::ConfigValueUnwrapper.coerce_into_type("string", CONFIG, "ENV")
124
+ assert_equal 42, Prefab::ConfigValueUnwrapper.coerce_into_type("42", CONFIG, "ENV")
125
+ assert_equal false, Prefab::ConfigValueUnwrapper.coerce_into_type("false", CONFIG, "ENV")
126
+ assert_equal 42.42, Prefab::ConfigValueUnwrapper.coerce_into_type("42.42", CONFIG, "ENV")
127
+ assert_equal ["a","b"], Prefab::ConfigValueUnwrapper.coerce_into_type("['a','b']", CONFIG, "ENV")
128
+
129
+ assert_equal "string", Prefab::ConfigValueUnwrapper.coerce_into_type("string", config_of(PrefabProto::Config::ValueType::STRING),"ENV")
130
+ assert_equal "42", Prefab::ConfigValueUnwrapper.coerce_into_type("42", config_of(PrefabProto::Config::ValueType::STRING),"ENV")
131
+ assert_equal "42.42", Prefab::ConfigValueUnwrapper.coerce_into_type("42.42", config_of(PrefabProto::Config::ValueType::STRING),"ENV")
132
+ assert_equal 42, Prefab::ConfigValueUnwrapper.coerce_into_type("42", config_of(PrefabProto::Config::ValueType::INT),"ENV")
133
+ assert_equal false, Prefab::ConfigValueUnwrapper.coerce_into_type("false", config_of(PrefabProto::Config::ValueType::BOOL),"ENV")
134
+ assert_equal 42.42, Prefab::ConfigValueUnwrapper.coerce_into_type("42.42", config_of(PrefabProto::Config::ValueType::DOUBLE),"ENV")
135
+ assert_equal ["a","b"], Prefab::ConfigValueUnwrapper.coerce_into_type("['a','b']", config_of(PrefabProto::Config::ValueType::STRING_LIST),"ENV")
136
+
137
+ assert_raises Prefab::Errors::EnvVarParseError do
138
+ Prefab::ConfigValueUnwrapper.coerce_into_type("not an int", config_of(PrefabProto::Config::ValueType::INT), "ENV")
139
+ end
140
+ assert_raises Prefab::Errors::EnvVarParseError do
141
+ Prefab::ConfigValueUnwrapper.coerce_into_type("not bool", config_of(PrefabProto::Config::ValueType::BOOL), "ENV")
142
+ end
143
+ assert_raises Prefab::Errors::EnvVarParseError do
144
+ Prefab::ConfigValueUnwrapper.coerce_into_type("not a double", config_of(PrefabProto::Config::ValueType::DOUBLE), "ENV")
145
+ end
146
+ assert_raises Prefab::Errors::EnvVarParseError do
147
+ Prefab::ConfigValueUnwrapper.coerce_into_type("not a list", config_of(PrefabProto::Config::ValueType::STRING_LIST), "ENV")
148
+ end
149
+ end
150
+
151
+ def test_unwrapping_provided_values_with_missing_env_var
152
+ value = PrefabProto::Provided.new(
153
+ source: :ENV_VAR,
154
+ lookup: "NON_EXISTENT_ENV_VAR_NAME"
155
+ )
156
+ config_value = PrefabProto::ConfigValue.new(provided: value)
157
+ assert_equal '', unwrap(config_value, CONFIG, EMPTY_CONTEXT)
158
+ end
159
+
160
+ def test_unwrapping_encrypted_values_decrypts
161
+ clear_text = "very secret stuff"
162
+ encrypted = Prefab::Encryption.new(DECRYPTION_KEY_VALUE).encrypt(clear_text)
163
+ config_value = PrefabProto::ConfigValue.new(string: encrypted, decrypt_with: "decryption.key")
164
+ assert_equal clear_text, unwrap(config_value, CONFIG, EMPTY_CONTEXT)
165
+ end
166
+
167
+ def test_confidential
168
+ config_value = PrefabProto::ConfigValue.new(confidential: true, string: "something confidential")
169
+ assert reportable_value(config_value, CONFIG, EMPTY_CONTEXT).start_with? Prefab::ConfigValueUnwrapper::CONFIDENTIAL_PREFIX
170
+ end
171
+
172
+ def test_unwrap_confiential_provided
173
+ with_env('PAAS_PASSWORD', "the password")do
174
+ value = PrefabProto::Provided.new(
175
+ source: :ENV_VAR,
176
+ lookup: "PAAS_PASSWORD"
177
+ )
178
+ config_value = PrefabProto::ConfigValue.new(provided: value, confidential: true)
179
+ assert_equal "the password", unwrap(config_value, CONFIG, EMPTY_CONTEXT)
180
+ assert reportable_value(config_value, CONFIG, EMPTY_CONTEXT).start_with? Prefab::ConfigValueUnwrapper::CONFIDENTIAL_PREFIX
181
+ end
63
182
  end
64
183
 
65
184
  private
66
185
 
186
+ def config_of(value_type)
187
+ PrefabProto::Config.new(
188
+ key: 'config-key',
189
+ value_type: value_type
190
+ )
191
+ end
192
+
67
193
  def context_with_key(key)
68
194
  Prefab::Context.new(user: { key: key })
69
195
  end
70
196
 
71
197
  def unwrap(config_value, config_key, context)
72
- Prefab::ConfigValueUnwrapper.deepest_value(config_value, config_key, context).unwrap
198
+ Prefab::ConfigValueUnwrapper.deepest_value(config_value, config_key, context, @mock_resolver).unwrap
199
+ end
200
+
201
+ def reportable_value(config_value, config_key, context)
202
+ Prefab::ConfigValueUnwrapper.deepest_value(config_value, config_key, context, @mock_resolver).reportable_value
203
+ end
204
+
205
+ class MockResolver
206
+ def get(key)
207
+ if DECRYPTION_KEY_NAME == key
208
+ Prefab::Evaluation.new(config: PrefabProto::Config.new(key: key),
209
+ value: PrefabProto::ConfigValue.new(string: DECRYPTION_KEY_VALUE),
210
+ value_index: 0,
211
+ config_row_index: 0,
212
+ context: Prefab::Context.new,
213
+ resolver: self
214
+ )
215
+
216
+ else
217
+ raise "unexpected key"
218
+ end
219
+ end
73
220
  end
74
221
  end
@@ -36,7 +36,6 @@ class TestContextShape < Minitest::Test
36
36
 
37
37
  # If this test fails, it means that we've added a new type to the ConfigValue
38
38
  def test_mapping_is_exhaustive
39
- #this removes provided for now
40
39
  unsupported = [:bytes, :limit_definition, :log_level, :weighted_values, :int_range, :provided]
41
40
  type_fields = PrefabProto::ConfigValue.descriptor.lookup_oneof("type").entries
42
41
  supported = type_fields.entries.reject do |entry|
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestEncryption < Minitest::Test
6
+ def test_encryption
7
+ secret = Prefab::Encryption.generate_new_hex_key
8
+
9
+ enc = Prefab::Encryption.new(secret)
10
+
11
+ clear_text = "hello world"
12
+ encrypted = enc.encrypt(clear_text)
13
+ decrypted = enc.decrypt(encrypted)
14
+ assert_equal clear_text, decrypted
15
+ end
16
+ end