wasabi-ng-1.6 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +90 -0
- data/Gemfile +6 -0
- data/LICENSE +20 -0
- data/README.md +60 -0
- data/Rakefile +7 -0
- data/lib/wasabi.rb +12 -0
- data/lib/wasabi/core_ext/string.rb +21 -0
- data/lib/wasabi/document.rb +166 -0
- data/lib/wasabi/parser.rb +304 -0
- data/lib/wasabi/resolver.rb +54 -0
- data/lib/wasabi/version.rb +5 -0
- data/spec/fixtures/authentication.wsdl +63 -0
- data/spec/fixtures/economic.wsdl +65660 -0
- data/spec/fixtures/encoded_endpoint.wsdl +52 -0
- data/spec/fixtures/geotrust.wsdl +156 -0
- data/spec/fixtures/import_port_types.wsdl +86 -0
- data/spec/fixtures/inherited.wsdl +46 -0
- data/spec/fixtures/juniper.wsdl +215 -0
- data/spec/fixtures/lower_camel.wsdl +52 -0
- data/spec/fixtures/multiple_namespaces.wsdl +61 -0
- data/spec/fixtures/multiple_types.wsdl +60 -0
- data/spec/fixtures/namespaced_actions.wsdl +307 -0
- data/spec/fixtures/no_message_parts.wsdl +59 -0
- data/spec/fixtures/no_namespace.wsdl +115 -0
- data/spec/fixtures/savon295.wsdl +52 -0
- data/spec/fixtures/soap12.wsdl +11 -0
- data/spec/fixtures/symbolic_endpoint.wsdl +190 -0
- data/spec/fixtures/two_bindings.wsdl +24 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/fixture.rb +40 -0
- data/spec/support/profiling.rb +18 -0
- data/spec/wasabi/core_ext/string_spec.rb +37 -0
- data/spec/wasabi/document/authentication_spec.rb +23 -0
- data/spec/wasabi/document/economic_spec.rb +13 -0
- data/spec/wasabi/document/encoded_endpoint_spec.rb +11 -0
- data/spec/wasabi/document/geotrust_spec.rb +24 -0
- data/spec/wasabi/document/inherited_spec.rb +38 -0
- data/spec/wasabi/document/multiple_namespaces_spec.rb +35 -0
- data/spec/wasabi/document/namespaced_actions_spec.rb +25 -0
- data/spec/wasabi/document/no_namespace_spec.rb +25 -0
- data/spec/wasabi/document/savon295_spec.rb +15 -0
- data/spec/wasabi/document/soap12_spec.rb +11 -0
- data/spec/wasabi/document/two_bindings_spec.rb +21 -0
- data/spec/wasabi/document_spec.rb +58 -0
- data/spec/wasabi/parser/get_servicename_spec.rb +19 -0
- data/spec/wasabi/parser/import_port_types_spec.rb +22 -0
- data/spec/wasabi/parser/juniper_spec.rb +23 -0
- data/spec/wasabi/parser/multiple_namespaces_spec.rb +40 -0
- data/spec/wasabi/parser/no_message_parts_spec.rb +26 -0
- data/spec/wasabi/parser/no_namespace_spec.rb +26 -0
- data/spec/wasabi/parser/no_target_namespace_spec.rb +36 -0
- data/spec/wasabi/parser/symbolic_endpoint_spec.rb +24 -0
- data/spec/wasabi/resolver_spec.rb +40 -0
- data/spec/wasabi/wasabi_spec.rb +12 -0
- data/wasabi.gemspec +27 -0
- 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
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
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
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
data/lib/wasabi.rb
ADDED
@@ -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
|