Skip to content

Commit

Permalink
added support for returning the soap response header. fixes issue #135.
Browse files Browse the repository at this point in the history
  • Loading branch information
rubiii committed Jan 26, 2011
1 parent 5c8ec13 commit c9261df
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 23 deletions.
24 changes: 18 additions & 6 deletions lib/savon/core_ext/hash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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
Expand Down
12 changes: 11 additions & 1 deletion lib/savon/soap/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,26 @@ 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.
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
Expand Down
16 changes: 11 additions & 5 deletions lib/savon/soap/xml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,28 @@ 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
# and returns the value as an Array. Defaults to return an empty Array in case the
# 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

Expand Down
13 changes: 13 additions & 0 deletions spec/fixtures/response/header.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns="http://webservices.somewhere.com/definitions">
<SessionNumber>ABCD1234</SessionNumber>
</soap:Header>
<soap:Body>
<AuthenticateReply xmlns="http://xml.somewhere.com/ABCD">
<processStatus>
<statusCode>P</statusCode>
</processStatus>
</AuthenticateReply>
</soap:Body>
</soap:Envelope>
22 changes: 17 additions & 5 deletions spec/savon/core_ext/hash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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" } }
Expand Down Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions spec/savon/soap/response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 ==
Expand All @@ -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)
Expand Down
19 changes: 13 additions & 6 deletions spec/savon/soap/xml_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand All @@ -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 }
Expand Down Expand Up @@ -86,7 +93,7 @@
it "should default to the global default" do
Savon.soap_version = 2
xml.version.should == 2

reset_soap_version
end

Expand Down Expand Up @@ -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(/<env:Envelope (.*)xmlns:env="#{uri}"(.*)>/)
reset_soap_version
Expand All @@ -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(/<env:Envelope (.*)xmlns:env="#{uri}"(.*)>/)
reset_soap_version
Expand Down

0 comments on commit c9261df

Please sign in to comment.