savon 0.7.4 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,6 +5,9 @@ module Savon
5
5
  # Represents the HTTP and SOAP response.
6
6
  class Response
7
7
 
8
+ # The maximum HTTP response code considered to be OK.
9
+ MaxNonErrorResponseCode = 299
10
+
8
11
  # The global setting of whether to raise errors.
9
12
  @@raise_errors = true
10
13
 
@@ -28,7 +31,7 @@ module Savon
28
31
 
29
32
  # Returns whether there was a SOAP fault.
30
33
  def soap_fault?
31
- @soap_fault ? true : false
34
+ !@soap_fault.blank?
32
35
  end
33
36
 
34
37
  # Returns the SOAP fault message.
@@ -36,7 +39,7 @@ module Savon
36
39
 
37
40
  # Returns whether there was an HTTP error.
38
41
  def http_error?
39
- @http_error ? true : false
42
+ !@http_error.blank?
40
43
  end
41
44
 
42
45
  # Returns the HTTP error message.
@@ -88,7 +91,7 @@ module Savon
88
91
  # Handles HTTP errors. Raises a Savon::HTTPError unless the default
89
92
  # behavior of raising errors was turned off.
90
93
  def handle_http_error
91
- if @http.code.to_i >= 300
94
+ if @http.code.to_i > MaxNonErrorResponseCode
92
95
  @http_error = "#{@http.message} (#{@http.code})"
93
96
  @http_error << ": #{@http.body}" unless @http.body.empty?
94
97
  raise Savon::HTTPError, http_error if self.class.raise_errors?
@@ -6,7 +6,7 @@ module Savon
6
6
  class SOAP
7
7
 
8
8
  # SOAP namespaces by SOAP version.
9
- SOAPNamespace = {
9
+ Namespace = {
10
10
  1 => "http://schemas.xmlsoap.org/soap/envelope/",
11
11
  2 => "http://www.w3.org/2003/05/soap-envelope"
12
12
  }
@@ -14,6 +14,15 @@ module Savon
14
14
  # Content-Types by SOAP version.
15
15
  ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
16
16
 
17
+ # Supported SOAP versions.
18
+ Versions = [1, 2]
19
+
20
+ # SOAP xs:dateTime format.
21
+ DateTimeFormat = "%Y-%m-%dT%H:%M:%SZ"
22
+
23
+ # SOAP xs:dateTime Regexp.
24
+ DateTimeRegexp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
25
+
17
26
  # The global SOAP version.
18
27
  @@version = 1
19
28
 
@@ -24,7 +33,7 @@ module Savon
24
33
 
25
34
  # Sets the global SOAP version.
26
35
  def self.version=(version)
27
- @@version = version if Savon::SOAPVersions.include? version
36
+ @@version = version if Versions.include? version
28
37
  end
29
38
 
30
39
  # Sets the global SOAP header. Expected to be a Hash that can be translated
@@ -51,7 +60,9 @@ module Savon
51
60
  end
52
61
 
53
62
  # Initialzes the SOAP object.
54
- def initialize
63
+ def initialize(operation, endpoint)
64
+ @action, @input = operation[:action], operation[:input]
65
+ @endpoint = endpoint.kind_of?(URI) ? endpoint : URI(endpoint)
55
66
  @builder = Builder::XmlMarkup.new
56
67
  end
57
68
 
@@ -97,7 +108,7 @@ module Savon
97
108
  # Returns the namespaces. A Hash containing the namespaces (keys)
98
109
  # and the corresponding URI's (values).
99
110
  def namespaces
100
- @namespaces ||= { "xmlns:env" => SOAPNamespace[version] }
111
+ @namespaces ||= { "xmlns:env" => Namespace[version] }
101
112
  end
102
113
 
103
114
  # Convenience method for setting the "xmlns:wsdl" namespace.
@@ -107,7 +118,7 @@ module Savon
107
118
 
108
119
  # Sets the SOAP version.
109
120
  def version=(version)
110
- @version = version if Savon::SOAPVersions.include? version
121
+ @version = version if Versions.include? version
111
122
  end
112
123
 
113
124
  # Returns the SOAP version. Defaults to the global default.
@@ -161,9 +172,15 @@ module Savon
161
172
  # Defaults to use the name of the SOAP action and may be an empty Array
162
173
  # in case the specified SOAP input seems invalid.
163
174
  def input_array
164
- return input.map { |i| i.is_a?(Hash) ? i : i.to_sym } unless input.blank?
165
- return [action.to_sym] unless action.blank?
166
- []
175
+ if !input.blank? && input.kind_of?(Array)
176
+ [input[0].to_sym, input[1]]
177
+ elsif !input.blank?
178
+ [input.to_sym]
179
+ elsif action.blank?
180
+ [action.to_sym]
181
+ else
182
+ []
183
+ end
167
184
  end
168
185
 
169
186
  end
@@ -7,7 +7,7 @@ module Savon
7
7
 
8
8
  # Initializer, expects a Savon::Request.
9
9
  def initialize(request)
10
- @request = request
10
+ @request, @enabled = request, true
11
11
  end
12
12
 
13
13
  # Sets whether to use the WSDL.
@@ -15,7 +15,7 @@ module Savon
15
15
 
16
16
  # Returns whether to use the WSDL. Defaults to +true+.
17
17
  def enabled?
18
- @enabled.nil? ? true : @enabled
18
+ @enabled
19
19
  end
20
20
 
21
21
  # Returns the namespace URI of the WSDL.
@@ -41,10 +41,17 @@ module Savon
41
41
 
42
42
  # Returns +true+ for available methods and SOAP actions.
43
43
  def respond_to?(method)
44
- return true if soap_actions.include? method
44
+ return true if !enabled? || soap_actions.include?(method)
45
45
  super
46
46
  end
47
47
 
48
+ # Returns a SOAP operation Hash containing the SOAP action and input
49
+ # for a given +soap_call+.
50
+ def operation_from(soap_action)
51
+ return operations[soap_action] if enabled?
52
+ { :action => soap_action.to_soap_key, :input => soap_action.to_soap_key }
53
+ end
54
+
48
55
  # Returns the raw WSDL document.
49
56
  def to_s
50
57
  @document ||= @request.wsdl.body
@@ -9,8 +9,7 @@ module Savon
9
9
  Sections = %w(definitions types message portType binding service)
10
10
 
11
11
  def initialize
12
- @path, @operations = [], {}
13
- @namespaces = {}
12
+ @path, @operations, @namespaces = [], {}, {}
14
13
  end
15
14
 
16
15
  # Returns the namespace URI.
@@ -27,12 +26,12 @@ module Savon
27
26
  # read xml namespaces if root element
28
27
  read_namespaces(attrs) if @path.empty?
29
28
 
30
- tag,namespace = tag.split(":").reverse
29
+ tag, namespace = tag.split(":").reverse
31
30
  @path << tag
32
31
 
33
32
  if @section == :binding && tag == "binding"
34
33
  # ensure that we are in an wsdl/soap namespace
35
- @section = nil unless @namespaces[namespace] == "http://schemas.xmlsoap.org/wsdl/soap/"
34
+ @section = nil unless @namespaces[namespace].starts_with? "http://schemas.xmlsoap.org/wsdl/soap"
36
35
  end
37
36
 
38
37
  @section = tag.to_sym if Sections.include?(tag) && depth <= 2
@@ -51,19 +50,18 @@ module Savon
51
50
  # Reads namespace definitions from a given +attrs+ Hash.
52
51
  def read_namespaces(attrs)
53
52
  attrs.each do |key, value|
54
- @namespaces[key.split(":").last] = value if key =~ /^xmlns:/
53
+ @namespaces[key.strip_namespace] = value if key.starts_with? "xmlns:"
55
54
  end
56
55
  end
57
56
 
58
57
  # Hook method called when the stream parser encounters a closing tag.
59
58
  def tag_end(tag)
60
59
  @path.pop
61
-
60
+
62
61
  if @section == :binding && @input && tag.strip_namespace == "operation"
63
62
  # no soapAction attribute found till now
64
63
  operation_from tag, "soapAction" => @input
65
64
  end
66
-
67
65
  end
68
66
 
69
67
  # Stores available operations from a given tag +name+ and +attrs+.
@@ -76,7 +74,6 @@ module Savon
76
74
 
77
75
  @operations[@input.snakecase.to_sym] = { :action => @action, :input => @input }
78
76
  @input, @action = nil, nil
79
- @input = nil
80
77
  end
81
78
  end
82
79
 
@@ -9,16 +9,16 @@ module Savon
9
9
  BaseAddress = "http://docs.oasis-open.org/wss/2004/01"
10
10
 
11
11
  # Namespace for WS Security Secext.
12
- WSENamespace = BaseAddress + "/oasis-200401-wss-wssecurity-secext-1.0.xsd"
12
+ WSENamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-secext-1.0.xsd"
13
13
 
14
14
  # Namespace for WS Security Utility.
15
- WSUNamespace = BaseAddress + "/oasis-200401-wss-wssecurity-utility-1.0.xsd"
15
+ WSUNamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-utility-1.0.xsd"
16
16
 
17
17
  # URI for "wsse:Password/@Type" #PasswordText.
18
- PasswordTextURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordText"
18
+ PasswordTextURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordText"
19
19
 
20
20
  # URI for "wsse:Password/@Type" #PasswordDigest.
21
- PasswordDigestURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
21
+ PasswordDigestURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
22
22
 
23
23
  # Global WSSE username.
24
24
  @@username = nil
@@ -30,8 +30,7 @@ module Savon
30
30
 
31
31
  # Sets the global WSSE username.
32
32
  def self.username=(username)
33
- @@username = username.to_s if username.respond_to? :to_s
34
- @@username = nil if username.nil?
33
+ @@username = username.nil? ? nil : username.to_s
35
34
  end
36
35
 
37
36
  # Global WSSE password.
@@ -44,8 +43,7 @@ module Savon
44
43
 
45
44
  # Sets the global WSSE password.
46
45
  def self.password=(password)
47
- @@password = password.to_s if password.respond_to? :to_s
48
- @@password = nil if password.nil?
46
+ @@password = password.nil? ? nil : password.to_s
49
47
  end
50
48
 
51
49
  # Global setting of whether to use WSSE digest.
@@ -63,8 +61,7 @@ module Savon
63
61
 
64
62
  # Sets the WSSE username per request.
65
63
  def username=(username)
66
- @username = username.to_s if username.respond_to? :to_s
67
- @username = nil if username.nil?
64
+ @username = username.nil? ? nil : username.to_s
68
65
  end
69
66
 
70
67
  # Returns the WSSE username. Defaults to the global setting.
@@ -74,8 +71,7 @@ module Savon
74
71
 
75
72
  # Sets the WSSE password per request.
76
73
  def password=(password)
77
- @password = password.to_s if password.respond_to? :to_s
78
- @password = nil if password.nil?
74
+ @password = password.nil? ? nil : password.to_s
79
75
  end
80
76
 
81
77
  # Returns the WSSE password. Defaults to the global setting.
@@ -129,7 +125,7 @@ module Savon
129
125
 
130
126
  # Returns a WSSE timestamp.
131
127
  def timestamp
132
- @timestamp ||= Time.now.strftime Savon::SOAPDateTimeFormat
128
+ @timestamp ||= Time.now.strftime Savon::SOAP::DateTimeFormat
133
129
  end
134
130
 
135
131
  end
@@ -0,0 +1,18 @@
1
+ Savon::Client is the main object to connect to a SOAP service. It includes methods to access both Savon::WSDL and Savon::Request objects.
2
+
3
+ == Instantiate Savon::Client
4
+
5
+ Depending on whether you aim to use Savon with or without Savon::WSDL, you need to instantiate Savon::Client by passing in the WSDL or SOAP endpoint.
6
+
7
+ # Client instance with a WSDL endpoint
8
+ client = Savon::Client.new "http://example.com/UserService?wsdl"
9
+
10
+ # Client instance with a SOAP endpoint (for using Savon without a WSDL)
11
+ client = Savon::Client.new "http://example.com/UserService"
12
+
13
+ == Using a proxy server
14
+
15
+ You can specify the URI to a proxy server via optional Hash arguments.
16
+
17
+ client = Savon::Client.new "http://example.com/UserService?wsdl",
18
+ :proxy => "http://proxy.example.com"
@@ -0,0 +1,11 @@
1
+ == HTTP errors
2
+
3
+ In case of an HTTP error, a Savon::HTTPError will be raised. Any HTTP response code >= 300 raises this error. The only exception to this rule is when the server also returns a SOAP fault. In this case, a Savon::SOAPFault is raised.
4
+
5
+ == SOAP faults
6
+
7
+ A Savon::SOAPFault is raised in case the server returned a SOAP fault.
8
+
9
+ == Rails tip
10
+
11
+ In case you're using Savon inside a Rails application, you can use {rescue_from}[http://api.rubyonrails.org/classes/ActiveSupport/Rescuable/ClassMethods.html] to handle these errors
@@ -0,0 +1,11 @@
1
+ Savon logs each request and response to STDOUT. Specifying your own logger is as easy as it gets:
2
+
3
+ Savon::Request.logger = RAILS_DEFAULT_LOGGER
4
+
5
+ The default log level is set to :debug. In case you need to change this:
6
+
7
+ Savon::Request.log_level = :info
8
+
9
+ You can also disable logging if you need to:
10
+
11
+ Savon::Request.log = false
@@ -0,0 +1,21 @@
1
+ == Provide feedback
2
+
3
+ Let me know about your experiences using Savon. What's good, what's not? {Open an issue}[http://github.com/rubiii/savon/issues] if you encounter a bug. Feedback is always appreciated.
4
+
5
+ == Spread the word
6
+
7
+ Let others know about Savon by talking, tweeting or blogging about it. The more people use Savon, the more stable it becomes.
8
+
9
+ == Fix bugs or implement features
10
+
11
+ You're welcome to fork the project to fix bugs, implement features, etc. But please make sure to follow "the rules":
12
+
13
+ * Follow the coding and naming conventions of the code
14
+ * Do not edit the CHANGELOG and .gemspec files
15
+ * Test all the fucking time!
16
+
17
+ Also, if you're thinking about adding some big new feature or if you're not sure about how to implement it, please just get in touch.
18
+
19
+ Please note that Savon {does not require Rubygems}[http://gist.github.com/54177]. So make sure your environment is set up to load the package manager of your choice. In order to run autospec, you could use the following alias:
20
+
21
+ alias autospec='RUBYLIB=./lib RUBYOPT=-rubygems AUTOFEATURE=true autospec'
@@ -0,0 +1,37 @@
1
+ Savon::Request handles both WSDL and SOAP requests.
2
+
3
+ == The Net::HTTP object
4
+
5
+ You can access the Net::HTTP object used for both WSDL and SOAP requests via:
6
+
7
+ client.request.http
8
+
9
+ Here's an example of how to set open and read timeouts on the Net::HTTP object.
10
+
11
+ client.request.http.open_timeout = 30
12
+ client.request.http.read_timeout = 30
13
+
14
+ Please refer to the {official documentation}[http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/index.html] for more information.
15
+
16
+ == HTTP basic authentication
17
+
18
+ Setting credentials for HTTP basic authentication:
19
+
20
+ client.request.basic_auth "username", "password"
21
+
22
+ == SSL client authentication
23
+
24
+ You can use the methods provided by Net::HTTP to set SSL client authentication or use this shortcut:
25
+
26
+ client.request.http.ssl_client_auth(
27
+ :cert => OpenSSL::X509::Certificate.new(File.read("client_cert.pem")),
28
+ :key => OpenSSL::PKey::RSA.new(File.read("client_key.pem"), "password if one exists"),
29
+ :ca_file => "cacert.pem",
30
+ :verify_mode => OpenSSL::SSL::VERIFY_PEER
31
+ )
32
+
33
+ == HTTP headers
34
+
35
+ There's an accessor for the Hash of HTTP headers sent with any SOAP call:
36
+
37
+ client.request.headers["custom"] = "header"
@@ -0,0 +1,46 @@
1
+ The response is wrapped in an object to give you various options of handling it.
2
+
3
+ == SOAP fault
4
+
5
+ Assuming the default behavior of raising errors is disabled, you can ask the response object if there was a SOAP fault or an HTTP error and get the SOAP fault or HTTP error message.
6
+
7
+ response.soap_fault?
8
+ => true
9
+
10
+ response.soap_fault
11
+ => "(soap:Server) Fault occurred while processing."
12
+
13
+ response.http_error?
14
+ => true
15
+
16
+ response.http_error
17
+ => "Not found (404)"
18
+
19
+ == Response as XML
20
+
21
+ To get the raw SOAP response XML, you can call to_xml or to_s on the response object.
22
+
23
+ response.to_xml
24
+ => "<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
25
+ => "..."
26
+ => "</soap:Envelope>"
27
+
28
+ == Response as a Hash
29
+
30
+ You can also let Savon translate the SOAP response body to a Hash.
31
+
32
+ response.to_hash
33
+ => { :findUserByIdResponse => {
34
+ => :id => "666",
35
+ => :username => "thedude"
36
+ => :firstname => "The",
37
+ => :lastname => "Dude",
38
+ => :active => true
39
+ => }
40
+
41
+ == Net::HTTP response
42
+
43
+ If for reason you need to access the Net::HTTP response object ... you can.
44
+
45
+ bc. response.http
46
+ => #<Net::HTTPOK:0x7f749a1aa4a8>
@@ -0,0 +1,71 @@
1
+ Savon::SOAP represents the SOAP request. Pass a block to your SOAP call and the SOAP object is passed to it as the first argument. The object allows setting the SOAP version, header, body and namespaces per request.
2
+
3
+ == Body
4
+
5
+ The body method lets you specify parameters to be received by the SOAP action. The value is expected to be a Hash (which will be translated to XML via Hash.to_soap_xml) or an XML String.
6
+
7
+ response = client.get_user_by_id do |soap|
8
+ soap.body = { :id => 666 }
9
+ end
10
+
11
+ response = client.get_user_by_id do |soap|
12
+ soap.body = "<id>666</id>"
13
+ end
14
+
15
+ In case your service requires the elements to be in a specific order, you have two options. The first is to specify your body as an XML String. The second is to add an ":order!" key containing an Array of elements in the specific order to your Hash. Here's an example:
16
+
17
+ response = client.add_user do |soap|
18
+ soap.body = { :user => { :name => "Lucy", :id => 666, :order! => [:id, :name] } }
19
+ end
20
+
21
+ == Version
22
+
23
+ Savon defaults to SOAP 1.1. In case your service uses SOAP 1.2, you can use the version method to change the default.
24
+
25
+ response = client.get_all_users do |soap|
26
+ soap.version = 2
27
+ end
28
+
29
+ You can also change the default to SOAP 1.2 for all request:
30
+
31
+ Savon::SOAP.version = 2
32
+
33
+ == Header
34
+
35
+ If you need to add custom XML into the SOAP header, you can use the header method. The value is expected to be a Hash (which will be translated to XML via Hash.to_soap_xml) or an XML String.
36
+
37
+ response = client.get_all_users do |soap|
38
+ soap.header["specialApiKey"] = "secret"
39
+ end
40
+
41
+ response = client.get_all_users do |soap|
42
+ soap.header = "<specialApiKey>secret</specialApiKey>"
43
+ end
44
+
45
+ == Namespaces
46
+
47
+ The namespaces method contains a Hash of namespaces for the SOAP envelope.
48
+
49
+ response = client.get_all_users do |soap|
50
+ soap.namespaces["xmlns:whatever"] = "http://example.com"
51
+ end
52
+
53
+ == Input
54
+
55
+ You can change the name of the SOAP input node in case you need to.
56
+
57
+ response = client.get_all_users do |soap|
58
+ soap.input = "GetAllUsersRequest"
59
+ end
60
+
61
+ The request is going to look something like this:
62
+
63
+ => <env:Envelope
64
+ => xmlns:wsdl="http://example.com/user/1.0/UserService"
65
+ => xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
66
+ => <env:Header></env:Header>
67
+ => <env:Body>
68
+ => <wsdl:GetAllUsersRequest></wsdl:GetAllUsersRequest>
69
+ => </env:Body>
70
+ => </env:Envelope>
71
+