mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
add 'system.multicall' support to XML-RPC. boxcarred methods must still exist
on the target service(s), value casting will still be performed, and recursive 'system.multicall' calls are not allowed. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2021 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
2eba713445
commit
9d79880e81
5 changed files with 107 additions and 9 deletions
|
@ -1,5 +1,7 @@
|
||||||
*SVN*
|
*SVN*
|
||||||
|
|
||||||
|
* Add XML-RPC 'system.multicall' support #1941 [jbonnar]
|
||||||
|
|
||||||
* Fix duplicate XSD entries for custom types shared across delegated/layered services #1729 [Tyler Kovacs]
|
* Fix duplicate XSD entries for custom types shared across delegated/layered services #1729 [Tyler Kovacs]
|
||||||
|
|
||||||
* Allow multiple invocations in the same test method #1720 [dkhawk]
|
* Allow multiple invocations in the same test method #1720 [dkhawk]
|
||||||
|
|
|
@ -16,11 +16,10 @@ module ActionWebService # :nodoc:
|
||||||
private
|
private
|
||||||
def invoke_web_service_request(protocol_request)
|
def invoke_web_service_request(protocol_request)
|
||||||
invocation = web_service_invocation(protocol_request)
|
invocation = web_service_invocation(protocol_request)
|
||||||
case web_service_dispatching_mode
|
if invocation.is_a?(Array) && protocol_request.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
|
||||||
when :direct
|
xmlrpc_multicall_invoke(invocation)
|
||||||
web_service_direct_invoke(invocation)
|
else
|
||||||
when :delegated, :layered
|
web_service_invoke(invocation)
|
||||||
web_service_delegated_invoke(invocation)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -47,10 +46,43 @@ module ActionWebService # :nodoc:
|
||||||
if cancellation_reason
|
if cancellation_reason
|
||||||
raise(DispatcherError, "request canceled: #{cancellation_reason}")
|
raise(DispatcherError, "request canceled: #{cancellation_reason}")
|
||||||
end
|
end
|
||||||
|
return_value
|
||||||
|
end
|
||||||
|
|
||||||
|
def web_service_invoke(invocation)
|
||||||
|
case web_service_dispatching_mode
|
||||||
|
when :direct
|
||||||
|
return_value = web_service_direct_invoke(invocation)
|
||||||
|
when :delegated, :layered
|
||||||
|
return_value = web_service_delegated_invoke(invocation)
|
||||||
|
end
|
||||||
web_service_create_response(invocation.protocol, invocation.protocol_options, invocation.api, invocation.api_method, return_value)
|
web_service_create_response(invocation.protocol, invocation.protocol_options, invocation.api, invocation.api_method, return_value)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def xmlrpc_multicall_invoke(invocations)
|
||||||
|
responses = []
|
||||||
|
invocations.each do |invocation|
|
||||||
|
begin
|
||||||
|
case web_service_dispatching_mode
|
||||||
|
when :direct
|
||||||
|
return_value = web_service_direct_invoke(invocation)
|
||||||
|
when :delegated, :layered
|
||||||
|
return_value = web_service_delegated_invoke(invocation)
|
||||||
|
end
|
||||||
|
api_method = invocation.api_method
|
||||||
|
if invocation.api.has_api_method?(api_method.name)
|
||||||
|
return_value = api_method.cast_returns(return_value)
|
||||||
|
end
|
||||||
|
responses << [return_value]
|
||||||
|
rescue Exception => e
|
||||||
|
responses << { 'faultCode' => 3, 'faultString' => e.message }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
invocation = invocations[0]
|
||||||
|
invocation.protocol.encode_response('system.multicall', responses, nil, invocation.protocol_options)
|
||||||
|
end
|
||||||
|
|
||||||
def web_service_invocation(request)
|
def web_service_invocation(request, level = 0)
|
||||||
public_method_name = request.method_name
|
public_method_name = request.method_name
|
||||||
invocation = Invocation.new
|
invocation = Invocation.new
|
||||||
invocation.protocol = request.protocol
|
invocation.protocol = request.protocol
|
||||||
|
@ -70,6 +102,28 @@ module ActionWebService # :nodoc:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if invocation.protocol.is_a? Protocol::XmlRpc::XmlRpcProtocol
|
||||||
|
if public_method_name == 'multicall' && invocation.service_name == 'system'
|
||||||
|
if level > 0
|
||||||
|
raise(DispatcherError, "Recursive system.multicall invocations not allowed")
|
||||||
|
end
|
||||||
|
multicall = request.method_params.dup
|
||||||
|
unless multicall.is_a?(Array) && multicall[0].is_a?(Array)
|
||||||
|
raise(DispatcherError, "Malformed multicall (expected array of Hash elements)")
|
||||||
|
end
|
||||||
|
multicall = multicall[0]
|
||||||
|
return multicall.map do |item|
|
||||||
|
raise(DispatcherError, "Multicall elements must be Hash") unless item.is_a?(Hash)
|
||||||
|
raise(DispatcherError, "Multicall elements must contain a 'methodName' key") unless item.has_key?('methodName')
|
||||||
|
method_name = item['methodName']
|
||||||
|
params = item.has_key?('params') ? item['params'] : []
|
||||||
|
multicall_request = request.dup
|
||||||
|
multicall_request.method_name = method_name
|
||||||
|
multicall_request.method_params = params
|
||||||
|
web_service_invocation(multicall_request, level + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
case web_service_dispatching_mode
|
case web_service_dispatching_mode
|
||||||
when :direct
|
when :direct
|
||||||
invocation.api = self.class.web_service_api
|
invocation.api = self.class.web_service_api
|
||||||
|
|
|
@ -41,7 +41,7 @@ module ActionWebService # :nodoc:
|
||||||
|
|
||||||
class Request # :nodoc:
|
class Request # :nodoc:
|
||||||
attr :protocol
|
attr :protocol
|
||||||
attr :method_name
|
attr_accessor :method_name
|
||||||
attr_accessor :method_params
|
attr_accessor :method_params
|
||||||
attr :service_name
|
attr :service_name
|
||||||
attr_accessor :api
|
attr_accessor :api
|
||||||
|
|
|
@ -107,11 +107,15 @@ module DispatcherTest
|
||||||
class MTAPI < ActionWebService::API::Base
|
class MTAPI < ActionWebService::API::Base
|
||||||
inflect_names false
|
inflect_names false
|
||||||
api_method :getCategories, :returns => [[:string]]
|
api_method :getCategories, :returns => [[:string]]
|
||||||
|
api_method :bool, :returns => [:bool]
|
||||||
|
api_method :alwaysFail
|
||||||
end
|
end
|
||||||
|
|
||||||
class BloggerAPI < ActionWebService::API::Base
|
class BloggerAPI < ActionWebService::API::Base
|
||||||
inflect_names false
|
inflect_names false
|
||||||
api_method :getCategories, :returns => [[:string]]
|
api_method :getCategories, :returns => [[:string]]
|
||||||
|
api_method :str, :expects => [:int], :returns => [:string]
|
||||||
|
api_method :alwaysFail
|
||||||
end
|
end
|
||||||
|
|
||||||
class MTService < ActionWebService::Base
|
class MTService < ActionWebService::Base
|
||||||
|
@ -120,6 +124,14 @@ module DispatcherTest
|
||||||
def getCategories
|
def getCategories
|
||||||
["mtCat1", "mtCat2"]
|
["mtCat1", "mtCat2"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def bool
|
||||||
|
'y'
|
||||||
|
end
|
||||||
|
|
||||||
|
def alwaysFail
|
||||||
|
raise "MT AlwaysFail"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class BloggerService < ActionWebService::Base
|
class BloggerService < ActionWebService::Base
|
||||||
|
@ -128,6 +140,17 @@ module DispatcherTest
|
||||||
def getCategories
|
def getCategories
|
||||||
["bloggerCat1", "bloggerCat2"]
|
["bloggerCat1", "bloggerCat2"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def str(int)
|
||||||
|
unless int.is_a?(Integer)
|
||||||
|
raise "Not an integer!"
|
||||||
|
end
|
||||||
|
500 + int
|
||||||
|
end
|
||||||
|
|
||||||
|
def alwaysFail
|
||||||
|
raise "Blogger AlwaysFail"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class AbstractController < ActionController::Base
|
class AbstractController < ActionController::Base
|
||||||
|
@ -439,8 +462,8 @@ module DispatcherCommonTests
|
||||||
public_method_name = real_method_name
|
public_method_name = real_method_name
|
||||||
request_env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{real_method_name}"
|
request_env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{real_method_name}"
|
||||||
end
|
end
|
||||||
api = container.web_service_object(service_name.to_sym).class.web_service_api
|
api = container.web_service_object(service_name.to_sym).class.web_service_api rescue nil
|
||||||
method = api.public_api_method_instance(real_method_name)
|
method = api.public_api_method_instance(real_method_name) rescue nil
|
||||||
service_name = self.service_name(container)
|
service_name = self.service_name(container)
|
||||||
end
|
end
|
||||||
protocol.register_api(api)
|
protocol.register_api(api)
|
||||||
|
|
|
@ -19,6 +19,25 @@ class TC_DispatcherActionControllerXmlRpc < Test::Unit::TestCase
|
||||||
assert_equal(["bloggerCat1", "bloggerCat2"], blogger_cats)
|
assert_equal(["bloggerCat1", "bloggerCat2"], blogger_cats)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_multicall
|
||||||
|
response = do_method_call(@layered_controller, 'system.multicall', [
|
||||||
|
{'methodName' => 'mt.getCategories'},
|
||||||
|
{'methodName' => 'blogger.getCategories'},
|
||||||
|
{'methodName' => 'mt.bool'},
|
||||||
|
{'methodName' => 'blogger.str', 'params' => ['2000']},
|
||||||
|
{'methodName' => 'mt.alwaysFail'},
|
||||||
|
{'methodName' => 'blogger.alwaysFail'}
|
||||||
|
])
|
||||||
|
assert_equal [
|
||||||
|
[["mtCat1", "mtCat2"]],
|
||||||
|
[["bloggerCat1", "bloggerCat2"]],
|
||||||
|
[true],
|
||||||
|
["2500"],
|
||||||
|
{"faultCode" => 3, "faultString" => "MT AlwaysFail"},
|
||||||
|
{"faultCode" => 3, "faultString" => "Blogger AlwaysFail"}
|
||||||
|
], response
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
def exception_message(xmlrpc_fault_exception)
|
def exception_message(xmlrpc_fault_exception)
|
||||||
xmlrpc_fault_exception.faultString
|
xmlrpc_fault_exception.faultString
|
||||||
|
|
Loading…
Reference in a new issue