Skip to content

Commit

Permalink
Merge pull request #3 from ansoni/master
Browse files Browse the repository at this point in the history
A semi-usable WSDL
  • Loading branch information
IvanShamatov committed May 16, 2014
2 parents 1818f61 + 41eb206 commit 07170d5
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 25 deletions.
18 changes: 17 additions & 1 deletion examples/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,24 @@

class App < Sinatra::Base
register Sinatra::Soap
soap :test do

set :service, "sinatra"
set :namespace, "http://schemas.xmlsoap.org/wsdl/"
set :endpoint, '/action'
set :wsdl_route, '/wsdl'

#soap :test do
# params
#end

soap :test, in: { hello: :string }, out: nil do
params
end

soap :ObjectTest, in: { TestObject: { a: :string, b: :string }}, out: { hello: :string } do
params
end


end
App.run!
21 changes: 10 additions & 11 deletions examples/client.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
require "savon"

client = Savon.client do
endpoint "http://127.0.0.1:4567/action"
namespace "http://schemas.xmlsoap.org/wsdl/"
end
#client = Savon.client do
# endpoint "http://127.0.0.1:4567/action"
# namespace "http://schemas.xmlsoap.org/wsdl/"
#end

puts client.call(:test, message: { :hello => :world })

#client = Savon.client(
# :wsdl => "http://www.webservicex.net/stockquote.asmx?WSDL",
# :open_timeout => 10,
# :read_timeout => 10,
# :log => true
#)
client = Savon.client(wsdl: "http://127.0.0.1:4567/wsdl")

puts client.operations

puts client.call(:test, message: { :hello => :world })
puts client.call(:test, message: { :should => :fail })


36 changes: 36 additions & 0 deletions lib/sinatra/soap/helper_methods.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative 'param'

module Sinatra
module Soap
module HelperMethods
Expand Down Expand Up @@ -27,6 +29,40 @@ def get_wsdl
builder :wsdl, locals: {wsdl: Soap::Wsdl.actions}, :views => self.soap_views
end
end

def wsdl_occurence(param, inject, extend_with = {})
param=Param.new(param[0], param[1])
extend_with = { :name => param.name, :type => param.namespaced_type }
data = !param.multiplied ? {} : {
"#{'xsi:' if inject}minOccurs" => 0,
"#{'xsi:' if inject}maxOccurs" => 'unbounded'
}
extend_with.merge(data)
end

def wsdl_type(xml, param, defined=[])
param = Param.new(param[0], param[1])
more = []
if param.struct?
if !defined.include?(param.basic_type)
xml.tag! "xsd:complexType", :name => param.basic_type do
xml.tag! "xsd:sequence" do
param.map.each do |value|
param_value = Param.new(value[0], value[1])
more << value if param_value.struct?
xml.tag! "xsd:element", wsdl_occurence(value, false)
end
end
end
defined << param.basic_type
elsif !param.classified?
raise RuntimeError, "Duplicate use of `#{param.basic_type}` type name. Consider using classified types."
end
end
more.each do |p|
wsdl_type xml, p, defined
end
end
end
end
end
181 changes: 181 additions & 0 deletions lib/sinatra/soap/param.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
module Sinatra
module Soap
class Param
attr_accessor :raw_name
attr_accessor :name
attr_accessor :map
attr_accessor :type
attr_accessor :multiplied
attr_accessor :value
attr_accessor :source_class

def initialize(name, type, multiplied = false)
type ||= {}
@name = name.to_s
@raw_name = name.to_s
@map = {}
@multiplied = multiplied

if type.is_a?(Symbol)
@type = type.to_s
elsif type.is_a?(Class)
@type = 'struct'
@source_class = type
@map = type
else
@type = 'struct'
@map = type
end
end

# Converts a generic externally derived Ruby value, such as String or
# Hash, to a native Ruby object according to the definition of this type.
def load(data, key)
if !data.has_key? key
raise Error, "Required SOAP parameter '#{key}' is missing"
end

data = data[key]
data = [data] if @multiplied && !data.is_a?(Array)

if struct?
data ||= {}
if @multiplied
data.map do |x|
map_struct x do |param, dat, elem|
param.load(dat, elem)
end
end
else
map_struct data do |param, dat, elem|
param.load(dat, elem)
end
end
else
operation = case type
when 'string'; :to_s
when 'integer'; :to_i
when 'double'; :to_f
when 'boolean'; lambda{|dat| dat === "0" ? false : !!dat}
when 'date'; :to_date
when 'datetime'; :to_datetime
when 'time'; :to_time
when 'base64Binary'; lambda{|dat| Base64.decode64(dat)}
else raise RuntimeError, "Invalid WashOut simple type: #{type}"
end

begin
if data.nil?
data
elsif @multiplied
return data.map{|x| x.send(operation)} if operation.is_a?(Symbol)
return data.map{|x| operation.call(x)} if operation.is_a?(Proc)
elsif operation.is_a? Symbol
data.send(operation)
else
operation.call(data)
end
rescue
raise Error, "Invalid SOAP parameter '#{key}' format"
end
end
end

# Checks if this Param defines a complex type.
def struct?
type == 'struct'
end

def classified?
!source_class.nil?
end

def basic_type
return name unless classified?
return source_class.wash_out_param_name(@soap_config)
end

def xsd_type
return 'int' if type.to_s == 'integer'
return 'dateTime' if type.to_s == 'datetime'
return type
end

# Returns a WSDL namespaced identifier for this type.
def namespaced_type
struct? ? "tns:#{basic_type}" : "xsd:#{xsd_type}"
end

# Parses a +definition+. The format of the definition is best described
# by the following BNF-like grammar.
#
# simple_type := :string | :integer | :double | :boolean
# nested_type := type_hash | simple_type | WashOut::Param instance
# type_hash := { :parameter_name => nested_type, ... }
# definition := [ WashOut::Param, ... ] |
# type_hash |
# simple_type
#
# If a simple type is passed as the +definition+, a single Param is returned
# with the +name+ set to "value".
# If a WashOut::Param instance is passed as a +nested_type+, the corresponding
# +:parameter_name+ is ignored.
#
# This function returns an array of WashOut::Param objects.
def self.parse_def(soap_config, definition)
raise RuntimeError, "[] should not be used in your params. Use nil if you want to mark empty set." if definition == []
return [] if definition == nil

if definition.is_a?(Class) && definition.ancestors.include?(WashOut::Type)
definition = definition.wash_out_param_map
end

if [Array, Symbol].include?(definition.class)
definition = { :value => definition }
end

if definition.is_a? Hash
definition.map do |name, opt|
if opt.is_a? WashOut::Param
opt
elsif opt.is_a? Array
WashOut::Param.new(soap_config, name, opt[0], true)
else
WashOut::Param.new(soap_config, name, opt)
end
end
else
raise RuntimeError, "Wrong definition: #{definition.inspect}"
end
end

def flat_copy
copy = self.class.new(@soap_config, @name, @type.to_sym, @multiplied)
copy.raw_name = raw_name
copy.source_class = copy.source_class
copy
end

private

# Used to load an entire structure.
def map_struct(data)
unless data.is_a?(Hash)
raise Error, "SOAP message structure is broken"
end

data = data.with_indifferent_access
struct = {}.with_indifferent_access

# RUBY18 Enumerable#each_with_object is better, but 1.9 only.
@map.map do |param|
if data.has_key? param.raw_name
struct[param.raw_name] = yield param, data, param.raw_name
end
end

struct
end
end
end
end
11 changes: 4 additions & 7 deletions lib/sinatra/soap/wsdl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@ def self.actions
end

def self.register(name, *args, &block)
@@actions = {} if @@actions.nil?
@@actions[name] = {}
args = args.pop
unless args.nil?
args.each do |key, value|
@@actions[name][key] = value
end
args = args.pop || {}
args.each do |key, value|
@@actions[name][key] = value || {}
end
@@actions[name][:block] = block if block_given?
end
Expand All @@ -39,4 +36,4 @@ def all

end
end
end
end
21 changes: 15 additions & 6 deletions lib/sinatra/views/wsdl.builder
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/',
xml.tag! "schema", :targetNamespace => settings.namespace, :xmlns => 'http://www.w3.org/2001/XMLSchema' do
defined = []
wsdl.each do |operation, formats|
(formats[:in] + formats[:out]).each do |p|
formats[:in]||={}
formats[:out]||={}
formats[:in].each do |p|
wsdl_type xml, p, defined
end
formats[:out].each do |p|
wsdl_type xml, p, defined
end
end
Expand Down Expand Up @@ -49,20 +54,24 @@ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/',

xml.service :name => "service" do
xml.port :name => "#{settings.service}_port", :binding => "tns:#{settings.service}_binding" do
xml.tag! "soap:address", :location => send("#{settings.service}_action_url")
xml.tag! "soap:address", :location => "http://#{request.host_with_port}#{settings.endpoint}"
end
end

wsdl.each do |operation, formats|
xml.message :name => "#{operation}" do
formats[:in] ||= []
formats[:in].each do |p|
xml.part wsdl_occurence(p, false, :name => p.name, :type => p.namespaced_type)
xml.part wsdl_occurence(p, false)
#, :name => param.name, :type => param.namespaced_type)
end
end
xml.message :name => "#{operation}Response}" do
xml.message :name => "#{operation}Response" do
formats[:out] ||= []
formats[:out].each do |p|
xml.part wsdl_occurence(p, false, :name => p.name, :type => p.namespaced_type)
xml.part wsdl_occurence(p, false)
#, :name => param.name, :type => param.namespaced_type)
end
end
end
end
end

0 comments on commit 07170d5

Please sign in to comment.