savon 0.7.9 → 0.8.0.beta.1

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.
Files changed (74) hide show
  1. data/.gitignore +9 -0
  2. data/.rspec +1 -0
  3. data/.yardopts +2 -0
  4. data/CHANGELOG.md +332 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +20 -0
  7. data/README.md +37 -0
  8. data/Rakefile +28 -39
  9. data/autotest/discover.rb +1 -0
  10. data/lib/savon.rb +10 -31
  11. data/lib/savon/client.rb +116 -98
  12. data/lib/savon/core_ext/array.rb +36 -22
  13. data/lib/savon/core_ext/datetime.rb +15 -6
  14. data/lib/savon/core_ext/hash.rb +122 -94
  15. data/lib/savon/core_ext/object.rb +19 -11
  16. data/lib/savon/core_ext/string.rb +62 -57
  17. data/lib/savon/core_ext/symbol.rb +13 -5
  18. data/lib/savon/error.rb +6 -0
  19. data/lib/savon/global.rb +75 -0
  20. data/lib/savon/http/error.rb +42 -0
  21. data/lib/savon/soap.rb +8 -283
  22. data/lib/savon/soap/fault.rb +48 -0
  23. data/lib/savon/soap/request.rb +61 -0
  24. data/lib/savon/soap/response.rb +65 -0
  25. data/lib/savon/soap/xml.rb +132 -0
  26. data/lib/savon/version.rb +2 -2
  27. data/lib/savon/wsdl/document.rb +107 -0
  28. data/lib/savon/wsdl/parser.rb +90 -0
  29. data/lib/savon/wsdl/request.rb +35 -0
  30. data/lib/savon/wsse.rb +42 -104
  31. data/savon.gemspec +26 -0
  32. data/spec/fixtures/response/response_fixture.rb +26 -26
  33. data/spec/fixtures/response/xml/list.xml +18 -0
  34. data/spec/fixtures/wsdl/wsdl_fixture.rb +6 -0
  35. data/spec/fixtures/wsdl/wsdl_fixture.yml +4 -4
  36. data/spec/savon/client_spec.rb +274 -51
  37. data/spec/savon/core_ext/datetime_spec.rb +1 -1
  38. data/spec/savon/core_ext/hash_spec.rb +40 -4
  39. data/spec/savon/core_ext/object_spec.rb +1 -1
  40. data/spec/savon/core_ext/string_spec.rb +0 -12
  41. data/spec/savon/http/error_spec.rb +52 -0
  42. data/spec/savon/savon_spec.rb +90 -0
  43. data/spec/savon/soap/fault_spec.rb +80 -0
  44. data/spec/savon/soap/request_spec.rb +45 -0
  45. data/spec/savon/soap/response_spec.rb +153 -0
  46. data/spec/savon/soap/xml_spec.rb +249 -0
  47. data/spec/savon/soap_spec.rb +4 -177
  48. data/spec/savon/{wsdl_spec.rb → wsdl/document_spec.rb} +54 -17
  49. data/spec/savon/wsdl/request_spec.rb +15 -0
  50. data/spec/savon/wsse_spec.rb +123 -92
  51. data/spec/spec_helper.rb +19 -4
  52. data/spec/support/endpoint.rb +25 -0
  53. metadata +97 -97
  54. data/.autotest +0 -5
  55. data/CHANGELOG +0 -176
  56. data/README.rdoc +0 -64
  57. data/lib/savon/core_ext.rb +0 -8
  58. data/lib/savon/core_ext/net_http.rb +0 -19
  59. data/lib/savon/core_ext/uri.rb +0 -10
  60. data/lib/savon/logger.rb +0 -56
  61. data/lib/savon/request.rb +0 -138
  62. data/lib/savon/response.rb +0 -174
  63. data/lib/savon/wsdl.rb +0 -137
  64. data/lib/savon/wsdl_stream.rb +0 -85
  65. data/spec/basic_spec_helper.rb +0 -11
  66. data/spec/endpoint_helper.rb +0 -23
  67. data/spec/http_stubs.rb +0 -26
  68. data/spec/integration/http_basic_auth_spec.rb +0 -16
  69. data/spec/integration/server.rb +0 -51
  70. data/spec/savon/core_ext/net_http_spec.rb +0 -38
  71. data/spec/savon/core_ext/uri_spec.rb +0 -19
  72. data/spec/savon/request_spec.rb +0 -117
  73. data/spec/savon/response_spec.rb +0 -179
  74. data/spec/spec.opts +0 -4
@@ -1,174 +0,0 @@
1
- module Savon
2
-
3
- # = Savon::Response
4
- #
5
- # Savon::Response represents both HTTP and SOAP response.
6
- #
7
- # == SOAP fault
8
- #
9
- # Assuming the default behavior of raising errors is disabled, you can ask the response object
10
- # if there was a SOAP fault or an HTTP error and get the SOAP fault or HTTP error message.
11
- #
12
- # response.soap_fault?
13
- # # => true
14
- #
15
- # response.soap_fault
16
- # # => "(soap:Server) Fault occurred while processing."
17
- #
18
- # response.http_error?
19
- # # => true
20
- #
21
- # response.http_error
22
- # # => "Not found (404)"
23
- #
24
- # == Response as XML
25
- #
26
- # To get the raw SOAP response XML, you can call to_xml or to_s on the response object.
27
- #
28
- # response.to_xml
29
- # => "<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
30
- # => "..."
31
- # => "</soap:Envelope>"
32
- #
33
- # == Response as a Hash
34
- #
35
- # You can also let Savon translate the SOAP response body to a Hash.
36
- #
37
- # response.to_hash
38
- # => { :findUserByIdResponse => {
39
- # => :id => "123",
40
- # => :username => "eve"
41
- # => :active => true
42
- # => }
43
- #
44
- # When translating the SOAP response to a Hash, some XML tags and values are converted to more
45
- # convenient Ruby objects. Translation is done through John Nunemaker's {Crack}[http://github.com/jnunemaker/crack]
46
- # library along with some custom mapping.
47
- #
48
- # * XML tags (Hash keys) are converted to snake_case Symbols and namespaces are stripped off
49
- # * SOAP xs:nil values are converted to nil objects
50
- # * XML values specified in xs:DateTime format are converted to DateTime objects
51
- # * XML values of "true" and "false" are converted to TrueClass and FalseClass
52
- #
53
- # == Net::HTTP response
54
- #
55
- # If for some reason you need to access the Net::HTTP response object ... you can.
56
- #
57
- # bc. response.http
58
- # => #<Net::HTTPOK:0x7f749a1aa4a8>
59
- class Response
60
-
61
- # The maximum HTTP response code considered to be OK.
62
- MaxNonErrorResponseCode = 299
63
-
64
- # The global setting of whether to raise errors.
65
- @@raise_errors = true
66
-
67
- # Sets the global setting of whether to raise errors.
68
- def self.raise_errors=(raise_errors)
69
- @@raise_errors = raise_errors
70
- end
71
-
72
- # Returns the global setting of whether to raise errors.
73
- def self.raise_errors?
74
- @@raise_errors
75
- end
76
-
77
- # Expects a Net::HTTPResponse and handles errors.
78
- def initialize(http)
79
- @http = http
80
-
81
- handle_soap_fault
82
- handle_http_error
83
- end
84
-
85
- # Returns whether there was a SOAP fault.
86
- def soap_fault?
87
- !@soap_fault.blank?
88
- end
89
-
90
- # Returns the SOAP fault message.
91
- attr_reader :soap_fault
92
-
93
- # Returns whether there was an HTTP error.
94
- def http_error?
95
- !@http_error.blank?
96
- end
97
-
98
- # Returns the HTTP error message.
99
- attr_reader :http_error
100
-
101
- # Returns the SOAP response body as a Hash.
102
- def to_hash
103
- @hash ||= (Crack::XML.parse(body) rescue {}).find_soap_body
104
- end
105
-
106
- # Returns the SOAP response XML.
107
- def to_xml
108
- body
109
- end
110
-
111
- # Returns the HTTP response object.
112
- attr_reader :http
113
-
114
- alias :to_s :to_xml
115
-
116
- private
117
-
118
- # Returns the response body.
119
- def body
120
- @body || gzipped_body? ? decoded_body : @http.body
121
- end
122
-
123
- # Returns whether the body is gzipped.
124
- def gzipped_body?
125
- @http["content-encoding"] == "gzip" || @http.body[0..1] == "\x1f\x8b"
126
- end
127
-
128
- # Returns the gzip decoded body.
129
- def decoded_body
130
- gz = Zlib::GzipReader.new StringIO.new(@http.body)
131
- gz.read
132
- ensure
133
- gz.close
134
- end
135
-
136
- # Handles SOAP faults. Raises a Savon::SOAPFault unless the default behavior of raising errors
137
- # was turned off.
138
- def handle_soap_fault
139
- if soap_fault_message
140
- @soap_fault = soap_fault_message
141
- raise Savon::SOAPFault, @soap_fault if self.class.raise_errors?
142
- end
143
- end
144
-
145
- # Returns a SOAP fault message in case a SOAP fault was found.
146
- def soap_fault_message
147
- @soap_fault_message ||= soap_fault_message_by_version to_hash[:fault]
148
- end
149
-
150
- # Expects a Hash that might contain information about a SOAP fault. Returns the SOAP fault
151
- # message in case one was found.
152
- def soap_fault_message_by_version(soap_fault)
153
- return unless soap_fault
154
-
155
- if soap_fault.keys.include? :faultcode
156
- "(#{soap_fault[:faultcode]}) #{soap_fault[:faultstring]}"
157
- elsif soap_fault.keys.include? :code
158
- "(#{soap_fault[:code][:value]}) #{soap_fault[:reason][:text]}"
159
- end
160
- end
161
-
162
- # Handles HTTP errors. Raises a Savon::HTTPError unless the default behavior of raising errors
163
- # was turned off.
164
- def handle_http_error
165
- if @http.code.to_i > MaxNonErrorResponseCode
166
- @http_error = "#{@http.message} (#{@http.code})"
167
- @http_error << ": #{body}" unless body.empty?
168
- raise Savon::HTTPError, http_error if self.class.raise_errors?
169
- end
170
- end
171
-
172
- end
173
- end
174
-
@@ -1,137 +0,0 @@
1
- module Savon
2
-
3
- # = Savon::WSDL
4
- #
5
- # Savon::WSDL represents the WSDL of your service, including information like the namespace URI,
6
- # the SOAP endpoint and available SOAP actions.
7
- #
8
- # == The WSDL document
9
- #
10
- # Retrieve the raw WSDL document:
11
- #
12
- # client.wsdl.to_s
13
- #
14
- # == Available SOAP actions
15
- #
16
- # Get an array of available SOAP actions:
17
- #
18
- # client.wsdl.soap_actions
19
- # # => [:get_all_users, :get_user_by_id]
20
- #
21
- # == Namespace URI
22
- #
23
- # Get the namespace URI:
24
- #
25
- # client.wsdl.namespace_uri
26
- # # => "http://ws.userservice.example.com"
27
- #
28
- # == SOAP endpoint
29
- #
30
- # Get the SOAP endpoint:
31
- #
32
- # client.wsdl.soap_endpoint
33
- # # => "http://example.com"
34
- #
35
- # == Disable Savon::WSDL
36
- #
37
- # Especially with large services (i.e. Ebay), getting and parsing the WSDL document can really
38
- # slow down your request. The WSDL is great for exploring a service, but it's recommended to
39
- # disable it for production.
40
- #
41
- # When disabling the WSDL, you need to pay attention to certain differences:
42
- #
43
- # 1. You instantiate Savon::Client with the actual SOAP endpoint instead of pointing it to the
44
- # WSDL of your service.
45
- # 2. You also need to manually specify the SOAP.namespace.
46
- # 3. Append an exclamation mark (!) to your SOAP call:
47
- #
48
- # client = Savon::Client.new "http://example.com"
49
- #
50
- # client.get_user_by_id! do |soap|
51
- # soap.namespace = "http://example.com/UserService"
52
- # soap.body = { :id => 666 }
53
- # end
54
- #
55
- # Without the WSDL, Savon also has to guess the name of the SOAP action and input tag. It takes
56
- # the name of the method called on its client instance, converts it from snake_case to lowerCamelCase
57
- # and uses the result.
58
- #
59
- # The example above expects a SOAP action with an original name of "getUserById". If you service
60
- # uses UpperCamelCase method names, you can just use the original name:
61
- #
62
- # client.GetAllUsers!
63
- #
64
- # For special cases, you could also specify the SOAP.action and SOAP.input inside the block:
65
- #
66
- # client.get_user_by_id! do |soap|
67
- # soap.namespace = "http://example.com/UserService"
68
- # soap.action = "GetUserById"
69
- # soap.input = "GetUserByIdRequest"
70
- # soap.body = { :id => 123 }
71
- # end
72
- class WSDL
73
-
74
- # Expects a Savon::Request and accepts a custom +soap_endpoint+.
75
- def initialize(request, soap_endpoint = nil)
76
- @request, @enabled, @soap_endpoint = request, true, soap_endpoint
77
- end
78
-
79
- # Sets whether to use the WSDL.
80
- attr_writer :enabled
81
-
82
- # Returns whether to use the WSDL. Defaults to +true+.
83
- def enabled?
84
- @enabled
85
- end
86
-
87
- # Returns the namespace URI of the WSDL.
88
- def namespace_uri
89
- @namespace_uri ||= stream.namespace_uri
90
- end
91
-
92
- # Returns an Array of available SOAP actions.
93
- def soap_actions
94
- @soap_actions ||= stream.operations.keys
95
- end
96
-
97
- # Returns a Hash of SOAP operations including their corresponding
98
- # SOAP actions and inputs.
99
- def operations
100
- @operations ||= stream.operations
101
- end
102
-
103
- # Returns the SOAP endpoint.
104
- def soap_endpoint
105
- @soap_endpoint ||= stream.soap_endpoint
106
- end
107
-
108
- # Returns +true+ for available methods and SOAP actions.
109
- def respond_to?(method)
110
- return true if !enabled? || soap_actions.include?(method)
111
- super
112
- end
113
-
114
- # Returns an Array containg the SOAP action and input for a given +soap_call+.
115
- def operation_from(soap_action)
116
- return [soap_action.to_soap_key, soap_action.to_soap_key] unless enabled?
117
- [operations[soap_action][:action], operations[soap_action][:input]]
118
- end
119
-
120
- # Returns the raw WSDL document.
121
- def to_s
122
- @document ||= @request.wsdl.body
123
- end
124
-
125
- private
126
-
127
- # Returns the Savon::WSDLStream.
128
- def stream
129
- unless @stream
130
- @stream = WSDLStream.new
131
- REXML::Document.parse_stream to_s, @stream
132
- end
133
- @stream
134
- end
135
-
136
- end
137
- end
@@ -1,85 +0,0 @@
1
- module Savon
2
-
3
- # = Savon::WSDLStream
4
- #
5
- # Savon::WSDLStream serves as a stream listener for parsing the WSDL document.
6
- class WSDLStream
7
-
8
- # The main sections of a WSDL document.
9
- Sections = %w(definitions types message portType binding service)
10
-
11
- def initialize
12
- @path, @operations, @namespaces = [], {}, {}
13
- end
14
-
15
- # Returns the namespace URI.
16
- attr_reader :namespace_uri
17
-
18
- # Returns the SOAP operations.
19
- attr_reader :operations
20
-
21
- # Returns the SOAP endpoint.
22
- attr_reader :soap_endpoint
23
-
24
- # Hook method called when the stream parser encounters a starting tag.
25
- def tag_start(tag, attrs)
26
- # read xml namespaces if root element
27
- read_namespaces(attrs) if @path.empty?
28
-
29
- tag, namespace = tag.split(":").reverse
30
- @path << tag
31
-
32
- if @section == :binding && tag == "binding"
33
- # ensure that we are in an wsdl/soap namespace
34
- @section = nil unless @namespaces[namespace].starts_with? "http://schemas.xmlsoap.org/wsdl/soap"
35
- end
36
-
37
- @section = tag.to_sym if Sections.include?(tag) && depth <= 2
38
-
39
- @namespace_uri ||= attrs["targetNamespace"] if @section == :definitions
40
- @soap_endpoint ||= URI(attrs["location"]) if @section == :service && tag == "address"
41
-
42
- operation_from tag, attrs if @section == :binding && tag == "operation"
43
- end
44
-
45
- # Returns our current depth in the WSDL document.
46
- def depth
47
- @path.size
48
- end
49
-
50
- # Reads namespace definitions from a given +attrs+ Hash.
51
- def read_namespaces(attrs)
52
- attrs.each do |key, value|
53
- @namespaces[key.strip_namespace] = value if key.starts_with? "xmlns:"
54
- end
55
- end
56
-
57
- # Hook method called when the stream parser encounters a closing tag.
58
- def tag_end(tag)
59
- @path.pop
60
-
61
- if @section == :binding && @input && tag.strip_namespace == "operation"
62
- # no soapAction attribute found till now
63
- operation_from tag, "soapAction" => @input
64
- end
65
- end
66
-
67
- # Stores available operations from a given tag +name+ and +attrs+.
68
- def operation_from(tag, attrs)
69
- @input = attrs["name"] if attrs["name"]
70
-
71
- if attrs["soapAction"]
72
- @action = !attrs["soapAction"].blank? ? attrs["soapAction"] : @input
73
- @input = @action.split("/").last if !@input || @input.empty?
74
-
75
- @operations[@input.snakecase.to_sym] = { :action => @action, :input => @input }
76
- @input, @action = nil, nil
77
- end
78
- end
79
-
80
- # Catches calls to unimplemented hook methods.
81
- def method_missing(method, *args)
82
- end
83
-
84
- end
85
- end
@@ -1,11 +0,0 @@
1
- require "rake"
2
- require "spec"
3
- require "mocha"
4
- require "fakeweb"
5
-
6
- Spec::Runner.configure do |config|
7
- config.mock_with :mocha
8
- end
9
-
10
- require "savon"
11
- Savon::Request.log = false
@@ -1,23 +0,0 @@
1
- class EndpointHelper
2
-
3
- # Returns the WSDL endpoint for a given +type+ of request.
4
- def self.wsdl_endpoint(type = nil)
5
- case type
6
- when :no_namespace then "http://nons.example.com/Service?wsdl"
7
- when :namespaced_actions then "http://nsactions.example.com/Service?wsdl"
8
- when :geotrust then "https://test-api.geotrust.com/webtrust/query.jws?WSDL"
9
- else soap_endpoint(type)
10
- end
11
- end
12
-
13
- # Returns the SOAP endpoint for a given +type+ of request.
14
- def self.soap_endpoint(type = nil)
15
- case type
16
- when :soap_fault then "http://soapfault.example.com/Service?wsdl"
17
- when :http_error then "http://httperror.example.com/Service?wsdl"
18
- when :invalid then "http://invalid.example.com/Service?wsdl"
19
- else "http://example.com/validation/1.0/AuthenticationService"
20
- end
21
- end
22
-
23
- end
@@ -1,26 +0,0 @@
1
- FakeWeb.allow_net_connect = false
2
-
3
- # Some WSDL and SOAP request.
4
- FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint, :body => WSDLFixture.authentication
5
- FakeWeb.register_uri :post, EndpointHelper.soap_endpoint, :body => ResponseFixture.authentication
6
-
7
- # WSDL and SOAP request with a Savon::SOAPFault.
8
- FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:soap_fault), :body => WSDLFixture.authentication
9
- FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:soap_fault), :body => ResponseFixture.soap_fault
10
-
11
- # WSDL and SOAP request with a Savon::HTTPError.
12
- FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:http_error), :body => WSDLFixture.authentication
13
- FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:http_error), :body => "", :status => ["404", "Not Found"]
14
-
15
- # WSDL and SOAP request with an invalid endpoint.
16
- FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:invalid), :body => ""
17
- FakeWeb.register_uri :post, EndpointHelper.soap_endpoint(:invalid), :body => "", :status => ["404", "Not Found"]
18
-
19
- # WSDL request returning a WSDL document where the main sections are not namespaced.
20
- FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:no_namespace), :body => WSDLFixture.no_namespace
21
-
22
- # WSDL request returning a WSDL document with namespaced SOAP actions.
23
- FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:namespaced_actions), :body => WSDLFixture.namespaced_actions
24
-
25
- # WSDL request returning a WSDL document with geotrust SOAP actions.
26
- FakeWeb.register_uri :get, EndpointHelper.wsdl_endpoint(:geotrust), :body => WSDLFixture.geotrust