diff options
-rw-r--r-- | actionwebservice/CHANGELOG | 4 | ||||
-rw-r--r-- | actionwebservice/TODO | 3 | ||||
-rw-r--r-- | actionwebservice/lib/action_web_service/dispatcher/abstract.rb | 8 | ||||
-rw-r--r-- | actionwebservice/lib/action_web_service/test_invoke.rb | 130 | ||||
-rw-r--r-- | actionwebservice/test/dispatcher_action_controller_soap_test.rb | 2 | ||||
-rw-r--r-- | actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb | 2 | ||||
-rw-r--r-- | actionwebservice/test/test_invoke_test.rb | 77 |
7 files changed, 223 insertions, 3 deletions
diff --git a/actionwebservice/CHANGELOG b/actionwebservice/CHANGELOG index f823c293ba..5dbae2ff88 100644 --- a/actionwebservice/CHANGELOG +++ b/actionwebservice/CHANGELOG @@ -1,5 +1,9 @@ *0.6.0* (Unreleased) +* Add action_controller/test_invoke, used for integrating AWS with the Rails testing infrastructure + +* Allow passing through options to the SOAP RPC driver for the SOAP client + * Make the SOAP WS marshaler use #columns to decide which fields to marshal as well, avoids providing attributes brought in by associations * Add <tt>ActionWebService::API::Base.allow_active_record_expects</tt> option, with a default of false. Setting this to true will allow specifying ActiveRecord::Base model classes in <tt>:expects</tt>. API writers should take care to validate the received ActiveRecord model objects when turning it on, and/or have an authentication mechanism in place to reduce the security risk. diff --git a/actionwebservice/TODO b/actionwebservice/TODO index 55c484b775..87d2a62c08 100644 --- a/actionwebservice/TODO +++ b/actionwebservice/TODO @@ -1,11 +1,12 @@ = 0.7.0 + - WS Test Integration - WS Scaffolding - WS Generators - - WS Test Integration = 0.8.0 - Consumption of WSDL services = Refactoring + - Port dispatcher tests to use test_invoke - Don't have clean way to go from SOAP Class object to the xsd:NAME type string -- NaHi possibly looking at remedying this situation diff --git a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb index 641f291533..1df03d1777 100644 --- a/actionwebservice/lib/action_web_service/dispatcher/abstract.rb +++ b/actionwebservice/lib/action_web_service/dispatcher/abstract.rb @@ -12,6 +12,14 @@ module ActionWebService # :nodoc: base.send(:include, ActionWebService::Dispatcher::InstanceMethods) end + def self.layered_service_name(public_method_name) # :nodoc: + if public_method_name =~ /^([^\.]+)\.(.*)$/ + $1 + else + nil + end + end + module InstanceMethods # :nodoc: private def invoke_web_service_request(protocol_request) diff --git a/actionwebservice/lib/action_web_service/test_invoke.rb b/actionwebservice/lib/action_web_service/test_invoke.rb new file mode 100644 index 0000000000..44601c1c11 --- /dev/null +++ b/actionwebservice/lib/action_web_service/test_invoke.rb @@ -0,0 +1,130 @@ +require 'test/unit' + +module Test + module Unit + class TestCase # :nodoc: + private + # invoke the specified API method + def invoke_direct(method_name, *args) + prepare_request('api', 'api', method_name, *args) + @controller.process(@request, @response) + decode_rpc_response + end + alias_method :invoke, :invoke_direct + + # invoke the specified API method on the specified service + def invoke_delegated(service_name, method_name, *args) + prepare_request(service_name.to_s, service_name, method_name, *args) + @controller.process(@request, @response) + decode_rpc_response + end + + # invoke the specified layered API method on the correct service + def invoke_layered(service_name, method_name, *args) + if protocol == :soap + raise "SOAP protocol support for :layered dispatching mode is not available" + end + prepare_request('api', service_name, method_name, *args) + @controller.process(@request, @response) + decode_rpc_response + end + + # ---------------------- internal --------------------------- + + def prepare_request(action, service_name, api_method_name, *args) + @request.request_parameters['action'] = action + @request.env['REQUEST_METHOD'] = 'POST' + @request.env['HTTP_CONTENT_TYPE'] = 'text/xml' + @request.env['RAW_POST_DATA'] = encode_rpc_call(service_name, api_method_name, *args) + case protocol + when :soap + soap_action = "/#{@controller.controller_name}/#{service_name}/#{public_method_name(service_name, api_method_name)}" + @request.env['HTTP_SOAPACTION'] = soap_action + when :xmlrpc + @request.env.delete('HTTP_SOAPACTION') + end + end + + def encode_rpc_call(service_name, api_method_name, *args) + case @controller.web_service_dispatching_mode + when :direct + api = @controller.class.web_service_api + when :delegated, :layered + api = @controller.web_service_object(service_name.to_sym).class.web_service_api + end + info = api.api_methods[api_method_name.to_sym] + ((info[:expects] || []) + (info[:returns] || [])).each do |spec| + marshaler.register_type spec + end + expects = info[:expects] + args = args.dup + (0..(args.length-1)).each do |i| + type_binding = marshaler.register_type(expects ? expects[i] : args[i].class) + info = WS::ParamInfo.create(expects ? expects[i] : args[i].class, type_binding, i) + args[i] = marshaler.marshal(WS::Param.new(args[i], info)) + end + encoder.encode_rpc_call(public_method_name(service_name, api_method_name), args) + end + + def decode_rpc_response + public_method_name, return_value = encoder.decode_rpc_response(@response.body) + result = marshaler.unmarshal(return_value).value + unless @return_exceptions + exception = is_exception?(result) + raise exception if exception + end + result + end + + def public_method_name(service_name, api_method_name) + public_name = service_api(service_name).public_api_method_name(api_method_name) + if @controller.web_service_dispatching_mode == :layered + '%s.%s' % [service_name.to_s, public_name] + else + public_name + end + end + + def service_api(service_name) + case @controller.web_service_dispatching_mode + when :direct + @controller.class.web_service_api + when :delegated, :layered + @controller.web_service_object(service_name.to_sym).class.web_service_api + end + end + + def protocol + @protocol ||= :soap + end + + def marshaler + case protocol + when :soap + @soap_marshaler ||= WS::Marshaling::SoapMarshaler.new 'urn:ActionWebService' + when :xmlrpc + @xmlrpc_marshaler ||= WS::Marshaling::XmlRpcMarshaler.new + end + end + + def encoder + case protocol + when :soap + @soap_encoder ||= WS::Encoding::SoapRpcEncoding.new 'urn:ActionWebService' + when :xmlrpc + @xmlrpc_encoder ||= WS::Encoding::XmlRpcEncoding.new + end + end + + def is_exception?(obj) + case protocol + when :soap + (obj.respond_to?(:detail) && obj.detail.respond_to?(:cause) && \ + obj.detail.cause.is_a?(Exception)) ? obj.detail.cause : nil + when :xmlrpc + obj.is_a?(XMLRPC::FaultException) ? obj : nil + end + end + end + end +end diff --git a/actionwebservice/test/dispatcher_action_controller_soap_test.rb b/actionwebservice/test/dispatcher_action_controller_soap_test.rb index 6d50bbba8a..dd945972d6 100644 --- a/actionwebservice/test/dispatcher_action_controller_soap_test.rb +++ b/actionwebservice/test/dispatcher_action_controller_soap_test.rb @@ -62,7 +62,7 @@ class TC_DispatcherActionControllerSoap < Test::Unit::TestCase test_request = ActionController::TestRequest.new test_request.request_parameters['action'] = service_name(container) test_request.env['REQUEST_METHOD'] = "POST" - test_request.env['HTTP_CONTENTTYPE'] = 'text/xml' + test_request.env['HTTP_CONTENT_TYPE'] = 'text/xml' test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name(container)}/#{public_method_name}" test_request.env['RAW_POST_DATA'] = body test_request diff --git a/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb b/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb index 87677dec3e..81ada70c9e 100644 --- a/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb +++ b/actionwebservice/test/dispatcher_action_controller_xmlrpc_test.rb @@ -33,7 +33,7 @@ class TC_DispatcherActionControllerXmlRpc < Test::Unit::TestCase test_request = ActionController::TestRequest.new test_request.request_parameters['action'] = service_name(container) test_request.env['REQUEST_METHOD'] = "POST" - test_request.env['HTTP_CONTENTTYPE'] = 'text/xml' + test_request.env['HTTP_CONTENT_TYPE'] = 'text/xml' test_request.env['RAW_POST_DATA'] = body test_request end diff --git a/actionwebservice/test/test_invoke_test.rb b/actionwebservice/test/test_invoke_test.rb new file mode 100644 index 0000000000..cbfde9c3df --- /dev/null +++ b/actionwebservice/test/test_invoke_test.rb @@ -0,0 +1,77 @@ +require File.dirname(__FILE__) + '/abstract_unit' +require 'action_web_service/test_invoke' + +class TestInvokeAPI < ActionWebService::API::Base + api_method :add, :expects => [:int, :int], :returns => [:int] +end + +class TestInvokeService < ActionWebService::Base + web_service_api TestInvokeAPI + + attr :invoked + + def add(a, b) + @invoked = true + a + b + end +end + +class TestController < ActionController::Base + def rescue_action(e); raise e; end +end + +class TestInvokeDirectController < TestController + web_service_api TestInvokeAPI + + attr :invoked + + def add + @invoked = true + @method_params[0] + @method_params[1] + end +end + +class TestInvokeDelegatedController < TestController + web_service_dispatching_mode :delegated + web_service :service, TestInvokeService.new +end + +class TestInvokeLayeredController < TestController + web_service_dispatching_mode :layered + web_service :one, TestInvokeService.new + web_service :two, TestInvokeService.new +end + +class TestInvokeTest < Test::Unit::TestCase + def setup + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + end + + def test_direct_add + @controller = TestInvokeDirectController.new + assert_equal nil, @controller.invoked + result = invoke :add, 25, 25 + assert_equal 50, result + assert_equal true, @controller.invoked + end + + def test_delegated_add + @controller = TestInvokeDelegatedController.new + assert_equal nil, @controller.web_service_object(:service).invoked + result = invoke_delegated :service, :add, 100, 50 + assert_equal 150, result + assert_equal true, @controller.web_service_object(:service).invoked + end + + def test_layered_add + @protocol = :xmlrpc + @controller = TestInvokeLayeredController.new + [:one, :two].each do |service| + assert_equal nil, @controller.web_service_object(service).invoked + result = invoke_layered service, :add, 200, -50 + assert_equal 150, result + assert_equal true, @controller.web_service_object(service).invoked + end + end +end |