zinfinit-actionwebservice 2.3.4.2
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 +323 -0
- data/MIT-LICENSE +21 -0
- data/README +381 -0
- data/Rakefile +173 -0
- data/examples/googlesearch/README +143 -0
- data/examples/googlesearch/autoloading/google_search_api.rb +50 -0
- data/examples/googlesearch/autoloading/google_search_controller.rb +57 -0
- data/examples/googlesearch/delegated/google_search_service.rb +108 -0
- data/examples/googlesearch/delegated/search_controller.rb +7 -0
- data/examples/googlesearch/direct/google_search_api.rb +50 -0
- data/examples/googlesearch/direct/search_controller.rb +58 -0
- data/examples/metaWeblog/README +17 -0
- data/examples/metaWeblog/apis/blogger_api.rb +60 -0
- data/examples/metaWeblog/apis/blogger_service.rb +34 -0
- data/examples/metaWeblog/apis/meta_weblog_api.rb +67 -0
- data/examples/metaWeblog/apis/meta_weblog_service.rb +48 -0
- data/examples/metaWeblog/controllers/xmlrpc_controller.rb +16 -0
- data/generators/web_service/USAGE +28 -0
- data/generators/web_service/templates/api_definition.rb +5 -0
- data/generators/web_service/templates/controller.rb +8 -0
- data/generators/web_service/templates/functional_test.rb +19 -0
- data/generators/web_service/web_service_generator.rb +29 -0
- data/lib/action_web_service.rb +68 -0
- data/lib/action_web_service/api.rb +297 -0
- data/lib/action_web_service/base.rb +38 -0
- data/lib/action_web_service/casting.rb +149 -0
- data/lib/action_web_service/client.rb +3 -0
- data/lib/action_web_service/client/base.rb +28 -0
- data/lib/action_web_service/client/soap_client.rb +113 -0
- data/lib/action_web_service/client/xmlrpc_client.rb +58 -0
- data/lib/action_web_service/container.rb +3 -0
- data/lib/action_web_service/container/action_controller_container.rb +93 -0
- data/lib/action_web_service/container/delegated_container.rb +86 -0
- data/lib/action_web_service/container/direct_container.rb +69 -0
- data/lib/action_web_service/dispatcher.rb +2 -0
- data/lib/action_web_service/dispatcher/abstract.rb +207 -0
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +397 -0
- data/lib/action_web_service/invocation.rb +202 -0
- data/lib/action_web_service/protocol.rb +4 -0
- data/lib/action_web_service/protocol/abstract.rb +112 -0
- data/lib/action_web_service/protocol/discovery.rb +37 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +176 -0
- data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +242 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +122 -0
- data/lib/action_web_service/scaffolding.rb +281 -0
- data/lib/action_web_service/simple.rb +53 -0
- data/lib/action_web_service/string_to_datetime_for_soap.rb +10 -0
- data/lib/action_web_service/struct.rb +68 -0
- data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
- data/lib/action_web_service/support/signature_types.rb +256 -0
- data/lib/action_web_service/templates/scaffolds/layout.html.erb +65 -0
- data/lib/action_web_service/templates/scaffolds/methods.html.erb +6 -0
- data/lib/action_web_service/templates/scaffolds/parameters.html.erb +29 -0
- data/lib/action_web_service/templates/scaffolds/result.html.erb +30 -0
- data/lib/action_web_service/test_invoke.rb +110 -0
- data/lib/action_web_service/version.rb +9 -0
- data/lib/actionwebservice.rb +1 -0
- data/setup.rb +1379 -0
- data/test/abstract_client.rb +183 -0
- data/test/abstract_dispatcher.rb +548 -0
- data/test/abstract_unit.rb +39 -0
- data/test/api_test.rb +102 -0
- data/test/apis/auto_load_api.rb +3 -0
- data/test/apis/broken_auto_load_api.rb +2 -0
- data/test/base_test.rb +42 -0
- data/test/casting_test.rb +94 -0
- data/test/client_soap_test.rb +155 -0
- data/test/client_xmlrpc_test.rb +153 -0
- data/test/container_test.rb +73 -0
- data/test/dispatcher_action_controller_soap_test.rb +138 -0
- data/test/dispatcher_action_controller_xmlrpc_test.rb +59 -0
- data/test/fixtures/db_definitions/mysql.sql +8 -0
- data/test/fixtures/users.yml +12 -0
- data/test/gencov +3 -0
- data/test/invocation_test.rb +185 -0
- data/test/run +6 -0
- data/test/scaffolded_controller_test.rb +146 -0
- data/test/struct_test.rb +52 -0
- data/test/test_invoke_test.rb +112 -0
- metadata +173 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
|
|
3
|
+
module ActionWebService # :nodoc:
|
|
4
|
+
module Dispatcher # :nodoc:
|
|
5
|
+
class DispatcherError < ActionWebService::ActionWebServiceError # :nodoc:
|
|
6
|
+
def initialize(*args)
|
|
7
|
+
super
|
|
8
|
+
set_backtrace(caller)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.included(base) # :nodoc:
|
|
13
|
+
base.class_inheritable_option(:web_service_dispatching_mode, :direct)
|
|
14
|
+
base.class_inheritable_option(:web_service_exception_reporting, true)
|
|
15
|
+
base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module InstanceMethods # :nodoc:
|
|
19
|
+
private
|
|
20
|
+
def invoke_web_service_request(protocol_request)
|
|
21
|
+
invocation = web_service_invocation(protocol_request)
|
|
22
|
+
if invocation.is_a?(Array) && protocol_request.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
|
|
23
|
+
xmlrpc_multicall_invoke(invocation)
|
|
24
|
+
else
|
|
25
|
+
web_service_invoke(invocation)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def web_service_direct_invoke(invocation)
|
|
30
|
+
@method_params = invocation.method_ordered_params
|
|
31
|
+
arity = method(invocation.api_method.name).arity rescue 0
|
|
32
|
+
if arity < 0 || arity > 0
|
|
33
|
+
params = @method_params
|
|
34
|
+
else
|
|
35
|
+
params = []
|
|
36
|
+
end
|
|
37
|
+
web_service_filtered_invoke(invocation, params)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def web_service_delegated_invoke(invocation)
|
|
41
|
+
web_service_filtered_invoke(invocation, invocation.method_ordered_params)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def web_service_filtered_invoke(invocation, params)
|
|
45
|
+
cancellation_reason = nil
|
|
46
|
+
return_value = invocation.service.perform_invocation(invocation.api_method.name, params) do |x|
|
|
47
|
+
cancellation_reason = x
|
|
48
|
+
end
|
|
49
|
+
if cancellation_reason
|
|
50
|
+
raise(DispatcherError, "request canceled: #{cancellation_reason}")
|
|
51
|
+
end
|
|
52
|
+
return_value
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def web_service_invoke(invocation)
|
|
56
|
+
case web_service_dispatching_mode
|
|
57
|
+
when :direct
|
|
58
|
+
return_value = web_service_direct_invoke(invocation)
|
|
59
|
+
when :delegated, :layered
|
|
60
|
+
return_value = web_service_delegated_invoke(invocation)
|
|
61
|
+
end
|
|
62
|
+
web_service_create_response(invocation.protocol, invocation.protocol_options, invocation.api, invocation.api_method, return_value)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def xmlrpc_multicall_invoke(invocations)
|
|
66
|
+
responses = []
|
|
67
|
+
invocations.each do |invocation|
|
|
68
|
+
if invocation.is_a?(Hash)
|
|
69
|
+
responses << [invocation, nil]
|
|
70
|
+
next
|
|
71
|
+
end
|
|
72
|
+
begin
|
|
73
|
+
case web_service_dispatching_mode
|
|
74
|
+
when :direct
|
|
75
|
+
return_value = web_service_direct_invoke(invocation)
|
|
76
|
+
when :delegated, :layered
|
|
77
|
+
return_value = web_service_delegated_invoke(invocation)
|
|
78
|
+
end
|
|
79
|
+
api_method = invocation.api_method
|
|
80
|
+
if invocation.api.has_api_method?(api_method.name)
|
|
81
|
+
response_type = (api_method.returns ? api_method.returns[0] : nil)
|
|
82
|
+
return_value = api_method.cast_returns(return_value)
|
|
83
|
+
else
|
|
84
|
+
response_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
|
|
85
|
+
end
|
|
86
|
+
responses << [return_value, response_type]
|
|
87
|
+
rescue Exception => e
|
|
88
|
+
responses << [{ 'faultCode' => 3, 'faultString' => e.message }, nil]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
invocation = invocations[0]
|
|
92
|
+
invocation.protocol.encode_multicall_response(responses, invocation.protocol_options)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def web_service_invocation(request, level = 0)
|
|
96
|
+
public_method_name = request.method_name
|
|
97
|
+
invocation = Invocation.new
|
|
98
|
+
invocation.protocol = request.protocol
|
|
99
|
+
invocation.protocol_options = request.protocol_options
|
|
100
|
+
invocation.service_name = request.service_name
|
|
101
|
+
if web_service_dispatching_mode == :layered
|
|
102
|
+
case invocation.protocol
|
|
103
|
+
when Protocol::Soap::SoapProtocol
|
|
104
|
+
soap_action = request.protocol_options[:soap_action]
|
|
105
|
+
if soap_action && soap_action =~ /^\/\w+\/(\w+)\//
|
|
106
|
+
invocation.service_name = $1
|
|
107
|
+
end
|
|
108
|
+
when Protocol::XmlRpc::XmlRpcProtocol
|
|
109
|
+
if request.method_name =~ /^([^\.]+)\.(.*)$/
|
|
110
|
+
public_method_name = $2
|
|
111
|
+
invocation.service_name = $1
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
if invocation.protocol.is_a? Protocol::XmlRpc::XmlRpcProtocol
|
|
116
|
+
if public_method_name == 'multicall' && invocation.service_name == 'system'
|
|
117
|
+
if level > 0
|
|
118
|
+
raise(DispatcherError, "Recursive system.multicall invocations not allowed")
|
|
119
|
+
end
|
|
120
|
+
multicall = request.method_params.dup
|
|
121
|
+
unless multicall.is_a?(Array) && multicall[0].is_a?(Array)
|
|
122
|
+
raise(DispatcherError, "Malformed multicall (expected array of Hash elements)")
|
|
123
|
+
end
|
|
124
|
+
multicall = multicall[0]
|
|
125
|
+
return multicall.map do |item|
|
|
126
|
+
raise(DispatcherError, "Multicall elements must be Hash") unless item.is_a?(Hash)
|
|
127
|
+
raise(DispatcherError, "Multicall elements must contain a 'methodName' key") unless item.has_key?('methodName')
|
|
128
|
+
method_name = item['methodName']
|
|
129
|
+
params = item.has_key?('params') ? item['params'] : []
|
|
130
|
+
multicall_request = request.dup
|
|
131
|
+
multicall_request.method_name = method_name
|
|
132
|
+
multicall_request.method_params = params
|
|
133
|
+
begin
|
|
134
|
+
web_service_invocation(multicall_request, level + 1)
|
|
135
|
+
rescue Exception => e
|
|
136
|
+
{'faultCode' => 4, 'faultMessage' => e.message}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
case web_service_dispatching_mode
|
|
142
|
+
when :direct
|
|
143
|
+
invocation.api = self.class.web_service_api
|
|
144
|
+
invocation.service = self
|
|
145
|
+
when :delegated, :layered
|
|
146
|
+
invocation.service = web_service_object(invocation.service_name)
|
|
147
|
+
invocation.api = invocation.service.class.web_service_api
|
|
148
|
+
end
|
|
149
|
+
if invocation.api.nil?
|
|
150
|
+
raise(DispatcherError, "no API attached to #{invocation.service.class}")
|
|
151
|
+
end
|
|
152
|
+
invocation.protocol.register_api(invocation.api)
|
|
153
|
+
request.api = invocation.api
|
|
154
|
+
if invocation.api.has_public_api_method?(public_method_name)
|
|
155
|
+
invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
|
|
156
|
+
else
|
|
157
|
+
if invocation.api.default_api_method.nil?
|
|
158
|
+
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
|
|
159
|
+
else
|
|
160
|
+
invocation.api_method = invocation.api.default_api_method_instance
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
if invocation.service.nil?
|
|
164
|
+
raise(DispatcherError, "no service available for service name #{invocation.service_name}")
|
|
165
|
+
end
|
|
166
|
+
unless invocation.service.respond_to?(invocation.api_method.name)
|
|
167
|
+
raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
|
|
168
|
+
end
|
|
169
|
+
request.api_method = invocation.api_method
|
|
170
|
+
begin
|
|
171
|
+
invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
|
|
172
|
+
rescue
|
|
173
|
+
logger.warn "Casting of method parameters failed" unless logger.nil?
|
|
174
|
+
invocation.method_ordered_params = request.method_params
|
|
175
|
+
end
|
|
176
|
+
request.method_params = invocation.method_ordered_params
|
|
177
|
+
invocation.method_named_params = {}
|
|
178
|
+
invocation.api_method.param_names.inject(0) do |m, n|
|
|
179
|
+
invocation.method_named_params[n] = invocation.method_ordered_params[m]
|
|
180
|
+
m + 1
|
|
181
|
+
end
|
|
182
|
+
invocation
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def web_service_create_response(protocol, protocol_options, api, api_method, return_value)
|
|
186
|
+
if api.has_api_method?(api_method.name)
|
|
187
|
+
return_type = api_method.returns ? api_method.returns[0] : nil
|
|
188
|
+
return_value = api_method.cast_returns(return_value)
|
|
189
|
+
else
|
|
190
|
+
return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
|
|
191
|
+
end
|
|
192
|
+
protocol.encode_response(api_method.public_name + 'Response', return_value, return_type, protocol_options)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class Invocation # :nodoc:
|
|
196
|
+
attr_accessor :protocol
|
|
197
|
+
attr_accessor :protocol_options
|
|
198
|
+
attr_accessor :service_name
|
|
199
|
+
attr_accessor :api
|
|
200
|
+
attr_accessor :api_method
|
|
201
|
+
attr_accessor :method_ordered_params
|
|
202
|
+
attr_accessor :method_named_params
|
|
203
|
+
attr_accessor :service
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
require 'benchmark'
|
|
2
|
+
require 'builder/xmlmarkup'
|
|
3
|
+
|
|
4
|
+
module ActionWebService # :nodoc:
|
|
5
|
+
module Dispatcher # :nodoc:
|
|
6
|
+
module ActionController # :nodoc:
|
|
7
|
+
def self.included(base) # :nodoc:
|
|
8
|
+
class << base
|
|
9
|
+
include ClassMethods
|
|
10
|
+
alias_method_chain :inherited, :action_controller
|
|
11
|
+
end
|
|
12
|
+
base.class_eval do
|
|
13
|
+
alias_method :web_service_direct_invoke_without_controller, :web_service_direct_invoke
|
|
14
|
+
end
|
|
15
|
+
base.add_web_service_api_callback do |klass, api|
|
|
16
|
+
if klass.web_service_dispatching_mode == :direct
|
|
17
|
+
klass.class_eval 'def api; dispatch_web_service_request; end'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
base.add_web_service_definition_callback do |klass, name, info|
|
|
21
|
+
if klass.web_service_dispatching_mode == :delegated
|
|
22
|
+
klass.class_eval "def #{name}; dispatch_web_service_request; end"
|
|
23
|
+
elsif klass.web_service_dispatching_mode == :layered
|
|
24
|
+
klass.class_eval 'def api; dispatch_web_service_request; end'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
base.send(:include, ActionWebService::Dispatcher::ActionController::InstanceMethods)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
module ClassMethods # :nodoc:
|
|
31
|
+
def inherited_with_action_controller(child)
|
|
32
|
+
inherited_without_action_controller(child)
|
|
33
|
+
child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlAction)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module InstanceMethods # :nodoc:
|
|
38
|
+
private
|
|
39
|
+
def dispatch_web_service_request
|
|
40
|
+
method = request.method.to_s.upcase
|
|
41
|
+
allowed_methods = self.class.web_service_api ? (self.class.web_service_api.allowed_http_methods || []) : [ :post ]
|
|
42
|
+
allowed_methods = allowed_methods.map{|m| m.to_s.upcase }
|
|
43
|
+
if !allowed_methods.include?(method)
|
|
44
|
+
render :text => "#{method} not supported", :status=>500
|
|
45
|
+
return
|
|
46
|
+
end
|
|
47
|
+
exception = nil
|
|
48
|
+
begin
|
|
49
|
+
ws_request = discover_web_service_request(request)
|
|
50
|
+
rescue Exception => e
|
|
51
|
+
exception = e
|
|
52
|
+
end
|
|
53
|
+
if ws_request
|
|
54
|
+
ws_response = nil
|
|
55
|
+
exception = nil
|
|
56
|
+
bm = Benchmark.measure do
|
|
57
|
+
begin
|
|
58
|
+
ws_response = invoke_web_service_request(ws_request)
|
|
59
|
+
rescue Exception => e
|
|
60
|
+
exception = e
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
log_request(ws_request, request.raw_post)
|
|
64
|
+
if exception
|
|
65
|
+
log_error(exception) unless logger.nil?
|
|
66
|
+
send_web_service_error_response(ws_request, exception)
|
|
67
|
+
else
|
|
68
|
+
send_web_service_response(ws_response, bm.real)
|
|
69
|
+
end
|
|
70
|
+
else
|
|
71
|
+
exception ||= DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
|
|
72
|
+
log_error(exception) unless logger.nil?
|
|
73
|
+
send_web_service_error_response(ws_request, exception)
|
|
74
|
+
end
|
|
75
|
+
rescue Exception => e
|
|
76
|
+
log_error(e) unless logger.nil?
|
|
77
|
+
send_web_service_error_response(ws_request, e)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def send_web_service_response(ws_response, elapsed=nil)
|
|
81
|
+
log_response(ws_response, elapsed)
|
|
82
|
+
options = { :type => ws_response.content_type, :disposition => 'inline' }
|
|
83
|
+
send_data(ws_response.body, options)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def send_web_service_error_response(ws_request, exception)
|
|
87
|
+
if ws_request
|
|
88
|
+
unless self.class.web_service_exception_reporting
|
|
89
|
+
exception = DispatcherError.new("Internal server error (exception raised)")
|
|
90
|
+
end
|
|
91
|
+
api_method = ws_request.api_method
|
|
92
|
+
public_method_name = api_method ? api_method.public_name : ws_request.method_name
|
|
93
|
+
return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
|
|
94
|
+
ws_response = ws_request.protocol.encode_response(public_method_name + 'Response', exception, return_type, ws_request.protocol_options)
|
|
95
|
+
send_web_service_response(ws_response)
|
|
96
|
+
else
|
|
97
|
+
if self.class.web_service_exception_reporting
|
|
98
|
+
message = exception.message
|
|
99
|
+
backtrace = "\nBacktrace:\n#{exception.backtrace.join("\n")}"
|
|
100
|
+
else
|
|
101
|
+
message = "Exception raised"
|
|
102
|
+
backtrace = ""
|
|
103
|
+
end
|
|
104
|
+
render :status => 500, :text => "Internal protocol error: #{message}#{backtrace}"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def web_service_direct_invoke(invocation)
|
|
109
|
+
invocation.method_named_params.each do |name, value|
|
|
110
|
+
params[name] = value
|
|
111
|
+
end
|
|
112
|
+
web_service_direct_invoke_without_controller(invocation)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def log_request(ws_request, body)
|
|
116
|
+
unless logger.nil?
|
|
117
|
+
name = ws_request.method_name
|
|
118
|
+
api_method = ws_request.api_method
|
|
119
|
+
params = ws_request.method_params
|
|
120
|
+
if api_method && api_method.expects
|
|
121
|
+
params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
|
|
122
|
+
else
|
|
123
|
+
params = params.map{ |param| param.inspect }
|
|
124
|
+
end
|
|
125
|
+
service = ws_request.service_name
|
|
126
|
+
logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
|
|
127
|
+
logger.debug(indent(body))
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def log_response(ws_response, elapsed=nil)
|
|
132
|
+
unless logger.nil?
|
|
133
|
+
elapsed = (elapsed ? " (%f):" % elapsed : ":")
|
|
134
|
+
logger.debug("\nWeb Service Response" + elapsed + " => #{ws_response.return_value.inspect}")
|
|
135
|
+
logger.debug(indent(ws_response.body))
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def indent(body)
|
|
140
|
+
body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
module WsdlAction # :nodoc:
|
|
145
|
+
XsdNs = 'http://www.w3.org/2001/XMLSchema'
|
|
146
|
+
WsdlNs = 'http://schemas.xmlsoap.org/wsdl/'
|
|
147
|
+
SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/'
|
|
148
|
+
SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/'
|
|
149
|
+
SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http'
|
|
150
|
+
|
|
151
|
+
def wsdl
|
|
152
|
+
case request.method
|
|
153
|
+
when :get
|
|
154
|
+
begin
|
|
155
|
+
options = { :type => 'text/xml', :disposition => 'inline' }
|
|
156
|
+
send_data(to_wsdl, options)
|
|
157
|
+
rescue Exception => e
|
|
158
|
+
log_error(e) unless logger.nil?
|
|
159
|
+
end
|
|
160
|
+
when :post
|
|
161
|
+
render :status => 500, :text => 'POST not supported'
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
def base_uri
|
|
167
|
+
host = request.host_with_port
|
|
168
|
+
relative_url_root = ::ActionController::Base.relative_url_root
|
|
169
|
+
scheme = request.ssl? ? 'https' : 'http'
|
|
170
|
+
'%s://%s%s/%s/' % [scheme, host, relative_url_root, self.class.controller_path]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def to_wsdl
|
|
174
|
+
xml = ''
|
|
175
|
+
dispatching_mode = web_service_dispatching_mode
|
|
176
|
+
global_service_name = wsdl_service_name
|
|
177
|
+
namespace = wsdl_namespace || 'urn:ActionWebService'
|
|
178
|
+
soap_action_base = "/#{controller_name}"
|
|
179
|
+
|
|
180
|
+
marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
|
|
181
|
+
apis = {}
|
|
182
|
+
case dispatching_mode
|
|
183
|
+
when :direct
|
|
184
|
+
api = self.class.web_service_api
|
|
185
|
+
web_service_name = controller_class_name.sub(/Controller$/, '').underscore
|
|
186
|
+
apis[web_service_name] = [api, register_api(api, marshaler)]
|
|
187
|
+
when :delegated, :layered
|
|
188
|
+
self.class.web_services.each do |web_service_name, info|
|
|
189
|
+
service = web_service_object(web_service_name)
|
|
190
|
+
api = service.class.web_service_api
|
|
191
|
+
apis[web_service_name] = [api, register_api(api, marshaler)]
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
custom_types = []
|
|
195
|
+
apis.values.each do |api, bindings|
|
|
196
|
+
bindings.each do |b|
|
|
197
|
+
custom_types << b unless custom_types.include?(b)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
xm = Builder::XmlMarkup.new(:target => xml, :indent => 2)
|
|
202
|
+
xm.instruct!
|
|
203
|
+
xm.definitions('name' => wsdl_service_name,
|
|
204
|
+
'targetNamespace' => namespace,
|
|
205
|
+
'xmlns:typens' => namespace,
|
|
206
|
+
'xmlns:xsd' => XsdNs,
|
|
207
|
+
'xmlns:soap' => SoapNs,
|
|
208
|
+
'xmlns:soapenc' => SoapEncodingNs,
|
|
209
|
+
'xmlns:wsdl' => WsdlNs,
|
|
210
|
+
'xmlns' => WsdlNs) do
|
|
211
|
+
# Generate XSD
|
|
212
|
+
if custom_types.size > 0
|
|
213
|
+
xm.types do
|
|
214
|
+
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
|
|
215
|
+
simple_types, array_types, complex_types = [], [], []
|
|
216
|
+
custom_types.each do |binding|
|
|
217
|
+
case
|
|
218
|
+
when binding.type.simple? : simple_types.push binding
|
|
219
|
+
when binding.type.array? : array_types.push binding
|
|
220
|
+
when binding.type.structured? : complex_types.push binding
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
simple_types.each do |binding|
|
|
224
|
+
# TODO Should be able to determine if it will use camelize option
|
|
225
|
+
xm.xsd(:simpleType, 'name' => binding.type_name) do
|
|
226
|
+
xm.xsd(:restriction, 'base' => "xsd:#{binding.type.base}") do
|
|
227
|
+
binding.type.restrictions do |name, value|
|
|
228
|
+
xm.xsd(name.to_sym, 'value' => value)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
array_types.each do |binding|
|
|
234
|
+
# TODO Should be able to determine if it will use camelize option
|
|
235
|
+
xm.xsd(:complexType, 'name' => binding.type_name) do
|
|
236
|
+
xm.xsd(:complexContent) do
|
|
237
|
+
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
|
|
238
|
+
xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
|
|
239
|
+
'wsdl:arrayType' => binding.element_binding.qualified_type_name('typens') + '[]')
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
complex_types.each do |binding|
|
|
245
|
+
# TODO Should be able to determine if it will use camelize option
|
|
246
|
+
xm.xsd(:complexType, 'name' => binding.type_name) do
|
|
247
|
+
xm.xsd(:all) do
|
|
248
|
+
binding.type.each_member do |name, type|
|
|
249
|
+
b = marshaler.register_type(type)
|
|
250
|
+
xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# APIs
|
|
260
|
+
apis.each do |api_name, values|
|
|
261
|
+
api = values[0]
|
|
262
|
+
api.api_methods.each do |name, method|
|
|
263
|
+
gen = lambda do |msg_name, direction|
|
|
264
|
+
xm.message('name' => message_name_for(api_name, msg_name)) do
|
|
265
|
+
sym = nil
|
|
266
|
+
if direction == :out
|
|
267
|
+
returns = method.returns
|
|
268
|
+
if returns
|
|
269
|
+
binding = marshaler.register_type(returns[0])
|
|
270
|
+
xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
|
|
271
|
+
end
|
|
272
|
+
else
|
|
273
|
+
expects = method.expects
|
|
274
|
+
expects.each do |type|
|
|
275
|
+
binding = marshaler.register_type(type)
|
|
276
|
+
xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
|
|
277
|
+
end if expects
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
public_name = method.public_name
|
|
282
|
+
gen.call(public_name, :in)
|
|
283
|
+
gen.call("#{public_name}Response", :out)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Port
|
|
287
|
+
port_name = port_name_for(global_service_name, api_name)
|
|
288
|
+
xm.portType('name' => port_name) do
|
|
289
|
+
api.api_methods.each do |name, method|
|
|
290
|
+
xm.operation('name' => method.public_name) do
|
|
291
|
+
xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
|
|
292
|
+
xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Bind it
|
|
298
|
+
binding_name = binding_name_for(global_service_name, api_name)
|
|
299
|
+
xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
|
|
300
|
+
xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
|
|
301
|
+
api.api_methods.each do |name, method|
|
|
302
|
+
xm.operation('name' => method.public_name) do
|
|
303
|
+
case web_service_dispatching_mode
|
|
304
|
+
when :direct
|
|
305
|
+
soap_action = soap_action_base + "/api/" + method.public_name
|
|
306
|
+
when :delegated, :layered
|
|
307
|
+
soap_action = soap_action_base \
|
|
308
|
+
+ "/" + api_name.to_s \
|
|
309
|
+
+ "/" + method.public_name
|
|
310
|
+
end
|
|
311
|
+
xm.soap(:operation, 'soapAction' => soap_action)
|
|
312
|
+
xm.input do
|
|
313
|
+
xm.soap(:body,
|
|
314
|
+
'use' => 'encoded',
|
|
315
|
+
'namespace' => namespace,
|
|
316
|
+
'encodingStyle' => SoapEncodingNs)
|
|
317
|
+
end
|
|
318
|
+
xm.output do
|
|
319
|
+
xm.soap(:body,
|
|
320
|
+
'use' => 'encoded',
|
|
321
|
+
'namespace' => namespace,
|
|
322
|
+
'encodingStyle' => SoapEncodingNs)
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Define it
|
|
330
|
+
xm.service('name' => "#{global_service_name}Service") do
|
|
331
|
+
apis.each do |api_name, values|
|
|
332
|
+
port_name = port_name_for(global_service_name, api_name)
|
|
333
|
+
binding_name = binding_name_for(global_service_name, api_name)
|
|
334
|
+
case web_service_dispatching_mode
|
|
335
|
+
when :direct, :layered
|
|
336
|
+
binding_target = 'api'
|
|
337
|
+
when :delegated
|
|
338
|
+
binding_target = api_name.to_s
|
|
339
|
+
end
|
|
340
|
+
xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
|
|
341
|
+
xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def port_name_for(global_service, service)
|
|
349
|
+
"#{global_service}#{service.to_s.camelize}Port"
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def binding_name_for(global_service, service)
|
|
353
|
+
"#{global_service}#{service.to_s.camelize}Binding"
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def message_name_for(api_name, message_name)
|
|
357
|
+
mode = web_service_dispatching_mode
|
|
358
|
+
if mode == :layered || mode == :delegated
|
|
359
|
+
api_name.to_s + '-' + message_name
|
|
360
|
+
else
|
|
361
|
+
message_name
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def register_api(api, marshaler)
|
|
366
|
+
bindings = {}
|
|
367
|
+
traverse_custom_types(api, marshaler, bindings) do |binding|
|
|
368
|
+
bindings[binding] = nil unless bindings.has_key?(binding)
|
|
369
|
+
element_binding = binding.element_binding
|
|
370
|
+
bindings[element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
|
|
371
|
+
end
|
|
372
|
+
bindings.keys
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def traverse_custom_types(api, marshaler, bindings, &block)
|
|
376
|
+
api.api_methods.each do |name, method|
|
|
377
|
+
expects, returns = method.expects, method.returns
|
|
378
|
+
expects.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if expects
|
|
379
|
+
returns.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if returns
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def traverse_type(marshaler, type, bindings, &block)
|
|
384
|
+
binding = marshaler.register_type(type)
|
|
385
|
+
return if bindings.has_key?(binding)
|
|
386
|
+
bindings[binding] = nil
|
|
387
|
+
yield binding
|
|
388
|
+
if type.array?
|
|
389
|
+
yield marshaler.register_type(type.element_type)
|
|
390
|
+
type = type.element_type
|
|
391
|
+
end
|
|
392
|
+
type.each_member{ |name, type| traverse_type(marshaler, type, bindings, &block) } if type.structured?
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
end
|