soaspec 0.2.23 → 0.2.24
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.
- checksums.yaml +4 -4
- data/.gitignore +15 -15
- data/.gitlab-ci.yml +33 -33
- data/.rspec +3 -3
- data/.rubocop.yml +2 -2
- data/CODE_OF_CONDUCT.md +74 -74
- data/ChangeLog +577 -573
- data/Gemfile +6 -6
- data/LICENSE.txt +21 -21
- data/README.md +230 -230
- data/Rakefile +42 -42
- data/Todo.md +15 -15
- data/exe/soaspec +123 -123
- data/exe/xml_to_yaml_file +42 -42
- data/lib/soaspec.rb +101 -101
- data/lib/soaspec/core_ext/hash.rb +35 -35
- data/lib/soaspec/cucumber/generic_steps.rb +85 -85
- data/lib/soaspec/demo.rb +4 -4
- data/lib/soaspec/exchange/exchange.rb +111 -111
- data/lib/soaspec/exchange/exchange_extractor.rb +83 -83
- data/lib/soaspec/exchange/exchange_properties.rb +26 -26
- data/lib/soaspec/exchange/exchange_repeater.rb +19 -19
- data/lib/soaspec/exchange/request_builder.rb +68 -68
- data/lib/soaspec/exchange/variable_storer.rb +22 -22
- data/lib/soaspec/exchange_handlers/exchange_handler.rb +126 -126
- data/lib/soaspec/exchange_handlers/handler_accessors.rb +130 -130
- data/lib/soaspec/exchange_handlers/response_extractor.rb +82 -82
- data/lib/soaspec/exchange_handlers/rest_exchanger_factory.rb +109 -109
- data/lib/soaspec/exchange_handlers/rest_handler.rb +259 -259
- data/lib/soaspec/exchange_handlers/rest_methods.rb +44 -44
- data/lib/soaspec/exchange_handlers/rest_parameters.rb +86 -86
- data/lib/soaspec/exchange_handlers/rest_parameters_defaults.rb +21 -21
- data/lib/soaspec/exchange_handlers/soap_handler.rb +235 -235
- data/lib/soaspec/exe_helpers.rb +92 -92
- data/lib/soaspec/generate_server.rb +37 -37
- data/lib/soaspec/generator/.rspec.erb +5 -5
- data/lib/soaspec/generator/.travis.yml.erb +5 -5
- data/lib/soaspec/generator/Gemfile.erb +8 -8
- data/lib/soaspec/generator/README.md.erb +29 -29
- data/lib/soaspec/generator/Rakefile.erb +19 -19
- data/lib/soaspec/generator/config/data/default.yml.erb +2 -2
- data/lib/soaspec/generator/css/bootstrap.css +6833 -6833
- data/lib/soaspec/generator/generate_exchange.html.erb +35 -35
- data/lib/soaspec/generator/lib/blz_service.rb.erb +26 -26
- data/lib/soaspec/generator/lib/dynamic_class_content.rb.erb +12 -12
- data/lib/soaspec/generator/lib/new_rest_service.rb.erb +51 -51
- data/lib/soaspec/generator/lib/new_soap_service.rb.erb +29 -29
- data/lib/soaspec/generator/lib/package_service.rb.erb +2 -2
- data/lib/soaspec/generator/lib/shared_example.rb.erb +8 -8
- data/lib/soaspec/generator/spec/dynamic_soap_spec.rb.erb +12 -12
- data/lib/soaspec/generator/spec/rest_spec.rb.erb +9 -9
- data/lib/soaspec/generator/spec/soap_spec.rb.erb +51 -51
- data/lib/soaspec/generator/spec/spec_helper.rb.erb +23 -23
- data/lib/soaspec/generator/template/soap_template.xml +6 -6
- data/lib/soaspec/indifferent_hash.rb +7 -7
- data/lib/soaspec/interpreter.rb +39 -39
- data/lib/soaspec/matchers.rb +114 -114
- data/lib/soaspec/not_found_errors.rb +13 -13
- data/lib/soaspec/o_auth2.rb +128 -128
- data/lib/soaspec/soaspec_shared_examples.rb +24 -24
- data/lib/soaspec/spec_logger.rb +121 -121
- data/lib/soaspec/template_reader.rb +28 -28
- data/lib/soaspec/test_server/bank.wsdl +90 -90
- data/lib/soaspec/test_server/get_bank.rb +164 -164
- data/lib/soaspec/test_server/id_manager.rb +39 -39
- data/lib/soaspec/test_server/invoices.rb +27 -27
- data/lib/soaspec/test_server/namespace.xml +14 -14
- data/lib/soaspec/test_server/note.xml +5 -5
- data/lib/soaspec/test_server/puppy_service.rb +19 -19
- data/lib/soaspec/test_server/test_attribute.rb +12 -12
- data/lib/soaspec/test_server/test_namespace.rb +12 -12
- data/lib/soaspec/version.rb +4 -3
- data/lib/soaspec/virtual_server.rb +174 -174
- data/lib/soaspec/wait.rb +41 -41
- data/lib/soaspec/wsdl_generator.rb +215 -215
- data/soaspec.gemspec +53 -53
- data/test.wsdl +116 -116
- data/test.xml +10 -10
- data/test_wsdl.rb +41 -41
- metadata +3 -4
@@ -1,23 +1,23 @@
|
|
1
|
-
|
2
|
-
require 'soaspec'
|
3
|
-
require 'require_all'
|
4
|
-
require_all 'lib'
|
5
|
-
require 'data_magic'
|
6
|
-
|
7
|
-
include DataMagic # Used as example of loading data smartly. Use 'data_for' method to load yml data
|
8
|
-
<% if @type == 'rest' %>
|
9
|
-
include Soaspec::RestMethods
|
10
|
-
<% end %>
|
11
|
-
|
12
|
-
RSpec.configure do |config|
|
13
|
-
# This will make backtrace much shorter by removing many lines from rspec failure message
|
14
|
-
config.backtrace_exclusion_patterns = [
|
15
|
-
/rspec/
|
16
|
-
]
|
17
|
-
<% if @virtual %>
|
18
|
-
# Close test server after all RSpec tests have run
|
19
|
-
config.after(:suite) do
|
20
|
-
Process.kill(:QUIT, ENV['test_server_pid'].to_i) if ENV['test_server_pid'] && !ENV['leave_server_running']
|
21
|
-
end
|
22
|
-
<% end %>
|
23
|
-
end
|
1
|
+
|
2
|
+
require 'soaspec'
|
3
|
+
require 'require_all'
|
4
|
+
require_all 'lib'
|
5
|
+
require 'data_magic'
|
6
|
+
|
7
|
+
include DataMagic # Used as example of loading data smartly. Use 'data_for' method to load yml data
|
8
|
+
<% if @type == 'rest' %>
|
9
|
+
include Soaspec::RestMethods
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
# This will make backtrace much shorter by removing many lines from rspec failure message
|
14
|
+
config.backtrace_exclusion_patterns = [
|
15
|
+
/rspec/
|
16
|
+
]
|
17
|
+
<% if @virtual %>
|
18
|
+
# Close test server after all RSpec tests have run
|
19
|
+
config.after(:suite) do
|
20
|
+
Process.kill(:QUIT, ENV['test_server_pid'].to_i) if ENV['test_server_pid'] && !ENV['leave_server_running']
|
21
|
+
end
|
22
|
+
<% end %>
|
23
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://thomas-bayer.com/blz/" xmlns:env="http://www.w3.org/2003/05/soap-envelope">
|
2
|
-
<env:Body>
|
3
|
-
<tns:getBank>
|
4
|
-
<tns:blz><%= test_values[:blz] || '70070010' %></tns:blz>
|
5
|
-
</tns:getBank>
|
6
|
-
</env:Body>
|
1
|
+
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://thomas-bayer.com/blz/" xmlns:env="http://www.w3.org/2003/05/soap-envelope">
|
2
|
+
<env:Body>
|
3
|
+
<tns:getBank>
|
4
|
+
<tns:blz><%= test_values[:blz] || '70070010' %></tns:blz>
|
5
|
+
</tns:getBank>
|
6
|
+
</env:Body>
|
7
7
|
</env:Envelope>
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require 'hashie'
|
2
|
-
|
3
|
-
# Hash that allows accessing hash with either string or Hash
|
4
|
-
class IndifferentHash < Hash
|
5
|
-
include Hashie::Extensions::MergeInitializer
|
6
|
-
include Hashie::Extensions::IndifferentAccess
|
7
|
-
end
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
# Hash that allows accessing hash with either string or Hash
|
4
|
+
class IndifferentHash < Hash
|
5
|
+
include Hashie::Extensions::MergeInitializer
|
6
|
+
include Hashie::Extensions::IndifferentAccess
|
7
|
+
end
|
data/lib/soaspec/interpreter.rb
CHANGED
@@ -1,39 +1,39 @@
|
|
1
|
-
# Help interpret the general type of a particular object
|
2
|
-
class Interpreter
|
3
|
-
class << self
|
4
|
-
# @param [Object] response API response
|
5
|
-
# @return [Symbol] Type of provided response
|
6
|
-
def response_type_for(response)
|
7
|
-
@response = response
|
8
|
-
if @response.is_a? String
|
9
|
-
if xml?
|
10
|
-
:xml
|
11
|
-
elsif json?
|
12
|
-
:json
|
13
|
-
else
|
14
|
-
:string
|
15
|
-
end
|
16
|
-
elsif response.is_a? Hash
|
17
|
-
:hash
|
18
|
-
elsif response.is_a?(Nokogiri::XML::NodeSet) || response.is_a?(Nokogiri::XML::Document)
|
19
|
-
:xml
|
20
|
-
else
|
21
|
-
:unknown
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [Boolean] Whether valid XML
|
26
|
-
def xml?
|
27
|
-
Nokogiri::XML(@response) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
|
28
|
-
rescue Nokogiri::XML::SyntaxError
|
29
|
-
false
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Boolean] Whether valid JSON
|
33
|
-
def json?
|
34
|
-
JSON.parse(@response)
|
35
|
-
rescue JSON::ParserError
|
36
|
-
false
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
1
|
+
# Help interpret the general type of a particular object
|
2
|
+
class Interpreter
|
3
|
+
class << self
|
4
|
+
# @param [Object] response API response
|
5
|
+
# @return [Symbol] Type of provided response
|
6
|
+
def response_type_for(response)
|
7
|
+
@response = response
|
8
|
+
if @response.is_a? String
|
9
|
+
if xml?
|
10
|
+
:xml
|
11
|
+
elsif json?
|
12
|
+
:json
|
13
|
+
else
|
14
|
+
:string
|
15
|
+
end
|
16
|
+
elsif response.is_a? Hash
|
17
|
+
:hash
|
18
|
+
elsif response.is_a?(Nokogiri::XML::NodeSet) || response.is_a?(Nokogiri::XML::Document)
|
19
|
+
:xml
|
20
|
+
else
|
21
|
+
:unknown
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean] Whether valid XML
|
26
|
+
def xml?
|
27
|
+
Nokogiri::XML(@response) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
|
28
|
+
rescue Nokogiri::XML::SyntaxError
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Boolean] Whether valid JSON
|
33
|
+
def json?
|
34
|
+
JSON.parse(@response)
|
35
|
+
rescue JSON::ParserError
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/soaspec/matchers.rb
CHANGED
@@ -1,114 +1,114 @@
|
|
1
|
-
require_relative 'core_ext/hash'
|
2
|
-
require_relative 'not_found_errors'
|
3
|
-
|
4
|
-
# Whether response has any element with the provided value
|
5
|
-
RSpec::Matchers.define :contain_value do |expected|
|
6
|
-
match do |actual|
|
7
|
-
expect(actual.exchange_handler.include_value?(actual.response, expected)).to be true
|
8
|
-
end
|
9
|
-
|
10
|
-
failure_message do |actual|
|
11
|
-
"expected that #{actual.exchange_handler.response_body(actual.response, format: :hash)} would contain value #{expected}"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
# Whether substring exists in body of response (more general than above)
|
16
|
-
RSpec::Matchers.define :include_in_body do |expected|
|
17
|
-
match do |actual|
|
18
|
-
expect(actual.exchange_handler.include_in_body?(actual.response, expected)).to be true
|
19
|
-
end
|
20
|
-
|
21
|
-
failure_message do |actual|
|
22
|
-
"expected that #{actual.exchange_handler.response_body(actual.response, format: :raw)} would contain value #{expected}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Whether an element exists at expected xpath
|
27
|
-
RSpec::Matchers.define :have_element_at_path do |xpath|
|
28
|
-
match do |object|
|
29
|
-
# Object like `response` returns the Exchange object from which a path can be obtained
|
30
|
-
exchange = object.respond_to?(:exchange) ? object.exchange : object
|
31
|
-
expect { exchange[xpath] }.to_not raise_error # Error will be raised if Path returns no value
|
32
|
-
end
|
33
|
-
|
34
|
-
# TODO: Would be better to print failure message
|
35
|
-
failure_message do |object|
|
36
|
-
# Object like `response` returns the Exchange object from which a path can be obtained
|
37
|
-
exchange = object.respond_to?(:exchange) ? object.exchange : object
|
38
|
-
"expected that #{exchange.exchange_handler.response_body(exchange.response, format: :raw)} would have element at path '#{xpath}'"
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
RSpec::Matchers.alias_matcher :have_element_at_xpath, :have_element_at_path
|
43
|
-
RSpec::Matchers.alias_matcher :contain_key, :have_element_at_path
|
44
|
-
|
45
|
-
# Whether an element at xpath (defined by key) has value (defined by value).
|
46
|
-
# @param [Hash] expected_hash Xpath => Value pair (e.g. '//xmlns:GetWeatherResult' => 'Data Not Found')
|
47
|
-
RSpec::Matchers.define :have_xpath_value do |expected_hash|
|
48
|
-
match do |object|
|
49
|
-
# Object like `response` returns the Exchange object from which a path can be obtained
|
50
|
-
exchange = object.respond_to?(:exchange) ? object.exchange : object
|
51
|
-
expected_hash = Hash[*expected_hash.flatten] if expected_hash.is_a?(Array) # For some reason Array was occuring
|
52
|
-
expect(exchange[expected_hash.keys.first]).to eq expected_hash.values.first
|
53
|
-
end
|
54
|
-
|
55
|
-
failure_message do |actual|
|
56
|
-
"expected that xpath '#{expected_hash.keys.first}' has value '#{expected_hash.values.first}' but was '#{actual[expected_hash.keys.first]}'"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
RSpec::Matchers.alias_matcher :have_jsonpath_value, :have_xpath_value
|
61
|
-
|
62
|
-
RSpec::Matchers.define :be_found do
|
63
|
-
match do |exchange|
|
64
|
-
expect(exchange.exchange_handler.found?(exchange.response)).to be true
|
65
|
-
end
|
66
|
-
|
67
|
-
failure_message do |exchange|
|
68
|
-
"expected result #{exchange.response} to be found. Status code is #{exchange.response.code}"
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Whether response has successful status code and correct mandatory elements and values
|
73
|
-
RSpec::Matchers.define :be_successful do
|
74
|
-
# @param [Exchange, RestClient::Response] actual Object that returns Exchange or is Exchange
|
75
|
-
# @return [Exchange] Exchange to use
|
76
|
-
def exchange_from(actual)
|
77
|
-
actual.respond_to?(:exchange) ? actual.exchange : actual
|
78
|
-
end
|
79
|
-
|
80
|
-
# @param [Exchange, RestClient::Response] exchange Object that returns Exchange or is Exchange
|
81
|
-
# @return [Array] List of errors when checking Exchange response is successful
|
82
|
-
def collect_errors(exchange)
|
83
|
-
failure_list = []
|
84
|
-
failure_list << "#{exchange.status_code} not valid status code" unless (200..299).cover?(exchange.status_code)
|
85
|
-
exchange.exchange_handler.expected_mandatory_elements.each do |mandatory_element_path|
|
86
|
-
begin
|
87
|
-
exchange[mandatory_element_path]
|
88
|
-
rescue NoElementAtPath => error
|
89
|
-
failure_list << error.message
|
90
|
-
end
|
91
|
-
end
|
92
|
-
exchange.exchange_handler.expected_mandatory_xpath_values.each do |path, value|
|
93
|
-
failure_list << "Expected value at xpath '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
|
94
|
-
end
|
95
|
-
exchange.exchange_handler.expected_mandatory_json_values.each do |path, value|
|
96
|
-
failure_list << "Expected value at json '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
|
97
|
-
end
|
98
|
-
failure_list
|
99
|
-
end
|
100
|
-
|
101
|
-
match do |actual|
|
102
|
-
exchange = exchange_from actual
|
103
|
-
failure_list = collect_errors exchange
|
104
|
-
raise failure_list.to_s unless failure_list.empty?
|
105
|
-
true
|
106
|
-
end
|
107
|
-
|
108
|
-
match_when_negated do |actual|
|
109
|
-
exchange = exchange_from actual
|
110
|
-
failure_list = collect_errors exchange
|
111
|
-
raise "Expected failure. Status code is #{exchange.status_code}" if failure_list.empty?
|
112
|
-
true
|
113
|
-
end
|
114
|
-
end
|
1
|
+
require_relative 'core_ext/hash'
|
2
|
+
require_relative 'not_found_errors'
|
3
|
+
|
4
|
+
# Whether response has any element with the provided value
|
5
|
+
RSpec::Matchers.define :contain_value do |expected|
|
6
|
+
match do |actual|
|
7
|
+
expect(actual.exchange_handler.include_value?(actual.response, expected)).to be true
|
8
|
+
end
|
9
|
+
|
10
|
+
failure_message do |actual|
|
11
|
+
"expected that #{actual.exchange_handler.response_body(actual.response, format: :hash)} would contain value #{expected}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Whether substring exists in body of response (more general than above)
|
16
|
+
RSpec::Matchers.define :include_in_body do |expected|
|
17
|
+
match do |actual|
|
18
|
+
expect(actual.exchange_handler.include_in_body?(actual.response, expected)).to be true
|
19
|
+
end
|
20
|
+
|
21
|
+
failure_message do |actual|
|
22
|
+
"expected that #{actual.exchange_handler.response_body(actual.response, format: :raw)} would contain value #{expected}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Whether an element exists at expected xpath
|
27
|
+
RSpec::Matchers.define :have_element_at_path do |xpath|
|
28
|
+
match do |object|
|
29
|
+
# Object like `response` returns the Exchange object from which a path can be obtained
|
30
|
+
exchange = object.respond_to?(:exchange) ? object.exchange : object
|
31
|
+
expect { exchange[xpath] }.to_not raise_error # Error will be raised if Path returns no value
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: Would be better to print failure message
|
35
|
+
failure_message do |object|
|
36
|
+
# Object like `response` returns the Exchange object from which a path can be obtained
|
37
|
+
exchange = object.respond_to?(:exchange) ? object.exchange : object
|
38
|
+
"expected that #{exchange.exchange_handler.response_body(exchange.response, format: :raw)} would have element at path '#{xpath}'"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
RSpec::Matchers.alias_matcher :have_element_at_xpath, :have_element_at_path
|
43
|
+
RSpec::Matchers.alias_matcher :contain_key, :have_element_at_path
|
44
|
+
|
45
|
+
# Whether an element at xpath (defined by key) has value (defined by value).
|
46
|
+
# @param [Hash] expected_hash Xpath => Value pair (e.g. '//xmlns:GetWeatherResult' => 'Data Not Found')
|
47
|
+
RSpec::Matchers.define :have_xpath_value do |expected_hash|
|
48
|
+
match do |object|
|
49
|
+
# Object like `response` returns the Exchange object from which a path can be obtained
|
50
|
+
exchange = object.respond_to?(:exchange) ? object.exchange : object
|
51
|
+
expected_hash = Hash[*expected_hash.flatten] if expected_hash.is_a?(Array) # For some reason Array was occuring
|
52
|
+
expect(exchange[expected_hash.keys.first]).to eq expected_hash.values.first
|
53
|
+
end
|
54
|
+
|
55
|
+
failure_message do |actual|
|
56
|
+
"expected that xpath '#{expected_hash.keys.first}' has value '#{expected_hash.values.first}' but was '#{actual[expected_hash.keys.first]}'"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
RSpec::Matchers.alias_matcher :have_jsonpath_value, :have_xpath_value
|
61
|
+
|
62
|
+
RSpec::Matchers.define :be_found do
|
63
|
+
match do |exchange|
|
64
|
+
expect(exchange.exchange_handler.found?(exchange.response)).to be true
|
65
|
+
end
|
66
|
+
|
67
|
+
failure_message do |exchange|
|
68
|
+
"expected result #{exchange.response} to be found. Status code is #{exchange.response.code}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Whether response has successful status code and correct mandatory elements and values
|
73
|
+
RSpec::Matchers.define :be_successful do
|
74
|
+
# @param [Exchange, RestClient::Response] actual Object that returns Exchange or is Exchange
|
75
|
+
# @return [Exchange] Exchange to use
|
76
|
+
def exchange_from(actual)
|
77
|
+
actual.respond_to?(:exchange) ? actual.exchange : actual
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param [Exchange, RestClient::Response] exchange Object that returns Exchange or is Exchange
|
81
|
+
# @return [Array] List of errors when checking Exchange response is successful
|
82
|
+
def collect_errors(exchange)
|
83
|
+
failure_list = []
|
84
|
+
failure_list << "#{exchange.status_code} not valid status code" unless (200..299).cover?(exchange.status_code)
|
85
|
+
exchange.exchange_handler.expected_mandatory_elements.each do |mandatory_element_path|
|
86
|
+
begin
|
87
|
+
exchange[mandatory_element_path]
|
88
|
+
rescue NoElementAtPath => error
|
89
|
+
failure_list << error.message
|
90
|
+
end
|
91
|
+
end
|
92
|
+
exchange.exchange_handler.expected_mandatory_xpath_values.each do |path, value|
|
93
|
+
failure_list << "Expected value at xpath '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
|
94
|
+
end
|
95
|
+
exchange.exchange_handler.expected_mandatory_json_values.each do |path, value|
|
96
|
+
failure_list << "Expected value at json '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
|
97
|
+
end
|
98
|
+
failure_list
|
99
|
+
end
|
100
|
+
|
101
|
+
match do |actual|
|
102
|
+
exchange = exchange_from actual
|
103
|
+
failure_list = collect_errors exchange
|
104
|
+
raise failure_list.to_s unless failure_list.empty?
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
match_when_negated do |actual|
|
109
|
+
exchange = exchange_from actual
|
110
|
+
failure_list = collect_errors exchange
|
111
|
+
raise "Expected failure. Status code is #{exchange.status_code}" if failure_list.empty?
|
112
|
+
true
|
113
|
+
end
|
114
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
|
-
# Raised to represent when there's no element at an Xpath
|
2
|
-
class NoElementAtPath < StandardError
|
3
|
-
def initialize(msg = 'No element at path found')
|
4
|
-
super
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
# Did not find any element by provided key in the Hash
|
9
|
-
class NoElementInHash < StandardError
|
10
|
-
def initialize(msg = 'No element in Hash found')
|
11
|
-
super
|
12
|
-
end
|
13
|
-
end
|
1
|
+
# Raised to represent when there's no element at an Xpath
|
2
|
+
class NoElementAtPath < StandardError
|
3
|
+
def initialize(msg = 'No element at path found')
|
4
|
+
super
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# Did not find any element by provided key in the Hash
|
9
|
+
class NoElementInHash < StandardError
|
10
|
+
def initialize(msg = 'No element in Hash found')
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
data/lib/soaspec/o_auth2.rb
CHANGED
@@ -1,128 +1,128 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
|
-
module Soaspec
|
4
|
-
# Handles working with OAuth2
|
5
|
-
class OAuth2
|
6
|
-
# How often to refresh access token
|
7
|
-
@refresh_token = :always
|
8
|
-
# List of access tokens. They are mapped according to the OAuth parameters used
|
9
|
-
@access_tokens = {}
|
10
|
-
# List of instance URLs. They are mapped according to the OAuth parameters used
|
11
|
-
@instance_urls = {}
|
12
|
-
class << self
|
13
|
-
# Default token url used across entire suite
|
14
|
-
attr_accessor :token_url
|
15
|
-
# @attr [Symbol] refresh_token How often to refresh access token
|
16
|
-
# Values are:
|
17
|
-
# * :always - (Default) Request token from token url every time it is needed
|
18
|
-
# * :once - Request token once for the entire execution of the suite
|
19
|
-
attr_accessor :refresh_token
|
20
|
-
# @attr [Hash] access_tokens List of access tokens. They are mapped according to the OAuth parameters used
|
21
|
-
attr_accessor :access_tokens
|
22
|
-
# List of URLs to that define the instance of an application
|
23
|
-
attr_accessor :instance_urls
|
24
|
-
# Specify whether to see params sent to and retrieved from oauth. This will put password in log file, only recommended for debugging
|
25
|
-
attr_writer :debug_oauth
|
26
|
-
|
27
|
-
# @return [Boolean] Whether to see params sent to & received from oauth URL
|
28
|
-
def debug_oauth?
|
29
|
-
@debug_oauth || false
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# @attr [Hash] OAuth parameters
|
34
|
-
attr_accessor :params
|
35
|
-
# @attr [Integer] Count of tries to obtain access token
|
36
|
-
attr_accessor :retry_count
|
37
|
-
|
38
|
-
# @param [Hash] params_sent Parameters to make OAuth request
|
39
|
-
# @option params_sent [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
|
40
|
-
# @option params_sent [client_id] Client ID
|
41
|
-
# @option params_sent [client_secret] Client Secret
|
42
|
-
# @option params_sent [username] Username used in password grant
|
43
|
-
# @option params_sent [password] Password used in password grant
|
44
|
-
# @option params_sent [security_token] Security Token used in password grant
|
45
|
-
# @param [String] api_username Username to use which can be set by Soaspec::ExchangeHandler
|
46
|
-
def initialize(params_sent, api_username = nil)
|
47
|
-
self.retry_count = 0 # No initial tries at getting access token
|
48
|
-
params = params_sent.transform_keys_to_symbols
|
49
|
-
params[:token_url] ||= Soaspec::OAuth2.token_url
|
50
|
-
raise ArgumentError, 'client_id and client_secret not set' unless params[:client_id] && params[:client_secret]
|
51
|
-
raise ArgumentError, 'token_url mandatory' unless params[:token_url]
|
52
|
-
|
53
|
-
self.params = params
|
54
|
-
params[:username] = api_username || ERB.new(params[:username]).result(binding) if params[:username]
|
55
|
-
params[:security_token] = ERB.new(params[:security_token]).result(binding) if params[:security_token]
|
56
|
-
params[:token_url] = ERB.new(params[:token_url]).result(binding) if params[:token_url]
|
57
|
-
params[:password] = ERB.new(params[:password]).result(binding) if params[:password]
|
58
|
-
end
|
59
|
-
|
60
|
-
# Retrieve whether to debug oauth parameters based on global settings
|
61
|
-
# @return [Boolean] Whether to see params sent to & received from oauth URL
|
62
|
-
def debug_oauth?
|
63
|
-
self.class.debug_oauth?
|
64
|
-
end
|
65
|
-
|
66
|
-
# Retrieve instance_url according to access token response.
|
67
|
-
# Some applications have a different instance
|
68
|
-
# It's assumed this will be constant for a set of oauth parameters
|
69
|
-
# @return [String] Instance url
|
70
|
-
def instance_url
|
71
|
-
Soaspec::OAuth2.instance_urls[params] ||= response['instance_url']
|
72
|
-
end
|
73
|
-
|
74
|
-
# @return [String] Existing or new access token, dependent on refresh_token attribute
|
75
|
-
def access_token
|
76
|
-
Soaspec::SpecLogger.info request_message
|
77
|
-
case Soaspec::OAuth2.refresh_token
|
78
|
-
when :once
|
79
|
-
Soaspec::OAuth2.access_tokens[params] ||= response['access_token']
|
80
|
-
else # Default is :always
|
81
|
-
response['access_token']
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# @return [Hash] Hash containing access token parameters
|
86
|
-
def response
|
87
|
-
Soaspec::SpecLogger.info "using oauth_params: #{params}" if debug_oauth?
|
88
|
-
response = RestClient.post(params[:token_url], payload, cache_control: 'no_cache', verify_ssl: false)
|
89
|
-
rescue RestClient::Exception => error
|
90
|
-
Soaspec::SpecLogger.info(["oauth_error: #{error.message}", "oauth_response: #{error.response}"])
|
91
|
-
self.retry_count += 1
|
92
|
-
sleep 0.1 # Wait if a bit before retying obtaining access token
|
93
|
-
retry if retry_count < 3
|
94
|
-
raise error
|
95
|
-
else
|
96
|
-
Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response.body}"]) if debug_oauth?
|
97
|
-
JSON.parse(response)
|
98
|
-
end
|
99
|
-
|
100
|
-
# @return [String] String to represent OAuth for logging logs
|
101
|
-
def request_message
|
102
|
-
if debug_oauth?
|
103
|
-
"request_params: #{payload}"
|
104
|
-
else
|
105
|
-
params[:username] ? "User '#{params[:username]}'" : 'client_credentials'
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# @return [String] Password to use in OAuth request
|
110
|
-
def password
|
111
|
-
params[:security_token] ? (params[:password] + params[:security_token]) : params[:password]
|
112
|
-
end
|
113
|
-
|
114
|
-
# Payload to add to o-auth request dependent on params provided
|
115
|
-
# @return [Hash] Payload for retrieving OAuth access token
|
116
|
-
def payload
|
117
|
-
payload = { client_id: params[:client_id], client_secret: params[:client_secret] }
|
118
|
-
payload.merge(if params[:password] && params[:username]
|
119
|
-
{
|
120
|
-
grant_type: 'password', username: params[:username],
|
121
|
-
password: password, multipart: true
|
122
|
-
}
|
123
|
-
else
|
124
|
-
{ grant_type: 'client_credentials' }
|
125
|
-
end)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Soaspec
|
4
|
+
# Handles working with OAuth2
|
5
|
+
class OAuth2
|
6
|
+
# How often to refresh access token
|
7
|
+
@refresh_token = :always
|
8
|
+
# List of access tokens. They are mapped according to the OAuth parameters used
|
9
|
+
@access_tokens = {}
|
10
|
+
# List of instance URLs. They are mapped according to the OAuth parameters used
|
11
|
+
@instance_urls = {}
|
12
|
+
class << self
|
13
|
+
# Default token url used across entire suite
|
14
|
+
attr_accessor :token_url
|
15
|
+
# @attr [Symbol] refresh_token How often to refresh access token
|
16
|
+
# Values are:
|
17
|
+
# * :always - (Default) Request token from token url every time it is needed
|
18
|
+
# * :once - Request token once for the entire execution of the suite
|
19
|
+
attr_accessor :refresh_token
|
20
|
+
# @attr [Hash] access_tokens List of access tokens. They are mapped according to the OAuth parameters used
|
21
|
+
attr_accessor :access_tokens
|
22
|
+
# List of URLs to that define the instance of an application
|
23
|
+
attr_accessor :instance_urls
|
24
|
+
# Specify whether to see params sent to and retrieved from oauth. This will put password in log file, only recommended for debugging
|
25
|
+
attr_writer :debug_oauth
|
26
|
+
|
27
|
+
# @return [Boolean] Whether to see params sent to & received from oauth URL
|
28
|
+
def debug_oauth?
|
29
|
+
@debug_oauth || false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @attr [Hash] OAuth parameters
|
34
|
+
attr_accessor :params
|
35
|
+
# @attr [Integer] Count of tries to obtain access token
|
36
|
+
attr_accessor :retry_count
|
37
|
+
|
38
|
+
# @param [Hash] params_sent Parameters to make OAuth request
|
39
|
+
# @option params_sent [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
|
40
|
+
# @option params_sent [client_id] Client ID
|
41
|
+
# @option params_sent [client_secret] Client Secret
|
42
|
+
# @option params_sent [username] Username used in password grant
|
43
|
+
# @option params_sent [password] Password used in password grant
|
44
|
+
# @option params_sent [security_token] Security Token used in password grant
|
45
|
+
# @param [String] api_username Username to use which can be set by Soaspec::ExchangeHandler
|
46
|
+
def initialize(params_sent, api_username = nil)
|
47
|
+
self.retry_count = 0 # No initial tries at getting access token
|
48
|
+
params = params_sent.transform_keys_to_symbols
|
49
|
+
params[:token_url] ||= Soaspec::OAuth2.token_url
|
50
|
+
raise ArgumentError, 'client_id and client_secret not set' unless params[:client_id] && params[:client_secret]
|
51
|
+
raise ArgumentError, 'token_url mandatory' unless params[:token_url]
|
52
|
+
|
53
|
+
self.params = params
|
54
|
+
params[:username] = api_username || ERB.new(params[:username]).result(binding) if params[:username]
|
55
|
+
params[:security_token] = ERB.new(params[:security_token]).result(binding) if params[:security_token]
|
56
|
+
params[:token_url] = ERB.new(params[:token_url]).result(binding) if params[:token_url]
|
57
|
+
params[:password] = ERB.new(params[:password]).result(binding) if params[:password]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Retrieve whether to debug oauth parameters based on global settings
|
61
|
+
# @return [Boolean] Whether to see params sent to & received from oauth URL
|
62
|
+
def debug_oauth?
|
63
|
+
self.class.debug_oauth?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Retrieve instance_url according to access token response.
|
67
|
+
# Some applications have a different instance
|
68
|
+
# It's assumed this will be constant for a set of oauth parameters
|
69
|
+
# @return [String] Instance url
|
70
|
+
def instance_url
|
71
|
+
Soaspec::OAuth2.instance_urls[params] ||= response['instance_url']
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [String] Existing or new access token, dependent on refresh_token attribute
|
75
|
+
def access_token
|
76
|
+
Soaspec::SpecLogger.info request_message
|
77
|
+
case Soaspec::OAuth2.refresh_token
|
78
|
+
when :once
|
79
|
+
Soaspec::OAuth2.access_tokens[params] ||= response['access_token']
|
80
|
+
else # Default is :always
|
81
|
+
response['access_token']
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [Hash] Hash containing access token parameters
|
86
|
+
def response
|
87
|
+
Soaspec::SpecLogger.info "using oauth_params: #{params}" if debug_oauth?
|
88
|
+
response = RestClient.post(params[:token_url], payload, cache_control: 'no_cache', verify_ssl: false)
|
89
|
+
rescue RestClient::Exception => error
|
90
|
+
Soaspec::SpecLogger.info(["oauth_error: #{error.message}", "oauth_response: #{error.response}"])
|
91
|
+
self.retry_count += 1
|
92
|
+
sleep 0.1 # Wait if a bit before retying obtaining access token
|
93
|
+
retry if retry_count < 3
|
94
|
+
raise error
|
95
|
+
else
|
96
|
+
Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response.body}"]) if debug_oauth?
|
97
|
+
JSON.parse(response)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [String] String to represent OAuth for logging logs
|
101
|
+
def request_message
|
102
|
+
if debug_oauth?
|
103
|
+
"request_params: #{payload}"
|
104
|
+
else
|
105
|
+
params[:username] ? "User '#{params[:username]}'" : 'client_credentials'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [String] Password to use in OAuth request
|
110
|
+
def password
|
111
|
+
params[:security_token] ? (params[:password] + params[:security_token]) : params[:password]
|
112
|
+
end
|
113
|
+
|
114
|
+
# Payload to add to o-auth request dependent on params provided
|
115
|
+
# @return [Hash] Payload for retrieving OAuth access token
|
116
|
+
def payload
|
117
|
+
payload = { client_id: params[:client_id], client_secret: params[:client_secret] }
|
118
|
+
payload.merge(if params[:password] && params[:username]
|
119
|
+
{
|
120
|
+
grant_type: 'password', username: params[:username],
|
121
|
+
password: password, multipart: true
|
122
|
+
}
|
123
|
+
else
|
124
|
+
{ grant_type: 'client_credentials' }
|
125
|
+
end)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|