actionwebservice 0.6.2 → 0.7.0
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.
- data/CHANGELOG +21 -0
- data/README +50 -6
- data/Rakefile +9 -9
- data/TODO +6 -6
- data/lib/action_web_service.rb +4 -3
- data/lib/action_web_service/api.rb +248 -1
- data/lib/action_web_service/casting.rb +111 -0
- data/lib/action_web_service/client/soap_client.rb +17 -33
- data/lib/action_web_service/client/xmlrpc_client.rb +10 -34
- data/lib/action_web_service/container/delegated_container.rb +1 -1
- data/lib/action_web_service/dispatcher/abstract.rb +52 -72
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +71 -55
- data/lib/action_web_service/protocol/abstract.rb +82 -3
- data/lib/action_web_service/protocol/discovery.rb +2 -2
- data/lib/action_web_service/protocol/soap_protocol.rb +95 -22
- data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +197 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +56 -24
- data/lib/action_web_service/scaffolding.rb +237 -0
- data/lib/action_web_service/struct.rb +17 -4
- data/lib/action_web_service/support/signature_types.rb +194 -0
- data/lib/action_web_service/templates/scaffolds/layout.rhtml +65 -0
- data/lib/action_web_service/templates/scaffolds/methods.rhtml +6 -0
- data/lib/action_web_service/templates/scaffolds/parameters.rhtml +28 -0
- data/lib/action_web_service/templates/scaffolds/result.rhtml +30 -0
- data/lib/action_web_service/test_invoke.rb +23 -42
- data/test/abstract_dispatcher.rb +102 -48
- data/test/abstract_unit.rb +1 -1
- data/test/api_test.rb +40 -7
- data/test/casting_test.rb +82 -0
- data/test/client_soap_test.rb +3 -0
- data/test/client_xmlrpc_test.rb +5 -1
- data/test/dispatcher_action_controller_soap_test.rb +9 -12
- data/test/dispatcher_action_controller_xmlrpc_test.rb +1 -11
- data/test/run +1 -0
- data/test/scaffolded_controller_test.rb +67 -0
- data/test/struct_test.rb +33 -21
- data/test/test_invoke_test.rb +1 -1
- metadata +18 -31
- data/lib/action_web_service/api/base.rb +0 -135
- data/lib/action_web_service/vendor/ws.rb +0 -4
- data/lib/action_web_service/vendor/ws/common.rb +0 -8
- data/lib/action_web_service/vendor/ws/encoding.rb +0 -3
- data/lib/action_web_service/vendor/ws/encoding/abstract.rb +0 -26
- data/lib/action_web_service/vendor/ws/encoding/soap_rpc_encoding.rb +0 -90
- data/lib/action_web_service/vendor/ws/encoding/xmlrpc_encoding.rb +0 -53
- data/lib/action_web_service/vendor/ws/marshaling.rb +0 -3
- data/lib/action_web_service/vendor/ws/marshaling/abstract.rb +0 -17
- data/lib/action_web_service/vendor/ws/marshaling/soap_marshaling.rb +0 -277
- data/lib/action_web_service/vendor/ws/marshaling/xmlrpc_marshaling.rb +0 -116
- data/lib/action_web_service/vendor/ws/types.rb +0 -165
- data/test/ws/abstract_encoding.rb +0 -68
- data/test/ws/abstract_unit.rb +0 -13
- data/test/ws/gencov +0 -3
- data/test/ws/run +0 -5
- data/test/ws/soap_marshaling_test.rb +0 -91
- data/test/ws/soap_rpc_encoding_test.rb +0 -47
- data/test/ws/types_test.rb +0 -43
- data/test/ws/xmlrpc_encoding_test.rb +0 -34
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module ActionWebService # :nodoc:
|
5
|
+
module Casting # :nodoc:
|
6
|
+
class CastingError < ActionWebServiceError # :nodoc:
|
7
|
+
end
|
8
|
+
|
9
|
+
# Performs casting of arbitrary values into the correct types for the signature
|
10
|
+
class BaseCaster
|
11
|
+
def initialize(api_method)
|
12
|
+
@api_method = api_method
|
13
|
+
end
|
14
|
+
|
15
|
+
# Coerces the parameters in +params+ (an Enumerable) into the types
|
16
|
+
# this method expects
|
17
|
+
def cast_expects(params)
|
18
|
+
self.class.cast_expects(@api_method, params)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Coerces the given +return_value+ into the the type returned by this
|
22
|
+
# method
|
23
|
+
def cast_returns(return_value)
|
24
|
+
self.class.cast_returns(@api_method, return_value)
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
include ActionWebService::SignatureTypes
|
29
|
+
|
30
|
+
def cast_expects(api_method, params) # :nodoc:
|
31
|
+
return [] if api_method.expects.nil?
|
32
|
+
api_method.expects.zip(params).map{ |type, param| cast(param, type) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def cast_returns(api_method, return_value) # :nodoc:
|
36
|
+
return nil if api_method.returns.nil?
|
37
|
+
cast(return_value, api_method.returns[0])
|
38
|
+
end
|
39
|
+
|
40
|
+
def cast(value, signature_type) # :nodoc:
|
41
|
+
return value if signature_type.nil? # signature.length != params.length
|
42
|
+
unless signature_type.array? || signature_type.structured?
|
43
|
+
return value if canonical_type(value.class) == signature_type.type
|
44
|
+
end
|
45
|
+
if signature_type.array?
|
46
|
+
unless value.respond_to?(:entries) && !value.is_a?(String)
|
47
|
+
raise CastingError, "Don't know how to cast #{value.class} into #{signature_type.type.inspect}"
|
48
|
+
end
|
49
|
+
value.entries.map do |entry|
|
50
|
+
cast(entry, signature_type.element_type)
|
51
|
+
end
|
52
|
+
elsif signature_type.structured?
|
53
|
+
cast_to_structured_type(value, signature_type)
|
54
|
+
elsif !signature_type.custom?
|
55
|
+
cast_base_type(value, signature_type)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def cast_base_type(value, signature_type) # :nodoc:
|
60
|
+
case signature_type.type
|
61
|
+
when :int
|
62
|
+
Integer(value)
|
63
|
+
when :string
|
64
|
+
value.to_s
|
65
|
+
when :bool
|
66
|
+
return false if value.nil?
|
67
|
+
return value if value == true || value == false
|
68
|
+
case value.to_s.downcase
|
69
|
+
when '1', 'true', 'y', 'yes'
|
70
|
+
true
|
71
|
+
when '0', 'false', 'n', 'no'
|
72
|
+
false
|
73
|
+
else
|
74
|
+
raise CastingError, "Don't know how to cast #{value.class} into Boolean"
|
75
|
+
end
|
76
|
+
when :float
|
77
|
+
Float(value)
|
78
|
+
when :time
|
79
|
+
Time.parse(value.to_s)
|
80
|
+
when :date
|
81
|
+
Date.parse(value.to_s)
|
82
|
+
when :datetime
|
83
|
+
DateTime.parse(value.to_s)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def cast_to_structured_type(value, signature_type) # :nodoc:
|
88
|
+
obj = nil
|
89
|
+
obj = value if canonical_type(value.class) == canonical_type(signature_type.type)
|
90
|
+
obj ||= signature_type.type_class.new
|
91
|
+
if value.respond_to?(:each_pair)
|
92
|
+
klass = signature_type.type_class
|
93
|
+
value.each_pair do |name, val|
|
94
|
+
type = klass.respond_to?(:member_type) ? klass.member_type(name) : nil
|
95
|
+
val = cast(val, type) if type
|
96
|
+
obj.__send__("#{name}=", val) if obj.respond_to?(name)
|
97
|
+
end
|
98
|
+
elsif value.respond_to?(:attributes)
|
99
|
+
signature_type.each_member do |name, type|
|
100
|
+
val = value.__send__(name)
|
101
|
+
obj.__send__("#{name}=", cast(val, type)) if obj.respond_to?(name)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
raise CastingError, "Don't know how to cast #{value.class} to #{signature_type.type_class}"
|
105
|
+
end
|
106
|
+
obj
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -46,8 +46,7 @@ module ActionWebService # :nodoc:
|
|
46
46
|
@type_namespace = options[:type_namespace] || 'urn:ActionWebService'
|
47
47
|
@method_namespace = options[:method_namespace] || 'urn:ActionWebService'
|
48
48
|
@driver_options = options[:driver_options] || {}
|
49
|
-
@
|
50
|
-
@encoder = WS::Encoding::SoapRpcEncoding.new @method_namespace
|
49
|
+
@protocol = ActionWebService::Protocol::Soap::SoapProtocol.new
|
51
50
|
@soap_action_base = options[:soap_action_base]
|
52
51
|
@soap_action_base ||= URI.parse(endpoint_uri).path
|
53
52
|
@driver = create_soap_rpc_driver(api, endpoint_uri)
|
@@ -58,7 +57,10 @@ module ActionWebService # :nodoc:
|
|
58
57
|
|
59
58
|
protected
|
60
59
|
def perform_invocation(method_name, args)
|
61
|
-
@
|
60
|
+
method = @api.api_methods[method_name.to_sym]
|
61
|
+
args = method.cast_expects(args.dup) rescue args
|
62
|
+
return_value = @driver.send(method_name, *args)
|
63
|
+
method.cast_returns(return_value.dup) rescue return_value
|
62
64
|
end
|
63
65
|
|
64
66
|
def soap_action(method_name)
|
@@ -67,48 +69,30 @@ module ActionWebService # :nodoc:
|
|
67
69
|
|
68
70
|
private
|
69
71
|
def create_soap_rpc_driver(api, endpoint_uri)
|
70
|
-
register_api(
|
72
|
+
@protocol.register_api(api)
|
71
73
|
driver = SoapDriver.new(endpoint_uri, nil)
|
72
|
-
driver.mapping_registry = @marshaler.registry
|
73
|
-
api.api_methods.each do |name,
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
returns = info[:returns]
|
74
|
+
driver.mapping_registry = @protocol.marshaler.registry
|
75
|
+
api.api_methods.each do |name, method|
|
76
|
+
qname = XSD::QName.new(@method_namespace, method.public_name)
|
77
|
+
action = soap_action(method.public_name)
|
78
|
+
expects = method.expects
|
79
|
+
returns = method.returns
|
79
80
|
param_def = []
|
80
|
-
i = 0
|
81
81
|
if expects
|
82
|
-
expects.each do |
|
83
|
-
|
84
|
-
|
85
|
-
param_def << ['in', param_name, type_binding.mapping]
|
86
|
-
i += 1
|
82
|
+
expects.each do |type|
|
83
|
+
type_binding = @protocol.marshaler.lookup_type(type)
|
84
|
+
param_def << ['in', type.name, type_binding.mapping]
|
87
85
|
end
|
88
86
|
end
|
89
87
|
if returns
|
90
|
-
type_binding = @marshaler.
|
88
|
+
type_binding = @protocol.marshaler.lookup_type(returns[0])
|
91
89
|
param_def << ['retval', 'return', type_binding.mapping]
|
92
90
|
end
|
93
|
-
driver.add_method(qname, action, name.to_s, param_def)
|
91
|
+
driver.add_method(qname, action, method.name.to_s, param_def)
|
94
92
|
end
|
95
93
|
driver
|
96
94
|
end
|
97
95
|
|
98
|
-
def register_api(marshaler, api)
|
99
|
-
type_bindings = []
|
100
|
-
api.api_methods.each do |name, info|
|
101
|
-
expects, returns = info[:expects], info[:returns]
|
102
|
-
if expects
|
103
|
-
expects.each{|type| type_bindings << marshaler.register_type(type)}
|
104
|
-
end
|
105
|
-
if returns
|
106
|
-
returns.each{|type| type_bindings << marshaler.register_type(type)}
|
107
|
-
end
|
108
|
-
end
|
109
|
-
type_bindings
|
110
|
-
end
|
111
|
-
|
112
96
|
class SoapDriver < SOAP::RPC::Driver # :nodoc:
|
113
97
|
def add_method(qname, soapaction, name, param_def)
|
114
98
|
@proxy.add_rpc_method(qname, soapaction, name, param_def)
|
@@ -30,47 +30,23 @@ module ActionWebService # :nodoc:
|
|
30
30
|
def initialize(api, endpoint_uri, options={})
|
31
31
|
@api = api
|
32
32
|
@handler_name = options[:handler_name]
|
33
|
+
@protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
|
33
34
|
@client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
|
34
|
-
@marshaler = WS::Marshaling::XmlRpcMarshaler.new
|
35
35
|
end
|
36
36
|
|
37
37
|
protected
|
38
38
|
def perform_invocation(method_name, args)
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
|
43
|
-
end
|
44
|
-
|
45
|
-
def transform_outgoing_method_params(method_name, params)
|
46
|
-
info = @api.api_methods[method_name.to_sym]
|
47
|
-
expects = info[:expects]
|
48
|
-
expects_length = expects.nil?? 0 : expects.length
|
49
|
-
if expects_length != params.length
|
50
|
-
raise(ClientError, "API declares #{public_name(method_name)} to accept " +
|
51
|
-
"#{expects_length} parameters, but #{params.length} parameters " +
|
52
|
-
"were supplied")
|
39
|
+
method = @api.api_methods[method_name.to_sym]
|
40
|
+
if method.expects && method.expects.length != args.length
|
41
|
+
raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
|
53
42
|
end
|
54
|
-
|
55
|
-
if
|
56
|
-
i =
|
57
|
-
expects.each do |spec|
|
58
|
-
type_binding = @marshaler.register_type(spec)
|
59
|
-
info = WS::ParamInfo.create(spec, type_binding, i)
|
60
|
-
params[i] = @marshaler.marshal(WS::Param.new(params[i], info))
|
61
|
-
i += 1
|
62
|
-
end
|
43
|
+
args = method.cast_expects(args.dup) rescue args
|
44
|
+
if method.expects
|
45
|
+
method.expects.each_with_index{ |type, i| args[i] = @protocol.value_to_xmlrpc_wire_format(args[i], type) }
|
63
46
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
def transform_return_value(method_name, return_value)
|
68
|
-
info = @api.api_methods[method_name.to_sym]
|
69
|
-
return true unless returns = info[:returns]
|
70
|
-
type_binding = @marshaler.register_type(returns[0])
|
71
|
-
info = WS::ParamInfo.create(returns[0], type_binding, 0)
|
72
|
-
info.name = 'return'
|
73
|
-
@marshaler.transform_inbound(WS::Param.new(return_value, info))
|
47
|
+
ok, return_value = @client.call2(public_name(method_name), *args)
|
48
|
+
return (method.cast_returns(return_value.dup) rescue return_value) if ok
|
49
|
+
raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
|
74
50
|
end
|
75
51
|
|
76
52
|
def public_name(method_name)
|
@@ -79,7 +79,7 @@ module ActionWebService # :nodoc:
|
|
79
79
|
raise(ContainerError, "no such web service '#{web_service_name}'")
|
80
80
|
end
|
81
81
|
service = info[:block]
|
82
|
-
service ? instance_eval(&service) : info[:object]
|
82
|
+
service ? self.instance_eval(&service) : info[:object]
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
@@ -12,14 +12,6 @@ module ActionWebService # :nodoc:
|
|
12
12
|
base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.layered_service_name(public_method_name) # :nodoc:
|
16
|
-
if public_method_name =~ /^([^\.]+)\.(.*)$/
|
17
|
-
$1
|
18
|
-
else
|
19
|
-
nil
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
15
|
module InstanceMethods # :nodoc:
|
24
16
|
private
|
25
17
|
def invoke_web_service_request(protocol_request)
|
@@ -34,30 +26,24 @@ module ActionWebService # :nodoc:
|
|
34
26
|
|
35
27
|
def web_service_direct_invoke(invocation)
|
36
28
|
@method_params = invocation.method_ordered_params
|
37
|
-
arity = method(invocation.
|
29
|
+
arity = method(invocation.api_method.name).arity rescue 0
|
38
30
|
if arity < 0 || arity > 0
|
39
|
-
return_value = self.__send__(invocation.
|
31
|
+
return_value = self.__send__(invocation.api_method.name, *@method_params)
|
40
32
|
else
|
41
|
-
return_value = self.__send__(invocation.
|
33
|
+
return_value = self.__send__(invocation.api_method.name)
|
42
34
|
end
|
43
|
-
|
44
|
-
returns = invocation.returns ? invocation.returns[0] : nil
|
45
|
-
else
|
46
|
-
returns = return_value.class
|
47
|
-
end
|
48
|
-
invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
|
35
|
+
web_service_create_response(invocation.protocol, invocation.api, invocation.api_method, return_value)
|
49
36
|
end
|
50
37
|
|
51
38
|
def web_service_delegated_invoke(invocation)
|
52
39
|
cancellation_reason = nil
|
53
|
-
return_value = invocation.service.perform_invocation(invocation.
|
40
|
+
return_value = invocation.service.perform_invocation(invocation.api_method.name, invocation.method_ordered_params) do |x|
|
54
41
|
cancellation_reason = x
|
55
42
|
end
|
56
43
|
if cancellation_reason
|
57
44
|
raise(DispatcherError, "request canceled: #{cancellation_reason}")
|
58
45
|
end
|
59
|
-
|
60
|
-
invocation.protocol.marshal_response(invocation.public_method_name, return_value, returns)
|
46
|
+
web_service_create_response(invocation.protocol, invocation.api, invocation.api_method, return_value)
|
61
47
|
end
|
62
48
|
|
63
49
|
def web_service_invocation(request)
|
@@ -66,86 +52,80 @@ module ActionWebService # :nodoc:
|
|
66
52
|
invocation.protocol = request.protocol
|
67
53
|
invocation.service_name = request.service_name
|
68
54
|
if web_service_dispatching_mode == :layered
|
69
|
-
|
70
|
-
|
71
|
-
|
55
|
+
case invocation.protocol
|
56
|
+
when Protocol::Soap::SoapProtocol
|
57
|
+
soap_action = request.protocol_options[:soap_action]
|
58
|
+
if soap_action && soap_action =~ /^\/\w+\/(\w+)\//
|
59
|
+
invocation.service_name = $1
|
60
|
+
end
|
61
|
+
when Protocol::XmlRpc::XmlRpcProtocol
|
62
|
+
if request.method_name =~ /^([^\.]+)\.(.*)$/
|
63
|
+
public_method_name = $2
|
64
|
+
invocation.service_name = $1
|
65
|
+
end
|
72
66
|
end
|
73
67
|
end
|
74
|
-
invocation.public_method_name = public_method_name
|
75
68
|
case web_service_dispatching_mode
|
76
69
|
when :direct
|
77
70
|
invocation.api = self.class.web_service_api
|
78
71
|
invocation.service = self
|
79
72
|
when :delegated, :layered
|
80
|
-
invocation.service = web_service_object(invocation.service_name)
|
81
|
-
unless invocation.service
|
82
|
-
raise(DispatcherError, "service #{invocation.service_name} not available")
|
83
|
-
end
|
73
|
+
invocation.service = web_service_object(invocation.service_name)
|
84
74
|
invocation.api = invocation.service.class.web_service_api
|
85
75
|
end
|
76
|
+
if invocation.api.nil?
|
77
|
+
raise(DispatcherError, "no API attached to #{invocation.service.class}")
|
78
|
+
end
|
79
|
+
invocation.protocol.register_api(invocation.api)
|
80
|
+
request.api = invocation.api
|
86
81
|
if invocation.api.has_public_api_method?(public_method_name)
|
87
|
-
invocation.
|
82
|
+
invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
|
88
83
|
else
|
89
84
|
if invocation.api.default_api_method.nil?
|
90
85
|
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
|
91
86
|
else
|
92
|
-
invocation.
|
87
|
+
invocation.api_method = invocation.api.default_api_method_instance
|
93
88
|
end
|
94
89
|
end
|
95
|
-
|
96
|
-
|
90
|
+
if invocation.service.nil?
|
91
|
+
raise(DispatcherError, "no service available for service name #{invocation.service_name}")
|
97
92
|
end
|
98
|
-
|
99
|
-
|
100
|
-
invocation.returns = info ? info[:returns] : nil
|
101
|
-
if invocation.expects
|
102
|
-
i = 0
|
103
|
-
invocation.method_ordered_params = request.method_params.map do |param|
|
104
|
-
if invocation.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
|
105
|
-
marshaler = invocation.protocol.marshaler
|
106
|
-
decoded_param = WS::Encoding::XmlRpcDecodedParam.new(param.info.name, param.value)
|
107
|
-
marshaled_param = marshaler.typed_unmarshal(decoded_param, invocation.expects[i]) rescue nil
|
108
|
-
param = marshaled_param ? marshaled_param : param
|
109
|
-
end
|
110
|
-
i += 1
|
111
|
-
param.value
|
112
|
-
end
|
113
|
-
i = 0
|
114
|
-
params = []
|
115
|
-
invocation.expects.each do |spec|
|
116
|
-
type_binding = invocation.protocol.register_signature_type(spec)
|
117
|
-
info = WS::ParamInfo.create(spec, type_binding, i)
|
118
|
-
params << WS::Param.new(invocation.method_ordered_params[i], info)
|
119
|
-
i += 1
|
120
|
-
end
|
121
|
-
invocation.method_ws_params = params
|
122
|
-
invocation.method_named_params = {}
|
123
|
-
invocation.method_ws_params.each do |param|
|
124
|
-
invocation.method_named_params[param.info.name] = param.value
|
125
|
-
end
|
126
|
-
else
|
127
|
-
invocation.method_ordered_params = []
|
128
|
-
invocation.method_named_params = {}
|
93
|
+
unless invocation.service.respond_to?(invocation.api_method.name)
|
94
|
+
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
|
129
95
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
96
|
+
request.api_method = invocation.api_method
|
97
|
+
begin
|
98
|
+
invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
|
99
|
+
rescue
|
100
|
+
logger.warn "Casting of method parameters failed" unless logger.nil?
|
101
|
+
invocation.method_ordered_params = request.method_params
|
102
|
+
end
|
103
|
+
request.method_params = invocation.method_ordered_params
|
104
|
+
invocation.method_named_params = {}
|
105
|
+
invocation.api_method.param_names.inject(0) do |m, n|
|
106
|
+
invocation.method_named_params[n] = invocation.method_ordered_params[m]
|
107
|
+
m + 1
|
134
108
|
end
|
135
109
|
invocation
|
136
110
|
end
|
137
111
|
|
112
|
+
def web_service_create_response(protocol, api, api_method, return_value)
|
113
|
+
if api.has_api_method?(api_method.name)
|
114
|
+
return_type = api_method.returns ? api_method.returns[0] : nil
|
115
|
+
return_value = api_method.cast_returns(return_value)
|
116
|
+
else
|
117
|
+
return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
|
118
|
+
end
|
119
|
+
protocol.encode_response(api_method.public_name + 'Response', return_value, return_type)
|
120
|
+
end
|
121
|
+
|
138
122
|
class Invocation # :nodoc:
|
139
123
|
attr_accessor :protocol
|
140
124
|
attr_accessor :service_name
|
141
125
|
attr_accessor :api
|
142
|
-
attr_accessor :
|
143
|
-
attr_accessor :api_method_name
|
126
|
+
attr_accessor :api_method
|
144
127
|
attr_accessor :method_ordered_params
|
145
128
|
attr_accessor :method_named_params
|
146
|
-
attr_accessor :method_ws_params
|
147
|
-
attr_accessor :expects
|
148
|
-
attr_accessor :returns
|
149
129
|
attr_accessor :service
|
150
130
|
end
|
151
131
|
end
|
@@ -38,9 +38,13 @@ module ActionWebService # :nodoc:
|
|
38
38
|
module InstanceMethods # :nodoc:
|
39
39
|
private
|
40
40
|
def dispatch_web_service_request
|
41
|
-
|
41
|
+
exception = nil
|
42
|
+
begin
|
43
|
+
request = discover_web_service_request(@request)
|
44
|
+
rescue Exception => e
|
45
|
+
exception = e
|
46
|
+
end
|
42
47
|
if request
|
43
|
-
log_request(request, @request.raw_post)
|
44
48
|
response = nil
|
45
49
|
exception = nil
|
46
50
|
bm = Benchmark.measure do
|
@@ -50,6 +54,7 @@ module ActionWebService # :nodoc:
|
|
50
54
|
exception = e
|
51
55
|
end
|
52
56
|
end
|
57
|
+
log_request(request, @request.raw_post)
|
53
58
|
if exception
|
54
59
|
log_error(exception) unless logger.nil?
|
55
60
|
send_web_service_error_response(request, exception)
|
@@ -57,7 +62,8 @@ module ActionWebService # :nodoc:
|
|
57
62
|
send_web_service_response(response, bm.real)
|
58
63
|
end
|
59
64
|
else
|
60
|
-
exception
|
65
|
+
exception ||= DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
|
66
|
+
log_error(exception) unless logger.nil?
|
61
67
|
send_web_service_error_response(request, exception)
|
62
68
|
end
|
63
69
|
rescue Exception => e
|
@@ -76,15 +82,20 @@ module ActionWebService # :nodoc:
|
|
76
82
|
unless self.class.web_service_exception_reporting
|
77
83
|
exception = DispatcherError.new("Internal server error (exception raised)")
|
78
84
|
end
|
79
|
-
|
85
|
+
api_method = request.api_method
|
86
|
+
public_method_name = api_method ? api_method.public_name : request.method_name
|
87
|
+
return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
|
88
|
+
response = request.protocol.encode_response(public_method_name + 'Response', exception, return_type)
|
80
89
|
send_web_service_response(response)
|
81
90
|
else
|
82
91
|
if self.class.web_service_exception_reporting
|
83
92
|
message = exception.message
|
93
|
+
backtrace = "\nBacktrace:\n#{exception.backtrace.join("\n")}"
|
84
94
|
else
|
85
95
|
message = "Exception raised"
|
96
|
+
backtrace = ""
|
86
97
|
end
|
87
|
-
render_text("Internal protocol error: #{message}", "500
|
98
|
+
render_text("Internal protocol error: #{message}#{backtrace}", "500 Internal Protocol Error")
|
88
99
|
end
|
89
100
|
end
|
90
101
|
|
@@ -95,7 +106,7 @@ module ActionWebService # :nodoc:
|
|
95
106
|
end
|
96
107
|
@session ||= {}
|
97
108
|
@assigns ||= {}
|
98
|
-
@params['action'] = invocation.
|
109
|
+
@params['action'] = invocation.api_method.name.to_s
|
99
110
|
if before_action == false
|
100
111
|
raise(DispatcherError, "Method filtered")
|
101
112
|
end
|
@@ -107,7 +118,13 @@ module ActionWebService # :nodoc:
|
|
107
118
|
def log_request(request, body)
|
108
119
|
unless logger.nil?
|
109
120
|
name = request.method_name
|
110
|
-
|
121
|
+
api_method = request.api_method
|
122
|
+
params = request.method_params
|
123
|
+
if api_method && api_method.expects
|
124
|
+
params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
|
125
|
+
else
|
126
|
+
params = params.map{ |param| param.inspect }
|
127
|
+
end
|
111
128
|
service = request.service_name
|
112
129
|
logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
|
113
130
|
logger.debug(indent(body))
|
@@ -116,7 +133,8 @@ module ActionWebService # :nodoc:
|
|
116
133
|
|
117
134
|
def log_response(response, elapsed=nil)
|
118
135
|
unless logger.nil?
|
119
|
-
|
136
|
+
elapsed = (elapsed ? " (%f):" % elapsed : ":")
|
137
|
+
logger.debug("\nWeb Service Response" + elapsed + " => #{response.return_value.inspect}")
|
120
138
|
logger.debug(indent(response.body))
|
121
139
|
end
|
122
140
|
end
|
@@ -160,14 +178,14 @@ module ActionWebService # :nodoc:
|
|
160
178
|
namespace = 'urn:ActionWebService'
|
161
179
|
soap_action_base = "/#{controller_name}"
|
162
180
|
|
163
|
-
marshaler =
|
181
|
+
marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
|
164
182
|
apis = {}
|
165
183
|
case dispatching_mode
|
166
184
|
when :direct
|
167
185
|
api = self.class.web_service_api
|
168
186
|
web_service_name = controller_class_name.sub(/Controller$/, '').underscore
|
169
187
|
apis[web_service_name] = [api, register_api(api, marshaler)]
|
170
|
-
when :delegated
|
188
|
+
when :delegated, :layered
|
171
189
|
self.class.web_services.each do |web_service_name, info|
|
172
190
|
service = web_service_object(web_service_name)
|
173
191
|
api = service.class.web_service_api
|
@@ -197,7 +215,7 @@ module ActionWebService # :nodoc:
|
|
197
215
|
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
|
198
216
|
custom_types.each do |binding|
|
199
217
|
case
|
200
|
-
when binding.
|
218
|
+
when binding.type.array?
|
201
219
|
xm.xsd(:complexType, 'name' => binding.type_name) do
|
202
220
|
xm.xsd(:complexContent) do
|
203
221
|
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
|
@@ -206,11 +224,11 @@ module ActionWebService # :nodoc:
|
|
206
224
|
end
|
207
225
|
end
|
208
226
|
end
|
209
|
-
when binding.
|
227
|
+
when binding.type.structured?
|
210
228
|
xm.xsd(:complexType, 'name' => binding.type_name) do
|
211
229
|
xm.xsd(:all) do
|
212
|
-
binding.each_member do |name,
|
213
|
-
b = marshaler.register_type(
|
230
|
+
binding.type.each_member do |name, type|
|
231
|
+
b = marshaler.register_type(type)
|
214
232
|
xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
|
215
233
|
end
|
216
234
|
end
|
@@ -224,34 +242,26 @@ module ActionWebService # :nodoc:
|
|
224
242
|
# APIs
|
225
243
|
apis.each do |api_name, values|
|
226
244
|
api = values[0]
|
227
|
-
api.api_methods.each do |name,
|
245
|
+
api.api_methods.each do |name, method|
|
228
246
|
gen = lambda do |msg_name, direction|
|
229
|
-
xm.message('name' => msg_name) do
|
247
|
+
xm.message('name' => message_name_for(api_name, msg_name)) do
|
230
248
|
sym = nil
|
231
249
|
if direction == :out
|
232
|
-
returns =
|
250
|
+
returns = method.returns
|
233
251
|
if returns
|
234
252
|
binding = marshaler.register_type(returns[0])
|
235
253
|
xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
|
236
254
|
end
|
237
255
|
else
|
238
|
-
expects =
|
239
|
-
i = 1
|
256
|
+
expects = method.expects
|
240
257
|
expects.each do |type|
|
241
|
-
if type.is_a?(Hash)
|
242
|
-
param_name = type.keys.shift
|
243
|
-
type = type.values.shift
|
244
|
-
else
|
245
|
-
param_name = "param#{i}"
|
246
|
-
end
|
247
258
|
binding = marshaler.register_type(type)
|
248
|
-
xm.part('name' =>
|
249
|
-
i += 1
|
259
|
+
xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
|
250
260
|
end if expects
|
251
261
|
end
|
252
262
|
end
|
253
263
|
end
|
254
|
-
public_name =
|
264
|
+
public_name = method.public_name
|
255
265
|
gen.call(public_name, :in)
|
256
266
|
gen.call("#{public_name}Response", :out)
|
257
267
|
end
|
@@ -259,11 +269,10 @@ module ActionWebService # :nodoc:
|
|
259
269
|
# Port
|
260
270
|
port_name = port_name_for(global_service_name, api_name)
|
261
271
|
xm.portType('name' => port_name) do
|
262
|
-
api.api_methods.each do |name,
|
263
|
-
|
264
|
-
|
265
|
-
xm.
|
266
|
-
xm.output('message' => "typens:#{public_name}Response")
|
272
|
+
api.api_methods.each do |name, method|
|
273
|
+
xm.operation('name' => method.public_name) do
|
274
|
+
xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
|
275
|
+
xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
|
267
276
|
end
|
268
277
|
end
|
269
278
|
end
|
@@ -272,16 +281,15 @@ module ActionWebService # :nodoc:
|
|
272
281
|
binding_name = binding_name_for(global_service_name, api_name)
|
273
282
|
xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
|
274
283
|
xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
|
275
|
-
api.api_methods.each do |name,
|
276
|
-
|
277
|
-
xm.operation('name' => public_name) do
|
284
|
+
api.api_methods.each do |name, method|
|
285
|
+
xm.operation('name' => method.public_name) do
|
278
286
|
case web_service_dispatching_mode
|
279
|
-
when :direct
|
280
|
-
soap_action = soap_action_base + "/api/" + public_name
|
281
|
-
when :delegated
|
287
|
+
when :direct
|
288
|
+
soap_action = soap_action_base + "/api/" + method.public_name
|
289
|
+
when :delegated, :layered
|
282
290
|
soap_action = soap_action_base \
|
283
291
|
+ "/" + api_name.to_s \
|
284
|
-
+ "/" + public_name
|
292
|
+
+ "/" + method.public_name
|
285
293
|
end
|
286
294
|
xm.soap(:operation, 'soapAction' => soap_action)
|
287
295
|
xm.input do
|
@@ -307,7 +315,7 @@ module ActionWebService # :nodoc:
|
|
307
315
|
port_name = port_name_for(global_service_name, api_name)
|
308
316
|
binding_name = binding_name_for(global_service_name, api_name)
|
309
317
|
case web_service_dispatching_mode
|
310
|
-
when :direct
|
318
|
+
when :direct, :layered
|
311
319
|
binding_target = 'api'
|
312
320
|
when :delegated
|
313
321
|
binding_target = api_name.to_s
|
@@ -328,32 +336,40 @@ module ActionWebService # :nodoc:
|
|
328
336
|
"#{global_service}#{service.to_s.camelize}Binding"
|
329
337
|
end
|
330
338
|
|
339
|
+
def message_name_for(api_name, message_name)
|
340
|
+
mode = web_service_dispatching_mode
|
341
|
+
if mode == :layered || mode == :delegated
|
342
|
+
api_name.to_s + '-' + message_name
|
343
|
+
else
|
344
|
+
message_name
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
331
348
|
def register_api(api, marshaler)
|
332
349
|
bindings = {}
|
333
350
|
traverse_custom_types(api, marshaler) do |binding|
|
334
|
-
bindings[binding] = nil unless bindings.has_key?(binding
|
351
|
+
bindings[binding] = nil unless bindings.has_key?(binding)
|
352
|
+
element_binding = binding.element_binding
|
353
|
+
bindings[binding.element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
|
335
354
|
end
|
336
355
|
bindings.keys
|
337
356
|
end
|
338
357
|
|
339
358
|
def traverse_custom_types(api, marshaler, &block)
|
340
|
-
api.api_methods.each do |name,
|
341
|
-
expects, returns =
|
342
|
-
expects.each{|
|
343
|
-
returns.each{|
|
359
|
+
api.api_methods.each do |name, method|
|
360
|
+
expects, returns = method.expects, method.returns
|
361
|
+
expects.each{ |type| traverse_type(marshaler, type, &block) if type.custom? } if expects
|
362
|
+
returns.each{ |type| traverse_type(marshaler, type, &block) if type.custom? } if returns
|
344
363
|
end
|
345
364
|
end
|
346
365
|
|
347
|
-
def
|
348
|
-
|
349
|
-
if
|
350
|
-
|
351
|
-
|
352
|
-
end
|
353
|
-
elsif binding.is_typed_array?
|
354
|
-
traverse_custom_type_spec(marshaler, binding.element_binding.type_class, &block)
|
366
|
+
def traverse_type(marshaler, type, &block)
|
367
|
+
yield marshaler.register_type(type)
|
368
|
+
if type.array?
|
369
|
+
yield marshaler.register_type(type.element_type)
|
370
|
+
type = type.element_type
|
355
371
|
end
|
356
|
-
|
372
|
+
type.each_member{ |name, type| traverse_type(marshaler, type, &block) } if type.structured?
|
357
373
|
end
|
358
374
|
end
|
359
375
|
end
|