julianmorrison-savon 0.6.8

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