savon 0.9.7 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,50 +14,49 @@ module Savon
14
14
 
15
15
  # Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object
16
16
  # to execute a SOAP request and returns the response.
17
- def self.execute(request, soap)
18
- new(request, soap).response
17
+ def self.execute(http, soap)
18
+ new(http, soap).response
19
19
  end
20
20
 
21
21
  # Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object.
22
- def initialize(request, soap)
23
- self.request = setup(request, soap)
22
+ def initialize(http, soap)
23
+ self.soap = soap
24
+ self.http = configure(http)
24
25
  end
25
26
 
26
- # Accessor for the <tt>HTTPI::Request</tt>.
27
- attr_accessor :request
27
+ attr_accessor :soap, :http
28
28
 
29
29
  # Executes the request and returns the response.
30
30
  def response
31
- @response ||= with_logging { HTTPI.post request }
31
+ @response ||= SOAP::Response.new(
32
+ Savon.hooks.select(:soap_request).call(self) || with_logging { HTTPI.post(http) }
33
+ )
32
34
  end
33
35
 
34
36
  private
35
37
 
36
- # Sets up the +request+ using a given +soap+ object.
37
- def setup(request, soap)
38
- url, body = soap.endpoint, soap.to_xml
39
-
40
- request.url = url
41
- request.body = body
42
- request.headers["Content-Type"] ||= ContentType[soap.version]
43
- request.headers["Content-Length"] ||= body.length.to_s
44
-
45
- request
38
+ # Configures a given +http+ from the +soap+ object.
39
+ def configure(http)
40
+ http.url = soap.endpoint
41
+ http.body = soap.to_xml
42
+ http.headers["Content-Type"] = ContentType[soap.version]
43
+ http.headers["Content-Length"] = soap.to_xml.bytesize.to_s
44
+ http
46
45
  end
47
46
 
48
47
  # Logs the HTTP request, yields to a given +block+ and returns a <tt>Savon::SOAP::Response</tt>.
49
48
  def with_logging
50
- log_request request.url, request.headers, request.body
49
+ log_request http.url, http.headers, http.body
51
50
  response = yield
52
51
  log_response response.code, response.body
53
- SOAP::Response.new response
52
+ response
54
53
  end
55
54
 
56
55
  # Logs the SOAP request +url+, +headers+ and +body+.
57
56
  def log_request(url, headers, body)
58
57
  Savon.log "SOAP request: #{url}"
59
58
  Savon.log headers.map { |key, value| "#{key}: #{value}" }.join(", ")
60
- Savon.log body
59
+ Savon.log body, :filter
61
60
  end
62
61
 
63
62
  # Logs the SOAP response +code+ and +body+.
@@ -1,5 +1,6 @@
1
1
  require "savon/soap/xml"
2
2
  require "savon/soap/fault"
3
+ require "savon/soap/invalid_response_error"
3
4
  require "savon/http/error"
4
5
 
5
6
  module Savon
@@ -50,11 +51,17 @@ module Savon
50
51
 
51
52
  # Returns the SOAP response header as a Hash.
52
53
  def header
54
+ if !hash.has_key? :envelope
55
+ raise Savon::SOAP::InvalidResponseError, "Unable to parse response body '#{to_xml}'"
56
+ end
53
57
  hash[:envelope][:header]
54
58
  end
55
59
 
56
60
  # Returns the SOAP response body as a Hash.
57
61
  def body
62
+ if !hash.has_key? :envelope
63
+ raise Savon::SOAP::InvalidResponseError, "Unable to parse response body '#{to_xml}'"
64
+ end
58
65
  hash[:envelope][:body]
59
66
  end
60
67
 
@@ -125,6 +125,14 @@ module Savon
125
125
  # Accessor for the <tt>Savon::WSSE</tt> object.
126
126
  attr_accessor :wsse
127
127
 
128
+ # Returns the SOAP request encoding. Defaults to "UTF-8".
129
+ def encoding
130
+ @encoding ||= "UTF-8"
131
+ end
132
+
133
+ # Sets the SOAP request encoding.
134
+ attr_writer :encoding
135
+
128
136
  # Accepts a +block+ and yields a <tt>Builder::XmlMarkup</tt> object to let you create
129
137
  # custom body XML.
130
138
  def body
@@ -161,7 +169,7 @@ module Savon
161
169
  private
162
170
 
163
171
  # Returns a new <tt>Builder::XmlMarkup</tt> object.
164
- def builder(directive_tag = :xml, attrs = {})
172
+ def builder(directive_tag = :xml, attrs = { :encoding => encoding })
165
173
  builder = Builder::XmlMarkup.new
166
174
  builder.instruct!(directive_tag, attrs) if directive_tag
167
175
  builder
@@ -1,5 +1,5 @@
1
1
  module Savon
2
2
 
3
- Version = "0.9.7"
3
+ Version = "0.9.8"
4
4
 
5
5
  end
@@ -27,6 +27,9 @@ Gem::Specification.new do |s|
27
27
  s.add_development_dependency "mocha", "~> 0.9.8"
28
28
  s.add_development_dependency "timecop", "~> 0.3.5"
29
29
 
30
+ s.add_development_dependency "autotest"
31
+ s.add_development_dependency "ZenTest", "4.5.0"
32
+
30
33
  s.files = `git ls-files`.split("\n")
31
34
  s.require_path = "lib"
32
35
  end
@@ -0,0 +1,194 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::Model do
4
+
5
+ let(:model) do
6
+ Class.new { extend Savon::Model }
7
+ end
8
+
9
+ describe ":model_soap_response hook" do
10
+
11
+ before(:all) do
12
+ model.actions :get_user, "GetAllUsers"
13
+ end
14
+
15
+ after do
16
+ Savon.hooks.reject! :test_hook
17
+ end
18
+
19
+ it "can be used for pre-processing SOAP responses" do
20
+ Savon.hooks.define(:test_hook, :model_soap_response) do |response|
21
+ "hello #{response}"
22
+ end
23
+
24
+ model.client.stubs(:request).returns("world") #
25
+ model.get_user.should == "hello world"
26
+ end
27
+
28
+ end
29
+
30
+ describe ".client" do
31
+
32
+ it "passes a given block to a new Savon::Client"
33
+
34
+ it "memoizes the Savon::Client" do
35
+ model.client.should equal(model.client)
36
+ end
37
+
38
+ end
39
+
40
+ describe ".endpoint" do
41
+
42
+ it "sets the SOAP endpoint" do
43
+ model.endpoint "http://example.com"
44
+ model.client.wsdl.endpoint.should == "http://example.com"
45
+ end
46
+
47
+ end
48
+
49
+ describe ".namespace" do
50
+
51
+ it "sets the target namespace" do
52
+ model.namespace "http://v1.example.com"
53
+ model.client.wsdl.namespace.should == "http://v1.example.com"
54
+ end
55
+
56
+ end
57
+
58
+ describe ".document" do
59
+
60
+ it "sets the WSDL document" do
61
+ model.document "http://example.com/?wsdl"
62
+ model.client.wsdl.document.should == "http://example.com/?wsdl"
63
+ end
64
+
65
+ end
66
+
67
+ describe ".headers" do
68
+
69
+ it "sets the HTTP headers" do
70
+ model.headers("Accept-Charset" => "utf-8")
71
+ model.client.http.headers.should == { "Accept-Charset" => "utf-8" }
72
+ end
73
+
74
+ end
75
+
76
+ describe ".basic_auth" do
77
+
78
+ it "sets HTTP Basic auth credentials" do
79
+ model.basic_auth "login", "password"
80
+ model.client.http.auth.basic.should == ["login", "password"]
81
+ end
82
+
83
+ end
84
+
85
+ describe ".wsse_auth" do
86
+
87
+ it "sets WSSE auth credentials" do
88
+ model.wsse_auth "login", "password", :digest
89
+
90
+ model.client.wsse.username.should == "login"
91
+ model.client.wsse.password.should == "password"
92
+ model.client.wsse.should be_digest
93
+ end
94
+
95
+ end
96
+
97
+ describe ".actions" do
98
+
99
+ before(:all) do
100
+ model.actions :get_user, "GetAllUsers"
101
+ end
102
+
103
+ it "defines class methods each action" do
104
+ model.should respond_to(:get_user, :get_all_users)
105
+ end
106
+
107
+ it "defines instance methods each action" do
108
+ model.new.should respond_to(:get_user, :get_all_users)
109
+ end
110
+
111
+ context "(class-level)" do
112
+
113
+ it "executes SOAP requests with a given body" do
114
+ model.client.expects(:request).with(:wsdl, :get_user, :body => { :id => 1 })
115
+ model.get_user :id => 1
116
+ end
117
+
118
+ it "accepts and passes Strings for action names" do
119
+ model.client.expects(:request).with(:wsdl, "GetAllUsers", :body => { :id => 1 })
120
+ model.get_all_users :id => 1
121
+ end
122
+ end
123
+
124
+ context "(instance-level)" do
125
+
126
+ it "delegates to the corresponding class method" do
127
+ model.expects(:get_all_users).with(:active => true)
128
+ model.new.get_all_users :active => true
129
+ end
130
+
131
+ end
132
+
133
+ end
134
+
135
+ describe "#client" do
136
+
137
+ it "returns the class-level Savon::Client" do
138
+ model.new.client.should == model.client
139
+ end
140
+
141
+ end
142
+
143
+ describe "overwriting action methods" do
144
+
145
+ context "(class-level)" do
146
+
147
+ let(:supermodel) do
148
+ supermodel = model.dup
149
+ supermodel.actions :get_user
150
+
151
+ def supermodel.get_user(body = nil, &block)
152
+ p "super"
153
+ super
154
+ end
155
+
156
+ supermodel
157
+ end
158
+
159
+ it "works" do
160
+ supermodel.client.expects(:request).with(:wsdl, :get_user, :body => { :id => 1 })
161
+ supermodel.expects(:p).with("super") # stupid, but works
162
+
163
+ supermodel.get_user :id => 1
164
+ end
165
+
166
+ end
167
+
168
+ context "(instance-level)" do
169
+
170
+ let(:supermodel) do
171
+ supermodel = model.dup
172
+ supermodel.actions :get_user
173
+ supermodel = supermodel.new
174
+
175
+ def supermodel.get_user(body = nil, &block)
176
+ p "super"
177
+ super
178
+ end
179
+
180
+ supermodel
181
+ end
182
+
183
+ it "works" do
184
+ supermodel.client.expects(:request).with(:wsdl, :get_user, :body => { :id => 1 })
185
+ supermodel.expects(:p).with("super") # stupid, but works
186
+
187
+ supermodel.get_user :id => 1
188
+ end
189
+
190
+ end
191
+
192
+ end
193
+
194
+ end
@@ -19,6 +19,39 @@ describe Savon do
19
19
  Savon.configure { |config| config.log = false }
20
20
  Savon.log?.should be_false
21
21
  end
22
+
23
+ context "when instructed to filter" do
24
+ before do
25
+ Savon.log = true
26
+ end
27
+
28
+ context "and no log filter set" do
29
+ it "should not filter the message" do
30
+ Savon.logger.expects(Savon.log_level).with(Fixture.response(:authentication))
31
+ Savon.log(Fixture.response(:authentication), :filter)
32
+ end
33
+ end
34
+
35
+ context "and multiple log filters" do
36
+ before do
37
+ Savon.configure { |config| config.log_filter = ["logType", "logTime"] }
38
+ end
39
+
40
+ it "should filter element values" do
41
+ filtered_values = /Notes Log|2010-09-21T18:22:01|2010-09-21T18:22:07/
42
+
43
+ Savon.logger.expects(Savon.log_level).with do |msg|
44
+ msg !~ filtered_values &&
45
+ msg.include?('<ns10:logTime>***FILTERED***</ns10:logTime>') &&
46
+ msg.include?('<ns10:logType>***FILTERED***</ns10:logType>') &&
47
+ msg.include?('<ns11:logTime>***FILTERED***</ns11:logTime>') &&
48
+ msg.include?('<ns11:logType>***FILTERED***</ns11:logType>')
49
+ end
50
+
51
+ Savon.log(Fixture.response(:list), :filter)
52
+ end
53
+ end
54
+ end
22
55
  end
23
56
 
24
57
  describe "logger" do
@@ -69,17 +102,6 @@ describe Savon do
69
102
  lambda { Savon.soap_version = 3 }.should raise_error(ArgumentError)
70
103
  end
71
104
  end
72
-
73
- describe "strip_namespaces" do
74
- it "should default to true" do
75
- Savon.strip_namespaces?.should == true
76
- end
77
-
78
- it "should not strip namespaces when set to false" do
79
- Savon.strip_namespaces = false
80
- Savon.strip_namespaces?.should == false
81
- end
82
- end
83
105
  end
84
106
 
85
107
  end
@@ -20,30 +20,34 @@ describe Savon::SOAP::Request do
20
20
 
21
21
  describe ".new" do
22
22
  it "uses the SOAP endpoint for the request" do
23
- soap_request.request.url.should == URI(soap.endpoint)
23
+ soap_request.http.url.should == URI(soap.endpoint)
24
24
  end
25
25
 
26
26
  it "sets the SOAP body for the request" do
27
- soap_request.request.body.should == soap.to_xml
27
+ soap_request.http.body.should == soap.to_xml
28
28
  end
29
29
 
30
30
  it "sets the Content-Type header for SOAP 1.1" do
31
- soap_request.request.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[1]
31
+ soap_request.http.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[1]
32
32
  end
33
33
 
34
34
  it "sets the Content-Type header for SOAP 1.2" do
35
35
  soap.version = 2
36
- soap_request.request.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[2]
36
+ soap_request.http.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[2]
37
37
  end
38
38
 
39
- it "does not set the Content-Type header if it's already specified" do
40
- headers = { "Content-Type" => "text/plain" }
41
- soap_request = Savon::SOAP::Request.new HTTPI::Request.new(:headers => headers), soap
42
- soap_request.request.headers["Content-Type"].should == headers["Content-Type"]
39
+ it "sets the Content-Length header" do
40
+ soap_request.http.headers["Content-Length"].should == soap.to_xml.length.to_s
43
41
  end
44
42
 
45
- it "sets the Content-Length header" do
46
- soap_request.request.headers["Content-Length"].should == soap.to_xml.length.to_s
43
+ it "sets the Content-Length header for every request" do
44
+ http = HTTPI::Request.new
45
+ soap_request = Savon::SOAP::Request.new(http, soap)
46
+ http.headers.should include("Content-Length" => "272")
47
+
48
+ soap = Savon::SOAP::XML.new Endpoint.soap, [nil, :create_user, {}], :id => 123
49
+ soap_request = Savon::SOAP::Request.new(http, soap)
50
+ http.headers.should include("Content-Length" => "280")
47
51
  end
48
52
  end
49
53
 
@@ -52,6 +56,16 @@ describe Savon::SOAP::Request do
52
56
  HTTPI.expects(:post).returns(HTTPI::Response.new 200, {}, Fixture.response(:authentication))
53
57
  soap_request.response.should be_a(Savon::SOAP::Response)
54
58
  end
59
+
60
+ it "logs the filtered SOAP request body" do
61
+ HTTPI.stubs(:post).returns(HTTPI::Response.new 200, {}, "")
62
+
63
+ Savon.stubs(:log).times(2)
64
+ Savon.expects(:log).with(soap.to_xml, :filter)
65
+ Savon.stubs(:log).times(2)
66
+
67
+ soap_request.response
68
+ end
55
69
  end
56
70
 
57
71
  end