savon 0.9.7 → 0.9.8

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.
@@ -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