wasabi 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad4024db6f2cc825dec1eb8223812bd8da45c884
4
+ data.tar.gz: af57d52095cb5daf9b530f9974b65b9d747cda11
5
+ SHA512:
6
+ metadata.gz: b61533673c5f483625c6bd884236c7592a03cfa0c6c8f5a47b18600cbc2e903ff5f83864fd85d53a42c3430de7f5a53907222c2bf0b19fc53338eb47fe625be6
7
+ data.tar.gz: 59212805b9b770b20a37e175e950f9df1a4ff973ce52d0ca4b8b88f41b94678025684591480251900a930d4236ade904120a4eb14cbf5965e1d1cd9376f1db16
@@ -1,3 +1,20 @@
1
+ ## 3.2.0 (2013-07-26)
2
+
3
+ * Feature: [#20](https://github.com/savonrb/wasabi/issues/20) Limited support for listing an
4
+ operation's parameters. Please note that if your WSDL defines imports, this method might
5
+ not return all types.
6
+
7
+ * Improvement: [#7](https://github.com/savonrb/wasabi/issues/7) Major speed improvements.
8
+
9
+ * Improvement: [#16](https://github.com/savonrb/wasabi/issues/16) Various improvements regarding
10
+ element order and type information.
11
+
12
+ * Fix: [#25](https://github.com/savonrb/wasabi/issues/25) Fixes a problem where Wasabi escaped
13
+ an already escaped endpoint URL.
14
+
15
+ * Fix: [#15](https://github.com/savonrb/wasabi/issues/15) Fixes a bug where the operation tag
16
+ name was not correctly extracted from the WSDL document.
17
+
1
18
  ## 3.1.0 (2013-04-21)
2
19
 
3
20
  * Feature: [#22](https://github.com/savonrb/wasabi/issues/22) added `Wasabi::Document#service_name`
@@ -15,22 +32,22 @@
15
32
 
16
33
  ## 2.5.1 (2012-08-22)
17
34
 
18
- * Fix: [#14](https://github.com/rubiii/wasabi/issues/14) fixes an issue where
35
+ * Fix: [#14](https://github.com/savonrb/wasabi/issues/14) fixes an issue where
19
36
  finding the correct SOAP input tag and namespace identifier fails when portTypes
20
37
  are imported, since imports are currently not supported.
21
38
 
22
- The bug was introduced in v2.2.0 by [583cf6](https://github.com/rubiii/wasabi/commit/583cf658f1953411a7a7a4c22923fa0a046c8d6d)
39
+ The bug was introduced in v2.2.0 by [583cf6](https://github.com/savonrb/wasabi/commit/583cf658f1953411a7a7a4c22923fa0a046c8d6d)
23
40
 
24
41
  * Refactoring: Removed `Object#blank?` core extension.
25
42
 
26
43
  ## 2.5.0 (2012-06-28)
27
44
 
28
- * Fix: [#10](https://github.com/rubiii/wasabi/issues/10) fixes an issue where
45
+ * Fix: [#10](https://github.com/savonrb/wasabi/issues/10) fixes an issue where
29
46
  Wasabi used the wrong operation name.
30
47
 
31
48
  ## 2.4.1 (2012-06-18)
32
49
 
33
- * Fix: [rubiii/savon#296](https://github.com/rubiii/savon/issues/296) fixes an issue where
50
+ * Fix: [savonrb/savon#296](https://github.com/savonrb/savon/issues/296) fixes an issue where
34
51
  the WSDL message element doesn't have part element.
35
52
 
36
53
  ## 2.4.0 (2012-06-08)
@@ -41,25 +58,25 @@
41
58
 
42
59
  ## 2.3.0 (2012-06-07)
43
60
 
44
- * Improvement: [#3](https://github.com/rubiii/wasabi/pull/3) adds object inheritance.
61
+ * Improvement: [#3](https://github.com/savonrb/wasabi/pull/3) adds object inheritance.
45
62
 
46
63
  ## 2.2.0 (2012-06-06)
47
64
 
48
- * Improvement: [#5](https://github.com/rubiii/wasabi/pull/5) - Get input from message
49
- element or portType input. See [rubiii/savon#277](https://github.com/rubiii/savon/pull/277)
65
+ * Improvement: [#5](https://github.com/savonrb/wasabi/pull/5) - Get input from message
66
+ element or portType input. See [savonrb/savon#277](https://github.com/savonrb/savon/pull/277)
50
67
  to get the full picture on how this all works together, and enables you to pass a single
51
68
  symbol into the `Savon::Client#request` method and get automatic namespace mapping, as well
52
69
  as the proper operation name -> input message mapping.
53
70
 
54
71
  ## 2.1.1 (2012-05-18)
55
72
 
56
- * Fix: [issue 7](https://github.com/rubiii/wasabi/issues/7) - Performance regression.
73
+ * Fix: [issue 7](https://github.com/savonrb/wasabi/issues/7) - Performance regression.
57
74
 
58
75
  ## 2.1.0 (2012-02-17)
59
76
 
60
77
  * Improvement: The value of elementFormDefault can now be manually specified/overwritten.
61
78
 
62
- * Improvement: [issue 2](https://github.com/rubiii/wasabi/issues/2) - Allow symbolic endpoints
79
+ * Improvement: [issue 2](https://github.com/savonrb/wasabi/issues/2) - Allow symbolic endpoints
63
80
  like "http://server:port".
64
81
 
65
82
  ## 2.0.0 (2011-07-07)
data/Gemfile CHANGED
@@ -1,2 +1,6 @@
1
- source "http://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
+
4
+ gem 'simplecov', :require => false
5
+ gem 'method_profiler', :require => false
6
+ gem 'coveralls', :require => false
data/README.md CHANGED
@@ -1,11 +1,14 @@
1
- Wasabi [![Build Status](https://secure.travis-ci.org/rubiii/wasabi.png)](http://travis-ci.org/rubiii/wasabi)
2
- =====
1
+ # Wasabi
3
2
 
4
3
  A simple WSDL parser.
5
4
 
5
+ [![Build Status](https://secure.travis-ci.org/savonrb/wasabi.png)](http://travis-ci.org/savonrb/wasabi)
6
+ [![Gem Version](https://badge.fury.io/rb/wasabi.png)](http://badge.fury.io/rb/wasabi)
7
+ [![Code Climate](https://codeclimate.com/github/savonrb/wasabi.png)](https://codeclimate.com/github/savonrb/wasabi)
8
+ [![Coverage Status](https://coveralls.io/repos/savonrb/wasabi/badge.png?branch=master)](https://coveralls.io/r/savonrb/wasabi)
6
9
 
7
- Installation
8
- ------------
10
+
11
+ ## Installation
9
12
 
10
13
  Wasabi is available through [Rubygems](http://rubygems.org/gems/wasabi) and can be installed via:
11
14
 
@@ -14,8 +17,7 @@ $ gem install wasabi
14
17
  ```
15
18
 
16
19
 
17
- Getting started
18
- ---------------
20
+ ## Getting started
19
21
 
20
22
  ``` ruby
21
23
  document = Wasabi.document File.read("some.wsdl")
data/Rakefile CHANGED
@@ -3,7 +3,5 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new
5
5
 
6
- task :release
7
-
8
6
  task :default => :spec
9
7
  task :test => :spec
@@ -83,13 +83,29 @@ module Wasabi
83
83
 
84
84
  attr_writer :service_name
85
85
 
86
+ # Returns a list of parameter names for a given +key+
87
+ def soap_action_parameters(key)
88
+ params = operation_input_parameters(key)
89
+ params.keys if params
90
+ end
91
+
92
+ # Returns a list of input parameters for a given +key+.
93
+ def operation_input_parameters(key)
94
+ parser.operations[key][:parameters] if operations[key]
95
+ end
96
+
86
97
  def type_namespaces
87
98
  @type_namespaces ||= begin
88
99
  namespaces = []
100
+
89
101
  parser.types.each do |type, info|
90
102
  namespaces << [[type], info[:namespace]]
91
- (info.keys - [:namespace]).each { |field| namespaces << [[type, field], info[:namespace]] }
103
+
104
+ element_keys(info).each do |field|
105
+ namespaces << [[type, field], info[:namespace]]
106
+ end
92
107
  end if document
108
+
93
109
  namespaces
94
110
  end
95
111
  end
@@ -97,13 +113,16 @@ module Wasabi
97
113
  def type_definitions
98
114
  @type_definitions ||= begin
99
115
  result = []
116
+
100
117
  parser.types.each do |type, info|
101
- (info.keys - [:namespace]).each do |field|
118
+ element_keys(info).each do |field|
102
119
  field_type = info[field][:type]
103
120
  tag, namespace = field_type.split(":").reverse
121
+
104
122
  result << [[type, field], tag] if user_defined(namespace)
105
123
  end
106
124
  end if document
125
+
107
126
  result
108
127
  end
109
128
  end
@@ -140,5 +159,8 @@ module Wasabi
140
159
  parser
141
160
  end
142
161
 
162
+ def element_keys(info)
163
+ info.keys - [:namespace, :order!, :base_type]
164
+ end
143
165
  end
144
166
  end
@@ -1,5 +1,4 @@
1
1
  require "uri"
2
- require "wasabi/xpath_helper"
3
2
  require "wasabi/core_ext/string"
4
3
 
5
4
  module Wasabi
@@ -8,7 +7,11 @@ module Wasabi
8
7
  #
9
8
  # Parses WSDL documents and remembers their important parts.
10
9
  class Parser
11
- include XPathHelper
10
+
11
+ XSD = "http://www.w3.org/2001/XMLSchema"
12
+ WSDL = "http://schemas.xmlsoap.org/wsdl/"
13
+ SOAP_1_1 = "http://schemas.xmlsoap.org/wsdl/soap/"
14
+ SOAP_1_2 = "http://schemas.xmlsoap.org/wsdl/soap12/"
12
15
 
13
16
  def initialize(document)
14
17
  self.document = document
@@ -51,16 +54,20 @@ module Wasabi
51
54
  parse_namespaces
52
55
  parse_endpoint
53
56
  parse_service_name
57
+ parse_messages
58
+ parse_port_types
59
+ parse_port_type_operations
54
60
  parse_operations
61
+ parse_operations_parameters
55
62
  parse_types
56
63
  parse_deferred_types
57
64
  end
58
65
 
59
66
  def parse_namespaces
60
- element_form_default = at_xpath("wsdl:definitions/wsdl:types/xs:schema/@elementFormDefault")
67
+ element_form_default = schemas.first && schemas.first['elementFormDefault']
61
68
  @element_form_default = element_form_default.to_s.to_sym if element_form_default
62
69
 
63
- namespace = at_xpath("wsdl:definitions/@targetNamespace")
70
+ namespace = document.root['targetNamespace']
64
71
  @namespace = namespace.to_s if namespace
65
72
 
66
73
  @namespaces = @document.namespaces.inject({}) do |memo, (key, value)|
@@ -70,38 +77,80 @@ module Wasabi
70
77
  end
71
78
 
72
79
  def parse_endpoint
73
- endpoint = at_xpath("wsdl:definitions/wsdl:service//soap11:address/@location")
74
- endpoint ||= at_xpath("wsdl:definitions/wsdl:service//soap12:address/@location")
75
-
76
- begin
77
- @endpoint = URI(URI.escape(endpoint.to_s)) if endpoint
78
- rescue URI::InvalidURIError
79
- @endpoint = nil
80
+ if service_node = service
81
+ endpoint = service_node.at_xpath(".//soap11:address/@location", 'soap11' => SOAP_1_1)
82
+ endpoint ||= service_node.at_xpath(service_node, ".//soap12:address/@location", 'soap12' => SOAP_1_2)
80
83
  end
84
+
85
+ @endpoint = parse_url(endpoint) if endpoint
86
+ end
87
+
88
+ def parse_url(url)
89
+ unescaped_url = URI.unescape(url.to_s)
90
+ escaped_url = URI.escape(unescaped_url)
91
+ URI.parse(escaped_url)
92
+ rescue URI::InvalidURIError
81
93
  end
82
94
 
83
95
  def parse_service_name
84
- service_name = at_xpath("wsdl:definitions/@name")
96
+ service_name = document.root['name']
85
97
  @service_name = service_name.to_s if service_name
86
98
  end
87
99
 
100
+ def parse_messages
101
+ messages = document.root.element_children.select { |node| node.name == 'message' }
102
+ @messages = Hash[messages.map { |node| [node['name'], node] }]
103
+ end
104
+
105
+ def parse_port_types
106
+ port_types = document.root.element_children.select { |node| node.name == 'portType' }
107
+ @port_types = Hash[port_types.map { |node| [node['name'], node] }]
108
+ end
109
+
110
+ def parse_port_type_operations
111
+ @port_type_operations = {}
112
+
113
+ @port_types.each do |port_type_name, port_type|
114
+ operations = port_type.element_children.select { |node| node.name == 'operation' }
115
+ @port_type_operations[port_type_name] = Hash[operations.map { |node| [node['name'], node] }]
116
+ end
117
+ end
118
+
119
+ def parse_operations_parameters
120
+ root_elements = document.xpath("wsdl:definitions/wsdl:types/*[local-name()='schema']/*[local-name()='element']", 'wsdl' => WSDL).each do |element|
121
+ name = element.attribute("name").to_s.snakecase.to_sym
122
+
123
+ if operation = @operations[name]
124
+ element.xpath("*[local-name() ='complexType']/*[local-name() ='sequence']/*[local-name() ='element']").each do |child_element|
125
+ attr_name = child_element.attribute("name").to_s
126
+ attr_type = (attr_type = child_element.attribute("type").to_s.split(":")).size > 1 ? attr_type[1] : attr_type[0]
127
+
128
+ operation[:parameters] ||= {}
129
+ operation[:parameters][attr_name.to_sym] = { :name => attr_name, :type => attr_type }
130
+ end
131
+ end
132
+ end
133
+ end
134
+
88
135
  def parse_operations
89
- operations = xpath("wsdl:definitions/wsdl:binding/wsdl:operation")
136
+ operations = document.xpath("wsdl:definitions/wsdl:binding/wsdl:operation", 'wsdl' => WSDL)
90
137
  operations.each do |operation|
91
138
  name = operation.attribute("name").to_s
92
139
 
93
- soap_action = at_xpath(operation, ".//soap11:operation/@soapAction")
94
- soap_action ||= at_xpath(operation, ".//soap12:operation/@soapAction")
140
+ # TODO: check for soap namespace?
141
+ soap_operation = operation.element_children.find { |node| node.name == 'operation' }
142
+ soap_action = soap_operation['soapAction'] if soap_operation
95
143
 
96
144
  if soap_action
97
145
  soap_action = soap_action.to_s
98
146
  action = soap_action && !soap_action.empty? ? soap_action : name
99
147
 
100
148
  # There should be a matching portType for each binding, so we will lookup the input from there.
149
+ namespace_id, output = output_for(operation)
101
150
  namespace_id, input = input_for(operation)
102
151
 
103
152
  # Store namespace identifier so this operation can be mapped to the proper namespace.
104
- @operations[name.snakecase.to_sym] = { :action => action, :input => input, :namespace_identifier => namespace_id }
153
+ @operations[name.snakecase.to_sym] = { :action => action, :input => input, :output => output, :namespace_identifier => namespace_id}
105
154
  elsif !@operations[name.snakecase.to_sym]
106
155
  @operations[name.snakecase.to_sym] = { :action => name, :input => name }
107
156
  end
@@ -109,38 +158,65 @@ module Wasabi
109
158
  end
110
159
 
111
160
  def parse_types
112
- xpath("wsdl:definitions/wsdl:types/xs:schema/xs:element[@name]").
113
- each { |type| process_type(at_xpath(type, "./xs:complexType"), type.attribute("name").to_s) }
114
-
115
- xpath("wsdl:definitions/wsdl:types/xs:schema/xs:complexType[@name]").
116
- each { |type| process_type(type, type.attribute("name").to_s) }
161
+ schemas.each do |schema|
162
+ schema_namespace = schema['targetNamespace']
163
+
164
+ schema.element_children.each do |node|
165
+ namespace = schema_namespace || @namespace
166
+
167
+ case node.name
168
+ when 'element'
169
+ complex_type = node.at_xpath('./xs:complexType', 'xs' => XSD)
170
+ process_type namespace, complex_type, node['name'].to_s if complex_type
171
+ when 'complexType'
172
+ process_type namespace, node, node['name'].to_s
173
+ end
174
+ end
175
+ end
117
176
  end
118
177
 
119
- def process_type(type, name)
120
- return unless type
121
- @types[name] ||= { :namespace => find_namespace(type) }
178
+ def process_type(namespace, type, name)
179
+ @types[name] ||= { :namespace => namespace }
180
+ @types[name][:order!] = []
122
181
 
123
- xpath(type, "./xs:sequence/xs:element").
124
- each { |inner| @types[name][inner.attribute("name").to_s] = { :type => inner.attribute("type").to_s } }
182
+ type.xpath("./xs:sequence/xs:element", 'xs' => XSD).each do |inner|
183
+ element_name = inner.attribute("name").to_s
184
+ @types[name][element_name] = { :type => inner.attribute("type").to_s }
125
185
 
126
- type.xpath("./xs:complexContent/xs:extension/xs:sequence/xs:element",
127
- "xs" => "http://www.w3.org/2001/XMLSchema"
128
- ).each do |inner_element|
129
- @types[name][inner_element.attribute('name').to_s] = {
130
- :type => inner_element.attribute('type').to_s
131
- }
186
+ [ :nillable, :minOccurs, :maxOccurs ].each do |attr|
187
+ if v = inner.attribute(attr.to_s)
188
+ @types[name][element_name][attr] = v.to_s
189
+ end
190
+ end
191
+
192
+ @types[name][:order!] << element_name
193
+ end
194
+
195
+ type.xpath("./xs:complexContent/xs:extension/xs:sequence/xs:element", 'xs' => XSD).each do |inner_element|
196
+ element_name = inner_element.attribute('name').to_s
197
+ @types[name][element_name] = { :type => inner_element.attribute('type').to_s }
198
+
199
+ @types[name][:order!] << element_name
132
200
  end
133
201
 
134
- type.xpath('./xs:complexContent/xs:extension[@base]',
135
- "xs" => "http://www.w3.org/2001/XMLSchema"
136
- ).each do |inherits|
202
+ type.xpath('./xs:complexContent/xs:extension[@base]', 'xs' => XSD).each do |inherits|
137
203
  base = inherits.attribute('base').value.match(/\w+$/).to_s
204
+
138
205
  if @types[base]
139
- @types[name].merge! @types[base]
206
+ # Reverse merge because we don't want subclass attributes to be overriden by base class
207
+ @types[name] = types[base].merge(types[name])
208
+ @types[name][:order!] = @types[base][:order!] | @types[name][:order!]
209
+ @types[name][:base_type] = base
140
210
  else
141
- deferred_types << Proc.new {
142
- @types[name].merge! @types[base] if @types[base]
143
- }
211
+ p = Proc.new do
212
+ if @types[base]
213
+ # Reverse merge because we don't want subclass attributes to be overriden by base class
214
+ @types[name] = @types[base].merge(@types[name])
215
+ @types[name][:order!] = @types[base][:order!] | @types[name][:order!]
216
+ @types[name][:base_type] = base
217
+ end
218
+ end
219
+ deferred_types << p
144
220
  end
145
221
  end
146
222
  end
@@ -149,28 +225,39 @@ module Wasabi
149
225
  deferred_types.each(&:call)
150
226
  end
151
227
 
152
- def find_namespace(type)
153
- schema_namespace = at_xpath(type, "ancestor::xs:schema/@targetNamespace")
154
- schema_namespace ? schema_namespace.to_s : @namespace
228
+ def input_for(operation)
229
+ input_output_for(operation, "input")
155
230
  end
156
231
 
157
- def input_for(operation)
232
+ def output_for(operation)
233
+ input_output_for(operation, "output")
234
+ end
235
+
236
+ def input_output_for(operation, input_output)
158
237
  operation_name = operation["name"]
159
238
 
160
239
  # Look up the input by walking up to portType, then up to the message.
161
240
 
162
- binding_type = at_xpath(operation, "../@type").to_s.split(':').last
163
- port_type_input = at_xpath(operation, "../../wsdl:portType[@name='#{binding_type}']/wsdl:operation[@name='#{operation_name}']/wsdl:input")
241
+ binding_type = operation.parent['type'].to_s.split(':').last
242
+ if @port_type_operations[binding_type]
243
+ port_type_operation = @port_type_operations[binding_type][operation_name]
244
+ end
245
+
246
+ port_type_input_output = port_type_operation &&
247
+ port_type_operation.element_children.find { |node| node.name == input_output }
164
248
 
165
249
  # TODO: Stupid fix for missing support for imports.
166
250
  # Sometimes portTypes are actually included in a separate WSDL.
167
- if port_type_input
168
- port_message_ns_id, port_message_type = port_type_input.attribute("message").to_s.split(':')
251
+ if port_type_input_output
252
+ port_message_ns_id, port_message_type = port_type_input_output.attribute("message").to_s.split(':')
169
253
 
170
254
  message_ns_id, message_type = nil
171
255
 
172
256
  # TODO: Support multiple 'part' elements in the message.
173
- if (port_message_part = at_xpath(port_type_input, "../../../wsdl:message[@name='#{port_message_type}']/wsdl:part[1]"))
257
+ message = @messages[port_message_type]
258
+ port_message_part = message.element_children.find { |node| node.name == 'part' }
259
+
260
+ if port_message_part
174
261
  if (port_message_part_element = port_message_part.attribute("element"))
175
262
  message_ns_id, message_type = port_message_part_element.to_s.split(':')
176
263
  end
@@ -180,12 +267,38 @@ module Wasabi
180
267
  if message_type
181
268
  [message_ns_id, message_type]
182
269
  else
183
- [port_message_ns_id, operation_name]
270
+ [port_message_ns_id, port_message_type]
184
271
  end
185
272
  else
186
273
  [nil, operation_name]
187
274
  end
188
275
  end
189
276
 
277
+ def schemas
278
+ types = section('types').first
279
+ types ? types.element_children : []
280
+ end
281
+
282
+ def service
283
+ services = section('service')
284
+ services.first if services # service nodes could be imported?
285
+ end
286
+
287
+ def section(section_name)
288
+ sections[section_name] || []
289
+ end
290
+
291
+ def sections
292
+ return @sections if @sections
293
+
294
+ sections = {}
295
+ document.root.element_children.each do |node|
296
+ (sections[node.name] ||= []) << node
297
+ end
298
+
299
+ @sections = sections
300
+ end
301
+
190
302
  end
303
+
191
304
  end