julianmorrison-savon 0.6.8

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 (42) hide show
  1. data/CHANGELOG +92 -0
  2. data/README.textile +71 -0
  3. data/Rakefile +27 -0
  4. data/lib/savon.rb +34 -0
  5. data/lib/savon/client.rb +84 -0
  6. data/lib/savon/core_ext.rb +3 -0
  7. data/lib/savon/core_ext/datetime.rb +8 -0
  8. data/lib/savon/core_ext/hash.rb +78 -0
  9. data/lib/savon/core_ext/object.rb +21 -0
  10. data/lib/savon/core_ext/string.rb +47 -0
  11. data/lib/savon/core_ext/symbol.rb +8 -0
  12. data/lib/savon/core_ext/uri.rb +10 -0
  13. data/lib/savon/request.rb +159 -0
  14. data/lib/savon/response.rb +108 -0
  15. data/lib/savon/soap.rb +138 -0
  16. data/lib/savon/wsdl.rb +122 -0
  17. data/lib/savon/wsse.rb +122 -0
  18. data/spec/endpoint_helper.rb +22 -0
  19. data/spec/fixtures/response/response_fixture.rb +32 -0
  20. data/spec/fixtures/response/xml/authentication.xml +14 -0
  21. data/spec/fixtures/response/xml/soap_fault.xml +8 -0
  22. data/spec/fixtures/response/xml/soap_fault12.xml +18 -0
  23. data/spec/fixtures/wsdl/wsdl_fixture.rb +37 -0
  24. data/spec/fixtures/wsdl/xml/authentication.xml +63 -0
  25. data/spec/fixtures/wsdl/xml/namespaced_actions.xml +307 -0
  26. data/spec/fixtures/wsdl/xml/no_namespace.xml +115 -0
  27. data/spec/http_stubs.rb +23 -0
  28. data/spec/savon/client_spec.rb +83 -0
  29. data/spec/savon/core_ext/datetime_spec.rb +12 -0
  30. data/spec/savon/core_ext/hash_spec.rb +134 -0
  31. data/spec/savon/core_ext/object_spec.rb +40 -0
  32. data/spec/savon/core_ext/string_spec.rb +68 -0
  33. data/spec/savon/core_ext/symbol_spec.rb +11 -0
  34. data/spec/savon/core_ext/uri_spec.rb +15 -0
  35. data/spec/savon/request_spec.rb +124 -0
  36. data/spec/savon/response_spec.rb +122 -0
  37. data/spec/savon/savon_spec.rb +23 -0
  38. data/spec/savon/soap_spec.rb +131 -0
  39. data/spec/savon/wsdl_spec.rb +84 -0
  40. data/spec/savon/wsse_spec.rb +132 -0
  41. data/spec/spec_helper.rb +16 -0
  42. metadata +166 -0
@@ -0,0 +1,115 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <definitions name="Api" xmlns:typens="urn:ActionWebService" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="urn:ActionWebService" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns="http://schemas.xmlsoap.org/wsdl/">
3
+ <types>
4
+ <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:ActionWebService">
5
+ <xsd:complexType name="MpUser">
6
+ <xsd:all>
7
+ <xsd:element name="avatar_thumb_url" type="xsd:string"/>
8
+ <xsd:element name="speciality" type="xsd:string"/>
9
+ <xsd:element name="avatar_icon_url" type="xsd:string"/>
10
+ <xsd:element name="firstname" type="xsd:string"/>
11
+ <xsd:element name="city" type="xsd:string"/>
12
+ <xsd:element name="mp_id" type="xsd:int"/>
13
+ <xsd:element name="lastname" type="xsd:string"/>
14
+ <xsd:element name="login" type="xsd:string"/>
15
+ </xsd:all>
16
+ </xsd:complexType>
17
+ <xsd:complexType name="MpUserArray">
18
+ <xsd:complexContent>
19
+ <xsd:restriction base="soapenc:Array">
20
+ <xsd:attribute wsdl:arrayType="typens:MpUser[]" ref="soapenc:arrayType"/>
21
+ </xsd:restriction>
22
+ </xsd:complexContent>
23
+ </xsd:complexType>
24
+ <xsd:complexType name="McContact">
25
+ <xsd:all>
26
+ <xsd:element name="last_name" type="xsd:string"/>
27
+ <xsd:element name="email" type="xsd:string"/>
28
+ <xsd:element name="mp_id" type="xsd:int"/>
29
+ <xsd:element name="role" type="xsd:int"/>
30
+ <xsd:element name="login" type="xsd:string"/>
31
+ <xsd:element name="first_name" type="xsd:string"/>
32
+ </xsd:all>
33
+ </xsd:complexType>
34
+ <xsd:complexType name="McContactArray">
35
+ <xsd:complexContent>
36
+ <xsd:restriction base="soapenc:Array">
37
+ <xsd:attribute wsdl:arrayType="typens:McContact[]" ref="soapenc:arrayType"/>
38
+ </xsd:restriction>
39
+ </xsd:complexContent>
40
+ </xsd:complexType>
41
+ </xsd:schema>
42
+ </types>
43
+ <message name="GetUserLoginById">
44
+ <part name="api_key" type="xsd:string"/>
45
+ <part name="id" type="xsd:int"/>
46
+ </message>
47
+ <message name="GetUserLoginByIdResponse">
48
+ <part name="return" type="xsd:string"/>
49
+ </message>
50
+ <message name="GetAllContacts">
51
+ <part name="api_key" type="xsd:string"/>
52
+ <part name="login" type="xsd:string"/>
53
+ </message>
54
+ <message name="GetAllContactsResponse">
55
+ <part name="return" type="typens:McContactArray"/>
56
+ </message>
57
+ <message name="SearchUser">
58
+ <part name="api_key" type="xsd:string"/>
59
+ <part name="phrase" type="xsd:string"/>
60
+ <part name="page" type="xsd:string"/>
61
+ <part name="per_page" type="xsd:string"/>
62
+ </message>
63
+ <message name="SearchUserResponse">
64
+ <part name="return" type="typens:MpUserArray"/>
65
+ </message>
66
+ <portType name="ApiApiPort">
67
+ <operation name="GetUserLoginById">
68
+ <input message="typens:GetUserLoginById"/>
69
+ <output message="typens:GetUserLoginByIdResponse"/>
70
+ </operation>
71
+ <operation name="GetAllContacts">
72
+ <input message="typens:GetAllContacts"/>
73
+ <output message="typens:GetAllContactsResponse"/>
74
+ </operation>
75
+ <operation name="SearchUser">
76
+ <input message="typens:SearchUser"/>
77
+ <output message="typens:SearchUserResponse"/>
78
+ </operation>
79
+ </portType>
80
+ <binding name="ApiApiBinding" type="typens:ApiApiPort">
81
+ <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
82
+ <operation name="GetUserLoginById">
83
+ <soap:operation soapAction="/api/api/GetUserLoginById"/>
84
+ <input>
85
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
86
+ </input>
87
+ <output>
88
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
89
+ </output>
90
+ </operation>
91
+ <operation name="GetAllContacts">
92
+ <soap:operation soapAction="/api/api/GetAllContacts"/>
93
+ <input>
94
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
95
+ </input>
96
+ <output>
97
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
98
+ </output>
99
+ </operation>
100
+ <operation name="SearchUser">
101
+ <soap:operation soapAction="/api/api/SearchUser"/>
102
+ <input>
103
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
104
+ </input>
105
+ <output>
106
+ <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:ActionWebService" use="encoded"/>
107
+ </output>
108
+ </operation>
109
+ </binding>
110
+ <service name="ApiService">
111
+ <port name="ApiApiPort" binding="typens:ApiApiBinding">
112
+ <soap:address location="http://example.com/api/api"/>
113
+ </port>
114
+ </service>
115
+ </definitions>
@@ -0,0 +1,23 @@
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
@@ -0,0 +1,83 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::Client do
4
+ before { @client = Savon::Client.new EndpointHelper.wsdl_endpoint }
5
+
6
+ it "is initialized with a SOAP endpoint String" do
7
+ Savon::Client.new EndpointHelper.wsdl_endpoint
8
+ end
9
+
10
+ it "accepts a proxy URI passed in via options" do
11
+ Savon::Client.new EndpointHelper.wsdl_endpoint, :proxy => "http://proxy"
12
+ end
13
+
14
+ it "accepts settings for SSL client authentication via options" do
15
+ Savon::Client.new EndpointHelper.wsdl_endpoint, :ssl => {
16
+ :client_cert => "client cert",
17
+ :client_key => "client key",
18
+ :ca_file => "ca file",
19
+ :verify => OpenSSL::SSL::VERIFY_PEER
20
+ }
21
+ end
22
+
23
+ it "has a getter for accessing the Savon::WSDL" do
24
+ @client.wsdl.should be_a(Savon::WSDL)
25
+ end
26
+
27
+ it "has a getter for accessing the Savon::Request" do
28
+ @client.request.should be_a(Savon::Request)
29
+ end
30
+
31
+ it "responds to SOAP actions while still behaving as usual otherwise" do
32
+ WSDLFixture.authentication(:operations).keys.each do |soap_action|
33
+ @client.respond_to?(soap_action).should be_true
34
+ end
35
+
36
+ @client.respond_to?(:object_id).should be_true
37
+ @client.respond_to?(:some_undefined_method).should be_false
38
+ end
39
+
40
+ it "dispatches SOAP calls via method_missing and returns the Savon::Response" do
41
+ @client.authenticate.should be_a(Savon::Response)
42
+ end
43
+
44
+ it "disables the WSDL when passed a method with an exclamation mark" do
45
+ @client.wsdl.enabled?.should be_true
46
+ [:respond_to?, :operations, :namespace_uri, :soap_endpoint].each do |method|
47
+ Savon::WSDL.any_instance.expects(method).never
48
+ end
49
+
50
+ response = @client.authenticate! do |soap|
51
+ soap.input.should == "authenticate"
52
+ soap.input.should == "authenticate"
53
+ end
54
+ response.should be_a(Savon::Response)
55
+ @client.wsdl.enabled?.should be_false
56
+ end
57
+
58
+ it "raises a Savon::SOAPFault in case of a SOAP fault" do
59
+ client = Savon::Client.new EndpointHelper.wsdl_endpoint(:soap_fault)
60
+ lambda { client.authenticate! }.should raise_error(Savon::SOAPFault)
61
+ end
62
+
63
+ it "raises a Savon::HTTPError in case of an HTTP error" do
64
+ client = Savon::Client.new EndpointHelper.wsdl_endpoint(:http_error)
65
+ lambda { client.authenticate! }.should raise_error(Savon::HTTPError)
66
+ end
67
+
68
+ it "yields the SOAP object to a block when it expects one argument" do
69
+ @client.authenticate { |soap| soap.should be_a(Savon::SOAP) }
70
+ end
71
+
72
+ it "yields the SOAP and WSSE object to a block when it expects two argument" do
73
+ @client.authenticate do |soap, wsse|
74
+ soap.should be_a(Savon::SOAP)
75
+ wsse.should be_a(Savon::WSSE)
76
+ end
77
+ end
78
+
79
+ it "still raises a NoMethodError for undefined methods" do
80
+ lambda { @client.some_undefined_method }.should raise_error(NoMethodError)
81
+ end
82
+
83
+ end
@@ -0,0 +1,12 @@
1
+ require "spec_helper"
2
+
3
+ describe DateTime do
4
+
5
+ describe "to_soap_value" do
6
+ it "returns an xs:dateTime compliant String" do
7
+ DateTime.new(2012, 03, 22, 16, 22, 33).to_soap_value.
8
+ should == "2012-03-22T16:22:33"
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,134 @@
1
+ require "spec_helper"
2
+
3
+ describe Hash do
4
+
5
+ describe "find_regexp" do
6
+ before do
7
+ @soap_fault_hash = { "soap:Envelope" => { "soap:Body" => { "soap:Fault" => {
8
+ "faultcode" => "soap:Server", "faultstring" => "Fault occurred while processing."
9
+ } } } }
10
+ end
11
+
12
+ it "returns an empty Hash in case it did not find the specified value" do
13
+ result = @soap_fault_hash.find_regexp "soap:Fault"
14
+
15
+ result.should be_a(Hash)
16
+ result.should be_empty
17
+ end
18
+
19
+ it "returns the value of the last Regexp filter found in the Hash" do
20
+ @soap_fault_hash.find_regexp([".+:Envelope", ".+:Body"]).
21
+ should == @soap_fault_hash["soap:Envelope"]["soap:Body"]
22
+
23
+ @soap_fault_hash.find_regexp([/.+:Envelope/, /.+:Body/, /.+Fault/]).
24
+ should == @soap_fault_hash["soap:Envelope"]["soap:Body"]["soap:Fault"]
25
+ end
26
+ end
27
+
28
+ describe "to_soap_xml" do
29
+ describe "returns SOAP request compatible XML" do
30
+ it "for a simple Hash" do
31
+ { :some => "user" }.to_soap_xml.should == "<some>user</some>"
32
+ end
33
+
34
+ it "for a nested Hash" do
35
+ { :some => { :new => "user" } }.to_soap_xml.
36
+ should == "<some><new>user</new></some>"
37
+ end
38
+
39
+ it "for a Hash with multiple keys" do
40
+ soap_xml = { :all => "users", :before => "whatever" }.to_soap_xml
41
+
42
+ soap_xml.should include("<all>users</all>")
43
+ soap_xml.should include("<before>whatever</before>")
44
+ end
45
+
46
+ it "for a Hash containing an Array" do
47
+ { :some => ["user", "gorilla"] }.to_soap_xml.
48
+ should == "<some>user</some><some>gorilla</some>"
49
+ end
50
+
51
+ it "for a Hash containing an Array of Hashes" do
52
+ { :some => [{ :new => "user" }, { :old => "gorilla" }] }.to_soap_xml.
53
+ should == "<some><new>user</new></some><some><old>gorilla</old></some>"
54
+ end
55
+ end
56
+
57
+ it "converts Hash key Symbols to lowerCamelCase" do
58
+ { :find_or_create => "user" }.to_soap_xml.
59
+ should == "<findOrCreate>user</findOrCreate>"
60
+ end
61
+
62
+ it "does not convert Hash key Strings" do
63
+ { "find_or_create" => "user" }.to_soap_xml.
64
+ should == "<find_or_create>user</find_or_create>"
65
+ end
66
+
67
+ it "converts DateTime objects to xs:dateTime compliant Strings" do
68
+ { :before => DateTime.new(2012, 03, 22, 16, 22, 33) }.to_soap_xml.
69
+ should == "<before>" << "2012-03-22T16:22:33" << "</before>"
70
+ end
71
+
72
+ it "converts Objects responding to to_datetime to xs:dateTime compliant Strings" do
73
+ singleton = Object.new
74
+ def singleton.to_datetime
75
+ DateTime.new(2012, 03, 22, 16, 22, 33)
76
+ end
77
+
78
+ { :before => singleton }.to_soap_xml.
79
+ should == "<before>" << "2012-03-22T16:22:33" << "</before>"
80
+ end
81
+
82
+ it "calls to_s on Strings even if they respond to to_datetime" do
83
+ object = "gorilla"
84
+ object.expects(:to_s).returns object
85
+ object.expects(:to_datetime).never
86
+
87
+ { :name => object }.to_soap_xml.should == "<name>gorilla</name>"
88
+ end
89
+
90
+ it "call to_s on any other Object" do
91
+ [666, true, false, nil].each do |object|
92
+ { :some => object }.to_soap_xml.should == "<some>#{object}</some>"
93
+ end
94
+ end
95
+ end
96
+
97
+ describe "map_soap_response" do
98
+ it "converts Hash key Strings to snake_case Symbols" do
99
+ { "userResponse" => { "accountStatus" => "active" } }.map_soap_response.
100
+ should == { :user_response => { :account_status => "active" } }
101
+ end
102
+
103
+ it "strips namespaces from Hash keys" do
104
+ { "ns:userResponse" => { "ns2:id" => "666" } }.map_soap_response.
105
+ should == { :user_response => { :id => "666" } }
106
+ end
107
+
108
+ it "converts Hash keys and values in Arrays" do
109
+ { "response" => [{ "name" => "dude" }, { "name" => "gorilla" }] }.map_soap_response.
110
+ should == { :response=> [{ :name => "dude" }, { :name => "gorilla" }] }
111
+ end
112
+
113
+ it "converts xsi:nil values to nil Objects" do
114
+ { "userResponse" => { "xsi:nil" => "true" } }.map_soap_response.
115
+ should == { :user_response => nil }
116
+ end
117
+
118
+ it "converts Hash values matching the xs:dateTime format into DateTime Objects" do
119
+ { "response" => { "at" => "2012-03-22T16:22:33" } }.map_soap_response.
120
+ should == { :response => { :at => DateTime.new(2012, 03, 22, 16, 22, 33) } }
121
+ end
122
+
123
+ it "converts Hash values matching 'true' to TrueClass" do
124
+ { "response" => { "active" => "false" } }.map_soap_response.
125
+ should == { :response => { :active => false } }
126
+ end
127
+
128
+ it "converts Hash values matching 'false' to FalseClass" do
129
+ { "response" => { "active" => "true" } }.map_soap_response.
130
+ should == { :response => { :active => true } }
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+
3
+ describe Object do
4
+
5
+ describe "blank?" do
6
+ it "returns true for Objects perceived to be blank" do
7
+ ["", false, nil, [], {}].each do |object|
8
+ object.should be_blank
9
+ end
10
+ end
11
+
12
+ it "returns false for every other Object" do
13
+ ["!blank", true, [:a], {:a => "b"}].each do |object|
14
+ object.should_not be_blank
15
+ end
16
+ end
17
+ end
18
+
19
+ describe "to_soap_key" do
20
+ it "calls to_s for every Object" do
21
+ Object.to_soap_key.should == Object.to_s
22
+ end
23
+ end
24
+
25
+ describe "to_soap_value" do
26
+ it "returns an xs:dateTime compliant String for Objects responding to to_datetime" do
27
+ singleton = Object.new
28
+ def singleton.to_datetime
29
+ DateTime.new(2012, 03, 22, 16, 22, 33)
30
+ end
31
+
32
+ singleton.to_soap_value.should == "2012-03-22T16:22:33"
33
+ end
34
+
35
+ it "calls to_s unless the Object responds to to_datetime" do
36
+ "value".to_soap_value.should == "value".to_s
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,68 @@
1
+ require "spec_helper"
2
+
3
+ describe String do
4
+
5
+ describe "self.random" do
6
+ it "returns a random 100-character String" do
7
+ String.random.should be_a(String)
8
+ String.random.length.should == 100
9
+ end
10
+
11
+ it "returns a random String of a given length" do
12
+ String.random(50).should be_a(String)
13
+ String.random(50).length.should == 50
14
+ end
15
+ end
16
+
17
+ describe "snakecase" do
18
+ it "converts a lowerCamelCase String to snakecase" do
19
+ "lowerCamelCase".snakecase.should == "lower_camel_case"
20
+ end
21
+
22
+ it "converts period characters to underscores" do
23
+ "User.GetEmail".snakecase.should == "user_get_email"
24
+ end
25
+ end
26
+
27
+ describe "lower_camelcase" do
28
+ it "converts a snakecase String to lowerCamelCase" do
29
+ "lower_camel_case".lower_camelcase.should == "lowerCamelCase"
30
+ end
31
+ end
32
+
33
+ describe "strip_namespace" do
34
+ it "strips the namespace from a namespaced String" do
35
+ "ns:customer".strip_namespace.should == "customer"
36
+ end
37
+
38
+ it "returns the original String for a String without namespace" do
39
+ "customer".strip_namespace.should == "customer"
40
+ end
41
+ end
42
+
43
+ describe "map_soap_response" do
44
+ it "returns a DateTime Object for Strings matching the xs:dateTime format" do
45
+ "2012-03-22T16:22:33".map_soap_response.should ==
46
+ DateTime.new(2012, 03, 22, 16, 22, 33)
47
+ end
48
+
49
+ it "returns true for Strings matching 'true'" do
50
+ "true".map_soap_response.should be_true
51
+ end
52
+
53
+ it "returns false for Strings matching 'false'" do
54
+ "false".map_soap_response.should be_false
55
+ end
56
+
57
+ it "defaults to return the original value" do
58
+ "whatever".map_soap_response.should == "whatever"
59
+ end
60
+ end
61
+
62
+ describe "to_soap_value" do
63
+ it "calls to_s, bypassing Rails to_datetime extension for Strings" do
64
+ "string".to_soap_value.should == "string".to_s
65
+ end
66
+ end
67
+
68
+ end