savon 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.7.2 (2010-01-17)
2
+ * Exposed the Net::HTTP response (added by Kevin Ingolfsland). Use the "http" accessor (response.http) on your
3
+ Savon::Response to access the Net::HTTP response object.
4
+ * Fix for Github issue #21 (savon is stripping ?SOAP off the end of WSDL locations).
5
+ * Fix for Github issue #22 (REXML::ParseException parsing 401 Unauthorized response body).
6
+ * Fix for Github issue #19 (Unable to set attribute in name-spaced WSSE password element).
7
+ * Added support for global header and namespaces. See Github issue #9 (Setting headers and namespaces).
8
+
1
9
  == 0.7.1 (2010-01-10)
2
10
  * The Hash of HTTP headers for SOAP calls is now public via Savon::Request#headers.
3
11
  Patch for: http://github.com/rubiii/savon/issues/#issue/8
@@ -122,20 +122,14 @@ module Savon
122
122
  # to an optional block.
123
123
  def request(type)
124
124
  request = case type
125
- when :wsdl then Net::HTTP::Get.new wsdl_endpoint
126
- when :soap then Net::HTTP::Post.new @soap.endpoint.path, headers.merge(soap_headers)
125
+ when :wsdl then Net::HTTP::Get.new @endpoint.to_s
126
+ when :soap then Net::HTTP::Post.new @soap.endpoint.to_s, headers.merge(soap_headers)
127
127
  end
128
128
  request.basic_auth *@basic_auth if @basic_auth
129
129
  yield request if block_given?
130
130
  request
131
131
  end
132
132
 
133
- # Returns the WSDL endpoint.
134
- def wsdl_endpoint
135
- return @endpoint.path unless @endpoint.query
136
- "#{@endpoint.path}?#{@endpoint.query}"
137
- end
138
-
139
133
  # Returns a Hash containing the SOAP headers for an HTTP request.
140
134
  def soap_headers
141
135
  { "Content-Type" => ContentType[@soap.version], "SOAPAction" => @soap.action }
@@ -19,8 +19,8 @@ module Savon
19
19
  end
20
20
 
21
21
  # Expects a Net::HTTPResponse and handles errors.
22
- def initialize(response)
23
- @response = response
22
+ def initialize(http)
23
+ @http = http
24
24
 
25
25
  handle_soap_fault
26
26
  handle_http_error
@@ -44,14 +44,17 @@ module Savon
44
44
 
45
45
  # Returns the SOAP response body as a Hash.
46
46
  def to_hash
47
- @body ||= Crack::XML.parse(@response.body).find_soap_body
47
+ @body ||= (Crack::XML.parse(@http.body) rescue {}).find_soap_body
48
48
  end
49
49
 
50
50
  # Returns the SOAP response XML.
51
51
  def to_xml
52
- @response.body
52
+ @http.body
53
53
  end
54
54
 
55
+ # Returns the HTTP response object.
56
+ attr_reader :http
57
+
55
58
  alias :to_s :to_xml
56
59
 
57
60
  private
@@ -85,9 +88,9 @@ module Savon
85
88
  # Handles HTTP errors. Raises a Savon::HTTPError unless the default
86
89
  # behavior of raising errors was turned off.
87
90
  def handle_http_error
88
- if @response.code.to_i >= 300
89
- @http_error = "#{@response.message} (#{@response.code})"
90
- @http_error << ": #{@response.body}" unless @response.body.empty?
91
+ if @http.code.to_i >= 300
92
+ @http_error = "#{@http.message} (#{@http.code})"
93
+ @http_error << ": #{@http.body}" unless @http.body.empty?
91
94
  raise Savon::HTTPError, http_error if self.class.raise_errors?
92
95
  end
93
96
  end
@@ -27,6 +27,30 @@ module Savon
27
27
  @@version = version if Savon::SOAPVersions.include? version
28
28
  end
29
29
 
30
+ # Sets the global SOAP header. Expected to be a Hash that can be translated
31
+ # to XML via Hash.to_soap_xml or any other Object responding to to_s.
32
+ def self.header=(header)
33
+ @@header = header
34
+ end
35
+
36
+ # Returns the global SOAP header. Defaults to an empty Hash.
37
+ def self.header
38
+ @@header ||= {}
39
+ end
40
+
41
+ # Sets the global namespaces. Expected to be a Hash containing the
42
+ # namespaces (keys) and the corresponding URI's (values).
43
+ def self.namespaces=(namespaces)
44
+ @@namespaces = namespaces if namespaces.kind_of? Hash
45
+ end
46
+
47
+ # Returns the global namespaces. A Hash containing the namespaces (keys)
48
+ # and the corresponding URI's (values).
49
+ def self.namespaces
50
+ @@namespaces ||= {}
51
+ end
52
+
53
+ # Initialzes the SOAP object.
30
54
  def initialize
31
55
  @builder = Builder::XmlMarkup.new
32
56
  end
@@ -70,8 +94,8 @@ module Savon
70
94
  # (keys) and the corresponding URI's (values).
71
95
  attr_writer :namespaces
72
96
 
73
- # Returns the namespaces. A Hash containing the namespaces (keys) and
74
- # the corresponding URI's (values).
97
+ # Returns the namespaces. A Hash containing the namespaces (keys)
98
+ # and the corresponding URI's (values).
75
99
  def namespaces
76
100
  @namespaces ||= { "xmlns:env" => SOAPNamespace[version] }
77
101
  end
@@ -94,7 +118,7 @@ module Savon
94
118
  # Returns the SOAP envelope XML.
95
119
  def to_xml
96
120
  unless @xml_body
97
- @xml_body = @builder.env :Envelope, namespaces do |xml|
121
+ @xml_body = @builder.env :Envelope, all_namespaces do |xml|
98
122
  xml_header xml
99
123
  xml_body xml
100
124
  end
@@ -107,7 +131,16 @@ module Savon
107
131
  # Adds a SOAP XML header to a given +xml+ Object.
108
132
  def xml_header(xml)
109
133
  xml.env(:Header) do
110
- xml << (header.to_soap_xml rescue header.to_s) + wsse_header
134
+ xml << all_header + wsse_header
135
+ end
136
+ end
137
+
138
+ # Returns a String containing the global and per request header.
139
+ def all_header
140
+ if self.class.header.kind_of?(Hash) && header.kind_of?(Hash)
141
+ self.class.header.merge(header).to_soap_xml
142
+ else
143
+ self.class.header.to_s + header.to_s
111
144
  end
112
145
  end
113
146
 
@@ -120,6 +153,11 @@ module Savon
120
153
  end
121
154
  end
122
155
 
156
+ # Returns a Hash containing the global and per request namespaces.
157
+ def all_namespaces
158
+ self.class.namespaces.merge namespaces
159
+ end
160
+
123
161
  # Returns an Array of SOAP input names to append to the :wsdl namespace.
124
162
  # Defaults to use the name of the SOAP action and may be an empty Array
125
163
  # in case the specified SOAP input seems invalid.
@@ -5,11 +5,20 @@ module Savon
5
5
  # Represents parameters for WSSE authentication.
6
6
  class WSSE
7
7
 
8
+ # Base address for WSSE docs.
9
+ BaseAddress = "http://docs.oasis-open.org/wss/2004/01"
10
+
8
11
  # Namespace for WS Security Secext.
9
- WSENamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
12
+ WSENamespace = BaseAddress + "/oasis-200401-wss-wssecurity-secext-1.0.xsd"
10
13
 
11
14
  # Namespace for WS Security Utility.
12
- WSUNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
15
+ WSUNamespace = BaseAddress + "/oasis-200401-wss-wssecurity-utility-1.0.xsd"
16
+
17
+ # URI for "wsse:Password/@Type" #PasswordText.
18
+ PasswordTextURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordText"
19
+
20
+ # URI for "wsse:Password/@Type" #PasswordDigest.
21
+ PasswordDigestURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
13
22
 
14
23
  # Global WSSE username.
15
24
  @@username = nil
@@ -93,7 +102,7 @@ module Savon
93
102
  xml.wsse :Username, username
94
103
  xml.wsse :Nonce, nonce
95
104
  xml.wsu :Created, timestamp
96
- xml.wsse :Password, password_node
105
+ xml.wsse :Password, password_node, :Type => password_type
97
106
  end
98
107
  end
99
108
  end
@@ -108,6 +117,11 @@ module Savon
108
117
  Base64.encode64(Digest::SHA1.hexdigest(token)).chomp!
109
118
  end
110
119
 
120
+ # Returns the URI for the "wsse:Password/@Type" attribute.
121
+ def password_type
122
+ digest? ? PasswordDigestURI : PasswordTextURI
123
+ end
124
+
111
125
  # Returns a WSSE nonce.
112
126
  def nonce
113
127
  @nonce ||= Digest::SHA1.hexdigest String.random + timestamp
@@ -5,16 +5,16 @@ class EndpointHelper
5
5
  case type
6
6
  when :no_namespace then "http://nons.example.com/Service?wsdl"
7
7
  when :namespaced_actions then "http://nsactions.example.com/Service?wsdl"
8
- else soap_endpoint(type) + "?wsdl"
8
+ else soap_endpoint(type)
9
9
  end
10
10
  end
11
11
 
12
12
  # Returns the SOAP endpoint for a given +type+ of request.
13
13
  def self.soap_endpoint(type = nil)
14
14
  case type
15
- when :soap_fault then "http://soapfault.example.com/Service"
16
- when :http_error then "http://httperror.example.com/Service"
17
- when :invalid then "http://invalid.example.com/Service"
15
+ when :soap_fault then "http://soapfault.example.com/Service?wsdl"
16
+ when :http_error then "http://httperror.example.com/Service?wsdl"
17
+ when :invalid then "http://invalid.example.com/Service?wsdl"
18
18
  else "http://example.com/validation/1.0/AuthenticationService"
19
19
  end
20
20
  end
@@ -1,12 +1,16 @@
1
1
  require "basic_spec_helper"
2
2
 
3
3
  describe Savon do
4
- before { @endpoint = "http://localhost:8080/http-basic-auth" }
4
+ before { @client = Savon::Client.new "http://localhost:8080/http-basic-auth" }
5
5
 
6
6
  it "should be able to handle HTTP basic authentication" do
7
- client = Savon::Client.new @endpoint
8
- client.request.basic_auth "user", "password"
9
- response = client.do_something!
7
+ @client.request.basic_auth "user", "password"
8
+ response = @client.do_something!
10
9
  response.to_hash[:authenticate_response][:return][:success].should == true
11
10
  end
11
+
12
+ it "should raise a Savon::HTTPError in case authentication failed" do
13
+ lambda { @client.do_something! }.should raise_error(Savon::HTTPError)
14
+ end
15
+
12
16
  end
@@ -110,6 +110,13 @@ describe Savon::Response do
110
110
  @response.to_s.should == ResponseFixture.authentication
111
111
  end
112
112
 
113
+ it "should return the Net::HTTP response object" do
114
+ @response.http.should be_a(Mocha::Mock)
115
+ @response.http.should respond_to(:code)
116
+ @response.http.should respond_to(:message)
117
+ @response.http.should respond_to(:body)
118
+ end
119
+
113
120
  def savon_response_with(error_type)
114
121
  mock = case error_type
115
122
  when :soap_fault then http_response_mock 200, ResponseFixture.soap_fault
@@ -46,6 +46,14 @@ describe Savon::SOAP do
46
46
  @soap.input = "FindUserRequest"
47
47
  end
48
48
 
49
+ it "has both getter and setter for global SOAP headers" do
50
+ header = { "some" => "header" }
51
+ Savon::SOAP.header = header
52
+ Savon::SOAP.header.should == header
53
+
54
+ Savon::SOAP.header = {}
55
+ end
56
+
49
57
  it "has both getter and setter for the SOAP header" do
50
58
  @soap.header.should be_a(Hash)
51
59
  @soap.header.should be_empty
@@ -76,6 +84,14 @@ describe Savon::SOAP do
76
84
  end
77
85
  end
78
86
 
87
+ it "has both getter and setter for global namespaces" do
88
+ namespaces = { "some" => "namespace" }
89
+ Savon::SOAP.namespaces = namespaces
90
+ Savon::SOAP.namespaces.should == namespaces
91
+
92
+ Savon::SOAP.namespaces = {}
93
+ end
94
+
79
95
  it "has a convenience method for setting the 'xmlns:wsdl' namespace" do
80
96
  @soap.namespaces.should == { "xmlns:env" => "http://schemas.xmlsoap.org/soap/envelope/" }
81
97
 
@@ -126,6 +142,30 @@ describe Savon::SOAP do
126
142
  Savon::SOAP.version = 2
127
143
  @soap.to_xml.should include(Savon::SOAP::SOAPNamespace[2])
128
144
  end
145
+
146
+ it "merges global and per request headers defined as Hashes" do
147
+ Savon::SOAP.header = { "API-KEY" => "secret", "SOME-KEY" => "something" }
148
+ @soap.header["SOME-KEY"] = "somethingelse"
149
+
150
+ @soap.to_xml.should include("<API-KEY>secret</API-KEY>")
151
+ @soap.to_xml.should include("<SOME-KEY>somethingelse</SOME-KEY>")
152
+ end
153
+
154
+ it "joins global and per request headers defined as Strings" do
155
+ Savon::SOAP.header = "<API-KEY>secret</API-KEY>"
156
+ @soap.header = "<SOME-KEY>somethingelse</SOME-KEY>"
157
+
158
+ @soap.to_xml.should include("<API-KEY>secret</API-KEY>")
159
+ @soap.to_xml.should include("<SOME-KEY>somethingelse</SOME-KEY>")
160
+ end
161
+
162
+ it "merges the global and per request namespaces" do
163
+ Savon::SOAP.namespaces = { "xmlns:wsdl" => "namespace", "xmlns:v1" => "v1namespace" }
164
+ @soap.namespaces["xmlns:v1"] = "newV1namespace"
165
+
166
+ @soap.to_xml.should include('xmlns:wsdl="namespace"')
167
+ @soap.to_xml.should include('xmlns:v1="newV1namespace"')
168
+ end
129
169
  end
130
170
 
131
171
  end
@@ -2,12 +2,8 @@ require "spec_helper"
2
2
 
3
3
  describe Savon::WSSE do
4
4
  before do
5
- Savon::WSSE.username = nil
6
- Savon::WSSE.password = nil
7
- Savon::WSSE.digest = false
8
-
9
- @wsse = Savon::WSSE.new
10
- @username, @password = "gorilla", "secret"
5
+ Savon::WSSE.username, Savon::WSSE.password, Savon::WSSE.digest = nil, nil, false
6
+ @wsse, @username, @password = Savon::WSSE.new, "gorilla", "secret"
11
7
  end
12
8
 
13
9
  it "contains the namespace for WS Security Secext" do
@@ -84,6 +80,7 @@ describe Savon::WSSE do
84
80
  header.should include_security_namespaces
85
81
  header.should include(@username)
86
82
  header.should include(@password)
83
+ header.should include(Savon::WSSE::PasswordTextURI)
87
84
  end
88
85
 
89
86
  it "with WSSE credentials specified via defaults" do
@@ -94,6 +91,7 @@ describe Savon::WSSE do
94
91
  header.should include_security_namespaces
95
92
  header.should include(@username)
96
93
  header.should include(@password)
94
+ header.should include(Savon::WSSE::PasswordTextURI)
97
95
  end
98
96
  end
99
97
 
@@ -107,6 +105,7 @@ describe Savon::WSSE do
107
105
  header.should include_security_namespaces
108
106
  header.should include(@username)
109
107
  header.should_not include(@password)
108
+ header.should include(Savon::WSSE::PasswordDigestURI)
110
109
  end
111
110
 
112
111
  it "via defaults" do
@@ -118,6 +117,7 @@ describe Savon::WSSE do
118
117
  header.should include_security_namespaces
119
118
  header.should include(@username)
120
119
  header.should_not include(@password)
120
+ header.should include(Savon::WSSE::PasswordDigestURI)
121
121
  end
122
122
  end
123
123
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: savon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-10 00:00:00 +01:00
12
+ date: 2010-01-17 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency