wsdsl 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/json_response_verification.rb +83 -0
- data/lib/params_verification.rb +3 -1
- data/lib/response.rb +12 -1
- data/spec/json_response_description_spec.rb +18 -0
- data/spec/json_response_verification_spec.rb +92 -0
- data/wsdsl.gemspec +4 -2
- metadata +4 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# include this module in WSDSL
|
2
|
+
# to add response verification methods.
|
3
|
+
#
|
4
|
+
module JSONResponseVerification
|
5
|
+
|
6
|
+
# Validates a hash against the service's response description.
|
7
|
+
#
|
8
|
+
# @return [Array<TrueClass, FalseClass, Array<String>>] True/false and an array of errors.
|
9
|
+
def validate_hash_response(hash)
|
10
|
+
errors = []
|
11
|
+
response.nodes.each do |node|
|
12
|
+
if node.name
|
13
|
+
# Verify that the named node exists in the hash
|
14
|
+
unless hash.has_key?(node.name.to_s)
|
15
|
+
errors << json_response_error(node, hash)
|
16
|
+
return [false, errors]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
errors += validate_hash_against_template_node(hash, node)
|
20
|
+
end
|
21
|
+
|
22
|
+
[errors.empty?, errors]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Recursively validates a hash representing a json response.
|
28
|
+
#
|
29
|
+
# @param [Hash>] hash the hash to verify.
|
30
|
+
# @param [WDSL::Response::Element>] node the reference element defined in the response description.
|
31
|
+
# @param [<TrueClass, FalseClass>] nested if the node/hash to verify is nested or not. If nested, the method expects to get the subhash
|
32
|
+
# & won't verify that the name exists since it was done a level higher.
|
33
|
+
# @param [Arrays<String>] errors the list of errors encountered while verifying.
|
34
|
+
#
|
35
|
+
# @return [<TrueClass, FalseClass>]
|
36
|
+
def validate_hash_against_template_node(hash, node, nested=false, errors=[])
|
37
|
+
if hash.nil?
|
38
|
+
errors << json_response_error(node, hash)
|
39
|
+
return errors
|
40
|
+
end
|
41
|
+
|
42
|
+
if node.name && !nested
|
43
|
+
if hash.has_key?(node.name.to_s)
|
44
|
+
subhash = hash[node.name.to_s]
|
45
|
+
else
|
46
|
+
errors << json_response_error(node, hash) unless hash.has_key?(node.name.to_s)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
node.properties.each do |prop|
|
51
|
+
subhash ||= hash
|
52
|
+
errors << json_response_error(prop, subhash) unless subhash.has_key?(prop.name.to_s)
|
53
|
+
errors << json_response_error(prop, subhash, true) unless valid_hash_type?(subhash, prop)
|
54
|
+
end
|
55
|
+
|
56
|
+
node.objects.each do |obj|
|
57
|
+
# recursive call
|
58
|
+
validate_hash_against_template_node(subhash[obj.name.to_s], obj, true, errors)
|
59
|
+
end if node.objects
|
60
|
+
|
61
|
+
errors
|
62
|
+
end
|
63
|
+
|
64
|
+
def json_response_error(el_or_attr, hash, type_error=false)
|
65
|
+
if el_or_attr.is_a?(WSDSL::Response::Element)
|
66
|
+
"#{el_or_attr.name || 'top level'} Node/Object/Element is missing"
|
67
|
+
elsif type_error
|
68
|
+
"#{el_or_attr.name || el_or_attr.inspect} was of wrong type"
|
69
|
+
else
|
70
|
+
"#{el_or_attr.name || el_or_attr.inspect} is missing in #{hash.inspect}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def valid_hash_type?(hash, prop_template)
|
75
|
+
type = prop_template.type
|
76
|
+
return true if type.nil?
|
77
|
+
rule = ParamsVerification.type_validations[type.to_sym]
|
78
|
+
return true if rule.nil?
|
79
|
+
attribute = hash[prop_template.name.to_s]
|
80
|
+
attribute.to_s =~ rule
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/lib/params_verification.rb
CHANGED
@@ -120,6 +120,7 @@ module ParamsVerification
|
|
120
120
|
elsif rule.options[:type]
|
121
121
|
verify_cast(param_name, param_value, rule.options[:type])
|
122
122
|
end
|
123
|
+
|
123
124
|
if rule.options[:options] || rule.options[:in]
|
124
125
|
choices = rule.options[:options] || rule.options[:in]
|
125
126
|
if rule.options[:type]
|
@@ -127,9 +128,10 @@ module ParamsVerification
|
|
127
128
|
param_value = params[param_name] = type_cast_value(rule.options[:type], param_value)
|
128
129
|
end
|
129
130
|
raise InvalidParamValue, "Value for parameter '#{param_name}' (#{param_value}) is not in the allowed set of values." unless choices.include?(param_value)
|
131
|
+
# You can have a "in" rule that also applies a min value since they are mutually exclusive
|
130
132
|
elsif rule.options[:minvalue]
|
131
133
|
min = rule.options[:minvalue]
|
132
|
-
raise InvalidParamValue, "Value for parameter '#{param_name}' is lower than the min accepted value (#{min})." if param_value.to_i
|
134
|
+
raise InvalidParamValue, "Value for parameter '#{param_name}' is lower than the min accepted value (#{min})." if param_value.to_i < min
|
133
135
|
end
|
134
136
|
# Returns the updated params
|
135
137
|
|
data/lib/response.rb
CHANGED
@@ -19,6 +19,9 @@ class WSDSL
|
|
19
19
|
@arrays = []
|
20
20
|
end
|
21
21
|
|
22
|
+
# Lists all top level simple elements and array elements.
|
23
|
+
#
|
24
|
+
# @return [Array<WSDSL::Response::Element, WSDSL::Response::Array>]
|
22
25
|
def nodes
|
23
26
|
elements + arrays
|
24
27
|
end
|
@@ -48,7 +51,7 @@ class WSDSL
|
|
48
51
|
# response.element(:name => "my_stats", :type => 'Leaderboard')
|
49
52
|
# end
|
50
53
|
#
|
51
|
-
# @return [
|
54
|
+
# @return [WSDSL::Response::Element]
|
52
55
|
# @api public
|
53
56
|
def element(opts={})
|
54
57
|
el = Element.new(opts[:name], opts[:type])
|
@@ -57,6 +60,14 @@ class WSDSL
|
|
57
60
|
el
|
58
61
|
end
|
59
62
|
|
63
|
+
# Defines an anonymous element
|
64
|
+
# Useful for JSON response description
|
65
|
+
#
|
66
|
+
# @return [WSDSL::Response::Element]
|
67
|
+
def object
|
68
|
+
yield element
|
69
|
+
end
|
70
|
+
|
60
71
|
# Returns a response element object based on its name
|
61
72
|
# @param [String, Symbol] The element name we want to match
|
62
73
|
#
|
@@ -102,5 +102,23 @@ describe "WSDSL JSON response description" do
|
|
102
102
|
name = @root_node.properties.find{|prop| prop.name == :name}
|
103
103
|
name.opts[:mock].should == "test"
|
104
104
|
end
|
105
|
+
|
106
|
+
it "should allow an anonymous object at the root of the response" do
|
107
|
+
service = describe_service "json_anonymous_obj" do |service|
|
108
|
+
service.formats :json
|
109
|
+
service.response do |response|
|
110
|
+
response.object do |obj|
|
111
|
+
obj.integer :id
|
112
|
+
obj.string :foo
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
response = service.response
|
117
|
+
response.nodes.should_not be_empty
|
118
|
+
obj = response.nodes.first
|
119
|
+
obj.should_not be_nil
|
120
|
+
obj.properties.find{|prop| prop.name == :id}.should_not be_nil
|
121
|
+
obj.properties.find{|prop| prop.name == :foo}.should_not be_nil
|
122
|
+
end
|
105
123
|
|
106
124
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
require File.expand_path("../lib/json_response_verification", File.dirname(__FILE__))
|
3
|
+
|
4
|
+
WSDSL.send(:include, JSONResponseVerification)
|
5
|
+
|
6
|
+
describe "JSON response verification" do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
@service = describe_service "json_response_verification" do |service|
|
10
|
+
service.response do |response|
|
11
|
+
response.element(:name => :user) do |user|
|
12
|
+
user.integer :id
|
13
|
+
user.string :name
|
14
|
+
user.datetime :created_at
|
15
|
+
user.object :creds do |creds|
|
16
|
+
creds.integer :id
|
17
|
+
creds.float :price
|
18
|
+
creds.boolean :enabled
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@second_service = describe_service "anonym_obj_json_response_verification" do |service|
|
25
|
+
service.response do |response|
|
26
|
+
response.object do |user|
|
27
|
+
user.integer :id
|
28
|
+
user.string :name
|
29
|
+
user.datetime :created_at
|
30
|
+
user.object :creds do |creds|
|
31
|
+
creds.integer :id
|
32
|
+
creds.float :price
|
33
|
+
creds.boolean :enabled
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid_response(namespaced=true)
|
42
|
+
response = {
|
43
|
+
"id" => 1,
|
44
|
+
"name" => "matt",
|
45
|
+
"created_at" => "2011-09-22T16:32:46-07:00",
|
46
|
+
"creds" => { "id" => 42, "price" => 2010.07, "enabled" => false }
|
47
|
+
}
|
48
|
+
namespaced ? {"user" => response} : response
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should validate the response" do
|
52
|
+
valid, errors = @service.validate_hash_response(valid_response)
|
53
|
+
valid.should be_true
|
54
|
+
errors.should be_empty
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should detect that the response is missing the top level object" do
|
58
|
+
response = valid_response
|
59
|
+
response.delete("user")
|
60
|
+
valid, errors = @service.validate_hash_response(response)
|
61
|
+
valid.should be_false
|
62
|
+
errors.should_not be_empty
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should detect that a property type is wrong" do
|
66
|
+
response = valid_response
|
67
|
+
response["user"]["id"] = 'test'
|
68
|
+
valid, errors = @service.validate_hash_response(response)
|
69
|
+
valid.should be_false
|
70
|
+
errors.should_not be_empty
|
71
|
+
errors.first.should match(/id/)
|
72
|
+
errors.first.should match(/wrong type/)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should detect that a nested object is missing" do
|
76
|
+
response = valid_response
|
77
|
+
response["user"].delete("creds")
|
78
|
+
valid, errors = @service.validate_hash_response(response)
|
79
|
+
valid.should be_false
|
80
|
+
errors.should_not be_empty
|
81
|
+
errors.first.should match(/creds/)
|
82
|
+
errors.first.should match(/missing/)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should validate non namespaced responses" do
|
86
|
+
valid, errors = @second_service.validate_hash_response(valid_response(false))
|
87
|
+
valid.should be_true
|
88
|
+
errors.should be_empty
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|
data/wsdsl.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{wsdsl}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = [%q{Matt Aimonetti}]
|
12
|
-
s.date = %q{2011-09-
|
12
|
+
s.date = %q{2011-09-23}
|
13
13
|
s.description = %q{A Ruby DSL describing Web Services without implementation details.}
|
14
14
|
s.email = %q{mattaimonetti@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
"lib/framework_ext/sinatra.rb",
|
26
26
|
"lib/framework_ext/sinatra_controller.rb",
|
27
27
|
"lib/inflection.rb",
|
28
|
+
"lib/json_response_verification.rb",
|
28
29
|
"lib/params.rb",
|
29
30
|
"lib/params_verification.rb",
|
30
31
|
"lib/response.rb",
|
@@ -33,6 +34,7 @@ Gem::Specification.new do |s|
|
|
33
34
|
"spec/hello_world_controller.rb",
|
34
35
|
"spec/hello_world_service.rb",
|
35
36
|
"spec/json_response_description_spec.rb",
|
37
|
+
"spec/json_response_verification_spec.rb",
|
36
38
|
"spec/params_verification_spec.rb",
|
37
39
|
"spec/preferences_service.rb",
|
38
40
|
"spec/spec_helper.rb",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wsdsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-09-
|
12
|
+
date: 2011-09-23 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: A Ruby DSL describing Web Services without implementation details.
|
15
15
|
email: mattaimonetti@gmail.com
|
@@ -27,6 +27,7 @@ files:
|
|
27
27
|
- lib/framework_ext/sinatra.rb
|
28
28
|
- lib/framework_ext/sinatra_controller.rb
|
29
29
|
- lib/inflection.rb
|
30
|
+
- lib/json_response_verification.rb
|
30
31
|
- lib/params.rb
|
31
32
|
- lib/params_verification.rb
|
32
33
|
- lib/response.rb
|
@@ -35,6 +36,7 @@ files:
|
|
35
36
|
- spec/hello_world_controller.rb
|
36
37
|
- spec/hello_world_service.rb
|
37
38
|
- spec/json_response_description_spec.rb
|
39
|
+
- spec/json_response_verification_spec.rb
|
38
40
|
- spec/params_verification_spec.rb
|
39
41
|
- spec/preferences_service.rb
|
40
42
|
- spec/spec_helper.rb
|