From c9261dfdc3794259b4aad7202a4360373d017d45 Mon Sep 17 00:00:00 2001 From: rubiii Date: Wed, 26 Jan 2011 11:03:42 +0100 Subject: [PATCH] added support for returning the soap response header. fixes issue #135. --- lib/savon/core_ext/hash.rb | 24 ++++++++++++++++++------ lib/savon/soap/response.rb | 12 +++++++++++- lib/savon/soap/xml.rb | 16 +++++++++++----- spec/fixtures/response/header.xml | 13 +++++++++++++ spec/savon/core_ext/hash_spec.rb | 22 +++++++++++++++++----- spec/savon/soap/response_spec.rb | 14 ++++++++++++++ spec/savon/soap/xml_spec.rb | 19 +++++++++++++------ 7 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 spec/fixtures/response/header.xml diff --git a/lib/savon/core_ext/hash.rb b/lib/savon/core_ext/hash.rb index 39792a37..633d16e7 100644 --- a/lib/savon/core_ext/hash.rb +++ b/lib/savon/core_ext/hash.rb @@ -18,12 +18,16 @@ def deep_merge!(other_hash) self end unless defined? deep_merge! - # Returns the values from the soap:Body element or an empty Hash in case the soap:Body tag could + # Returns the values from the soap:Header element or an empty Hash in case the element could + # not be found. + def find_soap_header + find_soap_element /.+:Header/ + end + + # Returns the values from the soap:Body element or an empty Hash in case the element could # not be found. def find_soap_body - envelope = self[keys.first] || {} - body_key = envelope.keys.find { |key| /.+:Body/ =~ key } rescue nil - body_key ? envelope[body_key].map_soap_response : {} + find_soap_element /.+:Body/ end # Maps keys and values of a Hash created from SOAP response XML to more convenient Ruby Objects. @@ -34,13 +38,13 @@ def map_soap_response when ::Array then value.map { |val| val.map_soap_response rescue val } when ::String then value.map_soap_response end - + new_key = if Savon.strip_namespaces? key.strip_namespace.snakecase.to_sym else key.snakecase end - + if hash[new_key] # key already exists, value should be added as an Array hash[new_key] = [hash[new_key], value].flatten result = hash @@ -51,6 +55,14 @@ def map_soap_response end end + private + + def find_soap_element(element) + envelope = self[keys.first] || {} + element_key = envelope.keys.find { |key| element =~ key } rescue nil + element_key ? envelope[element_key].map_soap_response : {} + end + end end end diff --git a/lib/savon/soap/response.rb b/lib/savon/soap/response.rb index 348e3dad..fbdd6ded 100644 --- a/lib/savon/soap/response.rb +++ b/lib/savon/soap/response.rb @@ -43,9 +43,14 @@ def http_error @http_error ||= HTTP::Error.new http end + # Returns the SOAP response header as a Hash. + def header + @header_hash ||= basic_hash.find_soap_header + end + # Returns the SOAP response body as a Hash. def to_hash - @hash ||= Savon::SOAP::XML.to_hash http.body + @hash ||= Savon::SOAP::XML.to_hash basic_hash end # Returns the SOAP response body as an Array. @@ -53,6 +58,11 @@ def to_array(*path) Savon::SOAP::XML.to_array to_hash, *path end + # Returns the complete SOAP response XML without normalization. + def basic_hash + @basic_hash ||= Savon::SOAP::XML.parse http.body + end + # Returns the SOAP response XML. def to_xml http.body diff --git a/lib/savon/soap/xml.rb b/lib/savon/soap/xml.rb index aa4c68f9..620fc893 100644 --- a/lib/savon/soap/xml.rb +++ b/lib/savon/soap/xml.rb @@ -20,9 +20,15 @@ class XML "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance" } - # Converts the given SOAP response +xml+ into a Hash. - def self.to_hash(xml) - (Crack::XML.parse(xml) rescue {}).find_soap_body + # Converts the given SOAP response +value+ (XML or Hash) into a normalized Hash. + def self.to_hash(value) + value = parse value unless value.kind_of? Hash + value.find_soap_body + end + + # Converts a given SOAP response +xml+ to a Hash. + def self.parse(xml) + Crack::XML.parse(xml) rescue {} end # Expects a SOAP response XML or Hash, traverses it for a given +path+ of Hash keys @@ -30,12 +36,12 @@ def self.to_hash(xml) # path does not exist or returns nil. def self.to_array(object, *path) hash = object.kind_of?(Hash) ? object : to_hash(object) - + result = path.inject hash do |memo, key| return [] unless memo[key] memo[key] end - + result.kind_of?(Array) ? result.compact : [result].compact end diff --git a/spec/fixtures/response/header.xml b/spec/fixtures/response/header.xml new file mode 100644 index 00000000..a04a63db --- /dev/null +++ b/spec/fixtures/response/header.xml @@ -0,0 +1,13 @@ + + + + ABCD1234 + + + + + P + + + + diff --git a/spec/savon/core_ext/hash_spec.rb b/spec/savon/core_ext/hash_spec.rb index 80dc02ab..65b13c72 100644 --- a/spec/savon/core_ext/hash_spec.rb +++ b/spec/savon/core_ext/hash_spec.rb @@ -6,12 +6,24 @@ it "should recursively merge two Hashes" do hash = { :one => 1, "two" => { "three" => 3 } } other_hash = { :four => 4, "two" => { "three" => "merge", :five => 5 } } - + hash.merge!(other_hash).should == { :one => 1, :four => 4, "two" => { "three" => "merge", :five => 5 } } end end - describe "find_soap_body" do + describe "#find_soap_header" do + it "should return the content from the 'soap:Header' element" do + soap_header = { "soap:Envelope" => { "soap:Header" => "content" } } + soap_header.find_soap_header.should == "content" + end + + it "should return an empty Hash in case the 'soap:Header' element could not be found" do + soap_header = { "some_hash" => "content" } + soap_header.find_soap_header.should == {} + end + end + + describe "#find_soap_body" do it "should return the content from the 'soap:Body' element" do soap_body = { "soap:Envelope" => { "soap:Body" => "content" } } soap_body.find_soap_body.should == "content" @@ -23,7 +35,7 @@ end end - describe "map_soap_response" do + describe "#map_soap_response" do it "should convert Hash key Strings to snake_case Symbols" do soap_response = { "userResponse" => { "accountStatus" => "active" } } result = { :user_response => { :account_status => "active" } } @@ -95,13 +107,13 @@ "ns11:case" => { "ns11:name" => "another_name" } } } - + result = { :history => { :case => [{ :name => "a_name" }, { :name => "another_name" }] } } - + soap_response.map_soap_response.should == result end end diff --git a/spec/savon/soap/response_spec.rb b/spec/savon/soap/response_spec.rb index fb484730..2b86c98d 100644 --- a/spec/savon/soap/response_spec.rb +++ b/spec/savon/soap/response_spec.rb @@ -116,6 +116,13 @@ end end + describe "#header" do + it "should return the SOAP response header as a Hash" do + response = soap_response :body => Fixture.response(:header) + response.header.should include(:session_number => "ABCD1234") + end + end + describe "#to_hash" do it "should return the SOAP response body as a Hash" do soap_response.to_hash[:authenticate_response][:return].should == @@ -130,6 +137,13 @@ end end + describe "#basic_hash" do + it "should return the complete SOAP response XML as a Hash" do + response = soap_response :body => Fixture.response(:header) + response.basic_hash["soap:Envelope"]["soap:Header"]["SessionNumber"].should == "ABCD1234" + end + end + describe "#to_xml" do it "should return the raw SOAP response body" do soap_response.to_xml.should == Fixture.response(:authentication) diff --git a/spec/savon/soap/xml_spec.rb b/spec/savon/soap/xml_spec.rb index bd17ec16..aeb90ea4 100644 --- a/spec/savon/soap/xml_spec.rb +++ b/spec/savon/soap/xml_spec.rb @@ -18,19 +18,26 @@ it "should return a Hash for a SOAP multiRef response" do hash = Savon::SOAP::XML.to_hash Fixture.response(:multi_ref) - + hash[:list_response].should be_a(Hash) hash[:multi_ref].should be_an(Array) end it "should add existing namespaced elements as an array" do hash = Savon::SOAP::XML.to_hash Fixture.response(:list) - + hash[:multi_namespaced_entry_response][:history].should be_a(Hash) hash[:multi_namespaced_entry_response][:history][:case].should be_an(Array) end end + describe ".parse" do + it "should convert the given XML into a Hash" do + hash = Savon::SOAP::XML.parse Fixture.response(:list) + hash["soapenv:Envelope"]["soapenv:Body"].should be_a(Hash) + end + end + describe ".to_array" do let(:response_hash) { Fixture.response_hash :authentication } @@ -57,7 +64,7 @@ describe ".new" do it "should accept an endpoint, an input tag and a SOAP body" do xml = Savon::SOAP::XML.new Endpoint.soap, :authentication, :id => 1 - + xml.endpoint.should == Endpoint.soap xml.input.should == :authentication xml.body.should == { :id => 1 } @@ -86,7 +93,7 @@ it "should default to the global default" do Savon.soap_version = 2 xml.version.should == 2 - + reset_soap_version end @@ -224,7 +231,7 @@ context "with the global SOAP version set to 1.2" do it "should contain the namespace for SOAP 1.2" do Savon.soap_version = 2 - + uri = "http://www.w3.org/2003/05/soap-envelope" xml.to_xml.should match(//) reset_soap_version @@ -235,7 +242,7 @@ it "should contain the namespace for the request SOAP version" do Savon.soap_version = 2 xml.version = 1 - + uri = "http://schemas.xmlsoap.org/soap/envelope/" xml.to_xml.should match(//) reset_soap_version