This repository has been archived by the owner on Jan 2, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactored JSONResponseVerification, adding support for top-level arrays
JSONResponseVerification#verify replaces JSONResponseVerification#validate_hash_response (which is still available for backguard compatibility).
- Loading branch information
Showing
2 changed files
with
124 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,109 +1,138 @@ | ||
# include this module in WeaselDiesel | ||
# Include this module in WeaselDiesel | ||
# to add response verification methods. | ||
# | ||
module JSONResponseVerification | ||
|
||
# Validates a hash against the service's response description. | ||
# Verifies the parsed body of a JSON response against the service's response description. | ||
# | ||
# @return [Array<TrueClass, FalseClass, Array<String>>] True/false and an array of errors. | ||
def validate_hash_response(hash) | ||
errors = [] | ||
# nodes without the arrays | ||
response.nodes.each do |node| | ||
if node.name | ||
# Verify that the named node exists in the hash | ||
unless hash.has_key?(node.name.to_s) | ||
errors << json_response_error(node, hash) | ||
return [false, errors] | ||
end | ||
end | ||
errors += validate_hash_against_template_node(hash, node) | ||
end | ||
|
||
def verify(parsed_json_body) | ||
errors = [verify_element(parsed_json_body, response.nodes.first)] | ||
errors.flatten! | ||
[errors.empty?, errors] | ||
end | ||
|
||
alias :validate_hash_response :verify # backguard compatibility | ||
|
||
private | ||
|
||
# Recursively validates a hash representing a json response. | ||
# Recursively validates an element found when parsing a JSON. | ||
# | ||
# @param [Hash>] hash the hash to verify. | ||
# @param [WDSL::Response::Element] node the reference element defined in the response description. | ||
# @param [TrueClass, FalseClass] nested if the node/hash to verify is nested or not. If nested, the method expects to get the subhash | ||
# & won't verify that the name exists since it was done a level higher. | ||
# @param [Arrays<String>] errors the list of errors encountered while verifying. | ||
# @param [] | ||
# @return [TrueClass, FalseClass] | ||
def validate_hash_against_template_node(hash, node, nested=false, errors=[], array_item=false) | ||
if hash.nil? | ||
errors << json_response_error(node, hash) | ||
return errors | ||
end | ||
|
||
if node.name && !nested | ||
if hash.has_key?(node.name.to_s) | ||
subhash = hash[node.name.to_s] | ||
# @param [Hash, Array, nil] el parsed JSON to be verified. | ||
# @param [WDSL::Response::Element] expected the reference element defined in the response description. | ||
# @param [TrueClass, FalseClass] verify_namespace if the nesting must be verified. | ||
# @return [Arrays<String>] errors the list of errors encountered while verifying. | ||
def verify_element(el, expected, verify_namespace=true) | ||
if expected.name && verify_namespace | ||
if verified_namespace?(el, expected.name) | ||
el = el[expected.name.to_s] | ||
verify_namespace = false | ||
else | ||
errors << json_response_error(node, hash) | ||
return something_is_missing_error(expected) | ||
end | ||
else | ||
verify_namespace = true | ||
end | ||
|
||
subhash ||= hash | ||
if node.is_a?(WeaselDiesel::Response::Vector) && !array_item | ||
errors << json_response_error(node, subhash, true) unless subhash.is_a?(Array) | ||
subhash.each do |obj| | ||
validate_hash_against_template_node(obj, node, true, errors, true) | ||
end | ||
if el.nil? | ||
something_is_missing_error(expected) | ||
elsif el.is_a?(Array) | ||
verify_array(el, expected, verify_namespace) | ||
else | ||
node.properties.each do |prop| | ||
if !array_item && !subhash.has_key?(prop.name.to_s) && (prop.opts && prop.respond_to?(:opts) && !prop.opts[:null]) | ||
errors << json_response_error(prop, subhash) | ||
end | ||
errors << json_response_error(prop, subhash, true) unless valid_hash_type?(subhash, prop) | ||
end | ||
|
||
node.objects.each do |obj| | ||
# recursive call | ||
validate_hash_against_template_node(subhash[obj.name.to_s], obj, true, errors) | ||
end if node.objects | ||
verify_object(el, expected, verify_namespace) | ||
end | ||
end | ||
|
||
errors | ||
# Verifies hash corresponding to a JSON response against a given namespace | ||
# | ||
# @param [Array] array array to be verified. | ||
# @param [WDSL::Response::Element] expected the reference element defined in the response description. | ||
# @return [TrueClass, FalseClass] if the nesting name found is correct. | ||
def verified_namespace?(hash, expected_name) | ||
hash.respond_to?(:has_key?) && hash.has_key?(expected_name.to_s) | ||
end | ||
|
||
# Validates an array found when parsing a JSON. | ||
# | ||
# @param [Array] array array to be verified. | ||
# @param [WDSL::Response::Element] expected the reference element defined in the response description. | ||
# @return [Arrays<String>] errors the list of errors encountered while verifying. | ||
def verify_array(array, expected, verify_nesting) | ||
return wrong_type_error(array, expected.name, expected.type) unless expected.is_a?(WeaselDiesel::Response::Vector) | ||
expected = expected.elements && expected.elements.any? ? expected.elements.first : expected | ||
array.map{ |el| verify_element(el, expected, verify_nesting) } | ||
end | ||
|
||
def json_response_error(el_or_attr, hash, type_error=false) | ||
if el_or_attr.is_a?(WeaselDiesel::Response::Element) | ||
"#{el_or_attr.name || 'top level'} Node/Object/Element is missing" | ||
elsif type_error | ||
error = "#{el_or_attr.name || el_or_attr.inspect} was of wrong type, expected #{el_or_attr.type}" | ||
if el_or_attr.name | ||
error << " and the value was: #{hash[el_or_attr.name.to_s]}" | ||
# Validates a hash corresponding to a JSON object. | ||
# | ||
# @param [Hash] hash hash to be verified. | ||
# @param [WDSL::Response::Element] expected the reference element defined in the response description. | ||
# @return [Arrays<String>] errors the list of errors encountered while verifying. | ||
def verify_object(hash, expected, verify_nesting) | ||
[verify_attributes(hash, expected)] + [verify_objects(hash, expected)] | ||
end | ||
|
||
# Validates the objects found in a hash corresponding to a JSON object. | ||
# | ||
# @param [Hash] hash hash representing a JSON object whose internal objects will be verified. | ||
# @param [WDSL::Response::Element] expected the reference element defined in the response description. | ||
# @return [Arrays<String>] errors the list of errors encountered while verifying. | ||
def verify_objects(hash, expected) | ||
return [] unless expected.objects | ||
expected.objects.map do |expected| | ||
found = hash[expected.name.to_s] | ||
null_allowed = expected.respond_to?(:opts) && expected.opts[:null] | ||
if found.nil? | ||
null_allowed ? [] : something_is_missing_error(expected) | ||
else | ||
verify_element(found, expected, false) # don't verify nesting | ||
end | ||
error | ||
else | ||
"#{el_or_attr.name || el_or_attr.inspect} is missing in #{hash.inspect}" | ||
end | ||
end | ||
|
||
def valid_hash_type?(hash, prop_template) | ||
name = prop_template.name.to_s | ||
attribute = hash[name] | ||
# Validates the attributes found in a hash corresponding to a JSON object. | ||
# | ||
# @param [Hash] hash hash whose attributes will be verified. | ||
# @param [WDSL::Response::Element] expected the reference element defined in the response description. | ||
# @return [Arrays<String>] errors the list of errors encountered while verifying. | ||
def verify_attributes(hash, expected) | ||
return [] unless expected.attributes | ||
expected.attributes.map{ |a| verify_attribute_value(hash[a.name.to_s], a) } | ||
end | ||
|
||
# Check for nullity | ||
if attribute.nil? | ||
return prop_template.opts[:null] == true | ||
# Validates a value against a found in a hash corresponding to a JSON object. | ||
# | ||
# @param [value] value value to be verified. | ||
# @param [WDSL::Response::Attribute] expected the reference element defined in the response description. | ||
# @return [Arrays<String>] errors the list of errors encountered while verifying. | ||
def verify_attribute_value(value, attribute) | ||
null_allowed = attribute.respond_to?(:opts) && !!attribute.opts[:null] | ||
if value.nil? | ||
null_allowed ? [] : wrong_type_error(value, attribute.name, attribute.type) | ||
else | ||
type = attribute.type | ||
return [] if type.to_sym == :string | ||
rule = ParamsVerification.type_validations[attribute.type.to_sym] | ||
puts "Don't know how to validate attributes of type #{type}" if rule.nil? | ||
(rule.nil? || value.to_s =~ rule) ? [] : wrong_type_error(value, attribute.name, attribute.type) | ||
end | ||
|
||
type = prop_template.type | ||
return true if type.nil? | ||
end | ||
|
||
rule = ParamsVerification.type_validations[type.to_sym] | ||
if rule.nil? | ||
puts "Don't know how to validate attributes of type #{type}" if type.to_sym != :string | ||
return true | ||
end | ||
# Returns an error message reporting that an expected data hasn't been found in the JSON response. | ||
# | ||
# @param [WDSL::Response::Element, WDSL::Response::Attribute] expected missing data. | ||
# @return [String] error message | ||
def something_is_missing_error(expected) | ||
"#{expected.name || 'top level'} Node/Object/Element is missing" | ||
end | ||
|
||
attribute.to_s =~ rule | ||
# Returns an error message reporting that a value doesn't correspond to an expected data type. | ||
# | ||
# @param [value] value which doesn't correspond to the expected type. | ||
# @param [data_name] data_name name of the data containing the value. | ||
# @param [expected_type] expected type. | ||
# @return [String] error message | ||
def wrong_type_error(value, data_name, expected_type) | ||
"#{data_name} was of wrong type, expected #{expected_type} and the value was #{value}" | ||
end | ||
|
||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters