Class: RightDevelop::Testing::Recording::Metadata
- Inherits:
-
Object
- Object
- RightDevelop::Testing::Recording::Metadata
- Defined in:
- lib/right_develop/testing/recording/metadata.rb
Overview
Metadata for record and playback.
Defined Under Namespace
Classes: ConfigurationError, MissingVariableFailure, PlaybackError, RecordingError, RetryableFailure
Constant Summary collapse
- HIDDEN_CREDENTIAL_VALUE =
value used for obfuscation.
'HIDDEN_CREDENTIAL'.freeze
- VERBS =
common API verbs.
%w(DELETE GET HEAD PATCH POST PUT).freeze
- MODES =
valid modes, determines how variables are substituted, etc.
%w(echo record playback validate)
- KINDS =
valid kinds, also keys under matchers.
%w(request response)
- DELAY_SECONDS_KEY =
route-relative config keys.
'delay_seconds'.freeze
- MATCHERS_KEY =
'matchers'.freeze
- SIGNIFICANT_KEY =
'significant'.freeze
- TIMEOUTS_KEY =
'timeouts'.freeze
- TRANSFORM_KEY =
'transform'.freeze
- VARIABLES_KEY =
'variables'.freeze
- VARIABLE_INDEX_REGEX =
finds the value index for a recorded variable, if any.
/\[(\d+)\]$/
- HALT =
throw/catch signals.
:halt_recording_metadata_generator
Instance Attribute Summary collapse
-
#body ⇒ Object
readonly
Returns the value of attribute body.
-
#checksum_data ⇒ Object
readonly
Returns the value of attribute checksum_data.
-
#effective_route_config ⇒ Object
readonly
Returns the value of attribute effective_route_config.
-
#headers ⇒ Object
readonly
Returns the value of attribute headers.
-
#http_status ⇒ Object
readonly
Returns the value of attribute http_status.
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
-
#typenames_to_values ⇒ Object
readonly
Returns the value of attribute typenames_to_values.
-
#uri ⇒ Object
readonly
Returns the value of attribute uri.
-
#variables ⇒ Object
readonly
Returns the value of attribute variables.
-
#verb ⇒ Object
readonly
Returns the value of attribute verb.
Class Method Summary collapse
-
.deep_sorted_data(data) ⇒ String
Duplicates and sorts hash keys for a consistent appearance (in JSON).
-
.deep_sorted_json(data, pretty = false) ⇒ String
Sorts data for a consistent appearance in JSON.
-
.normalize_header_key(key) ⇒ String
Establishes a normal header key form for agreement between configuration and metadata pieces.
-
.normalize_uri(url) ⇒ URI
Uri with scheme inserted if necessary.
Instance Method Summary collapse
-
#checksum ⇒ String
Computed checksum for normalized ‘significant’ data.
-
#delay_seconds ⇒ Float
Delay in seconds (of response) from effective configuration or empty.
-
#initialize(options) ⇒ Metadata
constructor
Computes the metadata used to identify where the request/response should be stored-to/retrieved-from.
-
#query ⇒ String
Normalized query string.
-
#timeouts ⇒ Hash
Timeouts from effective configuration or empty.
Constructor Details
#initialize(options) ⇒ Metadata
Computes the metadata used to identify where the request/response should be stored-to/retrieved-from. Recording the full request is not strictly necessary (because the request maps to a MD5 used for response-only) but it adds human-readability and the ability to manually customize some or all responses.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 100 def initialize() unless (@logger = [:logger]) raise ::ArgumentError, "options[:logger] is required: #{@logger.inspect}" end unless (@mode = [:mode].to_s) && MODES.include?(@mode) raise ::ArgumentError, "options[:mode] must be one of #{MODES.inspect}: #{@mode.inspect}" end unless (@kind = [:kind].to_s) && KINDS.include?(@kind) raise ::ArgumentError, "options[:kind] must be one of #{KINDS.inspect}: #{@kind.inspect}" end unless (@uri = [:uri]) && @uri.respond_to?(:path) raise ::ArgumentError, "options[:uri] must be a valid parsed URI: #{@uri.inspect}" end unless (@verb = [:verb]) && VERBS.include?(@verb) raise ::ArgumentError, "options[:verb] must be one of #{VERBS.inspect}: #{@verb.inspect}" end unless (@headers = [:headers]).kind_of?(::Hash) raise ::ArgumentError, "options[:headers] must be a hash: #{@headers.inspect}" end unless (@route_data = [:route_data]).kind_of?(::Hash) raise ::ArgumentError, "options[:route_data] must be a hash: #{@route_data.inspect}" end @http_status = [:http_status] if @kind == 'response' @http_status = Integer(@http_status) elsif !@http_status.nil? raise ::ArgumentError, "options[:http_status] is unexpected for #{@kind}." end unless (@variables = [:variables]).kind_of?(::Hash) raise ::ArgumentError, "options[:variables] must be a hash: #{@variables.inspect}" end if (@effective_route_config = [:effective_route_config]) && !@effective_route_config.kind_of?(::Hash) raise ::ArgumentError, "options[:effective_route_config] is not a hash: #{@effective_route_config.inspect}" end @body = [:body] # not required # merge one or more wildcard configurations matching the current uri and # parameters. @headers = normalize_headers(@headers) @typenames_to_values = compute_typenames_to_values # effective route config may already have been computed for request # (on record) or not (on playback). @effective_route_config ||= compute_effective_route_config # apply the configuration by substituting for variables in the request and # by obfuscating wherever a variable name is nil. erck = @effective_route_config[@kind] case @mode when 'validate' # used to validate the fixtures before playback; no variable # substitution should be performed. else if effective_variables = erck && erck[VARIABLES_KEY] recursive_replace_variables( [@kind, VARIABLES_KEY], @typenames_to_values, effective_variables, erck[TRANSFORM_KEY]) end end if logger.debug? logger.debug("#{@kind} effective_route_config = #{@effective_route_config[@kind].inspect}") logger.debug("#{@kind} typenames_to_values = #{@typenames_to_values.inspect}") end # recreate headers and body from data using variable substitutions and # obfuscations. @headers = @typenames_to_values[:header] @body = normalize_body(@headers, @typenames_to_values[:body] || @body) end |
Instance Attribute Details
#body ⇒ Object (readonly)
Returns the value of attribute body.
91 92 93 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 91 def body @body end |
#checksum_data ⇒ Object (readonly)
Returns the value of attribute checksum_data.
91 92 93 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 91 def checksum_data @checksum_data end |
#effective_route_config ⇒ Object (readonly)
Returns the value of attribute effective_route_config.
92 93 94 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 92 def effective_route_config @effective_route_config end |
#headers ⇒ Object (readonly)
Returns the value of attribute headers.
91 92 93 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 91 def headers @headers end |
#http_status ⇒ Object (readonly)
Returns the value of attribute http_status.
91 92 93 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 91 def http_status @http_status end |
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
92 93 94 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 92 def logger @logger end |
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
92 93 94 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 92 def mode @mode end |
#typenames_to_values ⇒ Object (readonly)
Returns the value of attribute typenames_to_values.
93 94 95 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 93 def typenames_to_values @typenames_to_values end |
#uri ⇒ Object (readonly)
Returns the value of attribute uri.
91 92 93 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 91 def uri @uri end |
#variables ⇒ Object (readonly)
Returns the value of attribute variables.
92 93 94 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 92 def variables @variables end |
#verb ⇒ Object (readonly)
Returns the value of attribute verb.
91 92 93 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 91 def verb @verb end |
Class Method Details
.deep_sorted_data(data) ⇒ String
Duplicates and sorts hash keys for a consistent appearance (in JSON). Traverses arrays to sort hash elements. Note this only works for Ruby 1.9+ due to hashes now preserving insertion order.
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 254 def self.deep_sorted_data(data) case data when ::Hash data = data.map { |k, v| [k.to_s, v] }.sort.inject({}) do |h, (k, v)| h[k] = deep_sorted_data(v) h end when Array data.map { |e| deep_sorted_data(e) } else if data.respond_to?(:to_hash) deep_sorted_data(data.to_hash) else data end end end |
.deep_sorted_json(data, pretty = false) ⇒ String
Sorts data for a consistent appearance in JSON.
HACK: replacement for ::RightSupport::Data::HashTools.deep_sorted_json method that can underflow the @state.depth field as -1 probably due to some (1.9.3+?) logic that resets the depth to zero when JSON data gets too deep or else @state.depth doesn’t mean what it used to mean in Ruby 1.8. need to fix the utility…
242 243 244 245 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 242 def self.deep_sorted_json(data, pretty = false) data = deep_sorted_data(data) pretty ? ::JSON.pretty_generate(data) : ::JSON.dump(data) end |
.normalize_header_key(key) ⇒ String
Establishes a normal header key form for agreement between configuration and metadata pieces.
204 205 206 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 204 def self.normalize_header_key(key) key.to_s.downcase.gsub('-', '_') end |
.normalize_uri(url) ⇒ URI
Returns uri with scheme inserted if necessary.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 211 def self.normalize_uri(url) # the following logic is borrowed from RestClient::Request#parse_url url = "http://#{url}" unless url.match(/^http/) uri = ::URI.parse(url) # need at least a (leading) forward-slash in path for any subsequent route # matching. uri.path = '/' if uri.path.empty? # strip proxied server details not needed for playback. # strip any basic authentication, which is never recorded. uri = ::URI.parse(url) uri.scheme = nil uri.host = nil uri.port = nil uri.user = nil uri.password = nil uri end |
Instance Method Details
#checksum ⇒ String
Returns computed checksum for normalized ‘significant’ data.
183 184 185 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 183 def checksum @checksum ||= compute_checksum end |
#delay_seconds ⇒ Float
Returns delay in seconds (of response) from effective configuration or empty.
189 190 191 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 189 def delay_seconds Float((@effective_route_config[@kind] || {})[DELAY_SECONDS_KEY] || 0) end |
#query ⇒ String
Returns normalized query string.
173 174 175 176 177 178 179 180 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 173 def query q = @typenames_to_values[:query] if q && !q.empty? build_query_string(q) else nil end end |
#timeouts ⇒ Hash
Returns timeouts from effective configuration or empty.
194 195 196 |
# File 'lib/right_develop/testing/recording/metadata.rb', line 194 def timeouts (@effective_route_config[@kind] || {})[TIMEOUTS_KEY] || {} end |