savon 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +119 -104
- data/README.md +12 -11
- data/Rakefile +0 -6
- data/lib/savon.rb +16 -14
- data/lib/savon/block_interface.rb +26 -0
- data/lib/savon/builder.rb +142 -0
- data/lib/savon/client.rb +36 -135
- data/lib/savon/header.rb +42 -0
- data/lib/savon/http_error.rb +27 -0
- data/lib/savon/log_message.rb +23 -25
- data/lib/savon/message.rb +35 -0
- data/lib/savon/mock.rb +5 -0
- data/lib/savon/mock/expectation.rb +70 -0
- data/lib/savon/mock/spec_helper.rb +62 -0
- data/lib/savon/model.rb +39 -61
- data/lib/savon/operation.rb +62 -0
- data/lib/savon/options.rb +265 -0
- data/lib/savon/qualified_message.rb +49 -0
- data/lib/savon/request.rb +92 -0
- data/lib/savon/response.rb +97 -0
- data/lib/savon/soap_fault.rb +40 -0
- data/lib/savon/version.rb +1 -1
- data/savon.gemspec +10 -8
- data/spec/integration/options_spec.rb +536 -0
- data/spec/integration/request_spec.rb +31 -16
- data/spec/integration/support/application.rb +80 -0
- data/spec/integration/support/server.rb +84 -0
- data/spec/savon/builder_spec.rb +81 -0
- data/spec/savon/client_spec.rb +90 -488
- data/spec/savon/http_error_spec.rb +49 -0
- data/spec/savon/log_message_spec.rb +33 -0
- data/spec/savon/mock_spec.rb +127 -0
- data/spec/savon/model_spec.rb +110 -99
- data/spec/savon/observers_spec.rb +92 -0
- data/spec/savon/operation_spec.rb +49 -0
- data/spec/savon/request_spec.rb +145 -0
- data/spec/savon/{soap/response_spec.rb → response_spec.rb} +22 -59
- data/spec/savon/soap_fault_spec.rb +94 -0
- data/spec/spec_helper.rb +5 -3
- data/spec/support/fixture.rb +5 -1
- metadata +202 -197
- data/lib/savon/config.rb +0 -46
- data/lib/savon/error.rb +0 -6
- data/lib/savon/hooks/group.rb +0 -68
- data/lib/savon/hooks/hook.rb +0 -61
- data/lib/savon/http/error.rb +0 -42
- data/lib/savon/logger.rb +0 -39
- data/lib/savon/null_logger.rb +0 -10
- data/lib/savon/soap.rb +0 -21
- data/lib/savon/soap/fault.rb +0 -59
- data/lib/savon/soap/invalid_response_error.rb +0 -13
- data/lib/savon/soap/request.rb +0 -86
- data/lib/savon/soap/request_builder.rb +0 -205
- data/lib/savon/soap/response.rb +0 -117
- data/lib/savon/soap/xml.rb +0 -257
- data/spec/savon/config_spec.rb +0 -38
- data/spec/savon/hooks/group_spec.rb +0 -71
- data/spec/savon/hooks/hook_spec.rb +0 -16
- data/spec/savon/http/error_spec.rb +0 -52
- data/spec/savon/logger_spec.rb +0 -51
- data/spec/savon/savon_spec.rb +0 -33
- data/spec/savon/soap/fault_spec.rb +0 -89
- data/spec/savon/soap/request_builder_spec.rb +0 -207
- data/spec/savon/soap/request_spec.rb +0 -112
- data/spec/savon/soap/xml_spec.rb +0 -357
- data/spec/savon/soap_spec.rb +0 -16
data/lib/savon/config.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require "savon/logger"
|
2
|
-
require "savon/null_logger"
|
3
|
-
require "savon/hooks/group"
|
4
|
-
require "savon/soap"
|
5
|
-
|
6
|
-
module Savon
|
7
|
-
Config = Struct.new(:_logger, :pretty_print_xml, :raise_errors, :soap_version, :env_namespace, :soap_header) do
|
8
|
-
|
9
|
-
def self.default
|
10
|
-
config = new
|
11
|
-
config._logger = Logger.new
|
12
|
-
config.raise_errors = true
|
13
|
-
config.soap_version = SOAP::DEFAULT_VERSION
|
14
|
-
config
|
15
|
-
end
|
16
|
-
|
17
|
-
alias_method :logger, :_logger
|
18
|
-
|
19
|
-
def logger=(logger)
|
20
|
-
_logger.subject = logger
|
21
|
-
end
|
22
|
-
|
23
|
-
def log_level=(level)
|
24
|
-
_logger.level = level
|
25
|
-
end
|
26
|
-
|
27
|
-
def log=(log)
|
28
|
-
if log == true
|
29
|
-
self._logger = Logger.new
|
30
|
-
else
|
31
|
-
self._logger = NullLogger.new
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def hooks
|
36
|
-
@hooks ||= Hooks::Group.new
|
37
|
-
end
|
38
|
-
|
39
|
-
def clone
|
40
|
-
config = super
|
41
|
-
config._logger = config._logger.clone
|
42
|
-
config
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
data/lib/savon/error.rb
DELETED
data/lib/savon/hooks/group.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
require "savon/hooks/hook"
|
2
|
-
|
3
|
-
module Savon
|
4
|
-
module Hooks
|
5
|
-
|
6
|
-
# = Savon::Hooks::Group
|
7
|
-
#
|
8
|
-
# Manages a list of hooks.
|
9
|
-
class Group
|
10
|
-
|
11
|
-
# Accepts an Array of +hooks+ to start with.
|
12
|
-
def initialize(hooks = [])
|
13
|
-
@hooks = hooks
|
14
|
-
end
|
15
|
-
|
16
|
-
# Returns whether this group contains hooks.
|
17
|
-
def empty?
|
18
|
-
hooks.empty?
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns the number of hooks in this group.
|
22
|
-
def count
|
23
|
-
hooks.count
|
24
|
-
end
|
25
|
-
|
26
|
-
# Adds a new hook.
|
27
|
-
def define(id, hook, &block)
|
28
|
-
hooks << Hook.new(id, hook, &block)
|
29
|
-
end
|
30
|
-
|
31
|
-
# Removes hooks matching the given +ids+.
|
32
|
-
def reject(*ids)
|
33
|
-
ids = ids.flatten
|
34
|
-
hooks.reject! { |hook| ids.include? hook.id }
|
35
|
-
end
|
36
|
-
|
37
|
-
# Fire a given +hook+ with any given +args+.
|
38
|
-
def fire(hook, *args, &callback)
|
39
|
-
callable = select(hook)
|
40
|
-
|
41
|
-
if callable.empty?
|
42
|
-
callback.call
|
43
|
-
else
|
44
|
-
args.unshift(callback) if callback
|
45
|
-
callable.call(*args)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Calls the hooks with the given +args+ and returns the
|
50
|
-
# value of the last hooks.
|
51
|
-
def call(*args)
|
52
|
-
hooks.inject(nil) { |memo, hook| hook.call(*args) }
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def hooks
|
58
|
-
@hooks ||= []
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns a new group for a given +hook+.
|
62
|
-
def select(hook)
|
63
|
-
Group.new hooks.select { |h| h.hook == hook }
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
data/lib/savon/hooks/hook.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
module Savon
|
2
|
-
module Hooks
|
3
|
-
|
4
|
-
# = Savon::Hooks::Hook
|
5
|
-
#
|
6
|
-
# A hook used somewhere in the system.
|
7
|
-
class Hook
|
8
|
-
|
9
|
-
HOOKS = [
|
10
|
-
|
11
|
-
# :soap_request
|
12
|
-
#
|
13
|
-
# Around filter wrapping the POST request executed to call a SOAP service.
|
14
|
-
# See: Savon::SOAP::Request#response
|
15
|
-
#
|
16
|
-
# Arguments
|
17
|
-
#
|
18
|
-
# [callback] A block to execute the SOAP request
|
19
|
-
# [request] The current <tt>Savon::SOAP::Request</tt>
|
20
|
-
#
|
21
|
-
# Examples
|
22
|
-
#
|
23
|
-
# Log the time before and after the SOAP call:
|
24
|
-
#
|
25
|
-
# Savon.config.hooks.define(:my_hook, :soap_request) do |callback, request|
|
26
|
-
# Timer.log(:start, Time.now)
|
27
|
-
# response = callback.call
|
28
|
-
# Timer.log(:end, Time.now)
|
29
|
-
# response
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# Replace the SOAP call and return a custom response:
|
33
|
-
#
|
34
|
-
# Savon.config.hooks.define(:mock_hook, :soap_request) do |_, request|
|
35
|
-
# HTTPI::Response.new(200, {}, "")
|
36
|
-
# end
|
37
|
-
:soap_request
|
38
|
-
|
39
|
-
]
|
40
|
-
|
41
|
-
# Expects an +id+, the name of the +hook+ to use and a +block+ to be called.
|
42
|
-
def initialize(id, hook, &block)
|
43
|
-
unless HOOKS.include?(hook)
|
44
|
-
raise ArgumentError, "No such hook: #{hook}. Expected one of: #{HOOKS.join(', ')}"
|
45
|
-
end
|
46
|
-
|
47
|
-
self.id = id
|
48
|
-
self.hook = hook
|
49
|
-
self.block = block
|
50
|
-
end
|
51
|
-
|
52
|
-
attr_accessor :id, :hook, :block
|
53
|
-
|
54
|
-
# Calls the +block+ with the given +args+.
|
55
|
-
def call(*args)
|
56
|
-
block.call(*args)
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
data/lib/savon/http/error.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
require "savon/error"
|
2
|
-
require "savon/soap/xml"
|
3
|
-
|
4
|
-
module Savon
|
5
|
-
module HTTP
|
6
|
-
|
7
|
-
# = Savon::HTTP::Error
|
8
|
-
#
|
9
|
-
# Represents an HTTP error. Contains the original <tt>HTTPI::Response</tt>.
|
10
|
-
class Error < Error
|
11
|
-
|
12
|
-
# Expects an <tt>HTTPI::Response</tt>.
|
13
|
-
def initialize(http)
|
14
|
-
self.http = http
|
15
|
-
end
|
16
|
-
|
17
|
-
# Accessor for the <tt>HTTPI::Response</tt>.
|
18
|
-
attr_accessor :http
|
19
|
-
|
20
|
-
# Returns whether an HTTP error is present.
|
21
|
-
def present?
|
22
|
-
http.error?
|
23
|
-
end
|
24
|
-
|
25
|
-
# Returns the HTTP error message.
|
26
|
-
def to_s
|
27
|
-
return "" unless present?
|
28
|
-
|
29
|
-
@message ||= begin
|
30
|
-
message = "HTTP error (#{http.code})"
|
31
|
-
message << ": #{http.body}" unless http.body.empty?
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Returns the HTTP response as a Hash.
|
36
|
-
def to_hash
|
37
|
-
@hash = { :code => http.code, :headers => http.headers, :body => http.body }
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/lib/savon/logger.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require "logger"
|
2
|
-
require "nokogiri"
|
3
|
-
require "savon/log_message"
|
4
|
-
|
5
|
-
module Savon
|
6
|
-
class Logger
|
7
|
-
|
8
|
-
def initialize(device = $stdout)
|
9
|
-
self.device = device
|
10
|
-
end
|
11
|
-
|
12
|
-
attr_accessor :device
|
13
|
-
|
14
|
-
def log(message, options = {})
|
15
|
-
log_raw LogMessage.new(message, filter, options).to_s
|
16
|
-
end
|
17
|
-
|
18
|
-
attr_writer :subject, :level, :filter
|
19
|
-
|
20
|
-
def subject
|
21
|
-
@subject ||= ::Logger.new(device)
|
22
|
-
end
|
23
|
-
|
24
|
-
def level
|
25
|
-
@level ||= :debug
|
26
|
-
end
|
27
|
-
|
28
|
-
def filter
|
29
|
-
@filter ||= []
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def log_raw(message)
|
35
|
-
subject.send(level, message)
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
data/lib/savon/null_logger.rb
DELETED
data/lib/savon/soap.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Savon
|
2
|
-
|
3
|
-
# = Savon::SOAP
|
4
|
-
#
|
5
|
-
# Contains various SOAP details.
|
6
|
-
module SOAP
|
7
|
-
|
8
|
-
# Default SOAP version.
|
9
|
-
DEFAULT_VERSION = 1
|
10
|
-
|
11
|
-
# Supported SOAP versions.
|
12
|
-
VERSIONS = 1..2
|
13
|
-
|
14
|
-
# SOAP namespaces by SOAP version.
|
15
|
-
NAMESPACE = {
|
16
|
-
1 => "http://schemas.xmlsoap.org/soap/envelope/",
|
17
|
-
2 => "http://www.w3.org/2003/05/soap-envelope"
|
18
|
-
}
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
data/lib/savon/soap/fault.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
require "savon/error"
|
2
|
-
require "savon/soap/xml"
|
3
|
-
|
4
|
-
module Savon
|
5
|
-
module SOAP
|
6
|
-
|
7
|
-
# = Savon::SOAP::Fault
|
8
|
-
#
|
9
|
-
# Represents a SOAP fault. Contains the original <tt>HTTPI::Response</tt>.
|
10
|
-
class Fault < Error
|
11
|
-
|
12
|
-
# Expects an <tt>HTTPI::Response</tt>.
|
13
|
-
def initialize(http)
|
14
|
-
self.http = http
|
15
|
-
end
|
16
|
-
|
17
|
-
# Accessor for the <tt>HTTPI::Response</tt>.
|
18
|
-
attr_accessor :http
|
19
|
-
|
20
|
-
# Returns whether a SOAP fault is present.
|
21
|
-
def present?
|
22
|
-
@present ||= http.body.include?("Fault>") && (soap1_fault? || soap2_fault?)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Returns the SOAP fault message.
|
26
|
-
def to_s
|
27
|
-
return "" unless present?
|
28
|
-
@message ||= message_by_version to_hash[:fault]
|
29
|
-
end
|
30
|
-
|
31
|
-
# Returns the SOAP response body as a Hash.
|
32
|
-
def to_hash
|
33
|
-
@hash ||= Nori.parse(http.body)[:envelope][:body]
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
# Returns whether the response contains a SOAP 1.1 fault.
|
39
|
-
def soap1_fault?
|
40
|
-
http.body.include?("faultcode>") && http.body.include?("faultstring>")
|
41
|
-
end
|
42
|
-
|
43
|
-
# Returns whether the response contains a SOAP 1.2 fault.
|
44
|
-
def soap2_fault?
|
45
|
-
http.body.include?("Code>") && http.body.include?("Reason>")
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns the SOAP fault message by version.
|
49
|
-
def message_by_version(fault)
|
50
|
-
if fault[:faultcode]
|
51
|
-
"(#{fault[:faultcode]}) #{fault[:faultstring]}"
|
52
|
-
elsif fault[:code]
|
53
|
-
"(#{fault[:code][:value]}) #{fault[:reason][:text]}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/lib/savon/soap/request.rb
DELETED
@@ -1,86 +0,0 @@
|
|
1
|
-
require "httpi"
|
2
|
-
require "savon/soap/response"
|
3
|
-
|
4
|
-
module Savon
|
5
|
-
module SOAP
|
6
|
-
|
7
|
-
# = Savon::SOAP::Request
|
8
|
-
#
|
9
|
-
# Executes SOAP requests.
|
10
|
-
class Request
|
11
|
-
|
12
|
-
# Content-Types by SOAP version.
|
13
|
-
CONTENT_TYPE = { 1 => "text/xml;charset=UTF-8", 2 => "application/soap+xml;charset=UTF-8" }
|
14
|
-
|
15
|
-
# Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object
|
16
|
-
# to execute a SOAP request and returns the response.
|
17
|
-
def self.execute(config, http, soap)
|
18
|
-
new(config, http, soap).response
|
19
|
-
end
|
20
|
-
|
21
|
-
# Expects an <tt>HTTPI::Request</tt>, a <tt>Savon::SOAP::XML</tt> object
|
22
|
-
# and a <tt>Savon::Config</tt>.
|
23
|
-
def initialize(config, http, soap)
|
24
|
-
self.config = config
|
25
|
-
self.soap = soap
|
26
|
-
self.http = configure(http)
|
27
|
-
end
|
28
|
-
|
29
|
-
attr_accessor :soap, :http, :config
|
30
|
-
|
31
|
-
# Executes the request and returns the response.
|
32
|
-
def response
|
33
|
-
@response ||= begin
|
34
|
-
response = config.hooks.fire(:soap_request, self) { with_logging { HTTPI.post(http) } }
|
35
|
-
SOAP::Response.new(config, response)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
# Configures a given +http+ from the +soap+ object.
|
42
|
-
def configure(http)
|
43
|
-
http.url = soap.endpoint
|
44
|
-
|
45
|
-
if soap.signature?
|
46
|
-
# First generate the document so that Signature can digest sections
|
47
|
-
soap.wsse.signature.document = soap.to_xml(true)
|
48
|
-
|
49
|
-
# Then re-generate the document so that Signature can sign the digest
|
50
|
-
soap.wsse.signature.document = soap.to_xml(true)
|
51
|
-
|
52
|
-
# The third time we generate the document, we should have a signature
|
53
|
-
http.body = soap.to_xml(true)
|
54
|
-
else
|
55
|
-
http.body = soap.to_xml
|
56
|
-
end
|
57
|
-
|
58
|
-
http.headers["Content-Type"] = CONTENT_TYPE[soap.version]
|
59
|
-
http.headers["Content-Length"] = soap.to_xml.bytesize.to_s
|
60
|
-
http
|
61
|
-
end
|
62
|
-
|
63
|
-
# Logs the HTTP request, yields to a given +block+ and returns a <tt>Savon::SOAP::Response</tt>.
|
64
|
-
def with_logging
|
65
|
-
log_request http.url, http.headers, http.body
|
66
|
-
response = yield
|
67
|
-
log_response response.code, response.body
|
68
|
-
response
|
69
|
-
end
|
70
|
-
|
71
|
-
# Logs the SOAP request +url+, +headers+ and +body+.
|
72
|
-
def log_request(url, headers, body)
|
73
|
-
config.logger.log "SOAP request: #{url}"
|
74
|
-
config.logger.log headers.map { |key, value| "#{key}: #{value}" }.join(", ")
|
75
|
-
config.logger.log body, :pretty => config.pretty_print_xml, :filter => true
|
76
|
-
end
|
77
|
-
|
78
|
-
# Logs the SOAP response +code+ and +body+.
|
79
|
-
def log_response(code, body)
|
80
|
-
config.logger.log "SOAP response (status #{code}):"
|
81
|
-
config.logger.log body, :pretty => config.pretty_print_xml
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|