sinatra-soap-current 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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