wasabi-ng-1.6 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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