savon 0.7.9 → 0.8.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
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