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