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 CHANGED
@@ -1 +1 @@
1
- 0.3.3
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
@@ -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 >= min
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
 
@@ -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 [Array<WSDSL::Response::Element>]
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{wsdsl}
8
- s.version = "0.3.3"
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}
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.3.3
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 00:00:00.000000000Z
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