prefab-cloud-ruby 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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