rubyjedi-actionwebservice 2.3.5.20100615120735
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 +335 -0
- data/MIT-LICENSE +21 -0
- data/README +381 -0
- data/Rakefile +180 -0
- data/TODO +32 -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/acts_as_web_service.rb +24 -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 +151 -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/client.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/container.rb +3 -0
- data/lib/action_web_service/dispatcher/abstract.rb +208 -0
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +396 -0
- data/lib/action_web_service/dispatcher.rb +2 -0
- data/lib/action_web_service/invocation.rb +202 -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/marshaler.rb +242 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +176 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +123 -0
- data/lib/action_web_service/protocol.rb +4 -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 +16 -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 +261 -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/action_web_service.rb +60 -0
- data/lib/actionwebservice.rb +1 -0
- data/setup.rb +1379 -0
- data/test/abstract_client.rb +184 -0
- data/test/abstract_dispatcher.rb +549 -0
- data/test/abstract_unit.rb +43 -0
- data/test/actionwebservice_unittest.db +0 -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 +95 -0
- data/test/client_soap_test.rb +156 -0
- data/test/client_xmlrpc_test.rb +154 -0
- data/test/container_test.rb +75 -0
- data/test/debug.log +12305 -0
- data/test/dispatcher_action_controller_soap_test.rb +139 -0
- data/test/dispatcher_action_controller_xmlrpc_test.rb +59 -0
- data/test/fixtures/db_definitions/mysql.sql +8 -0
- data/test/fixtures/db_definitions/sqlite3.sql +8 -0
- data/test/fixtures/users.yml +12 -0
- data/test/gencov +3 -0
- data/test/invocation_test.rb +186 -0
- data/test/run +6 -0
- data/test/scaffolded_controller_test.rb +147 -0
- data/test/struct_test.rb +84 -0
- data/test/test_invoke_test.rb +113 -0
- metadata +182 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
module ActionWebService # :nodoc:
|
2
|
+
module Protocol # :nodoc:
|
3
|
+
class ProtocolError < ActionWebServiceError # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
class AbstractProtocol # :nodoc:
|
7
|
+
def setup(controller)
|
8
|
+
end
|
9
|
+
|
10
|
+
def decode_action_pack_request(action_pack_request)
|
11
|
+
end
|
12
|
+
|
13
|
+
def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
|
14
|
+
klass = options[:request_class] || SimpleActionPackRequest
|
15
|
+
request = klass.new({})
|
16
|
+
request.request_parameters['action'] = service_name.to_s
|
17
|
+
request.env['RAW_POST_DATA'] = raw_body
|
18
|
+
request.env['REQUEST_METHOD'] = 'POST'
|
19
|
+
request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
|
20
|
+
request
|
21
|
+
end
|
22
|
+
|
23
|
+
def decode_request(raw_request, service_name, protocol_options={})
|
24
|
+
end
|
25
|
+
|
26
|
+
def encode_request(method_name, params, param_types)
|
27
|
+
end
|
28
|
+
|
29
|
+
def decode_response(raw_response)
|
30
|
+
end
|
31
|
+
|
32
|
+
def encode_response(method_name, return_value, return_type, protocol_options={})
|
33
|
+
end
|
34
|
+
|
35
|
+
def protocol_client(api, protocol_name, endpoint_uri, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def register_api(api)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Request # :nodoc:
|
43
|
+
attr :protocol
|
44
|
+
attr_accessor :method_name
|
45
|
+
attr_accessor :method_params
|
46
|
+
attr :service_name
|
47
|
+
attr_accessor :api
|
48
|
+
attr_accessor :api_method
|
49
|
+
attr :protocol_options
|
50
|
+
|
51
|
+
def initialize(protocol, method_name, method_params, service_name, api=nil, api_method=nil, protocol_options=nil)
|
52
|
+
@protocol = protocol
|
53
|
+
@method_name = method_name
|
54
|
+
@method_params = method_params
|
55
|
+
@service_name = service_name
|
56
|
+
@api = api
|
57
|
+
@api_method = api_method
|
58
|
+
@protocol_options = protocol_options || {}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Response # :nodoc:
|
63
|
+
attr :body
|
64
|
+
attr :content_type
|
65
|
+
attr :return_value
|
66
|
+
|
67
|
+
def initialize(body, content_type, return_value)
|
68
|
+
@body = body
|
69
|
+
@content_type = content_type
|
70
|
+
@return_value = return_value
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class SimpleActionPackRequest < ActionController::Request # :nodoc:
|
75
|
+
def initialize(env = {})
|
76
|
+
@env = env
|
77
|
+
@qparams = {}
|
78
|
+
@rparams = {}
|
79
|
+
@cookies = {}
|
80
|
+
reset_session
|
81
|
+
end
|
82
|
+
|
83
|
+
def query_parameters
|
84
|
+
@qparams
|
85
|
+
end
|
86
|
+
|
87
|
+
def request_parameters
|
88
|
+
@rparams
|
89
|
+
end
|
90
|
+
|
91
|
+
def env
|
92
|
+
@env
|
93
|
+
end
|
94
|
+
|
95
|
+
def host
|
96
|
+
''
|
97
|
+
end
|
98
|
+
|
99
|
+
def cookies
|
100
|
+
@cookies
|
101
|
+
end
|
102
|
+
|
103
|
+
def session
|
104
|
+
@session
|
105
|
+
end
|
106
|
+
|
107
|
+
def reset_session
|
108
|
+
@session = {}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActionWebService # :nodoc:
|
2
|
+
module Protocol # :nodoc:
|
3
|
+
module Discovery # :nodoc:
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.send(:include, ActionWebService::Protocol::Discovery::InstanceMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods # :nodoc:
|
10
|
+
def register_protocol(klass)
|
11
|
+
write_inheritable_array("web_service_protocols", [klass])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module InstanceMethods # :nodoc:
|
16
|
+
private
|
17
|
+
def discover_web_service_request(action_pack_request)
|
18
|
+
(self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
|
19
|
+
protocol = protocol.create(self)
|
20
|
+
request = protocol.decode_action_pack_request(action_pack_request)
|
21
|
+
return request unless request.nil?
|
22
|
+
end
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_web_service_client(api, protocol_name, endpoint_uri, options)
|
27
|
+
(self.class.read_inheritable_attribute("web_service_protocols") || []).each do |protocol|
|
28
|
+
protocol = protocol.create(self)
|
29
|
+
client = protocol.protocol_client(api, protocol_name, endpoint_uri, options)
|
30
|
+
return client unless client.nil?
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'soap/mapping'
|
2
|
+
|
3
|
+
# hack to improve the .Net interoperability
|
4
|
+
class SOAP::Mapping::Object
|
5
|
+
def each_pair
|
6
|
+
self.__xmlele.each { |n, v| yield n.name, v.to_s }
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ActionWebService
|
11
|
+
module Protocol
|
12
|
+
module Soap
|
13
|
+
# Workaround for SOAP4R return values changing
|
14
|
+
class Registry < SOAP::Mapping::Registry
|
15
|
+
if SOAP::Version >= "1.5.4"
|
16
|
+
def find_mapped_soap_class(obj_class)
|
17
|
+
return @map.instance_eval { @obj2soap[obj_class][0] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_mapped_obj_class(soap_class)
|
21
|
+
return @map.instance_eval { @soap2obj[soap_class][0] }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class SoapMarshaler
|
27
|
+
attr :namespace
|
28
|
+
attr :registry
|
29
|
+
|
30
|
+
def initialize(namespace=nil)
|
31
|
+
@namespace = namespace || 'urn:ActionWebService'
|
32
|
+
@registry = Registry.new
|
33
|
+
@type2binding = {}
|
34
|
+
register_static_factories
|
35
|
+
end
|
36
|
+
|
37
|
+
def soap_to_ruby(obj)
|
38
|
+
SOAP::Mapping.soap2obj(obj, @registry)
|
39
|
+
end
|
40
|
+
|
41
|
+
def ruby_to_soap(obj)
|
42
|
+
soap = SOAP::Mapping.obj2soap(obj, @registry)
|
43
|
+
soap.elename = XSD::QName.new if SOAP::Version >= "1.5.5" && soap.elename == XSD::QName::EMPTY
|
44
|
+
soap
|
45
|
+
end
|
46
|
+
|
47
|
+
def register_type(type)
|
48
|
+
return @type2binding[type] if @type2binding.has_key?(type)
|
49
|
+
|
50
|
+
if type.array?
|
51
|
+
array_mapping = @registry.find_mapped_soap_class(Array)
|
52
|
+
qname = XSD::QName.new(@namespace, soap_type_name(type.element_type.type_class.name) + 'Array')
|
53
|
+
element_type_binding = register_type(type.element_type)
|
54
|
+
@type2binding[type] = SoapBinding.new(self, qname, type, array_mapping, element_type_binding)
|
55
|
+
elsif (mapping = @registry.find_mapped_soap_class(type.type_class) rescue nil)
|
56
|
+
qname = mapping[2] ? mapping[2][:type] : nil
|
57
|
+
qname ||= soap_base_type_name(mapping[0])
|
58
|
+
@type2binding[type] = SoapBinding.new(self, qname, type, mapping)
|
59
|
+
else
|
60
|
+
qname = XSD::QName.new(@namespace, soap_type_name(type.type_class.name))
|
61
|
+
@registry.add(type.type_class,
|
62
|
+
SOAP::SOAPStruct,
|
63
|
+
typed_struct_factory(type.type_class),
|
64
|
+
{ :type => qname })
|
65
|
+
mapping = @registry.find_mapped_soap_class(type.type_class)
|
66
|
+
@type2binding[type] = SoapBinding.new(self, qname, type, mapping)
|
67
|
+
end
|
68
|
+
|
69
|
+
if type.structured?
|
70
|
+
type.each_member do |m_name, m_type|
|
71
|
+
register_type(m_type)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
@type2binding[type]
|
76
|
+
end
|
77
|
+
alias :lookup_type :register_type
|
78
|
+
|
79
|
+
def annotate_arrays(binding, value)
|
80
|
+
if value.nil?
|
81
|
+
return
|
82
|
+
elsif binding.type.array?
|
83
|
+
mark_typed_array(value, binding.element_binding.qname)
|
84
|
+
if binding.element_binding.type.custom?
|
85
|
+
value.each do |element|
|
86
|
+
annotate_arrays(binding.element_binding, element)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
elsif binding.type.structured?
|
90
|
+
binding.type.each_member do |name, type|
|
91
|
+
member_binding = register_type(type)
|
92
|
+
member_value = value.respond_to?('[]') ? value[name] : value.send(name)
|
93
|
+
annotate_arrays(member_binding, member_value) if type.custom?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def typed_struct_factory(type_class)
|
100
|
+
if Object.const_defined?('ActiveRecord')
|
101
|
+
if type_class.ancestors.include?(ActiveRecord::Base)
|
102
|
+
qname = XSD::QName.new(@namespace, soap_type_name(type_class.name))
|
103
|
+
type_class.instance_variable_set('@qname', qname)
|
104
|
+
return SoapActiveRecordStructFactory.new
|
105
|
+
end
|
106
|
+
end
|
107
|
+
SOAP::Mapping::Registry::TypedStructFactory
|
108
|
+
end
|
109
|
+
|
110
|
+
def mark_typed_array(array, qname)
|
111
|
+
(class << array; self; end).class_eval do
|
112
|
+
define_method(:arytype) do
|
113
|
+
qname
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def soap_base_type_name(type)
|
119
|
+
xsd_type = type.ancestors.find{ |c| c.const_defined? 'Type' }
|
120
|
+
xsd_type ? xsd_type.const_get('Type') : XSD::XSDAnySimpleType::Type
|
121
|
+
end
|
122
|
+
|
123
|
+
def soap_type_name(type_name)
|
124
|
+
type_name.gsub(/::/, '..')
|
125
|
+
end
|
126
|
+
|
127
|
+
def register_static_factories
|
128
|
+
@registry.add(ActionWebService::Base64, SOAP::SOAPBase64, SoapBase64Factory.new, nil)
|
129
|
+
mapping = @registry.find_mapped_soap_class(ActionWebService::Base64)
|
130
|
+
@type2binding[ActionWebService::Base64] =
|
131
|
+
SoapBinding.new(self, SOAP::SOAPBase64::Type, ActionWebService::Base64, mapping)
|
132
|
+
@registry.add(Array, SOAP::SOAPArray, SoapTypedArrayFactory.new, nil)
|
133
|
+
@registry.add(::BigDecimal, SOAP::SOAPDouble, SOAP::Mapping::Registry::BasetypeFactory, {:derived_class => true})
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class SoapBinding
|
138
|
+
attr :qname
|
139
|
+
attr :type
|
140
|
+
attr :mapping
|
141
|
+
attr :element_binding
|
142
|
+
|
143
|
+
def initialize(marshaler, qname, type, mapping, element_binding=nil)
|
144
|
+
@marshaler = marshaler
|
145
|
+
@qname = qname
|
146
|
+
@type = type
|
147
|
+
@mapping = mapping
|
148
|
+
@element_binding = element_binding
|
149
|
+
end
|
150
|
+
|
151
|
+
def type_name
|
152
|
+
@type.custom? ? @qname.name : nil
|
153
|
+
end
|
154
|
+
|
155
|
+
def qualified_type_name(ns=nil)
|
156
|
+
if @type.custom?
|
157
|
+
"#{ns ? ns : @qname.namespace}:#{@qname.name}"
|
158
|
+
else
|
159
|
+
ns = XSD::NS.new
|
160
|
+
ns.assign(XSD::Namespace, SOAP::XSDNamespaceTag)
|
161
|
+
ns.assign(SOAP::EncodingNamespace, "soapenc")
|
162
|
+
xsd_klass = mapping[0].ancestors.find{|c| c.const_defined?('Type')}
|
163
|
+
return ns.name(XSD::AnyTypeName) unless xsd_klass
|
164
|
+
ns.name(xsd_klass.const_get('Type'))
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def eql?(other)
|
169
|
+
@qname == other.qname
|
170
|
+
end
|
171
|
+
alias :== :eql?
|
172
|
+
|
173
|
+
def hash
|
174
|
+
@qname.hash
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
class SoapActiveRecordStructFactory < SOAP::Mapping::Factory
|
179
|
+
def obj2soap(soap_class, obj, info, map)
|
180
|
+
unless obj.is_a?(ActiveRecord::Base)
|
181
|
+
return nil
|
182
|
+
end
|
183
|
+
soap_obj = soap_class.new(obj.class.instance_variable_get('@qname'))
|
184
|
+
obj.class.columns.each do |column|
|
185
|
+
key = column.name.to_s
|
186
|
+
value = obj.send(key)
|
187
|
+
soap_obj[key] = SOAP::Mapping._obj2soap(value, map)
|
188
|
+
end
|
189
|
+
soap_obj
|
190
|
+
end
|
191
|
+
|
192
|
+
def soap2obj(obj_class, node, info, map)
|
193
|
+
unless node.type == obj_class.instance_variable_get('@qname')
|
194
|
+
return false
|
195
|
+
end
|
196
|
+
obj = obj_class.new
|
197
|
+
node.each do |key, value|
|
198
|
+
obj[key] = value.data
|
199
|
+
end
|
200
|
+
obj.instance_variable_set('@new_record', false)
|
201
|
+
return true, obj
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
class SoapTypedArrayFactory < SOAP::Mapping::Factory
|
206
|
+
def obj2soap(soap_class, obj, info, map)
|
207
|
+
unless obj.respond_to?(:arytype)
|
208
|
+
return nil
|
209
|
+
end
|
210
|
+
soap_obj = soap_class.new(SOAP::ValueArrayName, 1, obj.arytype)
|
211
|
+
mark_marshalled_obj(obj, soap_obj)
|
212
|
+
obj.each do |item|
|
213
|
+
child = SOAP::Mapping._obj2soap(item, map)
|
214
|
+
soap_obj.add(child)
|
215
|
+
end
|
216
|
+
soap_obj
|
217
|
+
end
|
218
|
+
|
219
|
+
def soap2obj(obj_class, node, info, map)
|
220
|
+
return false
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class SoapBase64Factory < SOAP::Mapping::Factory
|
225
|
+
def obj2soap(soap_class, obj, info, map)
|
226
|
+
unless obj.is_a?(ActionWebService::Base64)
|
227
|
+
return nil
|
228
|
+
end
|
229
|
+
return soap_class.new(obj)
|
230
|
+
end
|
231
|
+
|
232
|
+
def soap2obj(obj_class, node, info, map)
|
233
|
+
unless node.type == SOAP::SOAPBase64::Type
|
234
|
+
return false
|
235
|
+
end
|
236
|
+
return true, obj_class.new(node.string)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'action_web_service/protocol/soap_protocol/marshaler'
|
2
|
+
require 'soap/streamHandler'
|
3
|
+
require 'action_web_service/client/soap_client'
|
4
|
+
|
5
|
+
module ActionWebService # :nodoc:
|
6
|
+
module API # :nodoc:
|
7
|
+
class Base # :nodoc:
|
8
|
+
def self.soap_client(endpoint_uri, options={})
|
9
|
+
ActionWebService::Client::Soap.new self, endpoint_uri, options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Protocol # :nodoc:
|
15
|
+
module Soap # :nodoc:
|
16
|
+
def self.included(base)
|
17
|
+
base.register_protocol(SoapProtocol)
|
18
|
+
base.class_inheritable_option(:wsdl_service_name)
|
19
|
+
base.class_inheritable_option(:wsdl_namespace)
|
20
|
+
end
|
21
|
+
|
22
|
+
class SoapProtocol < AbstractProtocol # :nodoc:
|
23
|
+
AWSEncoding = 'UTF-8'
|
24
|
+
XSDEncoding = 'UTF8'
|
25
|
+
|
26
|
+
attr :marshaler
|
27
|
+
|
28
|
+
def initialize(namespace=nil)
|
29
|
+
namespace ||= 'urn:ActionWebService'
|
30
|
+
@marshaler = SoapMarshaler.new namespace
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.create(controller)
|
34
|
+
SoapProtocol.new(controller.wsdl_namespace)
|
35
|
+
end
|
36
|
+
|
37
|
+
def decode_action_pack_request(action_pack_request)
|
38
|
+
return nil unless soap_action = has_valid_soap_action?(action_pack_request)
|
39
|
+
service_name = action_pack_request.parameters['action']
|
40
|
+
input_encoding = parse_charset(action_pack_request.env['HTTP_CONTENT_TYPE'])
|
41
|
+
protocol_options = {
|
42
|
+
:soap_action => soap_action,
|
43
|
+
:charset => input_encoding
|
44
|
+
}
|
45
|
+
decode_request(action_pack_request.raw_post, service_name, protocol_options)
|
46
|
+
end
|
47
|
+
|
48
|
+
def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
|
49
|
+
request = super
|
50
|
+
request.env['HTTP_SOAPACTION'] = '/soap/%s/%s' % [service_name, public_method_name]
|
51
|
+
request
|
52
|
+
end
|
53
|
+
|
54
|
+
def decode_request(raw_request, service_name, protocol_options={})
|
55
|
+
envelope = SOAP::Processor.unmarshal(raw_request, :charset => protocol_options[:charset])
|
56
|
+
unless envelope
|
57
|
+
raise ProtocolError, "Failed to parse SOAP request message"
|
58
|
+
end
|
59
|
+
request = envelope.body.request
|
60
|
+
method_name = request.elename.name
|
61
|
+
params = request.collect{ |k, v| marshaler.soap_to_ruby(request[k]) }
|
62
|
+
Request.new(self, method_name, params, service_name, nil, nil, protocol_options)
|
63
|
+
end
|
64
|
+
|
65
|
+
def encode_request(method_name, params, param_types)
|
66
|
+
param_types.each{ |type| marshaler.register_type(type) } if param_types
|
67
|
+
qname = XSD::QName.new(marshaler.namespace, method_name)
|
68
|
+
param_def = []
|
69
|
+
if param_types
|
70
|
+
params = param_types.zip(params).map do |type, param|
|
71
|
+
param_def << ['in', type.name, marshaler.lookup_type(type).mapping]
|
72
|
+
[type.name, marshaler.ruby_to_soap(param)]
|
73
|
+
end
|
74
|
+
else
|
75
|
+
params = []
|
76
|
+
end
|
77
|
+
request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
|
78
|
+
request.set_param(params)
|
79
|
+
envelope = create_soap_envelope(request)
|
80
|
+
SOAP::Processor.marshal(envelope)
|
81
|
+
end
|
82
|
+
|
83
|
+
def decode_response(raw_response)
|
84
|
+
envelope = SOAP::Processor.unmarshal(raw_response)
|
85
|
+
unless envelope
|
86
|
+
raise ProtocolError, "Failed to parse SOAP request message"
|
87
|
+
end
|
88
|
+
method_name = envelope.body.request.elename.name
|
89
|
+
return_value = envelope.body.response
|
90
|
+
return_value = marshaler.soap_to_ruby(return_value) unless return_value.nil?
|
91
|
+
[method_name, return_value]
|
92
|
+
end
|
93
|
+
|
94
|
+
def encode_response(method_name, return_value, return_type, protocol_options={})
|
95
|
+
if return_type
|
96
|
+
return_binding = marshaler.register_type(return_type)
|
97
|
+
marshaler.annotate_arrays(return_binding, return_value)
|
98
|
+
end
|
99
|
+
qname = XSD::QName.new(marshaler.namespace, method_name)
|
100
|
+
if return_value.nil?
|
101
|
+
response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
|
102
|
+
else
|
103
|
+
if return_value.is_a?(Exception)
|
104
|
+
detail = SOAP::Mapping::SOAPException.new(return_value)
|
105
|
+
response = SOAP::SOAPFault.new(
|
106
|
+
SOAP::SOAPQName.new('%s:%s' % [SOAP::SOAPNamespaceTag, 'Server']),
|
107
|
+
SOAP::SOAPString.new(return_value.to_s),
|
108
|
+
SOAP::SOAPString.new(self.class.name),
|
109
|
+
marshaler.ruby_to_soap(detail))
|
110
|
+
else
|
111
|
+
if return_type
|
112
|
+
param_def = [['retval', 'return', marshaler.lookup_type(return_type).mapping]]
|
113
|
+
response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def)
|
114
|
+
response.retval = marshaler.ruby_to_soap(return_value)
|
115
|
+
else
|
116
|
+
response = SOAP::RPC::SOAPMethodResponse.new(qname, nil)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
envelope = create_soap_envelope(response)
|
121
|
+
|
122
|
+
# FIXME: This is not thread-safe, but StringFactory_ in SOAP4R only
|
123
|
+
# reads target encoding from the XSD::Charset.encoding variable.
|
124
|
+
# This is required to ensure $KCODE strings are converted
|
125
|
+
# correctly to UTF-8 for any values of $KCODE.
|
126
|
+
previous_encoding = XSD::Charset.encoding
|
127
|
+
XSD::Charset.encoding = XSDEncoding
|
128
|
+
response_body = SOAP::Processor.marshal(envelope, :charset => AWSEncoding)
|
129
|
+
XSD::Charset.encoding = previous_encoding
|
130
|
+
|
131
|
+
Response.new(response_body, "text/xml; charset=#{AWSEncoding}", return_value)
|
132
|
+
end
|
133
|
+
|
134
|
+
def protocol_client(api, protocol_name, endpoint_uri, options={})
|
135
|
+
return nil unless protocol_name == :soap
|
136
|
+
ActionWebService::Client::Soap.new(api, endpoint_uri, options)
|
137
|
+
end
|
138
|
+
|
139
|
+
def register_api(api)
|
140
|
+
api.api_methods.each do |name, method|
|
141
|
+
method.expects.each{ |type| marshaler.register_type(type) } if method.expects
|
142
|
+
method.returns.each{ |type| marshaler.register_type(type) } if method.returns
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
def has_valid_soap_action?(request)
|
148
|
+
return nil unless request.method == :post
|
149
|
+
soap_action = request.env['HTTP_SOAPACTION']
|
150
|
+
return nil unless soap_action
|
151
|
+
soap_action = soap_action.dup
|
152
|
+
soap_action.gsub!(/^"/, '')
|
153
|
+
soap_action.gsub!(/"$/, '')
|
154
|
+
soap_action.strip!
|
155
|
+
return nil if soap_action.empty?
|
156
|
+
soap_action
|
157
|
+
end
|
158
|
+
|
159
|
+
def create_soap_envelope(body)
|
160
|
+
header = SOAP::SOAPHeader.new
|
161
|
+
body = SOAP::SOAPBody.new(body)
|
162
|
+
SOAP::SOAPEnvelope.new(header, body)
|
163
|
+
end
|
164
|
+
|
165
|
+
def parse_charset(content_type)
|
166
|
+
return AWSEncoding if content_type.nil?
|
167
|
+
if /^text\/xml(?:\s*;\s*charset=([^"]+|"[^"]+"))$/i =~ content_type
|
168
|
+
$1
|
169
|
+
else
|
170
|
+
AWSEncoding
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'xmlrpc/marshal'
|
2
|
+
require 'action_web_service/client/xmlrpc_client'
|
3
|
+
|
4
|
+
module XMLRPC # :nodoc:
|
5
|
+
class FaultException # :nodoc:
|
6
|
+
alias :message :faultString
|
7
|
+
end
|
8
|
+
|
9
|
+
class Create
|
10
|
+
def wrong_type(value)
|
11
|
+
if BigDecimal === value
|
12
|
+
[true, value.to_f]
|
13
|
+
else
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ActionWebService # :nodoc:
|
21
|
+
module API # :nodoc:
|
22
|
+
class Base # :nodoc:
|
23
|
+
def self.xmlrpc_client(endpoint_uri, options={})
|
24
|
+
ActionWebService::Client::XmlRpc.new self, endpoint_uri, options
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Protocol # :nodoc:
|
30
|
+
module XmlRpc # :nodoc:
|
31
|
+
def self.included(base)
|
32
|
+
base.register_protocol(XmlRpcProtocol)
|
33
|
+
end
|
34
|
+
|
35
|
+
class XmlRpcProtocol < AbstractProtocol # :nodoc:
|
36
|
+
def self.create(controller)
|
37
|
+
XmlRpcProtocol.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def decode_action_pack_request(action_pack_request)
|
41
|
+
service_name = action_pack_request.parameters['action']
|
42
|
+
decode_request(action_pack_request.raw_post, service_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def decode_request(raw_request, service_name)
|
46
|
+
method_name, params = XMLRPC::Marshal.load_call(raw_request)
|
47
|
+
Request.new(self, method_name, params, service_name)
|
48
|
+
rescue
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def encode_request(method_name, params, param_types)
|
53
|
+
if param_types
|
54
|
+
params = params.dup
|
55
|
+
param_types.each_with_index{ |type, i| params[i] = value_to_xmlrpc_wire_format(params[i], type) }
|
56
|
+
end
|
57
|
+
XMLRPC::Marshal.dump_call(method_name, *params)
|
58
|
+
end
|
59
|
+
|
60
|
+
def decode_response(raw_response)
|
61
|
+
[nil, XMLRPC::Marshal.load_response(raw_response)]
|
62
|
+
end
|
63
|
+
|
64
|
+
def encode_response(method_name, return_value, return_type, protocol_options={})
|
65
|
+
if return_value && return_type
|
66
|
+
return_value = value_to_xmlrpc_wire_format(return_value, return_type)
|
67
|
+
end
|
68
|
+
return_value = false if return_value.nil?
|
69
|
+
raw_response = XMLRPC::Marshal.dump_response(return_value)
|
70
|
+
Response.new(raw_response, 'text/xml', return_value)
|
71
|
+
end
|
72
|
+
|
73
|
+
def encode_multicall_response(responses, protocol_options={})
|
74
|
+
result = responses.map do |return_value, return_type|
|
75
|
+
if return_value && return_type
|
76
|
+
return_value = value_to_xmlrpc_wire_format(return_value, return_type)
|
77
|
+
return_value = [return_value] unless return_value.nil?
|
78
|
+
end
|
79
|
+
return_value = false if return_value.nil?
|
80
|
+
return_value
|
81
|
+
end
|
82
|
+
raw_response = XMLRPC::Marshal.dump_response(result)
|
83
|
+
Response.new(raw_response, 'text/xml', result)
|
84
|
+
end
|
85
|
+
|
86
|
+
def protocol_client(api, protocol_name, endpoint_uri, options={})
|
87
|
+
return nil unless protocol_name == :xmlrpc
|
88
|
+
ActionWebService::Client::XmlRpc.new(api, endpoint_uri, options)
|
89
|
+
end
|
90
|
+
|
91
|
+
def value_to_xmlrpc_wire_format(value, value_type)
|
92
|
+
if value_type.array?
|
93
|
+
value.map{ |val| value_to_xmlrpc_wire_format(val, value_type.element_type) }
|
94
|
+
else
|
95
|
+
if value.is_a?(ActionWebService::Struct)
|
96
|
+
struct = {}
|
97
|
+
value.class.members.each do |name, type_options|
|
98
|
+
type, options = type_options
|
99
|
+
member_value = value[name]
|
100
|
+
next if member_value.nil?
|
101
|
+
struct[name.to_s] = value_to_xmlrpc_wire_format(member_value, type)
|
102
|
+
end
|
103
|
+
struct
|
104
|
+
elsif value.is_a?(ActiveRecord::Base)
|
105
|
+
struct = {}
|
106
|
+
value.attributes.each do |key, member_value|
|
107
|
+
next if member_value.nil?
|
108
|
+
struct[key.to_s] = member_value
|
109
|
+
end
|
110
|
+
struct
|
111
|
+
elsif value.is_a?(ActionWebService::Base64)
|
112
|
+
XMLRPC::Base64.new(value)
|
113
|
+
elsif value.is_a?(Exception) && !value.is_a?(XMLRPC::FaultException)
|
114
|
+
XMLRPC::FaultException.new(2, value.message)
|
115
|
+
else
|
116
|
+
value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|