wasabi_with_adapter 3.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +101 -0
  6. data/Gemfile +13 -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 +169 -0
  13. data/lib/wasabi/parser.rb +318 -0
  14. data/lib/wasabi/resolver.rb +55 -0
  15. data/lib/wasabi/version.rb +3 -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/marketo.wsdl +1630 -0
  25. data/spec/fixtures/multiple_namespaces.wsdl +61 -0
  26. data/spec/fixtures/multiple_parts_in_message.wsdl +66 -0
  27. data/spec/fixtures/multiple_types.wsdl +60 -0
  28. data/spec/fixtures/namespaced_actions.wsdl +307 -0
  29. data/spec/fixtures/no_message_parts.wsdl +85 -0
  30. data/spec/fixtures/no_namespace.wsdl +115 -0
  31. data/spec/fixtures/savon295.wsdl +52 -0
  32. data/spec/fixtures/soap12.wsdl +11 -0
  33. data/spec/fixtures/symbolic_endpoint.wsdl +190 -0
  34. data/spec/fixtures/tradetracker.wsdl +1330 -0
  35. data/spec/fixtures/two_bindings.wsdl +24 -0
  36. data/spec/spec_helper.rb +19 -0
  37. data/spec/support/fixture.rb +40 -0
  38. data/spec/support/profiling.rb +18 -0
  39. data/spec/wasabi/core_ext/string_spec.rb +37 -0
  40. data/spec/wasabi/document/authentication_spec.rb +23 -0
  41. data/spec/wasabi/document/economic_spec.rb +13 -0
  42. data/spec/wasabi/document/encoded_endpoint_spec.rb +11 -0
  43. data/spec/wasabi/document/geotrust_spec.rb +24 -0
  44. data/spec/wasabi/document/inherited_spec.rb +38 -0
  45. data/spec/wasabi/document/multiple_namespaces_spec.rb +35 -0
  46. data/spec/wasabi/document/namespaced_actions_spec.rb +25 -0
  47. data/spec/wasabi/document/no_namespace_spec.rb +25 -0
  48. data/spec/wasabi/document/savon295_spec.rb +15 -0
  49. data/spec/wasabi/document/soap12_spec.rb +11 -0
  50. data/spec/wasabi/document/two_bindings_spec.rb +21 -0
  51. data/spec/wasabi/document_spec.rb +58 -0
  52. data/spec/wasabi/parser/get_servicename_spec.rb +19 -0
  53. data/spec/wasabi/parser/import_port_types_spec.rb +22 -0
  54. data/spec/wasabi/parser/juniper_spec.rb +23 -0
  55. data/spec/wasabi/parser/marketo_spec.rb +17 -0
  56. data/spec/wasabi/parser/multiple_namespaces_spec.rb +40 -0
  57. data/spec/wasabi/parser/multiple_parts_in_message_spec.rb +31 -0
  58. data/spec/wasabi/parser/no_message_parts_spec.rb +32 -0
  59. data/spec/wasabi/parser/no_namespace_spec.rb +26 -0
  60. data/spec/wasabi/parser/no_target_namespace_spec.rb +36 -0
  61. data/spec/wasabi/parser/symbolic_endpoint_spec.rb +24 -0
  62. data/spec/wasabi/parser/tradetracker_spec.rb +17 -0
  63. data/spec/wasabi/resolver_spec.rb +40 -0
  64. data/spec/wasabi/wasabi_spec.rb +12 -0
  65. data/wasabi.gemspec +28 -0
  66. metadata +227 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2abb8c0b6c78e5b366b24f0e722c46f4d0d825ca
4
+ data.tar.gz: dcc9e2f3270e1ce7aa791895f12d052ea1991a01
5
+ SHA512:
6
+ metadata.gz: efc284b5d0d6483165e62843c0558b9161dbbfb35ef6d96b4df794da727cdafcb685cdcbdbae6a00ce1e1014df988b1091267474a566091e38b63c896644927c
7
+ data.tar.gz: bdb502dc0f3106619f26a9f4c45fa3cd2585f015123f1a018bdce0c8acd9cd5a68d1ea4c8c044cb9cb903dce00ac5e4bfda1f60258a59c5eecd6197635441874
@@ -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
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 2.0
5
+ - jruby-19mode
6
+ - rbx
@@ -0,0 +1,101 @@
1
+ # 3.2.3 (2013-12-16)
2
+ * Fix [#39](https://github.com/savonrb/wasabi/pull/39) fixes new regression in operation names. Huge thank you to #leoping for investigating this and issuing a pull request.
3
+
4
+ # 3.2.2 (2013-12-09)
5
+
6
+ * Fix: [#32](https://github.com/savonrb/wasabi/issues/32) fixes a regression in operation names that was adversely affecting Savon: https://github.com/savonrb/savon/issues/520
7
+
8
+ # 3.2.1 (2013-12-05)
9
+
10
+ * Feature: Drops requirement for Nokogiri <= 1.6 for modern rubies. This was in place for ruby 1.8 but since support for that is going away, we shouldn't prevent users from using newer versions of Nokogiri.
11
+
12
+ ## 3.2.0 (2013-07-26)
13
+
14
+ * Feature: [#20](https://github.com/savonrb/wasabi/issues/20) Limited support for listing an
15
+ operation's parameters. Please note that if your WSDL defines imports, this method might
16
+ not return all types.
17
+
18
+ * Improvement: [#7](https://github.com/savonrb/wasabi/issues/7) Major speed improvements.
19
+
20
+ * Improvement: [#16](https://github.com/savonrb/wasabi/issues/16) Various improvements regarding
21
+ element order and type information.
22
+
23
+ * Fix: [#25](https://github.com/savonrb/wasabi/issues/25) Fixes a problem where Wasabi escaped
24
+ an already escaped endpoint URL.
25
+
26
+ * Fix: [#15](https://github.com/savonrb/wasabi/issues/15) Fixes a bug where the operation tag
27
+ name was not correctly extracted from the WSDL document.
28
+
29
+ ## 3.1.0 (2013-04-21)
30
+
31
+ * Feature: [#22](https://github.com/savonrb/wasabi/issues/22) added `Wasabi::Document#service_name`
32
+ to return the name of the SOAP service. Original issue: [savonrb/savon#408](https://github.com/savonrb/savon/pull/408).
33
+
34
+ * Fix: [#21](https://github.com/savonrb/wasabi/issues/21) when the Resolver gets an
35
+ erroneous response (such as a 404), we now raise a more useful HTTPError.
36
+
37
+ * Fix: [#23](https://github.com/savonrb/wasabi/issues/23) ignore extension base elements
38
+ defined in imports.
39
+
40
+ ## 3.0.0 (2012-12-17)
41
+
42
+ * Updated to HTTPI 2.0 to play nicely with Savon 2.0.
43
+
44
+ ## 2.5.1 (2012-08-22)
45
+
46
+ * Fix: [#14](https://github.com/savonrb/wasabi/issues/14) fixes an issue where
47
+ finding the correct SOAP input tag and namespace identifier fails when portTypes
48
+ are imported, since imports are currently not supported.
49
+
50
+ The bug was introduced in v2.2.0 by [583cf6](https://github.com/savonrb/wasabi/commit/583cf658f1953411a7a7a4c22923fa0a046c8d6d)
51
+
52
+ * Refactoring: Removed `Object#blank?` core extension.
53
+
54
+ ## 2.5.0 (2012-06-28)
55
+
56
+ * Fix: [#10](https://github.com/savonrb/wasabi/issues/10) fixes an issue where
57
+ Wasabi used the wrong operation name.
58
+
59
+ ## 2.4.1 (2012-06-18)
60
+
61
+ * Fix: [savonrb/savon#296](https://github.com/savonrb/savon/issues/296) fixes an issue where
62
+ the WSDL message element doesn't have part element.
63
+
64
+ ## 2.4.0 (2012-06-08)
65
+
66
+ * Feature: `Wasabi::Document` now accepts either a URL of a remote document,
67
+ a path to a local file or raw XML. The code for this was moved from Savon over
68
+ here as a first step towards supporting WSDL imports.
69
+
70
+ ## 2.3.0 (2012-06-07)
71
+
72
+ * Improvement: [#3](https://github.com/savonrb/wasabi/pull/3) adds object inheritance.
73
+
74
+ ## 2.2.0 (2012-06-06)
75
+
76
+ * Improvement: [#5](https://github.com/savonrb/wasabi/pull/5) - Get input from message
77
+ element or portType input. See [savonrb/savon#277](https://github.com/savonrb/savon/pull/277)
78
+ to get the full picture on how this all works together, and enables you to pass a single
79
+ symbol into the `Savon::Client#request` method and get automatic namespace mapping, as well
80
+ as the proper operation name -> input message mapping.
81
+
82
+ ## 2.1.1 (2012-05-18)
83
+
84
+ * Fix: [issue 7](https://github.com/savonrb/wasabi/issues/7) - Performance regression.
85
+
86
+ ## 2.1.0 (2012-02-17)
87
+
88
+ * Improvement: The value of elementFormDefault can now be manually specified/overwritten.
89
+
90
+ * Improvement: [issue 2](https://github.com/savonrb/wasabi/issues/2) - Allow symbolic endpoints
91
+ like "http://server:port".
92
+
93
+ ## 2.0.0 (2011-07-07)
94
+
95
+ * Feature: Wasabi can now parse type definitions and namespaces.
96
+ Thanks to [jkingdon](https://github.com/jkingdon) for implementing this.
97
+
98
+ ## 1.0.0 (2011-07-03)
99
+
100
+ * Initial version extracted from the [Savon](http://rubygems.org/gems/savon) library.
101
+ Use it to build your own SOAP client and help to improve it!
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'simplecov', :require => false
5
+ gem 'method_profiler', :require => false
6
+ gem 'coveralls', :require => false
7
+
8
+ platform :rbx do
9
+ gem 'json'
10
+ gem 'racc'
11
+ gem 'rubysl'
12
+ gem 'rubinius-coverage'
13
+ end
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.
@@ -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
+ ```
@@ -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
@@ -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,169 @@
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, adapter = nil)
25
+ self.document = document
26
+ self.adapter = adapter
27
+ end
28
+
29
+ attr_accessor :document, :request, :adapter
30
+
31
+ attr_writer :xml
32
+
33
+ alias_method :document?, :document
34
+
35
+ # Returns the SOAP endpoint.
36
+ def endpoint
37
+ @endpoint ||= parser.endpoint
38
+ end
39
+
40
+ # Sets the SOAP endpoint.
41
+ attr_writer :endpoint
42
+
43
+ # Returns the target namespace.
44
+ def namespace
45
+ @namespace ||= parser.namespace
46
+ end
47
+
48
+ # Sets the target namespace.
49
+ attr_writer :namespace
50
+
51
+ # Returns the value of elementFormDefault.
52
+ def element_form_default
53
+ @element_form_default ||= document ? parser.element_form_default : :unqualified
54
+ end
55
+
56
+ # Sets the elementFormDefault value.
57
+ def element_form_default=(value)
58
+ self.class.validate_element_form_default!(value)
59
+ @element_form_default = value
60
+ end
61
+
62
+ # Returns a list of available SOAP actions.
63
+ def soap_actions
64
+ @soap_actions ||= parser.operations.keys
65
+ end
66
+
67
+ # Returns the SOAP action for a given +key+.
68
+ def soap_action(key)
69
+ operations[key][:action] if operations[key]
70
+ end
71
+
72
+ # Returns the SOAP input for a given +key+.
73
+ def soap_input(key)
74
+ operations[key][:input] if operations[key]
75
+ end
76
+
77
+ # Returns a map of SOAP operations.
78
+ def operations
79
+ @operations ||= parser.operations
80
+ end
81
+
82
+ # Returns the service name.
83
+ def service_name
84
+ @service_name ||= parser.service_name
85
+ end
86
+
87
+ attr_writer :service_name
88
+
89
+ # Returns a list of parameter names for a given +key+
90
+ def soap_action_parameters(key)
91
+ params = operation_input_parameters(key)
92
+ params.keys if params
93
+ end
94
+
95
+ # Returns a list of input parameters for a given +key+.
96
+ def operation_input_parameters(key)
97
+ parser.operations[key][:parameters] if operations[key]
98
+ end
99
+
100
+ def type_namespaces
101
+ @type_namespaces ||= begin
102
+ namespaces = []
103
+
104
+ parser.types.each do |type, info|
105
+ namespaces << [[type], info[:namespace]]
106
+
107
+ element_keys(info).each do |field|
108
+ namespaces << [[type, field], info[:namespace]]
109
+ end
110
+ end if document
111
+
112
+ namespaces
113
+ end
114
+ end
115
+
116
+ def type_definitions
117
+ @type_definitions ||= begin
118
+ result = []
119
+
120
+ parser.types.each do |type, info|
121
+ element_keys(info).each do |field|
122
+ field_type = info[field][:type]
123
+ tag, namespace = field_type.split(":").reverse
124
+
125
+ result << [[type, field], tag] if user_defined(namespace)
126
+ end
127
+ end if document
128
+
129
+ result
130
+ end
131
+ end
132
+
133
+ # Returns whether the given +namespace+ was defined manually.
134
+ def user_defined(namespace)
135
+ uri = parser.namespaces[namespace]
136
+ !(uri =~ %r{^http://schemas.xmlsoap.org} || uri =~ %r{^http://www.w3.org})
137
+ end
138
+
139
+ # Returns the raw WSDL document.
140
+ # Can be used as a hook to extend the library.
141
+ def xml
142
+ @xml ||= Resolver.new(document, request, adapter).resolve
143
+ end
144
+
145
+ # Parses the WSDL document and returns the <tt>Wasabi::Parser</tt>.
146
+ def parser
147
+ @parser ||= guard_parse && parse
148
+ end
149
+
150
+ private
151
+
152
+ # Raises an error if the WSDL document is missing.
153
+ def guard_parse
154
+ return true if document
155
+ raise ArgumentError, "Wasabi needs a WSDL document"
156
+ end
157
+
158
+ # Parses the WSDL document and returns <tt>Wasabi::Parser</tt>.
159
+ def parse
160
+ parser = Parser.new Nokogiri::XML(xml)
161
+ parser.parse
162
+ parser
163
+ end
164
+
165
+ def element_keys(info)
166
+ info.keys - [:namespace, :order!, :base_type]
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,318 @@
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
+ if port_type_input_output.attribute('message').to_s.include? ':'
253
+ port_message_ns_id, port_message_type = port_type_input_output.attribute('message').to_s.split(':')
254
+ else
255
+ port_message_type = port_type_input_output.attribute('message').to_s
256
+ end
257
+
258
+ message_ns_id, message_type = nil
259
+
260
+ # When there is a parts attribute in soap:body element, we should use that value
261
+ # to look up the message part from messages array.
262
+ input_output_element = operation.element_children.find { |node| node.name == input_output }
263
+ if input_output_element
264
+ soap_body_element = input_output_element.element_children.find { |node| node.name == 'body' }
265
+ soap_body_parts = soap_body_element['parts'] if soap_body_element
266
+ end
267
+
268
+ message = @messages[port_message_type]
269
+ port_message_part = message.element_children.find do |node|
270
+ soap_body_parts.nil? ? (node.name == 'part') : ( node.name == 'part' && node['name'] == soap_body_parts)
271
+ end
272
+
273
+ if port_message_part && port_element = port_message_part.attribute('element')
274
+ port_message_part = port_element.to_s
275
+ if port_message_part.include?(':')
276
+ message_ns_id, message_type = port_message_part.split(':')
277
+ else
278
+ message_type = port_message_part
279
+ end
280
+ end
281
+
282
+ # Fall back to the name of the binding operation
283
+ if message_type
284
+ [message_ns_id, message_type]
285
+ else
286
+ [port_message_ns_id, operation_name]
287
+ end
288
+ else
289
+ [nil, operation_name]
290
+ end
291
+ end
292
+
293
+ def schemas
294
+ types = section('types').first
295
+ types ? types.element_children : []
296
+ end
297
+
298
+ def service
299
+ services = section('service')
300
+ services.first if services # service nodes could be imported?
301
+ end
302
+
303
+ def section(section_name)
304
+ sections[section_name] || []
305
+ end
306
+
307
+ def sections
308
+ return @sections if @sections
309
+
310
+ sections = {}
311
+ document.root.element_children.each do |node|
312
+ (sections[node.name] ||= []) << node
313
+ end
314
+
315
+ @sections = sections
316
+ end
317
+ end
318
+ end