sinatra-soap-current 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 38b98a95ec28ca97366c1bd815ed35b50a2b0546
4
+ data.tar.gz: 145e978d20e525cef7887fb3bf45c7c0f5f3daa6
5
+ SHA512:
6
+ metadata.gz: 17fe741df3d1bfbc0810f930b3b60c1001b9b4f9f5be032592b1729b217a4fd0795048fa7380198a406b2402f3f98869be06ce79603a0a5799f794aef092c912
7
+ data.tar.gz: 5db6d0bef29b7f61a33f33c1acb30f1c66295f43a59f8590214598a6bbf49bade640e62ff3b9692e38dad2ba745d89c597d1127807fb6efd61c2208e52f2c433
@@ -0,0 +1,7 @@
1
+ .rbx/
2
+ .bundle/
3
+ *.gem
4
+ .idea/
5
+ .rvmrc
6
+ *.swp*.gem
7
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,5 @@
1
+ before_install:
2
+ - gem install bundler
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sinatra-soap.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ivan Shamatov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,120 @@
1
+ # Sinatra::Soap
2
+
3
+ [![Build Status](https://travis-ci.org/ejhayes/sinatra-soap.svg?branch=master)](https://travis-ci.org/ejhayes/sinatra-soap) [![Maintainability](https://api.codeclimate.com/v1/badges/97acacfcc02aa7f03f6a/maintainability)](https://codeclimate.com/github/ejhayes/sinatra-soap/maintainability) [![Gem Version](https://badge.fury.io/rb/sinatra-soap-current.png)](http://badge.fury.io/rb/sinatra-soap-current)
4
+
5
+ Sinatra-soap gem makes task to create SOAP API really simple. Inspired by WashOut gem for Rails. But remember, the only reason why you should use SOAP is legacy code.
6
+
7
+
8
+ ## Overview
9
+
10
+ In case of simplicity and quick first working release:
11
+
12
+ ## Usage
13
+
14
+ Add the following to your gemfile:
15
+
16
+ ```ruby
17
+ gem 'sinatra-soap-current'
18
+ ```
19
+
20
+ A classic application would work like that:
21
+
22
+ ```ruby
23
+ require 'sinatra'
24
+ require 'sinatra/soap'
25
+
26
+ soap "SomeAction" do
27
+ do_something_with_params # hash to be returned
28
+ end
29
+ ```
30
+
31
+ A modular application would look like that:
32
+
33
+ ```ruby
34
+ require 'sinatra/base'
35
+ require 'sinatra/soap'
36
+
37
+ class SoapAPI < Sinatra::Base
38
+
39
+ #remember to register extenstion if you are using modular style
40
+ register Sinatra::Soap
41
+
42
+ soap "SomeAction" do
43
+ params # hash to be returned
44
+ end
45
+ end
46
+ ```
47
+
48
+
49
+ ## Settings
50
+
51
+ * **:wsdl_route** — url for getting wsdl, either static or dynamically generated file
52
+ ```ruby
53
+ set :wsdl_route, '/wsdl'
54
+ ```
55
+ Defines route for app to response with wsdl. Default is '/wsdl'
56
+
57
+
58
+ * **:endpoint** — url for sending SOAP Requests
59
+ ```ruby
60
+ set :endpoint, '/action'
61
+ ```
62
+ Defines route for SOAP Requests. Default is '/action'
63
+
64
+
65
+ * **:wsdl_file** — app will send static file, if this setting specified
66
+ ```ruby
67
+ set :wsdl_file, "wsdl.xml"
68
+ ```
69
+ If wsdl_file is set, app will try to read wsdl file from ```:public_folder``` (by default ./public directory). If file does not exist, app will raise an error. You also don't need to specify ```:namespace``` or ```:service``` if you want to serve static wsdl.
70
+
71
+
72
+ * **:namespace** — wsdl setting, required for generating wsdl
73
+ ```ruby
74
+ set :namespace, "http://schemas.xmlsoap.org/wsdl/"
75
+ ```
76
+ Namespace is taking it's place in ```xmlns:tns``` and ```targetNamespace``` definitions of SOAP Envelope
77
+
78
+ * **:service** — wsdl setting, required for generating wsdl
79
+ ```ruby
80
+ set :service, "sinatra"
81
+ ```
82
+ Service involved in ```portType```, ```binding``` and ```service``` definitions as a prefix for name attribute.
83
+
84
+
85
+
86
+
87
+ ## Soap Arguments
88
+
89
+ If you want to be able to generate wsdl on a fly, you need to specify incoming and outgoing nodes with their types.
90
+
91
+ ```ruby
92
+ soap :test, in: {circle: {center: {x: :integer,
93
+ y: :integer},
94
+ radius: :double}
95
+ },
96
+ out: nil do
97
+ params #=> {circle: {center: {x: 3, y: 2}, radius: 12.0} }
98
+ nil
99
+ end
100
+ ```
101
+
102
+ The code above will respond to request like this:
103
+ ```xml
104
+ <?xml version="1.0" encoding="UTF-8"?>
105
+ <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="anynamespacehere" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
106
+ <env:Body>
107
+ <wsdl:test>
108
+ <circle>
109
+ <center>
110
+ <x>3</x>
111
+ <y>2</y>
112
+ </center>
113
+ <radius>12.0</radius>
114
+ </circle>
115
+ </wsdl:test>
116
+ </env:Body>
117
+ </env:Envelope>
118
+ ```
119
+
120
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'sinatra-soap-current'
4
+ gem 'savon', '~> 2.0'
@@ -0,0 +1,26 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/soap'
3
+
4
+ class App < Sinatra::Base
5
+ register Sinatra::Soap
6
+
7
+ set :service, "sinatra"
8
+ set :namespace, "http://schemas.xmlsoap.org/wsdl/"
9
+ set :endpoint, '/action'
10
+ set :wsdl_route, '/wsdl'
11
+
12
+ #soap :test do
13
+ # params
14
+ #end
15
+
16
+ soap :test, in: { hello: :string }, out: nil do
17
+ params
18
+ end
19
+
20
+ soap :ObjectTest, in: { TestObject: { a: :string, b: :string }}, out: { hello: :string } do
21
+ params
22
+ end
23
+
24
+
25
+ end
26
+ App.run!
@@ -0,0 +1,16 @@
1
+ require "savon"
2
+
3
+ #client = Savon.client do
4
+ # endpoint "http://127.0.0.1:4567/action"
5
+ # namespace "http://schemas.xmlsoap.org/wsdl/"
6
+ #end
7
+
8
+
9
+ client = Savon.client(wsdl: "http://127.0.0.1:4567/wsdl")
10
+
11
+ puts client.operations
12
+
13
+ puts client.call(:test, message: { :hello => :world })
14
+ puts client.call(:test, message: { :should => :fail })
15
+
16
+
@@ -0,0 +1,38 @@
1
+ require "sinatra/base"
2
+ require "sinatra/soap/version"
3
+ require "sinatra/soap/wsdl"
4
+ require "sinatra/soap/error"
5
+ require "sinatra/soap/dsl_methods"
6
+ require "sinatra/soap/helper_methods"
7
+ require "sinatra/soap/request"
8
+ require "sinatra/soap/response"
9
+ require "builder"
10
+
11
+
12
+ module Sinatra
13
+ module Soap
14
+
15
+ include DslMethods
16
+
17
+ def self.registered(app)
18
+ app.helpers Soap::HelperMethods
19
+
20
+ app.set :wsdl_route, '/wsdl' unless defined?(app.settings.wsdl_path)
21
+ app.set :namespace, 'http://schemas.xmlsoap.org/wsdl/' unless defined?(app.settings.namespace)
22
+ app.set :endpoint, '/action' unless defined?(app.settings.endpoint)
23
+ app.set :service, 'Sinatra' unless defined?(app.settings.service)
24
+
25
+ app.post(app.settings.endpoint) do
26
+ content_type 'text/xml'
27
+ call_action_block
28
+ end
29
+
30
+ app.get(app.settings.wsdl_route) do
31
+ content_type 'text/xml'
32
+ get_wsdl
33
+ end
34
+ end
35
+ end
36
+ Delegator.delegate :soap
37
+ register Soap
38
+ end
@@ -0,0 +1,11 @@
1
+ module Sinatra
2
+ module Soap
3
+ module DslMethods
4
+
5
+ def soap(action, *args, &block)
6
+ Soap::Wsdl.register(action, *args, &block)
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Sinatra
2
+ module Soap
3
+ class Error < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,68 @@
1
+ require_relative 'param'
2
+
3
+ module Sinatra
4
+ module Soap
5
+ module HelperMethods
6
+
7
+ # Return the location where we can find our views
8
+ def soap_views()
9
+ File.join(File.dirname(__FILE__), "..", "views")
10
+ end
11
+
12
+ def call_action_block
13
+ request = Soap::Request.new(env, request, params)
14
+ response = request.execute
15
+ builder :response, locals: {wsdl: response.wsdl, params: response.params}, :views => self.soap_views
16
+ rescue Soap::Error => e
17
+ builder :error, locals: {e: e}, :views => self.soap_views
18
+ end
19
+
20
+ def get_wsdl
21
+ if defined?(settings.wsdl_path)
22
+ path = File.join(settings.public_folder, settings.wsdl_path)
23
+ if File.exist?(path)
24
+ File.read(path)
25
+ else
26
+ raise "No wsdl file"
27
+ end
28
+ else
29
+ builder :wsdl, locals: {wsdl: Soap::Wsdl.actions}, :views => self.soap_views
30
+ end
31
+ end
32
+
33
+ def wsdl_occurence(param, inject, extend_with = {})
34
+ param=Param.new(param[0], param[1])
35
+ extend_with = { :name => param.name, :type => param.namespaced_type }
36
+ data = !param.multiplied ? {} : {
37
+ "#{'xsi:' if inject}minOccurs" => 0,
38
+ "#{'xsi:' if inject}maxOccurs" => 'unbounded'
39
+ }
40
+ extend_with.merge(data)
41
+ end
42
+
43
+ def wsdl_type(xml, param, defined=[])
44
+ param = Param.new(param[0], param[1])
45
+ more = []
46
+ if param.struct?
47
+ if !defined.include?(param.basic_type)
48
+ xml.tag! "xsd:complexType", :name => param.basic_type do
49
+ xml.tag! "xsd:sequence" do
50
+ param.map.each do |value|
51
+ param_value = Param.new(value[0], value[1])
52
+ more << value if param_value.struct?
53
+ xml.tag! "xsd:element", wsdl_occurence(value, false)
54
+ end
55
+ end
56
+ end
57
+ defined << param.basic_type
58
+ elsif !param.classified?
59
+ raise RuntimeError, "Duplicate use of `#{param.basic_type}` type name. Consider using classified types."
60
+ end
61
+ end
62
+ more.each do |p|
63
+ wsdl_type xml, p, defined
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,181 @@
1
+ module Sinatra
2
+ module Soap
3
+ class Param
4
+ attr_accessor :raw_name
5
+ attr_accessor :name
6
+ attr_accessor :map
7
+ attr_accessor :type
8
+ attr_accessor :multiplied
9
+ attr_accessor :value
10
+ attr_accessor :source_class
11
+
12
+ def initialize(name, type, multiplied = false)
13
+ type ||= {}
14
+ @name = name.to_s
15
+ @raw_name = name.to_s
16
+ @map = {}
17
+ @multiplied = multiplied
18
+
19
+ if type.is_a?(Symbol)
20
+ @type = type.to_s
21
+ elsif type.is_a?(Class)
22
+ @type = 'struct'
23
+ @source_class = type
24
+ @map = type
25
+ else
26
+ @type = 'struct'
27
+ @map = type
28
+ end
29
+ end
30
+
31
+ # Converts a generic externally derived Ruby value, such as String or
32
+ # Hash, to a native Ruby object according to the definition of this type.
33
+ def load(data, key)
34
+ if !data.has_key? key
35
+ raise Error, "Required SOAP parameter '#{key}' is missing"
36
+ end
37
+
38
+ data = data[key]
39
+ data = [data] if @multiplied && !data.is_a?(Array)
40
+
41
+ if struct?
42
+ data ||= {}
43
+ if @multiplied
44
+ data.map do |x|
45
+ map_struct x do |param, dat, elem|
46
+ param.load(dat, elem)
47
+ end
48
+ end
49
+ else
50
+ map_struct data do |param, dat, elem|
51
+ param.load(dat, elem)
52
+ end
53
+ end
54
+ else
55
+ operation = case type
56
+ when 'string'; :to_s
57
+ when 'integer'; :to_i
58
+ when 'double'; :to_f
59
+ when 'boolean'; lambda{|dat| dat === "0" ? false : !!dat}
60
+ when 'date'; :to_date
61
+ when 'datetime'; :to_datetime
62
+ when 'time'; :to_time
63
+ when 'base64Binary'; lambda{|dat| Base64.decode64(dat)}
64
+ else raise RuntimeError, "Invalid WashOut simple type: #{type}"
65
+ end
66
+
67
+ begin
68
+ if data.nil?
69
+ data
70
+ elsif @multiplied
71
+ return data.map{|x| x.send(operation)} if operation.is_a?(Symbol)
72
+ return data.map{|x| operation.call(x)} if operation.is_a?(Proc)
73
+ elsif operation.is_a? Symbol
74
+ data.send(operation)
75
+ else
76
+ operation.call(data)
77
+ end
78
+ rescue
79
+ raise Error, "Invalid SOAP parameter '#{key}' format"
80
+ end
81
+ end
82
+ end
83
+
84
+ # Checks if this Param defines a complex type.
85
+ def struct?
86
+ type == 'struct'
87
+ end
88
+
89
+ def classified?
90
+ !source_class.nil?
91
+ end
92
+
93
+ def basic_type
94
+ return name unless classified?
95
+ return source_class.wash_out_param_name(@soap_config)
96
+ end
97
+
98
+ def xsd_type
99
+ return 'int' if type.to_s == 'integer'
100
+ return 'dateTime' if type.to_s == 'datetime'
101
+ return type
102
+ end
103
+
104
+ # Returns a WSDL namespaced identifier for this type.
105
+ def namespaced_type
106
+ struct? ? "tns:#{basic_type}" : "xsd:#{xsd_type}"
107
+ end
108
+
109
+ # Parses a +definition+. The format of the definition is best described
110
+ # by the following BNF-like grammar.
111
+ #
112
+ # simple_type := :string | :integer | :double | :boolean
113
+ # nested_type := type_hash | simple_type | WashOut::Param instance
114
+ # type_hash := { :parameter_name => nested_type, ... }
115
+ # definition := [ WashOut::Param, ... ] |
116
+ # type_hash |
117
+ # simple_type
118
+ #
119
+ # If a simple type is passed as the +definition+, a single Param is returned
120
+ # with the +name+ set to "value".
121
+ # If a WashOut::Param instance is passed as a +nested_type+, the corresponding
122
+ # +:parameter_name+ is ignored.
123
+ #
124
+ # This function returns an array of WashOut::Param objects.
125
+ def self.parse_def(soap_config, definition)
126
+ raise RuntimeError, "[] should not be used in your params. Use nil if you want to mark empty set." if definition == []
127
+ return [] if definition == nil
128
+
129
+ if definition.is_a?(Class) && definition.ancestors.include?(WashOut::Type)
130
+ definition = definition.wash_out_param_map
131
+ end
132
+
133
+ if [Array, Symbol].include?(definition.class)
134
+ definition = { :value => definition }
135
+ end
136
+
137
+ if definition.is_a? Hash
138
+ definition.map do |name, opt|
139
+ if opt.is_a? WashOut::Param
140
+ opt
141
+ elsif opt.is_a? Array
142
+ WashOut::Param.new(soap_config, name, opt[0], true)
143
+ else
144
+ WashOut::Param.new(soap_config, name, opt)
145
+ end
146
+ end
147
+ else
148
+ raise RuntimeError, "Wrong definition: #{definition.inspect}"
149
+ end
150
+ end
151
+
152
+ def flat_copy
153
+ copy = self.class.new(@soap_config, @name, @type.to_sym, @multiplied)
154
+ copy.raw_name = raw_name
155
+ copy.source_class = copy.source_class
156
+ copy
157
+ end
158
+
159
+ private
160
+
161
+ # Used to load an entire structure.
162
+ def map_struct(data)
163
+ unless data.is_a?(Hash)
164
+ raise Error, "SOAP message structure is broken"
165
+ end
166
+
167
+ data = data.with_indifferent_access
168
+ struct = {}.with_indifferent_access
169
+
170
+ # RUBY18 Enumerable#each_with_object is better, but 1.9 only.
171
+ @map.map do |param|
172
+ if data.has_key? param.raw_name
173
+ struct[param.raw_name] = yield param, data, param.raw_name
174
+ end
175
+ end
176
+
177
+ struct
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,63 @@
1
+ require "nori"
2
+
3
+ module Sinatra
4
+ module Soap
5
+ class Request
6
+
7
+ attr_reader :wsdl, :action, :env, :request, :params
8
+
9
+ def initialize(env, request, params)
10
+ @env = env
11
+ @request = request
12
+ @params = params
13
+ parse_request
14
+ end
15
+
16
+
17
+ def execute
18
+ request_block = wsdl.block
19
+ response_hash = self.instance_eval(&request_block)
20
+ Soap::Response.new(wsdl, response_hash)
21
+ end
22
+
23
+ alias_method :orig_params, :params
24
+
25
+ def action
26
+ return orig_params[:action] unless orig_params[:action].nil?
27
+ orig_params[:action] = env['HTTP_SOAPACTION'].to_s.gsub(/^"(.*)"$/, '\1').to_sym
28
+ end
29
+
30
+
31
+ def params
32
+ return orig_params[:soap] unless orig_params[:soap].nil?
33
+ rack_input = env["rack.input"].read
34
+ env["rack.input"].rewind
35
+ orig_params[:soap] = nori.parse(rack_input)[:Envelope][:Body][action]
36
+ end
37
+
38
+
39
+ def wsdl
40
+ @wsdl = Soap::Wsdl.new(action)
41
+ end
42
+
43
+ private
44
+
45
+ def parse_request
46
+ action
47
+ params
48
+ end
49
+
50
+ def nori(snakecase=false)
51
+ Nori.new(
52
+ :strip_namespaces => true,
53
+ :advanced_typecasting => true,
54
+ :convert_tags_to => (
55
+ snakecase ? lambda { |tag| tag.snakecase.to_sym }
56
+ : lambda { |tag| tag.to_sym }
57
+ )
58
+ )
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ module Sinatra
2
+ module Soap
3
+ class Response
4
+
5
+ attr_reader :wsdl, :params
6
+
7
+ def initialize(wsdl, params)
8
+ @wsdl = wsdl
9
+ @params = params
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Sinatra
2
+ module Soap
3
+ VERSION = "0.1.8"
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ module Sinatra
2
+ module Soap
3
+ class Wsdl
4
+
5
+ @@actions = {}
6
+
7
+ def self.actions
8
+ @@actions
9
+ end
10
+
11
+ def self.register(name, *args, &block)
12
+ @@actions[name] = {}
13
+ args = args.pop || {}
14
+ args.each do |key, value|
15
+ @@actions[name][key] = value || {}
16
+ end
17
+ @@actions[name][:block] = block if block_given?
18
+ end
19
+
20
+ def self.generate
21
+ end
22
+
23
+ attr_accessor :action, :block, :arguments
24
+
25
+ def initialize(action)
26
+ data = all[action]
27
+ raise Soap::Error, "Undefined Soap Action" if data.nil?
28
+ @action = action
29
+ @block = data[:block]
30
+ @arguments = data.select {|k,v| k != :block}
31
+ end
32
+
33
+ def all
34
+ @@actions
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ xml.instruct!
2
+ xml.tag! 'soap:Envelope', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
3
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance' do
4
+ xml.tag! 'soap:Body' do
5
+ xml.tag! 'soap:Fault', :encodingStyle => 'http://schemas.xmlsoap.org/soap/encoding/' do
6
+ xml.faultcode 'Client'
7
+ xml.faultstring e.message
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ xml.instruct!
2
+ xml.tag! 'soap:Envelope', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
3
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance' do
4
+ xml.tag! 'soap:Body' do
5
+ xml.tag! "soap:#{wsdl.action}Response" do
6
+ params.each do |key, value|
7
+ xml.tag! key, value
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,77 @@
1
+ xml.instruct!
2
+ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/',
3
+ 'xmlns:tns' => settings.namespace,
4
+ 'xmlns:soap' => 'http://schemas.xmlsoap.org/wsdl/soap/',
5
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
6
+ 'xmlns:soap-enc' => 'http://schemas.xmlsoap.org/soap/encoding/',
7
+ 'xmlns:wsdl' => 'http://schemas.xmlsoap.org/wsdl/',
8
+ 'name' => settings.service,
9
+ 'targetNamespace' => settings.namespace do
10
+
11
+ xml.types do
12
+ xml.tag! "schema", :targetNamespace => settings.namespace, :xmlns => 'http://www.w3.org/2001/XMLSchema' do
13
+ defined = []
14
+ wsdl.each do |operation, formats|
15
+ formats[:in]||={}
16
+ formats[:out]||={}
17
+ formats[:in].each do |p|
18
+ wsdl_type xml, p, defined
19
+ end
20
+ formats[:out].each do |p|
21
+ wsdl_type xml, p, defined
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ xml.portType :name => "#{settings.service}_port" do
28
+ wsdl.keys.each do |operation|
29
+ xml.operation :name => operation do
30
+ xml.input :message => "tns:#{operation}"
31
+ xml.output :message => "tns:#{operation}Response"
32
+ end
33
+ end
34
+ end
35
+
36
+ xml.binding :name => "#{settings.service}_binding", :type => "tns:#{settings.service}_port" do
37
+ xml.tag! "soap:binding", :style => 'document', :transport => 'http://schemas.xmlsoap.org/soap/http'
38
+ wsdl.keys.each do |operation|
39
+ xml.operation :name => operation do
40
+ xml.tag! "soap:operation", :soapAction => operation
41
+ xml.input do
42
+ xml.tag! "soap:body",
43
+ :use => "literal",
44
+ :namespace => settings.namespace
45
+ end
46
+ xml.output do
47
+ xml.tag! "soap:body",
48
+ :use => "literal",
49
+ :namespace => settings.namespace
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ xml.service :name => "service" do
56
+ xml.port :name => "#{settings.service}_port", :binding => "tns:#{settings.service}_binding" do
57
+ xml.tag! "soap:address", :location => "http://#{request.host_with_port}#{settings.endpoint}"
58
+ end
59
+ end
60
+
61
+ wsdl.each do |operation, formats|
62
+ xml.message :name => "#{operation}" do
63
+ formats[:in] ||= []
64
+ formats[:in].each do |p|
65
+ xml.part wsdl_occurence(p, false)
66
+ #, :name => param.name, :type => param.namespaced_type)
67
+ end
68
+ end
69
+ xml.message :name => "#{operation}Response" do
70
+ formats[:out] ||= []
71
+ formats[:out].each do |p|
72
+ xml.part wsdl_occurence(p, false)
73
+ #, :name => param.name, :type => param.namespaced_type)
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sinatra/soap/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sinatra-soap-current"
8
+ spec.version = Sinatra::Soap::VERSION
9
+ spec.authors = ["Ivan Shamatov"]
10
+ spec.email = ["eric@deployfx.com"]
11
+ spec.description = %q{Sinatra-soap gem makes task to create SOAP API really simple. Inspired by WashOut gem for Rails. But remember, the only reason why you should use SOAP is legacy code.}
12
+ spec.summary = %q{Handling SOAP requests for sinatra inspired by washout}
13
+ spec.homepage = "https://github.com/IvanShamatov/sinatra-soap"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rack-test"
25
+ spec.add_development_dependency "byebug"
26
+
27
+
28
+ spec.add_runtime_dependency "builder"
29
+ spec.add_runtime_dependency "sinatra"
30
+ spec.add_runtime_dependency "nori", ">= 2.0.0"
31
+ spec.add_runtime_dependency "nokogiri"
32
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Request" do
4
+ def app
5
+ SoapApp
6
+ end
7
+
8
+ before :each do
9
+ headers = {"HTTP_SOAPACTION" => 'test'}
10
+ message = '<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="any" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><wsdl:test><par>one</par><par2>bar</par2><foo>wat</foo></wsdl:test></env:Body></env:Envelope>'
11
+ post '/action', message, headers
12
+ @request = Sinatra::Soap::Request.new(last_request.env, last_request, last_request.params)
13
+ end
14
+
15
+ it "should get soap_action" do
16
+ expect(@request.action).to eq(:test)
17
+ end
18
+
19
+ it "should get soap arguments" do
20
+ expect(@request.params).to eq({par: "one", par2: "bar", foo: "wat"})
21
+ end
22
+
23
+ it "should build response" do
24
+ expect(@request.execute).to be_an_instance_of(Sinatra::Soap::Response)
25
+ end
26
+
27
+ #it "should validate input with WSDL" do
28
+ # pending
29
+ #end
30
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe 'A default soap sinatra application' do
5
+ def app
6
+ SoapApp
7
+ end
8
+
9
+ it "should parse soap request and send response" do
10
+ headers = {"HTTP_SOAPACTION" => 'test'}
11
+ message = '<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="any" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><wsdl:test><par>one</par><par2>bar</par2><foo>wat</foo></wsdl:test></env:Body></env:Envelope>'
12
+ post '/action', message, headers
13
+ response =<<-XML
14
+ <?xml version="1.0" encoding="UTF-8"?>
15
+ <soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
16
+ <soap:Body>
17
+ <soap:testResponse>
18
+ <par>one</par>
19
+ <par2>bar</par2>
20
+ <foo>wat</foo>
21
+ </soap:testResponse>
22
+ </soap:Body>
23
+ </soap:Envelope>
24
+ XML
25
+ expect(last_response.body).to eq(response)
26
+ end
27
+
28
+
29
+ it "should raise soap fault on unknown action" do
30
+ headers = {"HTTP_SOAPACTION" => 'test2'}
31
+ message = '<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="any" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><wsdl:test><par>one</par><par2>bar</par2><foo>wat</foo></wsdl:test></env:Body></env:Envelope>'
32
+ post '/action', message, headers
33
+ response =<<-XML
34
+ <?xml version="1.0" encoding="UTF-8"?>
35
+ <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
36
+ <soap:Body>
37
+ <soap:Fault encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
38
+ <faultcode>Client</faultcode>
39
+ <faultstring>Undefined Soap Action</faultstring>
40
+ </soap:Fault>
41
+ </soap:Body>
42
+ </soap:Envelope>
43
+ XML
44
+ expect(last_response.body).to eq(response)
45
+ end
46
+
47
+ it "should have endpoint for soap actions" do
48
+ endpoint = app.routes["POST"].select {|k| k[0].to_s.match('action')}.count
49
+ expect(endpoint).to eq 1
50
+ end
51
+
52
+ it "should have route for wsdl" do
53
+ wsdl = app.routes["GET"].select {|k| k[0].to_s.match('wsdl')}.count
54
+ expect(wsdl).to eq(1)
55
+ end
56
+
57
+ it "should return a usable soap views directory" do
58
+ view_search = File.join(app.views, "*.builder")
59
+ expect(Dir.glob(view_search).count).to be > 0
60
+ end
61
+
62
+ end
@@ -0,0 +1,24 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+ require File.join(File.join(File.expand_path(File.dirname(__FILE__))), '..', 'lib', 'sinatra', 'soap')
3
+ require 'rspec'
4
+ require 'rack/test'
5
+
6
+ class SoapApp < Sinatra::Base
7
+ register Sinatra::Soap
8
+ soap :test do
9
+ params
10
+ end
11
+
12
+ soap :add_circle, in: {circle: {center: {x: :integer, y: :integer},
13
+ radius: :double}},
14
+ out: nil do
15
+ params #=> {circle: {center: {x: 3, y: 2}, radius: 12.0} }
16
+ nil
17
+ end
18
+ end
19
+
20
+ module RSpecMixin
21
+ include Rack::Test::Methods
22
+ end
23
+
24
+ RSpec.configure { |c| c.include RSpecMixin }
@@ -0,0 +1,10 @@
1
+ xml.instruct!
2
+ xml.tag! 'soap:Envelope', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
3
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance' do
4
+ xml.tag! 'soap:Body' do
5
+ xml.tag! 'soap:Fault', :encodingStyle => 'http://schemas.xmlsoap.org/soap/encoding/' do
6
+ xml.faultcode 'Client'
7
+ xml.faultstring e.message
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ xml.instruct!
2
+ xml.tag! 'soap:Envelope', 'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/',
3
+ 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance' do
4
+ xml.tag! 'soap:Body' do
5
+ xml.tag! "soap:#{wsdl.action}Response" do
6
+ params.each do |key, value|
7
+ xml.tag! key, value
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,73 @@
1
+ xml.instruct!
2
+ xml.definitions 'xmlns' => 'http://schemas.xmlsoap.org/wsdl/',
3
+ 'xmlns:tns' => settings.namespace,
4
+ 'xmlns:soap' => 'http://schemas.xmlsoap.org/wsdl/soap/',
5
+ 'xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema',
6
+ 'xmlns:soap-enc' => 'http://schemas.xmlsoap.org/soap/encoding/',
7
+ 'xmlns:wsdl' => 'http://schemas.xmlsoap.org/wsdl/',
8
+ 'name' => settings.service,
9
+ 'targetNamespace' => settings.namespace do
10
+
11
+ xml.types do
12
+ xml.tag! "schema", :targetNamespace => settings.namespace, :xmlns => 'http://www.w3.org/2001/XMLSchema' do
13
+ defined = []
14
+ wsdl.each do |operation, formats|
15
+ formats[:in]||={}
16
+ formats[:out]||={}
17
+ formats[:in].each do |p|
18
+ wsdl_type xml, p, defined
19
+ end
20
+ formats[:out].each do |p|
21
+ wsdl_type xml, p, defined
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ xml.portType :name => "#{settings.service}_port" do
28
+ wsdl.keys.each do |operation|
29
+ xml.operation :name => operation do
30
+ xml.input :message => "tns:#{operation}"
31
+ xml.output :message => "tns:#{operation}Response"
32
+ end
33
+ end
34
+ end
35
+
36
+ xml.binding :name => "#{settings.service}_binding", :type => "tns:#{settings.service}_port" do
37
+ xml.tag! "soap:binding", :style => 'document', :transport => 'http://schemas.xmlsoap.org/soap/http'
38
+ wsdl.keys.each do |operation|
39
+ xml.operation :name => operation do
40
+ xml.tag! "soap:operation", :soapAction => operation
41
+ xml.input do
42
+ xml.tag! "soap:body",
43
+ :use => "literal",
44
+ :namespace => settings.namespace
45
+ end
46
+ xml.output do
47
+ xml.tag! "soap:body",
48
+ :use => "literal",
49
+ :namespace => settings.namespace
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ xml.service :name => "service" do
56
+ xml.port :name => "#{settings.service}_port", :binding => "tns:#{settings.service}_binding" do
57
+ xml.tag! "soap:address", :location => send("#{settings.service}_action_url")
58
+ end
59
+ end
60
+
61
+ wsdl.each do |operation, formats|
62
+ xml.message :name => "#{operation}" do
63
+ formats[:in].each do |p|
64
+ xml.part wsdl_occurence(p, false, :name => p.name, :type => p.namespaced_type)
65
+ end
66
+ end
67
+ xml.message :name => "#{operation}Response}" do
68
+ formats[:out].each do |p|
69
+ xml.part wsdl_occurence(p, false, :name => p.name, :type => p.namespaced_type)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe "WSDL" do
5
+
6
+ def wsdl
7
+ Sinatra::Soap::Wsdl
8
+ end
9
+
10
+ it "should hold registered actions with arguments and blocks" do
11
+ [:test, :add_circle].each do |action|
12
+ expect(wsdl.actions).to include(action)
13
+ end
14
+ end
15
+
16
+ it "should hold blocks for registered actions" do
17
+ [:test, :add_circle].each do |action|
18
+ expect(wsdl.actions[action]).to include(:block)
19
+ end
20
+ end
21
+
22
+ it "should hould arguments types" do
23
+ expect(wsdl.actions[:add_circle]).to include(:in)
24
+ expect(wsdl.actions[:add_circle]).to include(:out)
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,209 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-soap-current
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.8
5
+ platform: ruby
6
+ authors:
7
+ - Ivan Shamatov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: builder
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sinatra
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: nori
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: 2.0.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 2.0.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: nokogiri
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: Sinatra-soap gem makes task to create SOAP API really simple. Inspired
140
+ by WashOut gem for Rails. But remember, the only reason why you should use SOAP
141
+ is legacy code.
142
+ email:
143
+ - eric@deployfx.com
144
+ executables: []
145
+ extensions: []
146
+ extra_rdoc_files: []
147
+ files:
148
+ - ".gitignore"
149
+ - ".rspec"
150
+ - ".travis.yml"
151
+ - Gemfile
152
+ - LICENSE.txt
153
+ - README.md
154
+ - Rakefile
155
+ - examples/Gemfile
156
+ - examples/app.rb
157
+ - examples/client.rb
158
+ - lib/sinatra/soap.rb
159
+ - lib/sinatra/soap/dsl_methods.rb
160
+ - lib/sinatra/soap/error.rb
161
+ - lib/sinatra/soap/helper_methods.rb
162
+ - lib/sinatra/soap/param.rb
163
+ - lib/sinatra/soap/request.rb
164
+ - lib/sinatra/soap/response.rb
165
+ - lib/sinatra/soap/version.rb
166
+ - lib/sinatra/soap/wsdl.rb
167
+ - lib/sinatra/views/error.builder
168
+ - lib/sinatra/views/response.builder
169
+ - lib/sinatra/views/wsdl.builder
170
+ - sinatra-soap.gemspec
171
+ - spec/request_spec.rb
172
+ - spec/soap_spec.rb
173
+ - spec/spec_helper.rb
174
+ - spec/views/error.builder
175
+ - spec/views/response.builder
176
+ - spec/views/wsdl.builder
177
+ - spec/wsdl_spec.rb
178
+ homepage: https://github.com/IvanShamatov/sinatra-soap
179
+ licenses:
180
+ - MIT
181
+ metadata: {}
182
+ post_install_message:
183
+ rdoc_options: []
184
+ require_paths:
185
+ - lib
186
+ required_ruby_version: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ">="
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ required_rubygems_version: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ requirements: []
197
+ rubyforge_project:
198
+ rubygems_version: 2.5.2
199
+ signing_key:
200
+ specification_version: 4
201
+ summary: Handling SOAP requests for sinatra inspired by washout
202
+ test_files:
203
+ - spec/request_spec.rb
204
+ - spec/soap_spec.rb
205
+ - spec/spec_helper.rb
206
+ - spec/views/error.builder
207
+ - spec/views/response.builder
208
+ - spec/views/wsdl.builder
209
+ - spec/wsdl_spec.rb