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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c153ee6c00aefaa6e938e7828a4be27acd769f32fd4a557b9f084429e76d373
4
- data.tar.gz: fd098eeefbf1a6b01dac20d37a3cb2cdfddf29933e798aabb36d6781bf2859a7
3
+ metadata.gz: 55134a75669f459bbb12d50cfa6e221937a5c00f53ca365e609f0cb436b24aff
4
+ data.tar.gz: f3507032152e1dfe7b48156e5b8795d3bfd9262e077fab18923fef7cbeb78e34
5
5
  SHA512:
6
- metadata.gz: 395fec34b4358ef5dd7b5dd39b29d1781485afd931fdc1030b796c4c45ef1a5ef62344164f0420af30504986775479188103c4a744ef0bcbbd768ec32ef80e55
7
- data.tar.gz: ed5859c38fa2743d95fb3550d9f850460c3174ba4c6b4d861a4cbd81ee6f64c4b328ae714af451a20fbbba69249e16b1a4da831472c52e66a8d3e1235829bdbe
6
+ metadata.gz: 839db6777d5e6c3a76d34b7812787e8241779b2ede48d4ffddbf5e96bb493494e8d30e980582a5d06518e8f64f789287417ac5e6abc66c7789e17082992c83fb
7
+ data.tar.gz: fd26b0d5ceb02a17724ede1a3329bda2a1ca7ee1c8d49e0776c61498bfe7e1b41ca81158e395192c4c73c35970824e5eb2d319b241a9489e0e215febe9ea2bb6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 1.3.0 - 2023-11-13
6
+ - Less logging when wifi is off and we load from cache (#157)
7
+ - Alpha: Add Provided & Secret Support (#152)
8
+ - Alpha: x_datafile (#156)
9
+ - Add single line action-controller output under rails.controller (#158)
10
+
3
11
  ## 1.2.1 - 2023-11-01
4
12
  - Update protobuf definitions (#154)
5
13
 
data/Gemfile CHANGED
@@ -7,6 +7,8 @@ gem 'google-protobuf', platforms: :ruby
7
7
  gem 'ld-eventsource'
8
8
  gem 'uuid'
9
9
 
10
+ gem 'activesupport', '>= 4'
11
+
10
12
  group :development do
11
13
  gem 'benchmark-ips'
12
14
  gem 'bundler'
data/Gemfile.lock CHANGED
@@ -1,17 +1,32 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
+ activesupport (7.1.2)
5
+ base64
6
+ bigdecimal
7
+ concurrent-ruby (~> 1.0, >= 1.0.2)
8
+ connection_pool (>= 2.2.5)
9
+ drb
10
+ i18n (>= 1.6, < 2)
11
+ minitest (>= 5.1)
12
+ mutex_m
13
+ tzinfo (~> 2.0)
4
14
  addressable (2.8.0)
5
15
  public_suffix (>= 2.0.2, < 5.0)
6
16
  ansi (1.5.0)
17
+ base64 (0.2.0)
7
18
  benchmark-ips (2.10.0)
19
+ bigdecimal (3.1.4)
8
20
  builder (3.2.4)
9
21
  concurrent-ruby (1.1.10)
22
+ connection_pool (2.4.1)
10
23
  descendants_tracker (0.0.4)
11
24
  thread_safe (~> 0.3, >= 0.3.1)
12
25
  docile (1.3.5)
13
26
  domain_name (0.5.20190701)
14
27
  unf (>= 0.0.5, < 1.0.0)
28
+ drb (2.2.0)
29
+ ruby2_keywords
15
30
  faraday (1.3.0)
16
31
  faraday-net_http (~> 1.0)
17
32
  multipart-post (>= 1.2, < 3)
@@ -43,6 +58,8 @@ GEM
43
58
  http-cookie (1.0.4)
44
59
  domain_name (~> 0.5)
45
60
  http-form_data (2.3.0)
61
+ i18n (1.14.1)
62
+ concurrent-ruby (~> 1.0)
46
63
  juwelier (2.4.9)
47
64
  builder
48
65
  bundler
@@ -78,6 +95,7 @@ GEM
78
95
  multi_json (1.15.0)
79
96
  multi_xml (0.6.0)
80
97
  multipart-post (2.1.1)
98
+ mutex_m (0.2.0)
81
99
  nokogiri (1.15.2)
82
100
  mini_portile2 (~> 2.8.2)
83
101
  racc (~> 1.4)
@@ -104,6 +122,8 @@ GEM
104
122
  systemu (2.6.5)
105
123
  thread_safe (0.3.6)
106
124
  timecop (0.9.4)
125
+ tzinfo (2.0.6)
126
+ concurrent-ruby (~> 1.0)
107
127
  unf (0.1.4)
108
128
  unf_ext
109
129
  unf_ext (0.0.8)
@@ -114,6 +134,7 @@ PLATFORMS
114
134
  ruby
115
135
 
116
136
  DEPENDENCIES
137
+ activesupport (>= 4)
117
138
  benchmark-ips
118
139
  bundler
119
140
  concurrent-ruby (~> 1.0, >= 1.0.5)
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2017 Jeff Dwyer
1
+ Copyright (c) 2023 Prefab, Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.1
1
+ 1.3.0
data/lib/prefab/client.rb CHANGED
@@ -22,6 +22,8 @@ module Prefab
22
22
 
23
23
  if @options.local_only?
24
24
  LOG.debug 'Prefab Running in Local Mode'
25
+ elsif @options.datafile?
26
+ LOG.debug 'Prefab Running in DataFile Mode'
25
27
  else
26
28
  @api_key = @options.api_key
27
29
  raise Prefab::Errors::InvalidApiKeyError, @api_key if @api_key.nil? || @api_key.empty? || api_key.count('-') < 1
@@ -96,6 +98,8 @@ module Prefab
96
98
  ActiveJob::Base.logger = log if defined?(ActiveJob)
97
99
  ActiveRecord::Base.logger = log
98
100
  ActiveStorage.logger = log if defined?(ActiveStorage)
101
+
102
+ LogSubscribers::ActionControllerSubscriber.attach_to :action_controller unless @options.disable_action_controller_logging
99
103
  end
100
104
 
101
105
  def on_update(&block)
@@ -31,6 +31,8 @@ module Prefab
31
31
 
32
32
  if @options.local_only?
33
33
  finish_init!(:local_only, nil)
34
+ elsif @options.datafile?
35
+ load_json_file(@options.datafile)
34
36
  else
35
37
  load_checkpoint
36
38
  start_checkpointing_thread
@@ -141,8 +143,16 @@ module Prefab
141
143
  LOG.info "Checkpoint #{source} failed to load. Response #{resp.status}"
142
144
  false
143
145
  end
146
+ rescue Faraday::ConnectionFailed => e
147
+ if @initialization_lock.write_locked?
148
+ LOG.warn "Connection Fail loading #{source} checkpoint."
149
+ else
150
+ LOG.debug "Connection Fail loading #{source} checkpoint."
151
+ end
152
+ false
144
153
  rescue StandardError => e
145
154
  LOG.warn "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
155
+ LOG.debug e.backtrace
146
156
  false
147
157
  end
148
158
 
@@ -156,7 +166,7 @@ module Prefab
156
166
  [
157
167
  context.type,
158
168
  context.values.keys.map do |k|
159
- [k, Prefab::ConfigValueUnwrapper.new(context.values[k]).unwrap]
169
+ [k, Prefab::ConfigValueUnwrapper.new(context.values[k], @config_resolver).unwrap]
160
170
  end.to_h
161
171
  ]
162
172
  end.to_h
@@ -210,12 +220,21 @@ module Prefab
210
220
  if hours_old > STALE_CACHE_WARN_HOURS
211
221
  LOG.info "Stale Cache Load: #{hours_old} hours old"
212
222
  end
223
+ true
213
224
  end
214
225
  rescue => e
215
226
  LOG.debug "Failed to read cached configs at #{cache_path}. #{e}"
216
227
  false
217
228
  end
218
229
 
230
+ def load_json_file(file)
231
+ File.open(file) do |f|
232
+ f.flock(File::LOCK_SH)
233
+ configs = PrefabProto::Configs.decode_json(f.read)
234
+ load_configs(configs, :datafile)
235
+ end
236
+ end
237
+
219
238
  # A thread that checks for a checkpoint
220
239
  def start_checkpointing_thread
221
240
  Thread.new do
@@ -54,6 +54,7 @@ module Prefab
54
54
  private
55
55
 
56
56
  def load_classpath_config
57
+ return {} if @prefab_options.datafile?
57
58
  classpath_dir = @prefab_options.prefab_config_classpath_dir
58
59
  rtn = load_glob(File.join(classpath_dir, '.prefab.default.config.yaml'))
59
60
  @prefab_options.prefab_envs.each do |env|
@@ -63,6 +64,7 @@ module Prefab
63
64
  end
64
65
 
65
66
  def load_local_overrides
67
+ return {} if @prefab_options.datafile?
66
68
  override_dir = @prefab_options.prefab_config_override_dir
67
69
  rtn = load_glob(File.join(override_dir, '.prefab.default.config.yaml'))
68
70
  @prefab_options.prefab_envs.each do |env|
@@ -3,37 +3,115 @@
3
3
  module Prefab
4
4
  class ConfigValueUnwrapper
5
5
  LOG = Prefab::InternalLogger.new(ConfigValueUnwrapper)
6
- attr_reader :value, :weighted_value_index
6
+ CONFIDENTIAL_PREFIX = "*****"
7
+ attr_reader :weighted_value_index
7
8
 
8
- def initialize(value, weighted_value_index = nil)
9
- @value = value
9
+ def initialize(config_value, resolver, weighted_value_index = nil)
10
+ @config_value = config_value
11
+ @resolver = resolver
10
12
  @weighted_value_index = weighted_value_index
11
13
  end
12
14
 
13
- def unwrap
14
- case value.type
15
- when :int, :string, :double, :bool, :log_level
16
- value.public_send(value.type)
17
- when :string_list
18
- value.string_list.values
15
+ def reportable_wrapped_value
16
+ if @config_value.confidential
17
+ # Unique hash for differentiation
18
+ Prefab::ConfigValueWrapper.wrap("#{CONFIDENTIAL_PREFIX}#{Digest::MD5.hexdigest(unwrap)[0,5]}")
19
19
  else
20
- LOG.error "Unknown type: #{config_value.type}"
21
- raise "Unknown type: #{config_value.type}"
20
+ @config_value
21
+ end
22
+ end
23
+
24
+ def reportable_value
25
+ Prefab::ConfigValueUnwrapper.new(reportable_wrapped_value, @resolver, @weighted_value_index).unwrap
26
+ end
27
+
28
+ def raw_config_value
29
+ @config_value
30
+ end
31
+
32
+ # this will return the actual value of confidential, use reportable_value unless you need it
33
+ def unwrap
34
+ raw = case @config_value.type
35
+ when :int, :string, :double, :bool, :log_level
36
+ @config_value.public_send(@config_value.type)
37
+ when :string_list
38
+ @config_value.string_list.values
39
+ else
40
+ LOG.error "Unknown type: #{@config_value.type}"
41
+ raise "Unknown type: #{@config_value.type}"
42
+ end
43
+ if @config_value.has_decrypt_with?
44
+ decryption_key = @resolver.get(@config_value.decrypt_with)&.unwrapped_value
45
+ if decryption_key.nil?
46
+ LOG.warn "No value for decryption key #{@config_value.decrypt_with} found."
47
+ return ""
48
+ else
49
+ unencrypted = Prefab::Encryption.new(decryption_key).decrypt(raw)
50
+ return unencrypted
51
+ end
22
52
  end
53
+
54
+ raw
55
+
23
56
  end
24
57
 
25
- def self.deepest_value(config_value, config_key, context)
58
+ def self.deepest_value(config_value, config, context, resolver)
26
59
  if config_value&.type == :weighted_values
27
60
  value, index = Prefab::WeightedValueResolver.new(
28
61
  config_value.weighted_values.weighted_values,
29
- config_key,
62
+ config.key,
30
63
  context.get(config_value.weighted_values.hash_by_property_name)
31
64
  ).resolve
32
65
 
33
- new(deepest_value(value.value, config_key, context).value, index)
66
+ new(deepest_value(value.value, config, context, resolver).raw_config_value, resolver, index)
67
+
68
+ elsif config_value&.type == :provided
69
+ if :ENV_VAR == config_value.provided.source
70
+ raw = ENV[config_value.provided.lookup]
71
+ if raw.nil?
72
+ LOG.warn "ENV Variable #{config_value.provided.lookup} not found. Using empty string."
73
+ new(Prefab::ConfigValueWrapper.wrap(""), resolver)
74
+ else
75
+ coerced = coerce_into_type(raw, config, config_value.provided.lookup)
76
+ new(Prefab::ConfigValueWrapper.wrap(coerced, confidential: config_value.confidential), resolver)
77
+ end
78
+ else
79
+ raise "Unknown Provided Source #{config_value.provided.source}"
80
+ end
81
+ else
82
+ new(config_value, resolver)
83
+ end
84
+ end
85
+
86
+ # Don't allow env vars to resolve to a value_type other than the config's value_type
87
+ def self.coerce_into_type(value_string, config, env_var_name)
88
+ case config.value_type
89
+ when :INT then Integer(value_string)
90
+ when :DOUBLE then Float(value_string)
91
+ when :STRING then String(value_string)
92
+ when :STRING_LIST then
93
+ maybe_string_list = YAML.load(value_string)
94
+ case maybe_string_list
95
+ when Array
96
+ maybe_string_list
97
+ else
98
+ raise raise Prefab::Errors::EnvVarParseError.new(value_string, config, env_var_name)
99
+ end
100
+ when :BOOL then
101
+ maybe_bool = YAML.load(value_string)
102
+ case maybe_bool
103
+ when TrueClass,FalseClass
104
+ maybe_bool
105
+ else
106
+ raise Prefab::Errors::EnvVarParseError.new(value_string, config, env_var_name)
107
+ end
108
+ when :NOT_SET_VALUE_TYPE
109
+ YAML.load(value_string)
34
110
  else
35
- new(config_value)
111
+ raise Prefab::Errors::EnvVarParseError.new(value_string, config, env_var_name)
36
112
  end
113
+ rescue ArgumentError
114
+ raise Prefab::Errors::EnvVarParseError.new(value_string, config, env_var_name)
37
115
  end
38
116
  end
39
117
  end
@@ -1,17 +1,17 @@
1
1
  module Prefab
2
2
  class ConfigValueWrapper
3
- def self.wrap(value)
3
+ def self.wrap(value, confidential: nil)
4
4
  case value
5
5
  when Integer
6
- PrefabProto::ConfigValue.new(int: value)
6
+ PrefabProto::ConfigValue.new(int: value, confidential: confidential)
7
7
  when Float
8
- PrefabProto::ConfigValue.new(double: value)
8
+ PrefabProto::ConfigValue.new(double: value, confidential: confidential)
9
9
  when TrueClass, FalseClass
10
- PrefabProto::ConfigValue.new(bool: value)
10
+ PrefabProto::ConfigValue.new(bool: value, confidential: confidential)
11
11
  when Array
12
- PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: value.map(&:to_s)))
12
+ PrefabProto::ConfigValue.new(string_list: PrefabProto::StringList.new(values: value.map(&:to_s)), confidential: confidential)
13
13
  else
14
- PrefabProto::ConfigValue.new(string: value.to_s)
14
+ PrefabProto::ConfigValue.new(string: value.to_s, confidential: confidential)
15
15
  end
16
16
  end
17
17
  end
@@ -22,7 +22,7 @@ module Prefab
22
22
  def evaluate(properties)
23
23
  rtn = evaluate_for_env(@project_env_id, properties) ||
24
24
  evaluate_for_env(0, properties)
25
- LOG.debug "Eval Key #{@config.key} Result #{rtn&.value} with #{properties.to_h}" unless @config.config_type == :LOG_LEVEL
25
+ LOG.debug "Eval Key #{@config.key} Result #{rtn&.reportable_value} with #{properties.to_h}" unless @config.config_type == :LOG_LEVEL
26
26
  rtn
27
27
  end
28
28
 
@@ -93,7 +93,8 @@ module Prefab
93
93
  value: conditional_value.value,
94
94
  value_index: value_index,
95
95
  config_row_index: index,
96
- context: properties
96
+ context: properties,
97
+ resolver: @resolver
97
98
  )
98
99
  end
99
100
  end
@@ -111,7 +112,7 @@ module Prefab
111
112
 
112
113
  def matches?(criterion, value, properties)
113
114
  criterion_value_or_values = Prefab::ConfigValueUnwrapper.deepest_value(criterion.value_to_match, @config.key,
114
- properties).unwrap
115
+ properties, @resolver).unwrap
115
116
 
116
117
  case criterion_value_or_values
117
118
  when Google::Protobuf::RepeatedField
@@ -0,0 +1,65 @@
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
@@ -0,0 +1,11 @@
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
@@ -5,18 +5,23 @@ module Prefab
5
5
  class Evaluation
6
6
  attr_reader :value
7
7
 
8
- def initialize(config:, value:, value_index:, config_row_index:, context:)
8
+ def initialize(config:, value:, value_index:, config_row_index:, context:, resolver:)
9
9
  @config = config
10
10
  @value = value
11
11
  @value_index = value_index
12
12
  @config_row_index = config_row_index
13
13
  @context = context
14
+ @resolver = resolver
14
15
  end
15
16
 
16
17
  def unwrapped_value
17
18
  deepest_value.unwrap
18
19
  end
19
20
 
21
+ def reportable_value
22
+ deepest_value.reportable_value
23
+ end
24
+
20
25
  def report_and_return(evaluation_summary_aggregator)
21
26
  report(evaluation_summary_aggregator)
22
27
 
@@ -27,7 +32,6 @@ module Prefab
27
32
 
28
33
  def report(evaluation_summary_aggregator)
29
34
  return if @config.config_type == :LOG_LEVEL
30
-
31
35
  evaluation_summary_aggregator&.record(
32
36
  config_key: @config.key,
33
37
  config_type: @config.config_type,
@@ -35,14 +39,14 @@ module Prefab
35
39
  config_id: @config.id,
36
40
  config_row_index: @config_row_index,
37
41
  conditional_value_index: @value_index,
38
- selected_value: deepest_value.value,
42
+ selected_value: deepest_value.reportable_wrapped_value,
39
43
  weighted_value_index: deepest_value.weighted_value_index,
40
44
  selected_index: nil # TODO
41
45
  })
42
46
  end
43
47
 
44
48
  def deepest_value
45
- @deepest_value ||= Prefab::ConfigValueUnwrapper.deepest_value(@value, @config.key, @context)
49
+ @deepest_value ||= Prefab::ConfigValueUnwrapper.deepest_value(@value, @config, @context, @resolver)
46
50
  end
47
51
  end
48
52
  end
@@ -1,33 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prefab
4
- class InternalLogger < ::Logger
4
+ class InternalLogger < StaticLogger
5
+ INTERNAL_PREFIX = 'cloud.prefab.client'
6
+
5
7
  def initialize(path)
6
8
  if path.is_a?(Class)
7
- @path = path.name.split('::').last.downcase
9
+ path_string = path.name.split('::').last.downcase
8
10
  else
9
- @path = path
11
+ path_string = path
10
12
  end
11
- end
12
-
13
- def debug msg
14
- Prefab::LoggerClient.instance.log_internal ::Logger::DEBUG, msg, @path
15
- end
16
-
17
- def info msg
18
- Prefab::LoggerClient.instance.log_internal ::Logger::INFO, msg, @path
19
- end
20
-
21
- def warn msg
22
- Prefab::LoggerClient.instance.log_internal ::Logger::WARN, msg, @path
23
- end
24
-
25
- def error msg
26
- Prefab::LoggerClient.instance.log_internal ::Logger::ERROR, msg, @path
27
- end
28
-
29
- def fatal msg
30
- Prefab::LoggerClient.instance.log_internal ::Logger::FATAL, msg, @path
13
+ super("#{INTERNAL_PREFIX}.#{path_string}")
31
14
  end
32
15
  end
33
16
  end
@@ -7,6 +7,10 @@ module Prefab
7
7
  if value.instance_of?(Hash)
8
8
  if value['feature_flag']
9
9
  config[key] = feature_flag_config(file, key, value)
10
+ elsif value['type'] == 'provided'
11
+ config[key] = provided_config(file, key, value)
12
+ elsif value['decrypt_with'] || value['confidential']
13
+ config[key] = complex_string(file, key, value)
10
14
  else
11
15
  value.each do |nest_key, nest_value|
12
16
  nested_key = "#{key}.#{nest_key}"
@@ -23,8 +27,8 @@ module Prefab
23
27
  key: key,
24
28
  rows: [
25
29
  PrefabProto::ConfigRow.new(values: [
26
- PrefabProto::ConditionalValue.new(value: value_from(key, value))
27
- ])
30
+ PrefabProto::ConditionalValue.new(value: value_from(key, value))
31
+ ])
28
32
  ]
29
33
  )
30
34
  }
@@ -79,6 +83,58 @@ module Prefab
79
83
  }
80
84
  end
81
85
 
86
+ def provided_config(file, key, value_hash)
87
+ value = PrefabProto::ConfigValue.new(provided: PrefabProto::Provided.new(
88
+ source: :ENV_VAR,
89
+ lookup: value_hash["lookup"],
90
+ ),
91
+ confidential: value_hash["confidential"],
92
+ )
93
+
94
+ row = PrefabProto::ConfigRow.new(
95
+ values: [
96
+ PrefabProto::ConditionalValue.new(
97
+ value: value
98
+ )
99
+ ]
100
+ )
101
+
102
+ {
103
+ source: file,
104
+ match: value.provided.lookup,
105
+ config: PrefabProto::Config.new(
106
+ config_type: :CONFIG,
107
+ key: key,
108
+ rows: [row]
109
+ )
110
+ }
111
+ end
112
+
113
+ def complex_string(file, key, value_hash)
114
+ value = PrefabProto::ConfigValue.new(
115
+ string: value_hash["value"],
116
+ confidential: value_hash["confidential"],
117
+ decrypt_with: value_hash["decrypt_with"],
118
+ )
119
+
120
+ row = PrefabProto::ConfigRow.new(
121
+ values: [
122
+ PrefabProto::ConditionalValue.new(
123
+ value: value
124
+ )
125
+ ]
126
+ )
127
+
128
+ {
129
+ source: file,
130
+ config: PrefabProto::Config.new(
131
+ config_type: :CONFIG,
132
+ key: key,
133
+ rows: [row]
134
+ )
135
+ }
136
+ end
137
+
82
138
  def parse_criterion(criterion)
83
139
  PrefabProto::Criterion.new(operator: criterion['operator'],
84
140
  property_name: criterion['property'],