wsdsl 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|