wasabi-ng-1.6 3.3.0

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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +9 -0
  5. data/CHANGELOG.md +90 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +20 -0
  8. data/README.md +60 -0
  9. data/Rakefile +7 -0
  10. data/lib/wasabi.rb +12 -0
  11. data/lib/wasabi/core_ext/string.rb +21 -0
  12. data/lib/wasabi/document.rb +166 -0
  13. data/lib/wasabi/parser.rb +304 -0
  14. data/lib/wasabi/resolver.rb +54 -0
  15. data/lib/wasabi/version.rb +5 -0
  16. data/spec/fixtures/authentication.wsdl +63 -0
  17. data/spec/fixtures/economic.wsdl +65660 -0
  18. data/spec/fixtures/encoded_endpoint.wsdl +52 -0
  19. data/spec/fixtures/geotrust.wsdl +156 -0
  20. data/spec/fixtures/import_port_types.wsdl +86 -0
  21. data/spec/fixtures/inherited.wsdl +46 -0
  22. data/spec/fixtures/juniper.wsdl +215 -0
  23. data/spec/fixtures/lower_camel.wsdl +52 -0
  24. data/spec/fixtures/multiple_namespaces.wsdl +61 -0
  25. data/spec/fixtures/multiple_types.wsdl +60 -0
  26. data/spec/fixtures/namespaced_actions.wsdl +307 -0
  27. data/spec/fixtures/no_message_parts.wsdl +59 -0
  28. data/spec/fixtures/no_namespace.wsdl +115 -0
  29. data/spec/fixtures/savon295.wsdl +52 -0
  30. data/spec/fixtures/soap12.wsdl +11 -0
  31. data/spec/fixtures/symbolic_endpoint.wsdl +190 -0
  32. data/spec/fixtures/two_bindings.wsdl +24 -0
  33. data/spec/spec_helper.rb +19 -0
  34. data/spec/support/fixture.rb +40 -0
  35. data/spec/support/profiling.rb +18 -0
  36. data/spec/wasabi/core_ext/string_spec.rb +37 -0
  37. data/spec/wasabi/document/authentication_spec.rb +23 -0
  38. data/spec/wasabi/document/economic_spec.rb +13 -0
  39. data/spec/wasabi/document/encoded_endpoint_spec.rb +11 -0
  40. data/spec/wasabi/document/geotrust_spec.rb +24 -0
  41. data/spec/wasabi/document/inherited_spec.rb +38 -0
  42. data/spec/wasabi/document/multiple_namespaces_spec.rb +35 -0
  43. data/spec/wasabi/document/namespaced_actions_spec.rb +25 -0
  44. data/spec/wasabi/document/no_namespace_spec.rb +25 -0
  45. data/spec/wasabi/document/savon295_spec.rb +15 -0
  46. data/spec/wasabi/document/soap12_spec.rb +11 -0
  47. data/spec/wasabi/document/two_bindings_spec.rb +21 -0
  48. data/spec/wasabi/document_spec.rb +58 -0
  49. data/spec/wasabi/parser/get_servicename_spec.rb +19 -0
  50. data/spec/wasabi/parser/import_port_types_spec.rb +22 -0
  51. data/spec/wasabi/parser/juniper_spec.rb +23 -0
  52. data/spec/wasabi/parser/multiple_namespaces_spec.rb +40 -0
  53. data/spec/wasabi/parser/no_message_parts_spec.rb +26 -0
  54. data/spec/wasabi/parser/no_namespace_spec.rb +26 -0
  55. data/spec/wasabi/parser/no_target_namespace_spec.rb +36 -0
  56. data/spec/wasabi/parser/symbolic_endpoint_spec.rb +24 -0
  57. data/spec/wasabi/resolver_spec.rb +40 -0
  58. data/spec/wasabi/wasabi_spec.rb +12 -0
  59. data/wasabi.gemspec +27 -0
  60. metadata +159 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4f5657201ca8ca58c80f372f9d46f164dc548af6
4
+ data.tar.gz: 1b07dede1bee0dc7ece80c96160becabb7dfcd81
5
+ SHA512:
6
+ metadata.gz: bcd9c7b4e64663c9f01731eb21339111480865422e1b2df97811fd97a69b53fe0a4c77758c80eb60f2d994a973ebe37cd901d0318e7dfb1cbcf9e4b372db00ca
7
+ data.tar.gz: 5fa9f8b47ffcac82f092c81afce91c18c1d258c2b504443075c3230ac51809b2a3337768915acc1562f1bc364e797d67adc242f4b447399ce84830f9deae9a97
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ .DS_Store
2
+ doc
3
+ coverage
4
+ pkg
5
+ *~
6
+ *.gem
7
+ .bundle
8
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - jruby-18mode
6
+ - jruby-19mode
7
+ - rbx-18mode
8
+ - rbx-19mode
9
+ - ree
data/CHANGELOG.md ADDED
@@ -0,0 +1,90 @@
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
+
18
+ ## 3.1.0 (2013-04-21)
19
+
20
+ * Feature: [#22](https://github.com/savonrb/wasabi/issues/22) added `Wasabi::Document#service_name`
21
+ to return the name of the SOAP service. Original issue: [savonrb/savon#408](https://github.com/savonrb/savon/pull/408).
22
+
23
+ * Fix: [#21](https://github.com/savonrb/wasabi/issues/21) when the Resolver gets an
24
+ erroneous response (such as a 404), we now raise a more useful HTTPError.
25
+
26
+ * Fix: [#23](https://github.com/savonrb/wasabi/issues/23) ignore extension base elements
27
+ defined in imports.
28
+
29
+ ## 3.0.0 (2012-12-17)
30
+
31
+ * Updated to HTTPI 2.0 to play nicely with Savon 2.0.
32
+
33
+ ## 2.5.1 (2012-08-22)
34
+
35
+ * Fix: [#14](https://github.com/savonrb/wasabi/issues/14) fixes an issue where
36
+ finding the correct SOAP input tag and namespace identifier fails when portTypes
37
+ are imported, since imports are currently not supported.
38
+
39
+ The bug was introduced in v2.2.0 by [583cf6](https://github.com/savonrb/wasabi/commit/583cf658f1953411a7a7a4c22923fa0a046c8d6d)
40
+
41
+ * Refactoring: Removed `Object#blank?` core extension.
42
+
43
+ ## 2.5.0 (2012-06-28)
44
+
45
+ * Fix: [#10](https://github.com/savonrb/wasabi/issues/10) fixes an issue where
46
+ Wasabi used the wrong operation name.
47
+
48
+ ## 2.4.1 (2012-06-18)
49
+
50
+ * Fix: [savonrb/savon#296](https://github.com/savonrb/savon/issues/296) fixes an issue where
51
+ the WSDL message element doesn't have part element.
52
+
53
+ ## 2.4.0 (2012-06-08)
54
+
55
+ * Feature: `Wasabi::Document` now accepts either a URL of a remote document,
56
+ a path to a local file or raw XML. The code for this was moved from Savon over
57
+ here as a first step towards supporting WSDL imports.
58
+
59
+ ## 2.3.0 (2012-06-07)
60
+
61
+ * Improvement: [#3](https://github.com/savonrb/wasabi/pull/3) adds object inheritance.
62
+
63
+ ## 2.2.0 (2012-06-06)
64
+
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)
67
+ to get the full picture on how this all works together, and enables you to pass a single
68
+ symbol into the `Savon::Client#request` method and get automatic namespace mapping, as well
69
+ as the proper operation name -> input message mapping.
70
+
71
+ ## 2.1.1 (2012-05-18)
72
+
73
+ * Fix: [issue 7](https://github.com/savonrb/wasabi/issues/7) - Performance regression.
74
+
75
+ ## 2.1.0 (2012-02-17)
76
+
77
+ * Improvement: The value of elementFormDefault can now be manually specified/overwritten.
78
+
79
+ * Improvement: [issue 2](https://github.com/savonrb/wasabi/issues/2) - Allow symbolic endpoints
80
+ like "http://server:port".
81
+
82
+ ## 2.0.0 (2011-07-07)
83
+
84
+ * Feature: Wasabi can now parse type definitions and namespaces.
85
+ Thanks to [jkingdon](https://github.com/jkingdon) for implementing this.
86
+
87
+ ## 1.0.0 (2011-07-03)
88
+
89
+ * Initial version extracted from the [Savon](http://rubygems.org/gems/savon) library.
90
+ Use it to build your own SOAP client and help to improve it!
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'simplecov', :require => false
5
+ gem 'method_profiler', :require => false
6
+ gem 'coveralls', :require => false
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Daniel Harrington
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # Wasabi
2
+
3
+ A simple WSDL parser.
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)
9
+
10
+
11
+ ## Installation
12
+
13
+ Wasabi is available through [Rubygems](http://rubygems.org/gems/wasabi) and can be installed via:
14
+
15
+ ```
16
+ $ gem install wasabi
17
+ ```
18
+
19
+
20
+ ## Getting started
21
+
22
+ ``` ruby
23
+ document = Wasabi.document File.read("some.wsdl")
24
+ ```
25
+
26
+ Get the SOAP endpoint:
27
+
28
+ ``` ruby
29
+ document.endpoint
30
+ # => "http://soap.example.com"
31
+ ```
32
+
33
+ Get the target namespace:
34
+
35
+ ``` ruby
36
+ document.namespace
37
+ # => "http://v1.example.com"
38
+ ```
39
+
40
+ Check whether elementFormDefault is set to `:qualified` or `:unqualified`:
41
+
42
+ ``` ruby
43
+ document.element_form_default
44
+ # => :qualified
45
+ ```
46
+
47
+ Get a list of available SOAP actions (snakecase for convenience):
48
+
49
+ ``` ruby
50
+ document.soap_actions
51
+ # => [:create_user, :find_user]
52
+ ```
53
+
54
+ Get a map of SOAP action Symbols, their input tag and original SOAP action name:
55
+
56
+ ``` ruby
57
+ document.operations
58
+ # => { :create_user => { :input => "createUser", :action => "createUser" },
59
+ # => :find_user => { :input => "findUser", :action => "findUser" } }
60
+ ```
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
data/lib/wasabi.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "wasabi/version"
2
+ require "wasabi/document"
3
+ require "wasabi/resolver"
4
+
5
+ module Wasabi
6
+
7
+ # Expects a WSDL document and returns a <tt>Wasabi::Document</tt>.
8
+ def self.document(document)
9
+ Document.new(document)
10
+ end
11
+
12
+ end
@@ -0,0 +1,21 @@
1
+ module Wasabi
2
+ module CoreExt
3
+ module String
4
+
5
+ # Returns the String in snakecase.
6
+ def snakecase
7
+ str = dup
8
+ str.gsub! /::/, '/'
9
+ str.gsub! /([A-Z]+)([A-Z][a-z])/, '\1_\2'
10
+ str.gsub! /([a-z\d])([A-Z])/, '\1_\2'
11
+ str.tr! ".", "_"
12
+ str.tr! "-", "_"
13
+ str.downcase!
14
+ str
15
+ end unless method_defined?(:snakecase)
16
+
17
+ end
18
+ end
19
+ end
20
+
21
+ String.send :include, Wasabi::CoreExt::String
@@ -0,0 +1,166 @@
1
+ require "nokogiri"
2
+ require "wasabi/resolver"
3
+ require "wasabi/parser"
4
+
5
+ module Wasabi
6
+
7
+ # = Wasabi::Document
8
+ #
9
+ # Represents a WSDL document.
10
+ class Document
11
+
12
+ ELEMENT_FORM_DEFAULTS = [:unqualified, :qualified]
13
+
14
+ # Validates if a given +value+ is a valid elementFormDefault value.
15
+ # Raises an +ArgumentError+ if the value is not valid.
16
+ def self.validate_element_form_default!(value)
17
+ return if ELEMENT_FORM_DEFAULTS.include?(value)
18
+
19
+ raise ArgumentError, "Invalid value for elementFormDefault: #{value}\n" +
20
+ "Must be one of: #{ELEMENT_FORM_DEFAULTS.inspect}"
21
+ end
22
+
23
+ # Accepts a WSDL +document+ to parse.
24
+ def initialize(document = nil)
25
+ self.document = document
26
+ end
27
+
28
+ attr_accessor :document, :request, :xml
29
+
30
+ alias_method :document?, :document
31
+
32
+ # Returns the SOAP endpoint.
33
+ def endpoint
34
+ @endpoint ||= parser.endpoint
35
+ end
36
+
37
+ # Sets the SOAP endpoint.
38
+ attr_writer :endpoint
39
+
40
+ # Returns the target namespace.
41
+ def namespace
42
+ @namespace ||= parser.namespace
43
+ end
44
+
45
+ # Sets the target namespace.
46
+ attr_writer :namespace
47
+
48
+ # Returns the value of elementFormDefault.
49
+ def element_form_default
50
+ @element_form_default ||= document ? parser.element_form_default : :unqualified
51
+ end
52
+
53
+ # Sets the elementFormDefault value.
54
+ def element_form_default=(value)
55
+ self.class.validate_element_form_default!(value)
56
+ @element_form_default = value
57
+ end
58
+
59
+ # Returns a list of available SOAP actions.
60
+ def soap_actions
61
+ @soap_actions ||= parser.operations.keys
62
+ end
63
+
64
+ # Returns the SOAP action for a given +key+.
65
+ def soap_action(key)
66
+ operations[key][:action] if operations[key]
67
+ end
68
+
69
+ # Returns the SOAP input for a given +key+.
70
+ def soap_input(key)
71
+ operations[key][:input] if operations[key]
72
+ end
73
+
74
+ # Returns a map of SOAP operations.
75
+ def operations
76
+ @operations ||= parser.operations
77
+ end
78
+
79
+ # Returns the service name.
80
+ def service_name
81
+ @service_name ||= parser.service_name
82
+ end
83
+
84
+ attr_writer :service_name
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
+
97
+ def type_namespaces
98
+ @type_namespaces ||= begin
99
+ namespaces = []
100
+
101
+ parser.types.each do |type, info|
102
+ namespaces << [[type], info[:namespace]]
103
+
104
+ element_keys(info).each do |field|
105
+ namespaces << [[type, field], info[:namespace]]
106
+ end
107
+ end if document
108
+
109
+ namespaces
110
+ end
111
+ end
112
+
113
+ def type_definitions
114
+ @type_definitions ||= begin
115
+ result = []
116
+
117
+ parser.types.each do |type, info|
118
+ element_keys(info).each do |field|
119
+ field_type = info[field][:type]
120
+ tag, namespace = field_type.split(":").reverse
121
+
122
+ result << [[type, field], tag] if user_defined(namespace)
123
+ end
124
+ end if document
125
+
126
+ result
127
+ end
128
+ end
129
+
130
+ # Returns whether the given +namespace+ was defined manually.
131
+ def user_defined(namespace)
132
+ uri = parser.namespaces[namespace]
133
+ !(uri =~ %r{^http://schemas.xmlsoap.org} || uri =~ %r{^http://www.w3.org})
134
+ end
135
+
136
+ # Returns the raw WSDL document.
137
+ # Can be used as a hook to extend the library.
138
+ def xml
139
+ @xml ||= Resolver.new(document, request).resolve
140
+ end
141
+
142
+ # Parses the WSDL document and returns the <tt>Wasabi::Parser</tt>.
143
+ def parser
144
+ @parser ||= guard_parse && parse
145
+ end
146
+
147
+ private
148
+
149
+ # Raises an error if the WSDL document is missing.
150
+ def guard_parse
151
+ return true if document
152
+ raise ArgumentError, "Wasabi needs a WSDL document"
153
+ end
154
+
155
+ # Parses the WSDL document and returns <tt>Wasabi::Parser</tt>.
156
+ def parse
157
+ parser = Parser.new Nokogiri::XML(xml)
158
+ parser.parse
159
+ parser
160
+ end
161
+
162
+ def element_keys(info)
163
+ info.keys - [:namespace, :order!, :base_type]
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,304 @@
1
+ require "uri"
2
+ require "wasabi/core_ext/string"
3
+
4
+ module Wasabi
5
+
6
+ # = Wasabi::Parser
7
+ #
8
+ # Parses WSDL documents and remembers their important parts.
9
+ class Parser
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/"
15
+
16
+ def initialize(document)
17
+ self.document = document
18
+ self.operations = {}
19
+ self.namespaces = {}
20
+ self.service_name = ''
21
+ self.types = {}
22
+ self.deferred_types = []
23
+ self.element_form_default = :unqualified
24
+ end
25
+
26
+ # Returns the Nokogiri document.
27
+ attr_accessor :document
28
+
29
+ # Returns the target namespace.
30
+ attr_accessor :namespace
31
+
32
+ # Returns a map from namespace identifier to namespace URI.
33
+ attr_accessor :namespaces
34
+
35
+ # Returns the SOAP operations.
36
+ attr_accessor :operations
37
+
38
+ # Returns a map from a type name to a Hash with type information.
39
+ attr_accessor :types
40
+
41
+ # Returns a map of deferred type Proc objects.
42
+ attr_accessor :deferred_types
43
+
44
+ # Returns the SOAP endpoint.
45
+ attr_accessor :endpoint
46
+
47
+ # Returns the SOAP Service Name
48
+ attr_accessor :service_name
49
+
50
+ # Returns the elementFormDefault value.
51
+ attr_accessor :element_form_default
52
+
53
+ def parse
54
+ parse_namespaces
55
+ parse_endpoint
56
+ parse_service_name
57
+ parse_messages
58
+ parse_port_types
59
+ parse_port_type_operations
60
+ parse_operations
61
+ parse_operations_parameters
62
+ parse_types
63
+ parse_deferred_types
64
+ end
65
+
66
+ def parse_namespaces
67
+ element_form_default = schemas.first && schemas.first['elementFormDefault']
68
+ @element_form_default = element_form_default.to_s.to_sym if element_form_default
69
+
70
+ namespace = document.root['targetNamespace']
71
+ @namespace = namespace.to_s if namespace
72
+
73
+ @namespaces = @document.namespaces.inject({}) do |memo, (key, value)|
74
+ memo[key.sub("xmlns:", "")] = value
75
+ memo
76
+ end
77
+ end
78
+
79
+ def parse_endpoint
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)
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
93
+ end
94
+
95
+ def parse_service_name
96
+ service_name = document.root['name']
97
+ @service_name = service_name.to_s if service_name
98
+ end
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
+
135
+ def parse_operations
136
+ operations = document.xpath("wsdl:definitions/wsdl:binding/wsdl:operation", 'wsdl' => WSDL)
137
+ operations.each do |operation|
138
+ name = operation.attribute("name").to_s
139
+
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
143
+
144
+ if soap_action
145
+ soap_action = soap_action.to_s
146
+ action = soap_action && !soap_action.empty? ? soap_action : name
147
+
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)
150
+ namespace_id, input = input_for(operation)
151
+
152
+ # Store namespace identifier so this operation can be mapped to the proper namespace.
153
+ @operations[name.snakecase.to_sym] = { :action => action, :input => input, :output => output, :namespace_identifier => namespace_id}
154
+ elsif !@operations[name.snakecase.to_sym]
155
+ @operations[name.snakecase.to_sym] = { :action => name, :input => name }
156
+ end
157
+ end
158
+ end
159
+
160
+ def parse_types
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
176
+ end
177
+
178
+ def process_type(namespace, type, name)
179
+ @types[name] ||= { :namespace => namespace }
180
+ @types[name][:order!] = []
181
+
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 }
185
+
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
200
+ end
201
+
202
+ type.xpath('./xs:complexContent/xs:extension[@base]', 'xs' => XSD).each do |inherits|
203
+ base = inherits.attribute('base').value.match(/\w+$/).to_s
204
+
205
+ if @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
210
+ else
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
220
+ end
221
+ end
222
+ end
223
+
224
+ def parse_deferred_types
225
+ deferred_types.each(&:call)
226
+ end
227
+
228
+ def input_for(operation)
229
+ input_output_for(operation, "input")
230
+ end
231
+
232
+ def output_for(operation)
233
+ input_output_for(operation, "output")
234
+ end
235
+
236
+ def input_output_for(operation, input_output)
237
+ operation_name = operation["name"]
238
+
239
+ # Look up the input by walking up to portType, then up to the message.
240
+
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 }
248
+
249
+ # TODO: Stupid fix for missing support for imports.
250
+ # Sometimes portTypes are actually included in a separate WSDL.
251
+ if port_type_input_output
252
+ port_message_ns_id, port_message_type = port_type_input_output.attribute("message").to_s.split(':')
253
+
254
+ message_ns_id, message_type = nil
255
+
256
+ # TODO: Support multiple 'part' elements in the message.
257
+ message = @messages[port_message_type]
258
+ port_message_part = message.element_children.find { |node| node.name == 'part' }
259
+
260
+ if port_message_part
261
+ if (port_message_part_element = port_message_part.attribute("element"))
262
+ message_ns_id, message_type = port_message_part_element.to_s.split(':')
263
+ end
264
+ end
265
+
266
+ # Fall back to the name of the binding operation
267
+ if message_type
268
+ [message_ns_id, message_type]
269
+ else
270
+ [port_message_ns_id, port_message_type]
271
+ end
272
+ else
273
+ [nil, operation_name]
274
+ end
275
+ end
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
+
302
+ end
303
+
304
+ end