tictoc-savon 0.7.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/.autotest +5 -0
  2. data/CHANGELOG +176 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +64 -0
  5. data/Rakefile +50 -0
  6. data/lib/savon.rb +35 -0
  7. data/lib/savon/client.rb +131 -0
  8. data/lib/savon/core_ext.rb +8 -0
  9. data/lib/savon/core_ext/array.rb +31 -0
  10. data/lib/savon/core_ext/datetime.rb +10 -0
  11. data/lib/savon/core_ext/hash.rb +107 -0
  12. data/lib/savon/core_ext/net_http.rb +19 -0
  13. data/lib/savon/core_ext/object.rb +16 -0
  14. data/lib/savon/core_ext/string.rb +69 -0
  15. data/lib/savon/core_ext/symbol.rb +8 -0
  16. data/lib/savon/core_ext/uri.rb +10 -0
  17. data/lib/savon/logger.rb +56 -0
  18. data/lib/savon/request.rb +138 -0
  19. data/lib/savon/response.rb +174 -0
  20. data/lib/savon/soap.rb +302 -0
  21. data/lib/savon/version.rb +5 -0
  22. data/lib/savon/wsdl.rb +137 -0
  23. data/lib/savon/wsdl_stream.rb +85 -0
  24. data/lib/savon/wsse.rb +163 -0
  25. data/spec/basic_spec_helper.rb +11 -0
  26. data/spec/endpoint_helper.rb +23 -0
  27. data/spec/fixtures/gzip/gzip_response_fixture.rb +7 -0
  28. data/spec/fixtures/gzip/message.gz +0 -0
  29. data/spec/fixtures/response/response_fixture.rb +36 -0
  30. data/spec/fixtures/response/xml/authentication.xml +14 -0
  31. data/spec/fixtures/response/xml/multi_ref.xml +39 -0
  32. data/spec/fixtures/response/xml/soap_fault.xml +8 -0
  33. data/spec/fixtures/response/xml/soap_fault12.xml +18 -0
  34. data/spec/fixtures/wsdl/wsdl_fixture.rb +37 -0
  35. data/spec/fixtures/wsdl/wsdl_fixture.yml +42 -0
  36. data/spec/fixtures/wsdl/xml/authentication.xml +63 -0
  37. data/spec/fixtures/wsdl/xml/geotrust.xml +156 -0
  38. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +307 -0
  39. data/spec/fixtures/wsdl/xml/no_namespace.xml +115 -0
  40. data/spec/http_stubs.rb +26 -0
  41. data/spec/integration/http_basic_auth_spec.rb +16 -0
  42. data/spec/integration/server.rb +51 -0
  43. data/spec/savon/client_spec.rb +86 -0
  44. data/spec/savon/core_ext/array_spec.rb +49 -0
  45. data/spec/savon/core_ext/datetime_spec.rb +21 -0
  46. data/spec/savon/core_ext/hash_spec.rb +190 -0
  47. data/spec/savon/core_ext/net_http_spec.rb +38 -0
  48. data/spec/savon/core_ext/object_spec.rb +34 -0
  49. data/spec/savon/core_ext/string_spec.rb +99 -0
  50. data/spec/savon/core_ext/symbol_spec.rb +12 -0
  51. data/spec/savon/core_ext/uri_spec.rb +19 -0
  52. data/spec/savon/request_spec.rb +117 -0
  53. data/spec/savon/response_spec.rb +179 -0
  54. data/spec/savon/soap_spec.rb +202 -0
  55. data/spec/savon/wsdl_spec.rb +107 -0
  56. data/spec/savon/wsse_spec.rb +132 -0
  57. data/spec/spec.opts +4 -0
  58. data/spec/spec_helper.rb +5 -0
  59. metadata +229 -0
@@ -0,0 +1,5 @@
1
+ module Savon
2
+
3
+ Version = "0.7.9"
4
+
5
+ end
data/lib/savon/wsdl.rb ADDED
@@ -0,0 +1,137 @@
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
@@ -0,0 +1,85 @@
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
data/lib/savon/wsse.rb ADDED
@@ -0,0 +1,163 @@
1
+ module Savon
2
+
3
+ # = Savon::WSSE
4
+ #
5
+ # Savon::WSSE represents WSSE authentication. Pass a block to your SOAP call and the WSSE object
6
+ # is passed to it as the second argument. The object allows setting the WSSE username, password
7
+ # and whether to use digest authentication.
8
+ #
9
+ # == Credentials
10
+ #
11
+ # By default, Savon does not use WSSE authentication. Simply specify a username and password to
12
+ # change this.
13
+ #
14
+ # response = client.get_all_users do |soap, wsse|
15
+ # wsse.username = "eve"
16
+ # wsse.password = "secret"
17
+ # end
18
+ #
19
+ # == Digest
20
+ #
21
+ # To use WSSE digest authentication, just use the digest method and set it to +true+.
22
+ #
23
+ # response = client.get_all_users do |soap, wsse|
24
+ # wsse.username = "eve"
25
+ # wsse.password = "secret"
26
+ # wsse.digest = true
27
+ # end
28
+ #
29
+ # == Default to WSSE
30
+ #
31
+ # In case all you're services require WSSE authentication, you can set your credentials and whether
32
+ # to use WSSE digest for every request:
33
+ #
34
+ # Savon::WSSE.username = "eve"
35
+ # Savon::WSSE.password = "secret"
36
+ # Savon::WSSE.digest = true
37
+ class WSSE
38
+
39
+ # Base address for WSSE docs.
40
+ BaseAddress = "http://docs.oasis-open.org/wss/2004/01"
41
+
42
+ # Namespace for WS Security Secext.
43
+ WSENamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-secext-1.0.xsd"
44
+
45
+ # Namespace for WS Security Utility.
46
+ WSUNamespace = "#{BaseAddress}/oasis-200401-wss-wssecurity-utility-1.0.xsd"
47
+
48
+ # URI for "wsse:Password/@Type" #PasswordText.
49
+ PasswordTextURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordText"
50
+
51
+ # URI for "wsse:Password/@Type" #PasswordDigest.
52
+ PasswordDigestURI = "#{BaseAddress}/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
53
+
54
+ # Global WSSE username.
55
+ @@username = nil
56
+
57
+ # Returns the global WSSE username.
58
+ def self.username
59
+ @@username
60
+ end
61
+
62
+ # Sets the global WSSE username.
63
+ def self.username=(username)
64
+ @@username = username.nil? ? nil : username.to_s
65
+ end
66
+
67
+ # Global WSSE password.
68
+ @@password = nil
69
+
70
+ # Returns the global WSSE password.
71
+ def self.password
72
+ @@password
73
+ end
74
+
75
+ # Sets the global WSSE password.
76
+ def self.password=(password)
77
+ @@password = password.nil? ? nil : password.to_s
78
+ end
79
+
80
+ # Global setting of whether to use WSSE digest.
81
+ @@digest = false
82
+
83
+ # Returns the global setting of whether to use WSSE digest.
84
+ def self.digest?
85
+ @@digest
86
+ end
87
+
88
+ # Global setting of whether to use WSSE digest.
89
+ def self.digest=(digest)
90
+ @@digest = digest
91
+ end
92
+
93
+ # Sets the WSSE username per request.
94
+ def username=(username)
95
+ @username = username.nil? ? nil : username.to_s
96
+ end
97
+
98
+ # Returns the WSSE username. Defaults to the global setting.
99
+ def username
100
+ @username || self.class.username
101
+ end
102
+
103
+ # Sets the WSSE password per request.
104
+ def password=(password)
105
+ @password = password.nil? ? nil : password.to_s
106
+ end
107
+
108
+ # Returns the WSSE password. Defaults to the global setting.
109
+ def password
110
+ @password || self.class.password
111
+ end
112
+
113
+ # Sets whether to use WSSE digest per request.
114
+ attr_writer :digest
115
+
116
+ # Returns whether to use WSSE digest. Defaults to the global setting.
117
+ def digest?
118
+ @digest || self.class.digest?
119
+ end
120
+
121
+ # Returns the XML for a WSSE header or an empty String unless both username and password
122
+ # were specified.
123
+ def header
124
+ return "" unless username && password
125
+
126
+ builder = Builder::XmlMarkup.new
127
+ builder.wsse :Security, "xmlns:wsse" => WSENamespace do |xml|
128
+ xml.wsse :UsernameToken, "xmlns:wsu" => WSUNamespace do
129
+ xml.wsse :Username, username
130
+ xml.wsse :Nonce, nonce
131
+ xml.wsu :Created, timestamp
132
+ xml.wsse :Password, password_node, :Type => password_type
133
+ end
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ # Returns the WSSE password. Encrypts the password for digest authentication.
140
+ def password_node
141
+ return password unless digest?
142
+
143
+ token = nonce + timestamp + password
144
+ Base64.encode64(Digest::SHA1.hexdigest(token)).chomp!
145
+ end
146
+
147
+ # Returns the URI for the "wsse:Password/@Type" attribute.
148
+ def password_type
149
+ digest? ? PasswordDigestURI : PasswordTextURI
150
+ end
151
+
152
+ # Returns a WSSE nonce.
153
+ def nonce
154
+ @nonce ||= Digest::SHA1.hexdigest String.random + timestamp
155
+ end
156
+
157
+ # Returns a WSSE timestamp.
158
+ def timestamp
159
+ @timestamp ||= Time.now.strftime Savon::SOAP::DateTimeFormat
160
+ end
161
+
162
+ end
163
+ end
@@ -0,0 +1,11 @@
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
@@ -0,0 +1,23 @@
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
@@ -0,0 +1,7 @@
1
+ class GzipResponseFixture
2
+
3
+ def self.message
4
+ File.read(File.join(File.dirname(__FILE__), 'message.gz'))
5
+ end
6
+ end
7
+
Binary file
@@ -0,0 +1,36 @@
1
+ class ResponseFixture
2
+
3
+ def self.authentication(value = nil)
4
+ case value
5
+ when :to_hash
6
+ { :success => true,
7
+ :authentication_value => {
8
+ :token => "a68d1d6379b62ff339a0e0c69ed4d9cf",
9
+ :token_hash => "AAAJxA;cIedoT;mY10ExZwG6JuKgp2OYKxow==",
10
+ :client => "radclient"
11
+ }
12
+ }
13
+ else
14
+ @@authentication ||= load_fixture :authentication
15
+ end
16
+ end
17
+
18
+ def self.soap_fault
19
+ @@soap_fault ||= load_fixture :soap_fault
20
+ end
21
+
22
+ def self.soap_fault12
23
+ @@soap_fault12 ||= load_fixture :soap_fault12
24
+ end
25
+
26
+ def self.multi_ref
27
+ @@multi_ref ||= load_fixture :multi_ref
28
+ end
29
+
30
+ private
31
+
32
+ def self.load_fixture(fixture)
33
+ File.read File.dirname(__FILE__) + "/xml/#{fixture}.xml"
34
+ end
35
+
36
+ end