johnreitano-savon 0.7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/CHANGELOG +124 -0
  2. data/README.textile +75 -0
  3. data/Rakefile +45 -0
  4. data/lib/savon/client.rb +84 -0
  5. data/lib/savon/core_ext/datetime.rb +8 -0
  6. data/lib/savon/core_ext/hash.rb +78 -0
  7. data/lib/savon/core_ext/net_http.rb +20 -0
  8. data/lib/savon/core_ext/object.rb +21 -0
  9. data/lib/savon/core_ext/string.rb +47 -0
  10. data/lib/savon/core_ext/symbol.rb +8 -0
  11. data/lib/savon/core_ext/uri.rb +10 -0
  12. data/lib/savon/core_ext.rb +3 -0
  13. data/lib/savon/request.rb +149 -0
  14. data/lib/savon/response.rb +99 -0
  15. data/lib/savon/soap.rb +176 -0
  16. data/lib/savon/wsdl.rb +122 -0
  17. data/lib/savon/wsse.rb +136 -0
  18. data/lib/savon.rb +34 -0
  19. data/spec/basic_spec_helper.rb +12 -0
  20. data/spec/endpoint_helper.rb +22 -0
  21. data/spec/fixtures/response/response_fixture.rb +36 -0
  22. data/spec/fixtures/response/xml/authentication.xml +14 -0
  23. data/spec/fixtures/response/xml/multi_ref.xml +39 -0
  24. data/spec/fixtures/response/xml/soap_fault.xml +8 -0
  25. data/spec/fixtures/response/xml/soap_fault12.xml +18 -0
  26. data/spec/fixtures/wsdl/wsdl_fixture.rb +37 -0
  27. data/spec/fixtures/wsdl/xml/authentication.xml +63 -0
  28. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +307 -0
  29. data/spec/fixtures/wsdl/xml/no_namespace.xml +115 -0
  30. data/spec/http_stubs.rb +23 -0
  31. data/spec/integration/http_basic_auth_spec.rb +16 -0
  32. data/spec/integration/server.rb +51 -0
  33. data/spec/savon/client_spec.rb +77 -0
  34. data/spec/savon/core_ext/datetime_spec.rb +12 -0
  35. data/spec/savon/core_ext/hash_spec.rb +138 -0
  36. data/spec/savon/core_ext/net_http_spec.rb +38 -0
  37. data/spec/savon/core_ext/object_spec.rb +40 -0
  38. data/spec/savon/core_ext/string_spec.rb +68 -0
  39. data/spec/savon/core_ext/symbol_spec.rb +11 -0
  40. data/spec/savon/core_ext/uri_spec.rb +15 -0
  41. data/spec/savon/request_spec.rb +89 -0
  42. data/spec/savon/response_spec.rb +137 -0
  43. data/spec/savon/savon_spec.rb +23 -0
  44. data/spec/savon/soap_spec.rb +171 -0
  45. data/spec/savon/wsdl_spec.rb +84 -0
  46. data/spec/savon/wsse_spec.rb +132 -0
  47. data/spec/spec_helper.rb +5 -0
  48. metadata +218 -0
data/lib/savon/wsdl.rb ADDED
@@ -0,0 +1,122 @@
1
+ module Savon
2
+
3
+ # Savon::WSDL
4
+ #
5
+ # Represents the WSDL document.
6
+ class WSDL
7
+
8
+ # Initializer, expects a Savon::Request.
9
+ def initialize(request)
10
+ @request = request
11
+ end
12
+
13
+ # Sets whether to use the WSDL.
14
+ attr_writer :enabled
15
+
16
+ # Returns whether to use the WSDL. Defaults to +true+.
17
+ def enabled?
18
+ @enabled.nil? ? true : @enabled
19
+ end
20
+
21
+ # Returns the namespace URI of the WSDL.
22
+ def namespace_uri
23
+ @namespace_uri ||= stream.namespace_uri
24
+ end
25
+
26
+ # Returns an Array of available SOAP actions.
27
+ def soap_actions
28
+ @soap_actions ||= stream.operations.keys
29
+ end
30
+
31
+ # Returns a Hash of SOAP operations including their corresponding
32
+ # SOAP actions and inputs.
33
+ def operations
34
+ @operations ||= stream.operations
35
+ end
36
+
37
+ # Returns the SOAP endpoint.
38
+ def soap_endpoint
39
+ @soap_endpoint ||= stream.soap_endpoint
40
+ end
41
+
42
+ # Returns +true+ for available methods and SOAP actions.
43
+ def respond_to?(method)
44
+ return true if soap_actions.include? method
45
+ super
46
+ end
47
+
48
+ # Returns the raw WSDL document.
49
+ def to_s
50
+ @document ||= @request.wsdl.body
51
+ end
52
+
53
+ private
54
+
55
+ # Returns the Savon::WSDLStream.
56
+ def stream
57
+ unless @stream
58
+ @stream = WSDLStream.new
59
+ REXML::Document.parse_stream to_s, @stream
60
+ end
61
+ @stream
62
+ end
63
+
64
+ end
65
+
66
+ # Savon::WSDLStream
67
+ #
68
+ # Stream listener for parsing the WSDL document.
69
+ class WSDLStream
70
+
71
+ # The main sections of a WSDL document.
72
+ Sections = %w(definitions types message portType binding service)
73
+
74
+ def initialize
75
+ @depth, @operations = 0, {}
76
+ end
77
+
78
+ # Returns the namespace URI.
79
+ attr_reader :namespace_uri
80
+
81
+ # Returns the SOAP operations.
82
+ attr_reader :operations
83
+
84
+ # Returns the SOAP endpoint.
85
+ attr_reader :soap_endpoint
86
+
87
+ # Hook method called when the stream parser encounters a starting tag.
88
+ def tag_start(tag, attrs)
89
+ @depth += 1
90
+ tag = tag.strip_namespace
91
+
92
+ @section = tag.to_sym if @depth <= 2 && Sections.include?(tag)
93
+ @namespace_uri ||= attrs["targetNamespace"] if @section == :definitions
94
+ @soap_endpoint ||= URI(attrs["location"]) if @section == :service && tag == "address"
95
+
96
+ operation_from tag, attrs if @section == :binding && tag == "operation"
97
+ end
98
+
99
+ # Hook method called when the stream parser encounters a closing tag.
100
+ def tag_end(tag)
101
+ @depth -= 1
102
+ end
103
+
104
+ # Stores available operations from a given tag +name+ and +attrs+.
105
+ def operation_from(tag, attrs)
106
+ @input = attrs["name"] if attrs["name"]
107
+
108
+ if attrs["soapAction"]
109
+ @action = !attrs["soapAction"].blank? ? attrs["soapAction"] : @input
110
+ @input = @action.split("/").last if !@input || @input.empty?
111
+
112
+ @operations[@input.snakecase.to_sym] = { :action => @action, :input => @input }
113
+ @input, @action = nil, nil
114
+ end
115
+ end
116
+
117
+ # Catches calls to unimplemented hook methods.
118
+ def method_missing(method, *args)
119
+ end
120
+
121
+ end
122
+ end
data/lib/savon/wsse.rb ADDED
@@ -0,0 +1,136 @@
1
+ module Savon
2
+
3
+ # Savon::WSSE
4
+ #
5
+ # Represents parameters for WSSE authentication.
6
+ class WSSE
7
+
8
+ # Base address for WSSE docs.
9
+ BaseAddress = "http://docs.oasis-open.org/wss/2004/01"
10
+
11
+ # Namespace for WS Security Secext.
12
+ WSENamespace = BaseAddress + "/oasis-200401-wss-wssecurity-secext-1.0.xsd"
13
+
14
+ # Namespace for WS Security Utility.
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"
22
+
23
+ # Global WSSE username.
24
+ @@username = nil
25
+
26
+ # Returns the global WSSE username.
27
+ def self.username
28
+ @@username
29
+ end
30
+
31
+ # Sets the global WSSE username.
32
+ def self.username=(username)
33
+ @@username = username.to_s if username.respond_to? :to_s
34
+ @@username = nil if username.nil?
35
+ end
36
+
37
+ # Global WSSE password.
38
+ @@password = nil
39
+
40
+ # Returns the global WSSE password.
41
+ def self.password
42
+ @@password
43
+ end
44
+
45
+ # Sets the global WSSE password.
46
+ def self.password=(password)
47
+ @@password = password.to_s if password.respond_to? :to_s
48
+ @@password = nil if password.nil?
49
+ end
50
+
51
+ # Global setting of whether to use WSSE digest.
52
+ @@digest = false
53
+
54
+ # Returns the global setting of whether to use WSSE digest.
55
+ def self.digest?
56
+ @@digest
57
+ end
58
+
59
+ # Global setting of whether to use WSSE digest.
60
+ def self.digest=(digest)
61
+ @@digest = digest
62
+ end
63
+
64
+ # Sets the WSSE username per request.
65
+ def username=(username)
66
+ @username = username.to_s if username.respond_to? :to_s
67
+ @username = nil if username.nil?
68
+ end
69
+
70
+ # Returns the WSSE username. Defaults to the global setting.
71
+ def username
72
+ @username || self.class.username
73
+ end
74
+
75
+ # Sets the WSSE password per request.
76
+ def password=(password)
77
+ @password = password.to_s if password.respond_to? :to_s
78
+ @password = nil if password.nil?
79
+ end
80
+
81
+ # Returns the WSSE password. Defaults to the global setting.
82
+ def password
83
+ @password || self.class.password
84
+ end
85
+
86
+ # Sets whether to use WSSE digest per request.
87
+ attr_writer :digest
88
+
89
+ # Returns whether to use WSSE digest. Defaults to the global setting.
90
+ def digest?
91
+ @digest || self.class.digest?
92
+ end
93
+
94
+ # Returns the XML for a WSSE header or an empty String unless both
95
+ # username and password were specified.
96
+ def header
97
+ return "" unless username && password
98
+
99
+ builder = Builder::XmlMarkup.new
100
+ builder.wsse :Security, "xmlns:wsse" => WSENamespace do |xml|
101
+ xml.wsse :UsernameToken, "xmlns:wsu" => WSUNamespace do
102
+ xml.wsse :Username, username
103
+ xml.wsse :Nonce, nonce
104
+ xml.wsu :Created, timestamp
105
+ xml.wsse :Password, password_node, :Type => password_type
106
+ end
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ # Returns the WSSE password. Encrypts the password for digest authentication.
113
+ def password_node
114
+ return password unless digest?
115
+
116
+ token = nonce + timestamp + password
117
+ Base64.encode64(Digest::SHA1.hexdigest(token)).chomp!
118
+ end
119
+
120
+ # Returns the URI for the "wsse:Password/@Type" attribute.
121
+ def password_type
122
+ digest? ? PasswordDigestURI : PasswordTextURI
123
+ end
124
+
125
+ # Returns a WSSE nonce.
126
+ def nonce
127
+ @nonce ||= Digest::SHA1.hexdigest String.random + timestamp
128
+ end
129
+
130
+ # Returns a WSSE timestamp.
131
+ def timestamp
132
+ @timestamp ||= Time.now.strftime Savon::SOAPDateTimeFormat
133
+ end
134
+
135
+ end
136
+ end
data/lib/savon.rb ADDED
@@ -0,0 +1,34 @@
1
+ module Savon
2
+
3
+ # Supported SOAP versions.
4
+ SOAPVersions = [1, 2]
5
+
6
+ # SOAP xs:dateTime format.
7
+ SOAPDateTimeFormat = "%Y-%m-%dT%H:%M:%S"
8
+
9
+ # SOAP xs:dateTime Regexp.
10
+ SOAPDateTimeRegexp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
11
+
12
+ # Raised in case of an HTTP error.
13
+ class HTTPError < StandardError; end
14
+
15
+ # Raised in case of a SOAP fault.
16
+ class SOAPFault < StandardError; end
17
+
18
+ end
19
+
20
+ # standard libs
21
+ %w(logger net/https openssl base64 digest/sha1 rexml/document).each do |lib|
22
+ require lib
23
+ end
24
+
25
+ # gems
26
+ require "rubygems"
27
+ %w(builder crack/xml).each do |gem|
28
+ require gem
29
+ end
30
+
31
+ # core files
32
+ %w(core_ext wsse soap request response wsdl client).each do |file|
33
+ require File.dirname(__FILE__) + "/savon/#{file}"
34
+ end
@@ -0,0 +1,12 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ require "spec"
4
+ require "mocha"
5
+ require "fakeweb"
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with :mocha
9
+ end
10
+
11
+ require "savon"
12
+ Savon::Request.log = false
@@ -0,0 +1,22 @@
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
+ else soap_endpoint(type)
9
+ end
10
+ end
11
+
12
+ # Returns the SOAP endpoint for a given +type+ of request.
13
+ def self.soap_endpoint(type = nil)
14
+ case type
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
+ else "http://example.com/validation/1.0/AuthenticationService"
19
+ end
20
+ end
21
+
22
+ end
@@ -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
@@ -0,0 +1,14 @@
1
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
2
+ <soap:Body>
3
+ <ns2:authenticateResponse xmlns:ns2="http://v1_0.ws.user.example.com">
4
+ <return>
5
+ <authenticationValue>
6
+ <token>a68d1d6379b62ff339a0e0c69ed4d9cf</token>
7
+ <tokenHash>AAAJxA;cIedoT;mY10ExZwG6JuKgp2OYKxow==</tokenHash>
8
+ <client>radclient</client>
9
+ </authenticationValue>
10
+ <success>true</success>
11
+ </return>
12
+ </ns2:authenticateResponse>
13
+ </soap:Body>
14
+ </soap:Envelope>
@@ -0,0 +1,39 @@
1
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2
+ <soapenv:Body>
3
+ <ns1:listResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://ws.example.com">
4
+ <listReturn soapenc:arrayType="ns2:HistoryEntry[3]" xsi:type="soapenc:Array" xmlns:ns2="http://ws.example.com/ws/history" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
5
+ <listReturn href="#id0"/>
6
+ <listReturn href="#id1"/>
7
+ <listReturn href="#id2"/>
8
+ </listReturn>
9
+ </ns1:listResponse>
10
+ <multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns5:HistoryEntry" xmlns:ns5="http://ws.example.com/ws/history" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
11
+ <date xsi:type="xsd:dateTime">2009-09-22T13:47:23.000Z</date>
12
+ <location xsi:type="soapenc:string">Archive</location>
13
+ <mailId href="#id9"/>
14
+ <referenceId href="#id8"/>
15
+ <state xsi:type="soapenc:string">Original</state>
16
+ <subject xsi:type="soapenc:string">Mail from 09-22-2009: Misc</subject>
17
+ </multiRef>
18
+ <multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns6:HistoryEntry" xmlns:ns6="http://ws.example.com/ws/history" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
19
+ <date xsi:type="xsd:dateTime">2009-04-30T06:38:34.000Z</date>
20
+ <location xsi:type="soapenc:string">Archive</location>
21
+ <mailId href="#id10"/>
22
+ <referenceId href="#id8"/>
23
+ <state xsi:type="soapenc:string">Original</state>
24
+ <subject xsi:type="soapenc:string">Mail from 04-29-2009: Technical support</subject>
25
+ </multiRef>
26
+ <multiRef id="id2" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns7:HistoryEntry" xmlns:ns7="http://ws.example.com/ws/history" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
27
+ <date xsi:type="xsd:dateTime">2009-12-18T15:43:21.000Z</date>
28
+ <location xsi:type="soapenc:string">Archive</location>
29
+ <mailId href="#id11"/>
30
+ <referenceId href="#id8"/>
31
+ <state xsi:type="soapenc:string">Original</state>
32
+ <subject xsi:type="soapenc:string">Mail from 12-17-2009: Misc</subject>
33
+ </multiRef>
34
+ <multiRef id="id11" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">972219</multiRef>
35
+ <multiRef id="id10" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">708021</multiRef>
36
+ <multiRef id="id8" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">0</multiRef>
37
+ <multiRef id="id9" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="soapenc:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">855763</multiRef>
38
+ </soapenv:Body>
39
+ </soapenv:Envelope>
@@ -0,0 +1,8 @@
1
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
2
+ <soap:Body>
3
+ <soap:Fault>
4
+ <faultcode>soap:Server</faultcode>
5
+ <faultstring>Fault occurred while processing.</faultstring>
6
+ </soap:Fault>
7
+ </soap:Body>
8
+ </soap:Envelope>
@@ -0,0 +1,18 @@
1
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:m="http://www.example.org/timeouts">
2
+ <soap:Body>
3
+ <soap:Fault>
4
+ <Code>
5
+ <Value>soap:Sender</Value>
6
+ <Subcode>
7
+ <Value>m:MessageTimeout</Value>
8
+ </Subcode>
9
+ </Code>
10
+ <Reason>
11
+ <Text xml:lang="en">Sender Timeout</Text>
12
+ </Reason>
13
+ <Detail>
14
+ <m:MaxTime>P5M</m:MaxTime>
15
+ </Detail>
16
+ </soap:Fault>
17
+ </soap:Body>
18
+ </soap:Envelope>